1<?php
2
3/**
4 * When a component wants to integrate the assessment question service to present questions
5 * to users in an assessment scenario, the following use cases needs to be handled by the component.
6 *
7 * This kind consume of the assessment question service does not support the offline export presentation yet.
8 */
9class exTestPlayerGUI
10{
11    /**
12     * When presenting an assessment question to a user, the ilAsqQuestionPresentation provides the
13     * interface methods to either get a renerable UI coponent for a question presentation or
14     * for a solution presentation. An instance implementing the ilAsqPresentation according to
15     * the given question type is provided by a factory method within ilAsqFactory.
16     *
17     * Both methods getQuestionPresentation and getSolutionPresentation gets an instance of ilAsqSolution injected.
18     * This solution can either be an user solution or the best solution.
19     *
20     * Additional interface methods in ilAsqQuestionPresentation return renderable UI components for the generic
21     * and specific feedbacks. These methods also need to get an ilAsqSolution instance injected.
22     *
23     * In case of presenting the best solution the required ilAsqSolution instance can be retrieved
24     * from the ilAsqQuestion interface.
25     *
26     * For any existing user response that is to be presented with the question the instance
27     * implementing ilAsqSolution can be requested from the ilAsqFactory using the solutionId
28     * that is to be registered within the consuming component in relation to the user's id
29     * and additional information (like e.g. test results).
30     *
31     * Variants in usage defined by the consumer:
32     * - question can be shown writable for the examine
33     * - question can be shown readable for the examine
34     * - feedbacks can be shown if required
35     * - best solution can be shown if required
36     */
37    public function showQuestion()
38    {
39        global $DIC; /* @var ILIAS\DI\Container $DIC */
40
41        $questionId = 0; // initialise with id of question to be shown
42
43        /**
44         * fetch possibly existing participant solution, an empty one is required otherwise
45         */
46
47        $participantSolution = $this->getParticipantSolution($questionId);
48
49        /**
50         * question presentation to be answered by the examine
51         */
52
53        $questionInstance = $DIC->question()->getQuestionInstance($questionId);
54        $questionPresentationGUI = $DIC->question()->getQuestionPresentationInstance($questionInstance);
55
56        $questionNavigationAware; /* @var ilAsqQuestionNavigationAware $questionNavigationAware */
57        $questionPresentationGUI->setQuestionNavigation($questionNavigationAware);
58
59        $questionPresentationGUI->setRenderPurpose(ilAsqQuestionPresentation::RENDER_PURPOSE_PLAYBACK);
60
61        if ($participantSolutionLocked = false) {
62            $renderer = $questionPresentationGUI->getSolutionPresentation($participantSolution);
63        } else {
64            $renderer = $questionPresentationGUI->getQuestionPresentation($participantSolution);
65        }
66
67        $playerQstPageHTML = $renderer->getContent();
68
69        /**
70         * feedback presentation for the given
71         */
72
73        if ($showFeedbacks = true && !$participantSolution->isEmpty()) {
74            $genericFeedbackRenderer = $questionPresentationGUI->getGenericFeedbackOutput($participantSolution);
75            $playerQstPageHTML .= $genericFeedbackRenderer->getContent();
76
77            $specificFeedbackRenderer = $questionPresentationGUI->getSpecificFeedbackOutput($participantSolution);
78            $playerQstPageHTML .= $specificFeedbackRenderer->getContent();
79        }
80
81        /**
82         * best solution presentation to be answered by the examine
83         */
84
85        if ($showBestSolution = true) {
86            $renderer = $questionPresentationGUI->getSolutionPresentation(
87                $questionInstance->getBestSolution()
88            );
89
90            $playerQstPageHTML .= $renderer->getContent();
91        }
92
93        $playerQstPageHTML; // complete question page html
94    }
95
96    /**
97     * With the presentation of an assessment question, this question also gets submitted having any solution
98     * filled out by any user. With the first presentation there should be no previous user response available.
99     * The consuming component needs to request an empty ilAsqSolution instance for the given questionId.
100     *
101     * The ilAsqSolution interface method initFromServerRequest is to be used to initialize the object instance
102     * with the user response. With the current concept the newly introduced \Psr\Http\Message\ServerRequestInterface
103     * needs to be injected to this method, but may simply passing $_POST could be an alternative.
104     * This depends on the future strategy of abstracting the http server request in ILIAS.
105     *
106     * After having this solution saved, the consuming component needs to register the now available solutionId
107     * together with the questionId and the userId. Additionally this ilAsqSolution instance can be used
108     * with a question corresponding ilAsqResultCalculator to retrieve the information about right/wrong
109     * (used for e.g. answer status in CTM's test sequence) and reached points (used as a future ilTestResult)
110     * to be stored as any result within the consuming component.
111     *
112     * After the first submission of any user response the consuming component needs to provide the corresponsing
113     * solutionId to request the existing ilAsqSolution instance from the ilAsqFactory for every additional submit.
114     *
115     * The way harvesting and handling solution data in short:
116     * - post submit gets parsed by ilAsqSolution
117     * - ilAsqResultCalculator calculates points and right/wrong
118     * - the test object can use these information for different purposes
119     * - points can be saved as an ilTestResult referenced by the questionId and the participantId
120     * - right/wrong can be used for determining the correct feedbacks for the feedback loop
121     * - right/wrong can be used as the answer status within the CTM test sequence
122     */
123    public function submitSolution()
124    {
125        global $DIC; /* @var ILIAS\DI\Container $DIC */
126
127        // this can also be $_REQUEST or any other future ilias post-request handler
128        $serverRequestObject; /* @var \Psr\Http\Message\ServerRequestInterface $serverRequestObject */
129
130        $questionId = 0; // initialise with id of question that just submits
131
132        /**
133         * fetch possibly existing participant solution, an empty one is required otherwise
134         */
135
136        $participantSolution = $this->getParticipantSolution($questionId);
137
138        /**
139         * let the solution object instance harvest the submission post data
140         */
141
142        $participantSolution->initFromServerRequest($serverRequestObject);
143
144        /**
145         * get results calculator to be used to retrieve calculated reached points
146         * that can be stored in a test result storage managed by the test object
147         */
148
149        $questionInstance = $DIC->question()->getQuestionInstance($questionId);
150        $solutionInstance = $this->getParticipantSolution($questionId);
151        $resultCalculator = $DIC->question()->getResultCalculator($questionInstance, $solutionInstance);
152
153        $resultInstance = $resultCalculator->calculate();
154
155        /**
156         * handle the calculated result in any kind
157         */
158
159        // can be stored in any ilTestResult object managed by the test
160        $reachedPoints = $resultInstance->getPoints();
161
162        // can be used to differ answer status in CTM's test sequence
163        $isCorrect = $resultInstance->isCorrect();
164    }
165
166    /**
167     * this method returns either an initialised solution object instance, or and empty one,
168     * depending on self managed test results (handled by a future ilTestResult)
169     *
170     * @param integer $questionId
171     * @return ilAsqQuestionSolution
172     */
173    public function getParticipantSolution($questionId)
174    {
175        global $DIC; /* @var ILIAS\DI\Container $DIC */
176
177        /**
178         * when the test has any test result based on an existing participant solution,
179         * the solution id needs to be looked up. an empty solution is returned otherwise.
180         */
181
182        $solutionId = 0;
183
184        if ($solutionId) {
185            return $DIC->question()->getQuestionSolutionInstance($questionId, $solutionId);
186        }
187
188        return $DIC->question()->getEmptyQuestionSolutionInstance($questionId);
189    }
190}
191