ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.ilTestEvaluationGUI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22use ILIAS\Test\Results\Presentation\TitlesBuilder as ResultsTitlesBuilder;
25use ILIAS\UI\Component\ViewControl\Mode as ViewControlMode;
26use ILIAS\UI\Component\Link\Standard as StandardLink;
27use ILIAS\UI\Component\Panel\Sub as SubPanel;
29
49{
50 private const DEFAULT_CMD = 'outUserListOfAnswerPasses';
54
55 public function __construct(ilObjTest $object)
56 {
57 parent::__construct($object);
58 $this->participant_access_filter = new ilTestParticipantAccessFilterFactory($this->access);
59 $this->participant_repository = TestDIC::dic()['participant.repository'];
60
61 $this->processLockerFactory = new ilTestProcessLockerFactory(
62 new ilSetting('assessment'),
63 $this->db,
64 $this->object->getTestLogger()
65 );
66 }
67
68 public function getTestAccess(): ilTestAccess
69 {
70 return $this->testAccess;
71 }
72
73 public function setTestAccess($testAccess): void
74 {
75 $this->testAccess = $testAccess;
76 }
77
78 public function executeCommand()
79 {
80 $cmd = $this->ctrl->getCmd(self::DEFAULT_CMD);
81 $next_class = $this->ctrl->getNextClass($this);
82 $this->ctrl->saveParameter($this, 'sequence');
83 $this->ctrl->saveParameter($this, 'active_id');
84
85 switch ($next_class) {
86 case 'iltestpassdetailsoverviewtablegui':
87 $tableGUI = new ilTestPassDetailsOverviewTableGUI($this->ctrl, $this, 'outUserPassDetails');
88 $this->ctrl->forwardCommand($tableGUI);
89 break;
90
91 default:
92 if (in_array($cmd, ['excel_scored_test_run', 'excel_all_test_runs'])) {
93 $ret = $this->exportEvaluation($cmd);
94 } elseif (in_array($cmd, ['excel_all_test_runs_a'])) {
95 $ret = $this->exportAggregatedResults($cmd);
96 } else {
97 $ret = $this->$cmd();
98 }
99 break;
100 }
101 return $ret;
102 }
103
110 public function getEvaluationQuestionId($question_id, $original_id = '')
111 {
112 if ($original_id > 0) {
113 return $original_id;
114 } else {
115 return $question_id;
116 }
117 }
118
119 protected function setCss(): void
120 {
121 $this->tpl->addCss(ilUtil::getStyleSheetLocation('output', 'test_print.css'), 'print');
122 if ($this->object->getShowSolutionAnswersOnly()) {
123 $this->tpl->addCss(ilUtil::getStyleSheetLocation('output', 'test_print_hide_content.css'), 'print');
124 }
125 $this->tpl->addCss(ilObjStyleSheet::getContentStylePath(0));
126 }
127
128 public function printResults(): void
129 {
130 $this->ctrl->saveParameterByClass(self::class, 'active_ids');
131 $this->global_screen->tool()->context()->current()->addAdditionalData(
132 PrintLayoutProvider::TEST_CONTEXT_PRINT,
133 true
134 );
135
136 $selected_active_ids = explode(',', $this->testrequest->strVal('active_ids'));
137 $results_panel = $this->ui_factory->panel()->report(
138 $this->lng->txt('tst_results'),
139 array_map(
140 function (string $v): SubPanel {
141 $value = (int) $v;
142 $attempt_id = ilObjTest::_getResultPass($value);
143 $components = $this->buildAttemptComponents($value, $attempt_id, false, true);
144 return $this->ui_factory->panel()->sub(
145 $this->buildResultsTitle($value, $attempt_id),
147 );
148 },
149 $selected_active_ids
150 )
151 );
152
153 $this->tpl->setVariable(
154 'ADM_CONTENT',
155 $this->ui_renderer->render([
156 $results_panel,
157 $this->ui_factory->legacy()->content('')->withAdditionalOnLoadCode(
158 fn(string $id): string => 'setTimeout(() => {window.print();}, 50)'
159 )
160 ])
161 );
162 }
163
164 public function showResults(): void
165 {
166 $this->setCss();
167 $this->ctrl->saveParameterByClass(self::class, 'active_ids');
168 $selected_active_ids = explode(',', $this->testrequest->strVal('active_ids'));
169
172
173 $current_active_id = (int) $selected_active_ids[0];
174 if (count($selected_active_ids) > 1
175 && ($selected_active_id = $this->testrequest->getActiveId()) > 0
176 && array_search($selected_active_id, $selected_active_ids) !== false) {
177 $current_active_id = $selected_active_id;
178 }
179
180 if ($this->testrequest->isset('attempt')) {
181 $attempt_id = min($this->testrequest->int('attempt'), ilObjTest::_getMaxPass($current_active_id));
182 } else {
183 $attempt_id = ilObjTest::_getResultPass($current_active_id);
184 }
185
186 $results_panel = $this->ui_factory->panel()->report(
187 $this->buildResultsTitle($current_active_id, $attempt_id),
188 $this->buildAttemptComponents($current_active_id, $attempt_id, true, false)
189 );
190
191 $attempts_ids_array = $this->results_data_factory->getAttemptIdsArrayFor(
192 $this->object,
193 $current_active_id
194 );
195
196 if (count($attempts_ids_array) > 1) {
197 $results_panel = $results_panel->withViewControls([
199 $attempts_ids_array,
200 ++$attempt_id
201 )
202 ]);
203 }
204
205 if (count($selected_active_ids) > 1) {
206 $this->addParticipantSelectorToToolbar($selected_active_ids, $current_active_id);
207 }
208
209 $this->tpl->setVariable(
210 'ADM_CONTENT',
211 $this->ui_renderer->render($results_panel)
212 );
213
214 $this->tabs->setBackTarget(
215 $this->lng->txt('back'),
216 $this->ctrl->getLinkTargetByClass(['ilTestParticipantsGUI'])
217 );
218 }
219
220 public function outUserPassDetails(): void
221 {
222 $this->tabs->clearSubTabs();
223 $this->tabs->setBackTarget($this->lng->txt('tst_results_back_overview'), $this->ctrl->getLinkTarget($this));
224
225 $test_session = $this->test_session_factory->getSession();
226
227 if (!$this->object->getShowPassDetails()) {
228 $this->ctrl->redirectByClass([ilRepositoryGUI::class, ilObjTestGUI::class, ilInfoScreenGUI::class]);
229 }
230
231 $active_id = $test_session->getActiveId();
232 $user_id = $test_session->getUserId();
233
234 $this->ctrl->saveParameter($this, 'pass');
235 $pass = $this->testrequest->int('pass');
236
237 $test_result_header_label_builder = new ResultsTitlesBuilder($this->lng, $this->obj_cache);
238
239 $objectives_list = null;
240
241 $consider_hidden_questions = true;
242 $consider_optional_questions = true;
243
244 if ($this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
245 $consider_hidden_questions = false;
246 $consider_optional_questions = true;
247
248 $test_sequence = $this->test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $pass);
249 $test_sequence->loadFromDb();
250 $test_sequence->loadQuestions();
251
252 if ($this->object->isRandomTest() && !$test_sequence->isAnsweringOptionalQuestionsConfirmed()) {
253 $consider_optional_questions = false;
254 }
255
256 $objectivesAdapter = ilLOTestQuestionAdapter::getInstance($test_session);
257
258 $objectives_list = $this->buildQuestionRelatedObjectivesList($objectivesAdapter, $test_sequence);
259 $objectives_list->loadObjectivesTitles();
260
261 $test_result_header_label_builder->setObjectiveOrientedContainerId($test_session->getObjectiveOrientedContainerId());
262 $test_result_header_label_builder->setUserId($test_session->getUserId());
263 $test_result_header_label_builder->setTestObjId($this->object->getId());
264 $test_result_header_label_builder->setTestRefId($this->object->getRefId());
265 $test_result_header_label_builder->initObjectiveOrientedMode();
266 }
267
268 $tpl = new ilTemplate('tpl.il_as_tst_pass_details_overview_participants.html', true, true, 'components/ILIAS/Test');
269
271
272 if ((new ilCertificateDownloadValidator())->isCertificateDownloadable($user_id, $this->object->getId())) {
274 }
275
276 $tpl->setCurrentBlock('signature');
277 $tpl->setVariable('SIGNATURE', $this->getResultsSignature());
279
280 if ($this->object->isShowExamIdInTestResultsEnabled()) {
281 if ($this->object->isShowExamIdInTestResultsEnabled()) {
283 $test_session->getActiveId(),
284 $pass
285 ));
286 $tpl->setVariable('EXAM_ID_TXT', $this->lng->txt('exam_id'));
287 }
288 }
289
290 if (!$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired() &&
291 $this->isGradingMessageRequired() && $this->object->getNrOfTries() == 1) {
292 $grading_message_builder = $this->getGradingMessageBuilder($active_id);
293 $grading_message_builder->buildMessage();
294 $grading_message_builder->sendMessage();
295 }
296
297 $data = $this->object->getCompleteEvaluationData();
298 $reached = $data->getParticipant($active_id)->getPass($pass)->getReachedPoints();
299 $max = $data->getParticipant($active_id)->getPass($pass)->getMaxPoints();
300 $percent = $max ? $reached / $max * 100.0 : 0;
301 $result = $data->getParticipant($active_id)->getPass($pass)->getReachedPoints() . ' ' . strtolower($this->lng->txt('of')) . ' ' . $data->getParticipant($active_id)->getPass($pass)->getMaxPoints() . ' (' . sprintf('%2.2f', $percent) . ' %' . ')';
302 $tpl->setCurrentBlock('total_score');
303 $tpl->setVariable('TOTAL_RESULT_TEXT', $this->lng->txt('tst_stat_result_resultspoints'));
304 $tpl->setVariable('TOTAL_RESULT', $result);
306
307 $tpl->setVariable('TEXT_RESULTS', $test_result_header_label_builder->getPassDetailsHeaderLabel($pass + 1));
308 $tpl->setVariable('FORMACTION', $this->ctrl->getFormAction($this));
309
310 $this->populateExamId($tpl, $active_id, (int) $pass);
311 $this->populatePassFinishDate($tpl, ilObjTest::lookupLastTestPassAccess($active_id, $pass));
312
313 $this->setCss();
314
315 $settings = $this->results_presentation_factory->getAttemptResultsSettings(
316 $this->object,
317 true
318 );
319 $table = $this->results_presentation_factory->getAttemptResultsPresentationTable(
320 $this->results_data_factory->getAttemptResultsFor(
321 $settings,
322 $this->object,
323 $active_id,
324 $pass,
325 true
326 ),
327 $settings,
328 $this->buildResultsTitle($active_id, $pass),
329 false
330 );
331
332 $tpl->setVariable('LIST_OF_ANSWERS', $table->render());
333
334 $this->tpl->addCss(ilObjStyleSheet::getContentStylePath(0));
335
336 $this->tpl->setContent(
337 $tpl->get()
338 );
339 }
340
341 public function outUserResultsOverview()
342 {
343 $test_session = $this->test_session_factory->getSession();
344 $active_id = $test_session->getActiveId();
345 $user_id = $this->user->getId();
346 $uname = $this->object->userLookupFullName($user_id, true);
347
348 if (!$this->object->canShowTestResults($test_session)) {
349 $this->ctrl->redirectByClass([ilRepositoryGUI::class, ilObjTestGUI::class, ilInfoScreenGUI::class]);
350 }
351
352 $templatehead = new ilTemplate('tpl.il_as_tst_results_participants.html', true, true, 'components/ILIAS/Test');
353 $template = new ilTemplate('tpl.il_as_tst_results_participant.html', true, true, 'components/ILIAS/Test');
354
356
357 if ((new ilCertificateDownloadValidator())->isCertificateDownloadable($user_id, $this->object->getId())) {
359 }
360
361 $test_result_header_label_builder = new ResultsTitlesBuilder($this->lng, $this->obj_cache);
362 if ($this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
363 $test_result_header_label_builder->setObjectiveOrientedContainerId($test_session->getObjectiveOrientedContainerId());
364 $test_result_header_label_builder->setUserId($test_session->getUserId());
365 $test_result_header_label_builder->setTestObjId($this->object->getId());
366 $test_result_header_label_builder->setTestRefId($this->object->getRefId());
367 $test_result_header_label_builder->initObjectiveOrientedMode();
368 }
369
370 $template->setCurrentBlock('pass_overview');
371
372 $test_passes_selector = new ilTestPassesSelector($this->db, $this->object);
373 $test_passes_selector->setActiveId($test_session->getActiveId());
374 $test_passes_selector->setLastFinishedPass($test_session->getLastFinishedPass());
375
376 $pass_overview_table_gui = $this->buildPassOverviewTableGUI($this);
377 $pass_overview_table_gui->setActiveId($test_session->getActiveId());
378 $pass_overview_table_gui->setResultPresentationEnabled(true);
379 if ($this->object->getShowPassDetails()) {
380 $pass_overview_table_gui->setPassDetailsCommand('outUserPassDetails');
381 }
382 if ($this->object->isPassDeletionAllowed()) {
383 $pass_overview_table_gui->setPassDeletionCommand('confirmDeletePass');
384 }
385 $pass_overview_table_gui->init();
386 $pass_overview_table_gui->setData($this->getPassOverviewTableData($test_session, $test_passes_selector->getReportablePasses(), true));
387 $pass_overview_table_gui->setTitle($test_result_header_label_builder->getPassOverviewHeaderLabel());
388 $overview = $pass_overview_table_gui->getHTML();
389 if ($this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
390 $lo_status = new ilTestLearningObjectivesStatusGUI(
391 $this->lng,
392 $this->ctrl,
393 $this->ui_factory,
394 $this->ui_renderer,
395 $this->testrequest
396 );
397 $lo_status->setCrsObjId($this->getObjectiveOrientedContainer()->getObjId());
398 $lo_status->setUsrId($test_session->getUserId());
399 $overview .= '<br />' . $lo_status->getHTML();
400 }
401 $template->setVariable('PASS_OVERVIEW', $overview);
402 $template->parseCurrentBlock();
403
404 if ($this->isGradingMessageRequired()) {
405 $grading_message_builder = $this->getGradingMessageBuilder($active_id);
406 $grading_message_builder->buildMessage();
407 $grading_message_builder->sendMessage();
408 }
409
410 $user_data = $this->getAdditionalUsrDataHtmlAndPopulateWindowTitle($test_session, $active_id, true);
411
412 if (!$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
413 if ($this->object->getAnonymity()) {
414 $template->setVariable('TEXT_HEADING', $this->lng->txt('tst_result'));
415 } else {
416 $template->setVariable('TEXT_HEADING', sprintf($this->lng->txt('tst_result_user_name'), $uname));
417 $template->setVariable('USER_DATA', $user_data);
418 }
419 }
420
421 $this->setCss();
422 $templatehead->setVariable('RESULTS_PARTICIPANT', $template->get());
423 $this->tpl->setContent($templatehead->get());
424 }
425
427 {
428 if (!$this->object->getShowSolutionPrintview()) {
429 $this->tpl->setOnScreenMessage('info', $this->lng->txt('no_permission'), true);
430 $this->ctrl->redirectByClass([ilRepositoryGUI::class, ilObjTestGUI::class, ilInfoScreenGUI::class]);
431 }
432
433 $template = new ilTemplate('tpl.il_as_tst_info_list_of_answers.html', true, true, 'components/ILIAS/Test');
434
435 $user_id = $this->user->getId();
436
437 $test_session = $this->test_session_factory->getSession();
438 $active_id = $test_session->getActiveId();
439
440 if ($this->testrequest->isset('attempt')) {
441 $attempt = $this->testrequest->int('attempt');
442 } else {
443 $attempt = \ilObjTest::_getResultPass($test_session->getActiveId());
444 }
445
446 $test_passes_selector = new ilTestPassesSelector($this->db, $this->object);
447 $test_passes_selector->setActiveId($test_session->getActiveId());
448 $test_passes_selector->setLastFinishedPass($test_session->getLastFinishedPass());
449
450 if (count($test_passes_selector->getClosedPasses()) > 1) {
451 $this->addAttemptSwitchingViewControlToToolbar($test_passes_selector->getClosedPasses(), $attempt);
452 }
453
455
456 $test_result_header_label_builder = new ResultsTitlesBuilder($this->lng, $this->obj_cache);
457 $test_result_header_label_builder->setAttemptLastAccessDate(
458 (new \DateTimeImmutable(
459 '@' . ilObjTest::lookupLastTestPassAccess($test_session->getActiveId(), $attempt)
460 ))->setTimezone(new \DateTimeZone($this->user->getTimeZone()))
461 ->format($this->user->getDateTimeFormat()->toString())
462 );
463
464 $objectives_list = null;
465 if ($this->getObjectiveOrientedContainer()?->isObjectiveOrientedPresentationRequired()) {
466 $test_sequence = $this->test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $attempt);
467 $test_sequence->loadFromDb();
468 $test_sequence->loadQuestions();
469
470 $objectivesAdapter = ilLOTestQuestionAdapter::getInstance($test_session);
471
472 $objectives_list = $this->buildQuestionRelatedObjectivesList($objectivesAdapter, $test_sequence);
473 $objectives_list->loadObjectivesTitles();
474
475 $test_result_header_label_builder->setObjectiveOrientedContainerId($test_session->getObjectiveOrientedContainerId());
476 $test_result_header_label_builder->setUserId($test_session->getUserId());
477 $test_result_header_label_builder->setTestObjId($this->object->getId());
478 $test_result_header_label_builder->setTestRefId($this->object->getRefId());
479 $test_result_header_label_builder->initObjectiveOrientedMode();
480 }
481
482 $result_array = $this->object->getTestResult(
483 $active_id,
484 $attempt,
485 false,
486 !$this->getObjectiveOrientedContainer()?->isObjectiveOrientedPresentationRequired()
487 );
488
489 $signature = $this->getResultsSignature();
490 $show_all_answers = true;
491 if ($this->object->isExecutable($test_session, $user_id)) {
492 $show_all_answers = false;
493 }
494 $this->setContextResultPresentation(false);
495 $answers = $this->getPassListOfAnswers(
496 $result_array,
497 $active_id,
498 $attempt,
499 false,
500 $show_all_answers,
501 false,
502 false,
503 false,
504 $objectives_list,
505 $test_result_header_label_builder
506 );
507 $template->setVariable('PASS_DETAILS', $answers);
508
509 $user_data = $this->getAdditionalUsrDataHtmlAndPopulateWindowTitle($test_session, $active_id, true);
510 $template->setVariable('USER_DATA', $user_data);
511 if (strlen($signature)) {
512 $template->setVariable('SIGNATURE', $signature);
513 }
514 if (!is_null($attempt) && $this->object->isShowExamIdInTestResultsEnabled()) {
515 $template->setCurrentBlock('exam_id_footer');
516 $template->setVariable('EXAM_ID_VAL', ilObjTest::lookupExamId(
517 $test_session->getActiveId(),
518 $attempt
519 ));
520 $template->setVariable('EXAM_ID_TXT', $this->lng->txt('exam_id'));
521 $template->parseCurrentBlock();
522 }
523
524 $this->setCss();
525 $this->tpl->setVariable('ADM_CONTENT', $template->get());
526 }
527
528 public function outCertificate()
529 {
530 $ilUserCertificateRepository = new ilUserCertificateRepository($this->db, $this->logger->getComponentLogger());
531 $pdfGenerator = new ilPdfGenerator($ilUserCertificateRepository);
532
533 $pdfAction = new ilCertificatePdfAction(
534 $pdfGenerator,
536 $this->lng->txt('error_creating_certificate_pdf')
537 );
538
539 $pdfAction->downloadPdf($this->user->getId(), $this->object->getId());
540 }
541
542 public function confirmDeletePass()
543 {
544 if ($this->testrequest->isset('context') && strlen($this->testrequest->raw('context'))) {
545 $context = $this->testrequest->raw('context');
546 } else {
548 }
549
550 if (!$this->object->isPassDeletionAllowed()) {
551 $this->redirectToPassDeletionContext($context);
552 }
553
554 $confirm = new ilTestPassDeletionConfirmationGUI($this->ctrl, $this->lng, $this);
555 $confirm->build($this->testrequest->getActiveId('active_id'), $this->testrequest->int('pass'), $context);
556
557 $this->tpl->setContent($this->ctrl->getHTML($confirm));
558 }
559
560 public function cancelDeletePass(): void
561 {
562 $this->redirectToPassDeletionContext($this->testrequest->strVal('context'));
563 }
564
565 private function redirectToPassDeletionContext(string $context): void
566 {
567 switch ($context) {
569
570 $this->ctrl->redirect($this);
571
572 // no break
574 $this->ctrl->redirectByClass([ilRepositoryGUI::class, ilObjTestGUI::class, ilInfoScreenGUI::class]);
575 }
576 }
577
578 public function performDeletePass(): void
579 {
580 $context = $this->testrequest->strVal('context') ?? ilTestPassDeletionConfirmationGUI::CONTEXT_PASS_OVERVIEW;
581 $active_fi = $this->testrequest->int('active_id');
582 $pass = $this->testrequest->int('pass');
583
584 if (!$this->object->isPassDeletionAllowed()) {
585 $this->redirectToPassDeletionContext($context);
586 }
587
589
590 if ($active_fi === 0 || !$this->testrequest->isset('pass')) {
591 $this->ctrl->redirect($this, 'outUserResultsOverview');
592 }
593
594 if ($pass === ilObjTest::_getResultPass($active_fi)) {
595 $this->ctrl->redirect($this, 'outUserResultsOverview');
596 }
597
598 // Get information
599 $result = $ilDB->query("
600 SELECT tst_active.tries, tst_active.last_finished_pass, tst_sequence.pass
601 FROM tst_active
602 LEFT JOIN tst_sequence
603 ON tst_sequence.active_fi = tst_active.active_id
604 AND tst_sequence.pass = tst_active.tries
605 WHERE tst_active.active_id = {$ilDB->quote($active_fi, 'integer')}
606 ");
607
608 $row = $ilDB->fetchAssoc($result);
609
610 $tries = $row['tries'];
611 $lastFinishedPass = is_numeric($row['last_finished_pass']) ? $row['last_finished_pass'] : -1;
612
613 if ($pass < $lastFinishedPass) {
614 $isActivePass = false;
615 $must_renumber = true;
616 } elseif ($pass == $lastFinishedPass) {
617 $isActivePass = false;
618
619 if ($tries == $row['pass']) {
620 $must_renumber = true;
621 } else {
622 $must_renumber = false;
623 }
624 } elseif ($pass == $row['pass']) {
625 $isActivePass = true;
626 $must_renumber = false;
627 } else {
628 throw new ilTestException('This should not happen, please contact Bjoern Heyser to clean up this pass salad!');
629 }
630
631 if ($isActivePass) {
632 $this->ctrl->redirect($this, 'outUserResultsOverview');
633 }
634
635 if ($pass == 0 && (
636 ($lastFinishedPass == 0 && $tries == 1 && $tries != $row['pass'])
637 || ($isActivePass == true) // should be equal to || ($lastFinishedPass == -1 && $tries == 0)
638 )) {
639 $last_pass = true;
640 } else {
641 $last_pass = false;
642 }
643
644 // Work on tables:
645 // tst_active
646 if ($last_pass) {
647 $ilDB->manipulate(
648 'DELETE
649 FROM tst_active
650 WHERE active_id = ' . $ilDB->quote($active_fi, 'integer')
651 );
652 } elseif (!$isActivePass) {
653 $ilDB->manipulate(
654 'UPDATE tst_active
655 SET tries = ' . $ilDB->quote($tries - 1, 'integer') . ',
656 last_finished_pass = ' . $ilDB->quote($lastFinishedPass - 1, 'integer') . '
657 WHERE active_id = ' . $ilDB->quote($active_fi, 'integer')
658 );
659 }
660 // tst_manual_fb
661 $ilDB->manipulate(
662 'DELETE
663 FROM tst_manual_fb
664 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
665 AND pass = ' . $ilDB->quote($pass, 'integer')
666 );
667
668 if ($must_renumber) {
669 $ilDB->manipulate(
670 'UPDATE tst_manual_fb
671 SET pass = pass - 1
672 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
673 AND pass > ' . $ilDB->quote($pass, 'integer')
674 );
675 }
676
677 // tst_mark -> nothing to do
678 //
679 // tst_pass_result
680 $ilDB->manipulate(
681 'DELETE
682 FROM tst_pass_result
683 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
684 AND pass = ' . $ilDB->quote($pass, 'integer')
685 );
686
687 if ($must_renumber) {
688 $ilDB->manipulate(
689 'UPDATE tst_pass_result
690 SET pass = pass - 1
691 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
692 AND pass > ' . $ilDB->quote($pass, 'integer')
693 );
694 }
695
696 $ilDB->manipulate(
697 'DELETE
698 FROM tst_sequence
699 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
700 AND pass = ' . $ilDB->quote($pass, 'integer')
701 );
702
703 if ($must_renumber) {
704 $ilDB->manipulate(
705 'UPDATE tst_sequence
706 SET pass = pass - 1
707 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
708 AND pass > ' . $ilDB->quote($pass, 'integer')
709 );
710 }
711
712 // tst_solutions
713 $ilDB->manipulate(
714 'DELETE
715 FROM tst_solutions
716 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
717 AND pass = ' . $ilDB->quote($pass, 'integer')
718 );
719
720 if ($must_renumber) {
721 $ilDB->manipulate(
722 'UPDATE tst_solutions
723 SET pass = pass - 1
724 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
725 AND pass > ' . $ilDB->quote($pass, 'integer')
726 );
727 }
728
729 // tst_test_result
730 $ilDB->manipulate(
731 'DELETE
732 FROM tst_test_result
733 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
734 AND pass = ' . $ilDB->quote($pass, 'integer')
735 );
736
737 if ($must_renumber) {
738 $ilDB->manipulate(
739 'UPDATE tst_test_result
740 SET pass = pass - 1
741 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
742 AND pass > ' . $ilDB->quote($pass, 'integer')
743 );
744 }
745
746 // tst_test_rnd_qst -> nothing to do
747
748 // tst_times
749 $ilDB->manipulate(
750 'DELETE
751 FROM tst_times
752 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
753 AND pass = ' . $ilDB->quote($pass, 'integer')
754 );
755
756 if ($must_renumber) {
757 $ilDB->manipulate(
758 'UPDATE tst_times
759 SET pass = pass - 1
760 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
761 AND pass > ' . $ilDB->quote($pass, 'integer')
762 );
763 }
764
765 $this->test_result_repository->updateTestResultCache((int) $active_fi);
766
767 $this->redirectToPassDeletionContext($context);
768 }
769
771 {
772 $this->ctrl->redirectByClass(ilTestParticipantsGUI::class);
773 }
774
775 protected function sendPage(string $page)
776 {
777 $this->http->saveResponse($this->http->response()->withBody(
778 Streams::ofString($page)
779 ));
780 $this->http->sendResponse();
781 $this->http->close();
782 }
783
784 protected function buildResultsTitle(int $active_id, int $pass): string
785 {
786 if ($this->object->getAnonymity()) {
787 return sprintf(
788 $this->lng->txt('tst_eval_results_by_pass_lo'),
789 $pass + 1
790 );
791 }
792 return sprintf(
793 $this->lng->txt('tst_result_user_name_pass'),
794 $pass + 1,
795 $this->participant_repository->getParticipantByActiveId($this->object->getTestId(), $active_id)->getDisplayName($this->lng)
796 );
797 }
798
799 private function buildAttemptComponents(
800 int $active_id,
801 int $attempt_id,
802 bool $with_test_results_overview,
803 bool $for_print
804 ): array {
805 $settings = $this->results_presentation_factory->getAttemptResultsSettings(
806 $this->object,
807 false
808 );
809 $attempt_overview = $this->ui_factory->panel()->sub(
810 $this->lng->txt('question_summary'),
811 $this->results_data_factory->getAttemptOverviewFor(
812 $settings,
813 $this->object,
814 $active_id,
815 $attempt_id
816 )->getAsDescriptiveListing(
817 $this->lng,
818 $this->ui_factory,
819 [
820 'timezone' => new DateTimeZone($this->user->getTimeZone()),
821 'datetimeformat' => $this->user->getDateTimeFormat()->toString()
822 ]
823 )
824 );
825
826 if ($with_test_results_overview) {
827 $attempt_overview = $attempt_overview->withFurtherInformation(
828 $this->ui_factory->card()->standard($this->lng->txt('overview'))->withSections([
829 $this->results_data_factory->getOverviewDataForTest($this->object)
830 ->getAsDescriptiveListing(
831 $this->lng,
832 $this->ui_factory
833 )
834 ])
835 );
836 }
837
838 $results_presentation_table = $this->results_presentation_factory->getAttemptResultsPresentationTable(
839 $this->results_data_factory->getAttemptResultsFor(
840 $settings,
841 $this->object,
842 $active_id,
843 $attempt_id,
844 false
845 ),
846 $settings,
847 '',
848 $for_print
849 )->getTableComponent();
850
851 if ($for_print) {
852 $signal = $results_presentation_table->getExpandAllSignal();
853 $signal_options = json_encode(['options' => $signal->getOptions()]);
854 $results_presentation_table = [
855 $results_presentation_table,
856 $this->ui_factory->legacy()->content('')->withAdditionalOnLoadCode(
857 static fn(string $id): string => "$(document).trigger('{$signal->getId()}', $signal_options);"
858 )
859 ];
860 }
861
862
863
864 $attempt_details = $this->ui_factory->panel()->sub(
865 $this->lng->txt('details'),
866 $results_presentation_table
867 );
868
869 return [$attempt_overview, $attempt_details];
870 }
871
872 private function addPrintResultsButtonToToolbar(): void
873 {
874 $link = $this->ctrl->getLinkTargetByClass(self::class, 'printResults');
875 $this->toolbar->addComponent(
876 $this->ui_factory->button()->standard(
877 $this->lng->txt('print'),
878 ''
879 )->withOnLoadCode(
880 fn($id): string => "document.getElementById('{$id}').addEventListener('click', "
881 . "(e) => {window.open('{$link}');}"
882 . ');'
883 )
884 );
885 }
886
887 private function addPrintButtonToToolbar(): void
888 {
889 $this->toolbar->addComponent(
890 $this->ui_factory->button()->standard(
891 $this->lng->txt('print'),
892 ''
893 )->withOnLoadCode(
894 fn($id): string => "document.getElementById('{$id}').addEventListener('click', "
895 . "()=>{window.print();}"
896 . ');'
897 )
898 );
899 }
900
902 {
903 $this->toolbar->addComponent(
904 $this->ui_factory->button()->standard(
905 $this->lng->txt('certificate'),
906 $this->ctrl->getLinkTargetByClass(self::class, 'outCertificate')
907 )
908 );
909 }
910
912 {
913 if ($this->testrequest->isset('show_best_solutions')) {
915 'tst_results_show_best_solutions',
916 $this->testrequest->int('show_best_solutions') === 1
917 );
918 }
919
920 if (ilSession::get('tst_results_show_best_solutions')) {
921 $this->ctrl->setParameter($this, 'show_best_solutions', '0');
922 $label = $this->lng->txt('tst_btn_hide_best_solutions');
923 } else {
924 $this->ctrl->setParameter($this, 'show_best_solutions', '1');
925 $label = $this->lng->txt('tst_btn_show_best_solutions');
926 }
927
928 $this->toolbar->addSeparator();
929 $this->toolbar->addComponent(
930 $this->ui_factory->button()->standard(
931 $label,
932 $this->ctrl->getLinkTargetByClass(self::class, $this->ctrl->getCmd(self::DEFAULT_CMD))
933 )
934 );
935 $this->ctrl->clearParameters($this, 'show_best_solutions');
936 }
937
939 array $selected_active_ids,
940 int $current_active_id
941 ): void {
942 $this->toolbar->addSeparator();
943 $this->toolbar->addComponent(
944 $this->ui_factory->dropdown()
945 ->standard(
946 $this->buildParticipantSelectorArray($selected_active_ids, $current_active_id)
947 )->withLabel($this->lng->txt('tst_res_jump_to_participant_hint_opt'))
948 );
949 }
950
952 array $selected_active_ids,
953 int $current_active_id
954 ): array {
955 $this->ctrl->setParameterByClass(self::class, 'active_ids', implode(',', $selected_active_ids));
956 unset($selected_active_ids[array_search($current_active_id, $selected_active_ids)]);
957 $available_user_links = array_map(
958 function (int $v): StandardLink {
959 $this->ctrl->setParameterByClass(self::class, 'active_id', $v);
960 return $this->ui_factory->link()->standard(
961 ilObjUser::_lookupFullname($this->object->_getUserIdFromActiveId($v)),
962 $this->ctrl->getLinkTargetByClass(self::class, 'showResults')
963 );
964 },
965 $selected_active_ids
966 );
967 $this->ctrl->clearParameterByClass(self::class, 'active_id');
968 $this->ctrl->clearParameterByClass(self::class, 'active_ids');
969 return $available_user_links;
970 }
971
973 array $available_attempts,
974 int $selected_attempt
975 ): void {
976 $selected_attempt++;
977 $this->toolbar->addComponent(
978 $this->buildAttemptSwitchingViewControl(
979 $available_attempts,
980 $selected_attempt
981 )
982 );
983 $this->ctrl->clearParameterByClass(self::class, 'attempt');
984 }
985
987 array $available_attempts,
988 int $selected_attempt
989 ): ViewControlMode {
990 return $this->ui_factory->viewControl()->mode(
991 array_reduce(
992 $available_attempts,
993 function (array $c, int $v): array {
994 $this->ctrl->setParameterByClass(self::class, 'attempt', $v);
995 $attempt = $v + 1;
996 $c["{$this->lng->txt('tst_attempt')} {$attempt}"] = $this
997 ->ctrl->getLinkTargetByClass(self::class, $this->ctrl->getCmd(self::DEFAULT_CMD));
998 return $c;
999 },
1000 []
1001 ),
1002 $this->lng->txt('select_attempt')
1003 )->withActive("{$this->lng->txt('tst_attempt')} {$selected_attempt}");
1004 }
1005}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$components
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
Validates if an active certificate is stored in the database and can be downloaded by the user.
Just a wrapper class to create Unit Test for other classes.
static getInstance(ilTestSession $a_test_session)
static getContentStylePath(int $a_style_id, bool $add_random=true, bool $add_token=true)
get content style path static (to avoid full reading)
static _getResultPass($active_id)
Retrieves the pass number that should be counted for a given user.
static lookupExamId($active_id, $pass)
static _getMaxPass($active_id)
Retrieves the maximum pass of a given user for a given test in which the user answered at least one q...
static _lookupFullname(int $a_user_id)
static get(string $a_var)
static set(string $a_var, $a_val)
Set a value.
ILIAS Setting Class.
special template class to simplify handling of ITX/PEAR
Output class for assessment test evaluation.
addParticipantSelectorToToolbar(array $selected_active_ids, int $current_active_id)
readonly ParticipantRepository $participant_repository
__construct(ilObjTest $object)
getEvaluationQuestionId($question_id, $original_id='')
Returns the ID of a question for evaluation purposes.
buildParticipantSelectorArray(array $selected_active_ids, int $current_active_id)
ilTestProcessLockerFactory $processLockerFactory
buildAttemptSwitchingViewControl(array $available_attempts, int $selected_attempt)
addAttemptSwitchingViewControlToToolbar(array $available_attempts, int $selected_attempt)
buildResultsTitle(int $active_id, int $pass)
buildAttemptComponents(int $active_id, int $attempt_id, bool $with_test_results_overview, bool $for_print)
redirectToPassDeletionContext(string $context)
Base Exception for all Exceptions relating to Modules/Test.
Service GUI class for tests.
buildPassOverviewTableGUI(ilTestEvaluationGUI $target_gui)
setContextResultPresentation(bool $contextResultPresentation)
buildQuestionRelatedObjectivesList(ilLOTestQuestionAdapter $objectives_adapter, ilTestQuestionSequence $test_sequence)
getResultsSignature()
Returns HTML code for a signature field.
populatePassFinishDate(ilTemplate $tpl, ?int $pass_finish_date)
getPassListOfAnswers(&$result_array, $active_id, $pass, $show_solutions=false, $only_answered_questions=false, $show_question_only=false, $show_reached_points=false, $anchorNav=false, ?ilTestQuestionRelatedObjectivesList $objectives_list=null, ?ResultsTitlesBuilder $testResultHeaderLabelBuilder=null)
Returns the list of answers of a users test pass.
populateExamId(ilTemplate $tpl, int $active_id, int $pass)
readonly ilDBInterface $db
ilGlobalTemplateInterface ilTemplate $tpl
sk 2023-08-01: We need this union type, even if it is wrong! To change this
readonly ilSetting $settings
getAdditionalUsrDataHtmlAndPopulateWindowTitle($testSession, $active_id, $overwrite_anonymity=false)
Returns the user data for a test results output.
getPassOverviewTableData(ilTestSession $test_session, array $passes, bool $with_results)
getGradingMessageBuilder(int $active_id)
static getStyleSheetLocation(string $mode="output", string $a_css_name="")
get full style sheet file name (path inclusive) of current user
$c
Definition: deliver.php:25
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setVariable(string $variable, $value='')
Sets the given variable to the given value.
parseCurrentBlock(string $block_name=self::DEFAULT_BLOCK)
Parses the given block.
setCurrentBlock(string $part=self::DEFAULT_BLOCK)
Sets the template to the given block.
get(string $part=self::DEFAULT_BLOCK)
Renders the given block and returns the html string.
This describes a Sub Panel.
Definition: Sub.php:30
This describes a Mode Control.
Definition: Mode.php:29
static http()
Fetches the global http state from ILIAS.
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc