ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilTestCorrectionsGUI.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
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  {
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->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 
362  if ($question_gui->isAnswerFrequencyStatisticSupported()) {
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 
378  protected function populatePageTitleAndDescription(assQuestionGUI $question_gui): void
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 }
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:25
Question page GUI class.
showQuestion(?ilPropertyFormGUI $form=null)
prepareReprintableCorrectionsForm(ilPropertyFormGUI $form)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
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,)
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=[])
setFileDownloadLink(string $a_download_link)
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:41
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)