ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
class.ilTestCorrectionsGUI.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
27 
37 {
38  private ?assQuestionGUI $question_gui = null;
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  {
149  $question_gui = $this->question_gui;
150  $form = $this->buildQuestionCorrectionForm($question_gui);
151  $form->setValuesByPost();
152 
153  if (!$form->checkInput()) {
154  $question_gui->prepareReprintableCorrectionsForm($form);
155 
156  $this->showQuestion($form);
157  return;
158  }
159 
160  $question_gui->saveCorrectionsFormProperties($form);
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  {
192  $question_gui = $this->question_gui;
193  $page_gui = new ilAssQuestionPageGUI($question_gui->getObject()->getId());
194  $page_gui->setRenderPageContainer(false);
195  $page_gui->setEditPreview(true);
196  $page_gui->setEnabledTabs(false);
197 
198  $solution_html = $question_gui->getSolutionOutput(
199  0,
200  null,
201  false,
202  false,
203  true,
204  false,
205  true,
206  false,
207  true
208  );
209 
210  $page_gui->setQuestionHTML([$question_gui->getObject()->getId() => $solution_html]);
211  $page_gui->setPresentationTitle($question_gui->getObject()->getTitleForHTMLOutput());
212 
213  $tpl = new ilTemplate('tpl.tst_corrections_solution_presentation.html', true, true, 'components/ILIAS/Test');
214  $tpl->setVariable('SOLUTION_PRESENTATION', $page_gui->preview());
215 
216  $this->setCorrectionTabsContext($question_gui, 'solution');
217  $this->populatePageTitleAndDescription($question_gui);
218 
219  $this->main_tpl->setContent($tpl->get());
220 
221  $this->main_tpl->setCurrentBlock("ContentStyle");
222  $this->main_tpl->setVariable(
223  "LOCATION_CONTENT_STYLESHEET",
225  );
226  $this->main_tpl->parseCurrentBlock();
227 
228  $this->main_tpl->setCurrentBlock("SyntaxStyle");
229  $this->main_tpl->setVariable(
230  "LOCATION_SYNTAX_STYLESHEET",
232  );
233  $this->main_tpl->parseCurrentBlock();
234  }
235 
239  protected function showAnswerStatistic(?array $participant_results = null): void
240  {
241  $solutions = $participant_results
242  ? $this->getSolutionsByParticipantResults($this->question_gui->getObject(), $participant_results)
243  : $this->getSolutions($this->question_gui->getObject());
244 
245  $this->setCorrectionTabsContext($this->question_gui, 'answers');
246 
247  $tablesHtml = '';
248 
249  foreach ($this->question_gui->getSubQuestionsIndex() as $subQuestionIndex) {
250  $table = $this->question_gui->getAnswerFrequencyTableGUI(
251  $this,
252  'showAnswerStatistic',
253  $solutions,
254  $subQuestionIndex
255  );
256 
257  $tablesHtml .= $table->getHTML() . $table->getAdditionalHtml();
258  }
259 
260  $this->populatePageTitleAndDescription($this->question_gui);
261  $this->main_tpl->setContent($tablesHtml);
262  }
263 
264  protected function addAnswer()
265  {
266  $form = (new ilAddAnswerFormBuilder(
267  $this->ui_factory,
268  $this->refinery,
269  $this->language,
270  $this->ctrl
271  ))->buildAddAnswerModal('')
272  ->withRequest($this->testrequest->getRequest());
273 
274  $data = $form->getData();
275 
276  $question_index = $data['question_index'];
277  $answer_value = $data['answer_value'];
278  $points = $data['points'];
279 
280  if (!$points) {
281  $this->main_tpl->setOnScreenMessage('failure', $this->language->txt('err_no_numeric_value'));
282  $this->showAnswerStatistic();
283  return;
284  }
285 
286  $question = $this->question_gui->getObject();
287  if ($question->isAddableAnswerOptionValue($question_index, $answer_value)) {
288  $question->addAnswerOptionValue($question_index, $answer_value, $points);
289  $question->saveToDb();
290  }
291 
292  $scoring = new TestScoring(
293  $this->test_obj,
294  $this->scorer,
295  $this->database,
296  $this->language
297  );
298  $scoring->setPreserveManualScores(true);
299  $scoring->setQuestionId($question_index);
300  $participant_results = $scoring->recalculateSolutions();
301 
302  if ($this->logger->isLoggingEnabled()) {
303  $this->logger->logQuestionAdministrationInteraction(
304  $question->toQuestionAdministrationInteraction(
305  $this->logger->getAdditionalInformationGenerator(),
306  $this->test_obj->getRefId(),
308  )
309  );
310  }
311 
312  $this->main_tpl->setOnScreenMessage('success', $this->language->txt('saved_successfully'));
313  $this->showAnswerStatistic($participant_results);
314  }
315 
316  protected function addHiddenItemsFromArray(ilConfirmationGUI $gui, $array, $curPath = [])
317  {
318  foreach ($array as $name => $value) {
319  if ($name == 'cmd' && !count($curPath)) {
320  continue;
321  }
322 
323  if (count($curPath)) {
324  $name = "[{$name}]";
325  }
326 
327  if (is_array($value)) {
328  $nextPath = array_merge($curPath, [$name]);
329  $this->addHiddenItemsFromArray($gui, $value, $nextPath);
330  } else {
331  $postVar = implode('', $curPath) . $name;
332  $gui->addHiddenItem($postVar, $value);
333  }
334  }
335  }
336 
337  protected function setCorrectionTabsContext(assQuestionGUI $question_gui, string $active_tab_id): void
338  {
339  $this->tabs->clearTargets();
340  $this->tabs->clearSubTabs();
341 
342  $this->help->setScreenIdComponent('tst');
343  $this->help->setScreenId('scoringadjust');
344  $this->help->setSubScreenId($active_tab_id);
345 
346  $this->tabs->addTab(
347  'question',
348  $this->language->txt('tst_corrections_tab_question'),
349  $this->ctrl->getLinkTargetByClass([ilObjTestGUI::class, self::class], 'showQuestion')
350  );
351 
352  $this->tabs->addTab(
353  'solution',
354  $this->language->txt('tst_corrections_tab_solution'),
355  $this->ctrl->getLinkTargetByClass([ilObjTestGUI::class, self::class], 'showSolution')
356  );
357 
358  if ($question_gui->isAnswerFrequencyStatisticSupported()) {
359  $this->tabs->addTab(
360  'answers',
361  $this->language->txt('tst_corrections_tab_statistics'),
362  $this->ctrl->getLinkTargetByClass([ilObjTestGUI::class, self::class], 'showAnswerStatistic')
363  );
364  }
365 
366  $this->tabs->setBackTarget(
367  $this->language->txt('back'),
368  $this->ctrl->getLinkTargetByClass(ilObjTestGUI::class, 'showQuestions')
369  );
370 
371  $this->tabs->activateTab($active_tab_id);
372  }
373 
374  protected function populatePageTitleAndDescription(assQuestionGUI $question_gui): void
375  {
376  $this->main_tpl->setTitle($question_gui->getObject()->getTitleForHTMLOutput());
377  $this->main_tpl->setDescription($question_gui->outQuestionType());
378  }
379 
380  protected function checkQuestion(): bool
381  {
382  if (!$this->test_obj->isTestQuestion($this->question_gui->getObject()->getId())) {
383  return false;
384  }
385 
386  if (!$this->supportsAdjustment($this->question_gui)) {
387  return false;
388  }
389 
390  return true;
391  }
392 
393  public function getRefId(): int
394  {
395  return $this->test_obj->getRefId();
396  }
397 
398  protected function getQuestionGUI(int $question_id): ?assQuestionGUI
399  {
400  $question_gui = assQuestion::instantiateQuestionGUI($question_id);
401  if ($question_gui === null) {
402  return null;
403  }
404  $question = $question_gui->getObject();
405  $question->setPoints($question_gui->getObject()->getMaximumPoints());
406  $question_gui->setObject($question);
407  return $question_gui;
408  }
409 
410  protected function getSolutions(assQuestion $question): array
411  {
412  $solution_rows = [];
413 
414  foreach (array_keys($this->test_obj->getParticipants()) as $active_id) {
415  $passes_selector = new ilTestPassesSelector($this->database, $this->test_obj);
416  $passes_selector->setActiveId($active_id);
417  $passes_selector->loadLastFinishedPass();
418 
419  foreach ($passes_selector->getClosedPasses() as $pass) {
420  foreach ($question->getSolutionValues($active_id, $pass) as $row) {
421  $solution_rows[] = $row;
422  }
423  }
424  }
425 
426  return $solution_rows;
427  }
428 
432  private function getSolutionsByParticipantResults(assQuestion $question, array $participant_results): array
433  {
434  $solutions = [];
435 
436  foreach ($participant_results as $active_id => $result) {
437  foreach ($result->getPasses() as $pass) {
438  foreach ($question->getSolutionValues($active_id, $pass->getPass()) as $row) {
439  $solutions[] = $row;
440  }
441  }
442  }
443 
444  return $solutions;
445  }
446 
447  protected function getQuestions(): array
448  {
449 
450  if (!$this->test_obj->getGlobalSettings()->isAdjustingQuestionsWithResultsAllowed()) {
451  return [];
452  }
453 
454  return array_reduce(
455  $this->test_obj->getTestQuestions(),
456  function (array $c, array $v): array {
457  $question_gui = $this->getQuestionGUI($v['question_id']);
458 
459  if (!$this->supportsAdjustment($question_gui)) {
460  return $c;
461  }
462 
463  $c[] = $v;
464  return $c;
465  },
466  []
467  );
468  }
469 
477  protected function supportsAdjustment(\assQuestionGUI $question_object): bool
478  {
479  return ($question_object instanceof ilGuiQuestionScoringAdjustable
480  || $question_object instanceof ilGuiAnswerScoringAdjustable)
481  && ($question_object->getObject() instanceof ilObjQuestionScoringAdjustable
482  || $question_object->getObject() instanceof ilObjAnswerScoringAdjustable);
483  }
484 }
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
getSolutionValues(int $active_id, ?int $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Help GUI class.
showAnswerStatistic(?array $participant_results=null)
setObject(assQuestion $question)
addHiddenItem(string $a_post_var, string $a_value)
$c
Definition: deliver.php:9
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
prepareReprintableCorrectionsForm(ilPropertyFormGUI $form)
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
__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,)
setRenderPageContainer(bool $a_val)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getSolutions(assQuestion $question)
setCorrectionTabsContext(assQuestionGUI $question_gui, string $active_tab_id)
static getContentStylePath(int $a_style_id, bool $add_random=true, bool $add_token=true)
get content style path static (to avoid full reading)
supportsAdjustment(\assQuestionGUI $question_object)
Returns if the given question object support scoring adjustment.
addHiddenItemsFromArray(ilConfirmationGUI $gui, $array, $curPath=[])
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...
getSolutionsByParticipantResults(assQuestion $question, array $participant_results)
buildQuestionCorrectionForm(assQuestionGUI $question_gui)
language()
description: > Example for rendring a language glyph.
Definition: language.php:25
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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)
populatePageTitleAndDescription(assQuestionGUI $question_gui)
showQuestion(ilPropertyFormGUI $form=null)