ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilTestCorrectionsGUI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
25use ILIAS\UI\Factory as UIFactory;
26use ILIAS\Refinery\Factory as RefineryFactory;
27
37{
40
41 public function __construct(
42 private readonly ilDBInterface $database,
43 private readonly ilCtrlInterface $ctrl,
44 private readonly ilLanguage $language,
45 private readonly ilTabsGUI $tabs,
46 private readonly ilHelpGUI $help,
47 private readonly UIFactory $ui_factory,
48 private readonly ilGlobalTemplateInterface $main_tpl,
49 private readonly RefineryFactory $refinery,
50 private readonly TestLogger $logger,
51 private readonly RequestDataCollector $testrequest,
52 private readonly ilObjTest $test_obj,
53 private readonly ilObjUser $scorer,
54 ) {
55 $question_id = $this->testrequest->getQuestionId();
56 if ($question_id !== 0) {
57 $this->question_gui = $this->getQuestionGUI($question_id);
58 }
59 $this->test_access = new ilTestAccess($test_obj->getRefId());
60 }
61
62 public function executeCommand()
63 {
64 if (!$this->test_access->checkCorrectionsAccess()
65 || $this->question_gui !== null
66 && !$this->checkQuestion()) {
67 ilObjTestGUI::accessViolationRedirect();
68 }
69
70 if ($this->testrequest->isset('removeQid') && (int) $this->testrequest->raw('removeQid')) {
71 $this->confirmQuestionRemoval();
72 return;
73 }
74
75 $this->ctrl->saveParameterByClass(self::class, 'q_id');
76 $command = $this->ctrl->getCmd('showQuestionList');
77 $this->{$command}();
78 }
79
80 protected function showQuestion(?ilPropertyFormGUI $form = null)
81 {
82 $this->setCorrectionTabsContext($this->question_gui, 'question');
83
84 $form ??= $this->buildQuestionCorrectionForm($this->question_gui);
85
86 $this->populatePageTitleAndDescription($this->question_gui);
87 $this->main_tpl->setContent($form->getHTML());
88 }
89
91 {
92 $form = new ilPropertyFormGUI();
93 $form->setFormAction($this->ctrl->getFormAction($this));
94 $form->setId('tst_question_correction');
95
96 $form->setTitle($this->language->txt('tst_corrections_qst_form'));
97
98 $question_gui->populateCorrectionsFormProperties($form);
99
100 $scoring = new TestScoring(
101 $this->test_obj,
102 $this->scorer,
103 $this->database,
104 $this->language
105 );
106 $scoring->setQuestionId($question_gui->getObject()->getId());
107
108 if ($scoring->getNumManualScorings()) {
109 $form->addCommandButton('confirmManualScoringReset', $this->language->txt('save'));
110 } else {
111 $form->addCommandButton('saveQuestion', $this->language->txt('save'));
112 }
113
114 return $form;
115 }
116
117 protected function confirmManualScoringReset()
118 {
119 $this->setCorrectionTabsContext($this->question_gui, 'question');
120
121 $scoring = new TestScoring(
122 $this->test_obj,
123 $this->scorer,
124 $this->database,
125 $this->language
126 );
127 $scoring->setQuestionId($this->question_gui->getObject()->getId());
128
129 $confirmation = sprintf(
130 $this->language->txt('tst_corrections_manscore_reset_warning'),
131 $scoring->getNumManualScorings(),
132 $this->question_gui->getObject()->getTitleForHTMLOutput(),
133 $this->question_gui->getObject()->getId()
134 );
135
136 $gui = new ilConfirmationGUI();
137 $gui->setHeaderText($confirmation);
138 $gui->setFormAction($this->ctrl->getFormAction($this));
139 $gui->setCancel($this->language->txt('cancel'), 'showQuestion');
140 $gui->setConfirm($this->language->txt('confirm'), 'saveQuestion');
141
142 $this->addHiddenItemsFromArray($gui, $this->testrequest->getParsedBody());
143
144 $this->main_tpl->setContent($gui->getHTML());
145 }
146
147 protected function saveQuestion()
148 {
150 $form = $this->buildQuestionCorrectionForm($question_gui);
151 $form->setValuesByPost();
152
153 if (!$form->checkInput()) {
155
156 $this->showQuestion($form);
157 return;
158 }
159
161 $question = $question_gui->getObject();
162 $question->setPoints($question_gui->getObject()->getMaximumPoints());
163 $question_gui->setObject($question);
164 $question_gui->getObject()->saveToDb();
165
166 $scoring = new TestScoring(
167 $this->test_obj,
168 $this->scorer,
169 $this->database,
170 $this->language
171 );
172 $scoring->setPreserveManualScores(false);
173 $scoring->setQuestionId($question_gui->getObject()->getId());
174 $scoring->recalculateSolutions();
175
176 if ($this->logger->isLoggingEnabled()) {
177 $this->logger->logQuestionAdministrationInteraction(
178 $question_gui->getObject()->toQuestionAdministrationInteraction(
179 $this->logger->getAdditionalInformationGenerator(),
180 $this->test_obj->getRefId(),
182 )
183 );
184 }
185
186 $this->main_tpl->setOnScreenMessage('success', $this->language->txt('saved_successfully'), true);
187 $this->ctrl->redirectByClass([ilObjTestGUI::class, self::class], 'showQuestion');
188 }
189
190 protected function showSolution()
191 {
193 $page_gui = new ilAssQuestionPageGUI($question_gui->getObject()->getId());
194 $page_gui->setFileDownloadLink(
195 $this->ctrl->getLinkTargetByClass(ilObjTestGUI::class, 'downloadFile')
196 );
197 $page_gui->setRenderPageContainer(false);
198 $page_gui->setEditPreview(true);
199 $page_gui->setEnabledTabs(false);
200
201 $solution_html = $question_gui->getSolutionOutput(
202 0,
203 null,
204 false,
205 false,
206 true,
207 false,
208 true,
209 false,
210 true
211 );
212
213 $page_gui->setQuestionHTML([$question_gui->getObject()->getId() => $solution_html]);
214 $page_gui->setPresentationTitle($question_gui->getObject()->getTitleForHTMLOutput());
215
216 $tpl = new ilTemplate('tpl.tst_corrections_solution_presentation.html', true, true, 'components/ILIAS/Test');
217 $tpl->setVariable('SOLUTION_PRESENTATION', $page_gui->preview());
218
219 $this->setCorrectionTabsContext($question_gui, 'solution');
220 $this->populatePageTitleAndDescription($question_gui);
221
222 $this->main_tpl->setContent($tpl->get());
223
224 $this->main_tpl->setCurrentBlock("ContentStyle");
225 $this->main_tpl->setVariable(
226 "LOCATION_CONTENT_STYLESHEET",
228 );
229 $this->main_tpl->parseCurrentBlock();
230
231 $this->main_tpl->setCurrentBlock("SyntaxStyle");
232 $this->main_tpl->setVariable(
233 "LOCATION_SYNTAX_STYLESHEET",
235 );
236 $this->main_tpl->parseCurrentBlock();
237 }
238
242 protected function showAnswerStatistic(?array $participant_results = null): void
243 {
244 $solutions = $participant_results
245 ? $this->getSolutionsByParticipantResults($this->question_gui->getObject(), $participant_results)
246 : $this->getSolutions($this->question_gui->getObject());
247
248 $this->setCorrectionTabsContext($this->question_gui, 'answers');
249
250 $tablesHtml = '';
251
252 foreach ($this->question_gui->getSubQuestionsIndex() as $subQuestionIndex) {
253 $table = $this->question_gui->getAnswerFrequencyTableGUI(
254 $this,
255 'showAnswerStatistic',
256 $solutions,
257 $subQuestionIndex
258 );
259
260 $tablesHtml .= $table->getHTML() . $table->getAdditionalHtml();
261 }
262
263 $this->populatePageTitleAndDescription($this->question_gui);
264 $this->main_tpl->setContent($tablesHtml);
265 }
266
267 protected function addAnswer()
268 {
269 $form = (new ilAddAnswerFormBuilder(
270 $this->ui_factory,
271 $this->refinery,
272 $this->language,
273 $this->ctrl
274 ))->buildAddAnswerModal('')
275 ->withRequest($this->testrequest->getRequest());
276
277 $data = $form->getData();
278
279 $question_index = $data['question_index'];
280 $answer_value = $data['answer_value'];
281 $points = $data['points'];
282
283 if (!$points) {
284 $this->main_tpl->setOnScreenMessage('failure', $this->language->txt('err_no_numeric_value'));
285 $this->showAnswerStatistic();
286 return;
287 }
288
289 $question = $this->question_gui->getObject();
290 if ($question->isAddableAnswerOptionValue($question_index, $answer_value)) {
291 $question->addAnswerOptionValue($question_index, $answer_value, $points);
292 $question->saveToDb();
293 }
294
295 $scoring = new TestScoring(
296 $this->test_obj,
297 $this->scorer,
298 $this->database,
299 $this->language
300 );
301 $scoring->setPreserveManualScores(true);
302 $scoring->setQuestionId($question_index);
303 $participant_results = $scoring->recalculateSolutions();
304
305 if ($this->logger->isLoggingEnabled()) {
306 $this->logger->logQuestionAdministrationInteraction(
307 $question->toQuestionAdministrationInteraction(
308 $this->logger->getAdditionalInformationGenerator(),
309 $this->test_obj->getRefId(),
311 )
312 );
313 }
314
315 $this->main_tpl->setOnScreenMessage('success', $this->language->txt('saved_successfully'));
316 $this->showAnswerStatistic($participant_results);
317 }
318
319 protected function addHiddenItemsFromArray(ilConfirmationGUI $gui, $array, $curPath = [])
320 {
321 foreach ($array as $name => $value) {
322 if ($name == 'cmd' && !count($curPath)) {
323 continue;
324 }
325
326 if (count($curPath)) {
327 $name = "[{$name}]";
328 }
329
330 if (is_array($value)) {
331 $nextPath = array_merge($curPath, [$name]);
332 $this->addHiddenItemsFromArray($gui, $value, $nextPath);
333 } else {
334 $postVar = implode('', $curPath) . $name;
335 $gui->addHiddenItem($postVar, $value);
336 }
337 }
338 }
339
340 protected function setCorrectionTabsContext(assQuestionGUI $question_gui, string $active_tab_id): void
341 {
342 $this->tabs->clearTargets();
343 $this->tabs->clearSubTabs();
344
345 $this->help->setScreenIdComponent('tst');
346 $this->help->setScreenId('scoringadjust');
347 $this->help->setSubScreenId($active_tab_id);
348
349 $this->ctrl->setParameterByClass(self::class, 'q_id', $question_gui->getObject()->getId());
350 $this->tabs->addTab(
351 'question',
352 $this->language->txt('tst_corrections_tab_question'),
353 $this->ctrl->getLinkTargetByClass([ilObjTestGUI::class, self::class], 'showQuestion')
354 );
355
356 $this->tabs->addTab(
357 'solution',
358 $this->language->txt('tst_corrections_tab_solution'),
359 $this->ctrl->getLinkTargetByClass([ilObjTestGUI::class, self::class], 'showSolution')
360 );
361
363 $this->tabs->addTab(
364 'answers',
365 $this->language->txt('tst_corrections_tab_statistics'),
366 $this->ctrl->getLinkTargetByClass([ilObjTestGUI::class, self::class], 'showAnswerStatistic')
367 );
368 }
369
370 $this->tabs->setBackTarget(
371 $this->language->txt('back'),
372 $this->ctrl->getLinkTargetByClass(ilObjTestGUI::class, 'showQuestions')
373 );
374
375 $this->tabs->activateTab($active_tab_id);
376 }
377
379 {
380 $this->main_tpl->setTitle($question_gui->getObject()->getTitleForHTMLOutput());
381 $this->main_tpl->setDescription($question_gui->outQuestionType());
382 }
383
384 protected function checkQuestion(): bool
385 {
386 if (!$this->test_obj->isTestQuestion($this->question_gui->getObject()->getId())) {
387 return false;
388 }
389
390 if (!$this->supportsAdjustment($this->question_gui)) {
391 return false;
392 }
393
394 return true;
395 }
396
397 public function getRefId(): int
398 {
399 return $this->test_obj->getRefId();
400 }
401
402 protected function getQuestionGUI(int $question_id): ?assQuestionGUI
403 {
404 $question_gui = assQuestion::instantiateQuestionGUI($question_id);
405 if ($question_gui === null) {
406 return null;
407 }
408 $question = $question_gui->getObject();
409 $question->setPoints($question_gui->getObject()->getMaximumPoints());
410 $question_gui->setObject($question);
411 return $question_gui;
412 }
413
414 protected function getSolutions(assQuestion $question): array
415 {
416 $solution_rows = [];
417
418 foreach (array_keys($this->test_obj->getParticipants()) as $active_id) {
419 $passes_selector = new ilTestPassesSelector($this->database, $this->test_obj);
420 $passes_selector->setActiveId($active_id);
421 $passes_selector->loadLastFinishedPass();
422
423 foreach ($passes_selector->getClosedPasses() as $pass) {
424 foreach ($question->getSolutionValues($active_id, $pass) as $row) {
425 $solution_rows[] = $row;
426 }
427 }
428 }
429
430 return $solution_rows;
431 }
432
436 private function getSolutionsByParticipantResults(assQuestion $question, array $participant_results): array
437 {
438 $solutions = [];
439
440 foreach ($participant_results as $active_id => $result) {
441 foreach ($result->getPasses() as $pass) {
442 foreach ($question->getSolutionValues($active_id, $pass->getPass()) as $row) {
443 $solutions[] = $row;
444 }
445 }
446 }
447
448 return $solutions;
449 }
450
451 protected function getQuestions(): array
452 {
453
454 if (!$this->test_obj->getGlobalSettings()->isAdjustingQuestionsWithResultsAllowed()) {
455 return [];
456 }
457
458 return array_reduce(
459 $this->test_obj->getTestQuestions(),
460 function (array $c, array $v): array {
461 $question_gui = $this->getQuestionGUI($v['question_id']);
462
463 if (!$this->supportsAdjustment($question_gui)) {
464 return $c;
465 }
466
467 $c[] = $v;
468 return $c;
469 },
470 []
471 );
472 }
473
481 protected function supportsAdjustment(\assQuestionGUI $question_object): bool
482 {
483 return ($question_object instanceof ilGuiQuestionScoringAdjustable
484 || $question_object instanceof ilGuiAnswerScoringAdjustable)
485 && ($question_object->getObject() instanceof ilObjQuestionScoringAdjustable
486 || $question_object->getObject() instanceof ilObjAnswerScoringAdjustable);
487 }
488}
Builds a Color from either hex- or rgb values.
Definition: Factory.php:31
Builds data types.
Definition: Factory.php:36
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
prepareReprintableCorrectionsForm(ilPropertyFormGUI $form)
setObject(assQuestion $question)
getSolutionOutput(int $active_id, ?int $pass=null, bool $graphical_output=false, bool $result_output=false, bool $show_question_only=true, bool $show_feedback=false, bool $show_correct_solution=false, bool $show_manual_scoring=false, bool $show_question_text=true, bool $show_inline_feedback=true)
getSolutionValues(int $active_id, ?int $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
Question page GUI class.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
addHiddenItem(string $a_post_var, string $a_value)
Help GUI class.
language handling
static getContentStylePath(int $a_style_id, bool $add_random=true, bool $add_token=true)
get content style path static (to avoid full reading)
User class.
This class represents a property form user interface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
special template class to simplify handling of ITX/PEAR
buildQuestionCorrectionForm(assQuestionGUI $question_gui)
getSolutionsByParticipantResults(assQuestion $question, array $participant_results)
supportsAdjustment(\assQuestionGUI $question_object)
Returns if the given question object support scoring adjustment.
addHiddenItemsFromArray(ilConfirmationGUI $gui, $array, $curPath=[])
getSolutions(assQuestion $question)
populatePageTitleAndDescription(assQuestionGUI $question_gui)
showQuestion(?ilPropertyFormGUI $form=null)
showAnswerStatistic(?array $participant_results=null)
__construct(private readonly ilDBInterface $database, private readonly ilCtrlInterface $ctrl, private readonly ilLanguage $language, private readonly ilTabsGUI $tabs, private readonly ilHelpGUI $help, private readonly UIFactory $ui_factory, private readonly ilGlobalTemplateInterface $main_tpl, private readonly RefineryFactory $refinery, private readonly TestLogger $logger, private readonly RequestDataCollector $testrequest, private readonly ilObjTest $test_obj, private readonly ilObjUser $scorer,)
setCorrectionTabsContext(assQuestionGUI $question_gui, string $active_tab_id)
$c
Definition: deliver.php:25
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Interface ilDBInterface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...