ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilTestArchiver.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
21 use ILIAS\Test\Results\Presentation\TitlesBuilder as ResultsTitleBuilder;
33 
38 {
39  public const DIR_SEP = DIRECTORY_SEPARATOR;
40 
41  public const EXPORT_DIRECTORY = 'archive_exports';
42 
43  private const PASS_MATERIALS_PATH_COMPONENT = 'materials';
44  private const QUESTION_PATH_COMPONENT_PREFIX = 'q_';
45 
46  private const TEST_BEST_SOLUTION_PATH_COMPONENT = 'best_solution';
47  private const HTML_BEST_SOLUTION_FILENAME = 'best_solution.html';
48  private const TEST_MATERIALS_PATH_COMPONENT = 'materials';
49 
50  private const TEST_RESULT_FILENAME = 'test_result.html';
51 
52  private const LOG_DTSGROUP_FORMAT = 'D M j G:i:s T Y';
53  private const LOG_ADDITION_STRING = ' Adding ';
54 
55  private const TEST_LOG_FILENAME = 'test_log.xlsx';
56  private const DATA_INDEX_FILENAME = 'data_index.csv';
57  private const ARCHIVE_LOG = 'archive.log';
58 
59  private string $external_directory_path;
60  private string $client_id = CLIENT_ID;
62 
64 
66 
68 
69  public function __construct(
70  private readonly ilLanguage $lng,
71  private readonly ilDBInterface $db,
72  private readonly ilObjUser $user,
73  private readonly UIFactory $ui_factory,
74  private readonly UIRenderer $ui_renderer,
75  private readonly IRSS $irss,
76  private readonly ServerRequestInterface $request,
77  private readonly ilObjectDataCache $obj_cache,
78  private readonly ilTestParticipantAccessFilterFactory $participant_access_filter_factory,
79  private readonly TestLogViewer $log_viewer,
80  private readonly int $test_obj_id,
81  private ?int $test_ref_id = null
82  ) {
84  global $DIC;
85  $ilias = $DIC['ilias'];
86  $local_dic = TestDIC::dic();
87 
88  $this->html_generator = new ilTestHTMLGenerator();
89  $this->external_directory_path = $ilias->ini_ilias->readVariable('clients', 'datadir');
90  $this->archive_data_index = $this->readArchiveDataIndex();
91  $this->test_result_repository = $local_dic['results.data.repository'];
92  }
93 
95  {
97  }
98 
99  public function setParticipantData(ilTestParticipantData $participant_data): void
100  {
101  $this->participant_data = $participant_data;
102  }
103 
105  int $active_fi,
106  int $pass,
107  int $question_fi,
108  string $original_filename,
109  string $file_path
110  ): void {
112  $this->ensurePassDataDirectoryIsAvailable($active_fi, $pass);
113 
114  $pass_question_directory = $this->getPassDataDirectory($active_fi, $pass)
115  . DIRECTORY_SEPARATOR . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
116  if (!is_dir($pass_question_directory)) {
117  mkdir($pass_question_directory, 0777, true);
118  }
119 
120  copy($file_path, $pass_question_directory . DIRECTORY_SEPARATOR . $original_filename);
121 
122  $this->logArchivingProcess(
123  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
124  . $pass_question_directory . DIRECTORY_SEPARATOR . $original_filename
125  );
126  }
127 
128  public function handInParticipantMisc(
129  int $active_fi,
130  int $pass,
131  string $original_filename,
132  string $file_path
133  ): void {
135  $this->ensurePassDataDirectoryIsAvailable($active_fi, $pass);
136  $new_path = $this->getPassDataDirectory($active_fi, $pass) . DIRECTORY_SEPARATOR . $original_filename;
137  copy($file_path, $new_path);
138  $this->logArchivingProcess(date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING . $new_path);
139  }
140 
141  public function handInTestBestSolution(string $best_solution): void
142  {
144 
145  $best_solution_path = $this->getTestArchive() . DIRECTORY_SEPARATOR . self::TEST_BEST_SOLUTION_PATH_COMPONENT;
146  if (!is_dir($best_solution_path)) {
147  mkdir($best_solution_path, 0777, true);
148  }
149 
150  $this->html_generator->generateHTML(
151  $best_solution,
152  $best_solution_path . DIRECTORY_SEPARATOR . self::HTML_BEST_SOLUTION_FILENAME
153  );
154 
155  $this->logArchivingProcess(
156  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
157  . $best_solution_path . DIRECTORY_SEPARATOR . self::HTML_BEST_SOLUTION_FILENAME
158  );
159 
160  $this->logArchivingProcess(
161  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING . $best_solution_path
162  );
163  }
164 
166  int $active_fi,
167  int $pass,
168  ilObjTest $tst_obj
169  ): void {
170  $questions = $tst_obj->getQuestionsOfPass($active_fi, $pass);
171  foreach ($questions as $question) {
172  $question = $tst_obj->getQuestionDataset($question['question_fi']);
173  if ($question->type_tag === 'assFileUpload') {
175  $this->ensurePassDataDirectoryIsAvailable($active_fi, $pass);
176  $this->ensurePassMaterialsDirectoryIsAvailable($active_fi, $pass);
177  $pass_material_directory = $this->getPassMaterialsDirectory($active_fi, $pass);
178  $archive_folder = $pass_material_directory . DIRECTORY_SEPARATOR . $question->question_id . DIRECTORY_SEPARATOR;
179  if (!file_exists($archive_folder)) {
180  mkdir($archive_folder, 0777, true);
181  }
182  $resource_id = $tst_obj->getTextAnswer($active_fi, $question->question_id, $pass);
183  if ($resource_id === '') {
184  continue;
185  }
186  $irss_unique_id = $this->irss->manage()->find($resource_id);
187  if ($irss_unique_id != null) {
188  $resource = $this->irss->manage()->getResource($irss_unique_id);
189  $information = $resource->getCurrentRevision()->getInformation();
190  $stream = $this->irss->consume()->stream($irss_unique_id);
191  // this feels unnecessary..
192  $file_stream = fopen($stream->getStream()->getMetadata('uri'), 'r');
193  $file_content = stream_get_contents($file_stream);
194  fclose($file_stream);
195  $target_destination = $archive_folder . $information->getTitle();
196  file_put_contents($target_destination, $file_content);
197  }
198  }
199  }
200  }
201 
203  int $question_fi,
204  string $orginial_filename,
205  string $file_path
206  ): void {
208 
209  $best_solution_path = $this->getTestArchive() . DIRECTORY_SEPARATOR . self::TEST_BEST_SOLUTION_PATH_COMPONENT;
210  if (!is_dir($best_solution_path)) {
211  mkdir($best_solution_path, 0777, true);
212  }
213 
214  $materials_path = $best_solution_path . DIRECTORY_SEPARATOR . self::TEST_MATERIALS_PATH_COMPONENT;
215  if (!is_dir($materials_path)) {
216  mkdir($materials_path, 0777, true);
217  }
218 
219  $question_materials_path = $materials_path . DIRECTORY_SEPARATOR . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
220  if (!is_dir($question_materials_path)) {
221  mkdir($question_materials_path, 0777, true);
222  }
223 
224  copy($file_path, $question_materials_path . DIRECTORY_SEPARATOR . $orginial_filename);
225 
226  $this->logArchivingProcess(
227  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
228  . $question_materials_path . DIRECTORY_SEPARATOR . $orginial_filename
229  );
230  }
231 
232  public function handInTestResult(int $active_fi, int $pass, string $pdf_path): void
233  {
235  $this->ensurePassDataDirectoryIsAvailable($active_fi, $pass);
236  $new_path = $this->getPassDataDirectory($active_fi, $pass) . DIRECTORY_SEPARATOR . self::TEST_RESULT_FILENAME;
237  copy($pdf_path, $new_path);
238  $this->logArchivingProcess(date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING . $new_path);
239  }
240 
241  protected function hasTestArchive(): bool
242  {
243  return is_dir($this->getTestArchive());
244  }
245 
246  protected function createArchiveForTest(): void
247  {
249  }
250 
251  protected function getTestArchive(): string
252  {
253  $test_archive_directory = $this->external_directory_path . DIRECTORY_SEPARATOR . $this->client_id . DIRECTORY_SEPARATOR . 'tst_data'
254  . DIRECTORY_SEPARATOR . 'archive' . DIRECTORY_SEPARATOR . 'tst_' . $this->test_obj_id;
255  return $test_archive_directory;
256  }
257 
258  protected function ensureTestArchiveIsAvailable(): void
259  {
260  if (!$this->hasTestArchive()) {
261  $this->createArchiveForTest();
262  }
263  return;
264  }
265 
266  public function updateTestArchive(): void
267  {
268  $this->log_viewer->getLogExportForRefjId(
269  $this->test_ref_id
270  )->writeToFile(
271  $this->getTestArchive() . DIRECTORY_SEPARATOR . self::TEST_LOG_FILENAME
272  );
273 
274  // Generate test pass overview
275  $test = new ilObjTest($this->test_obj_id, false);
276  if ($this->test_ref_id !== null) {
277  $test->setRefId($this->test_ref_id);
278  }
279 
280  $array_of_actives = [];
281  $participants = $test->getParticipants();
282 
283  foreach (array_keys($participants) as $key) {
284  $array_of_actives[] = $key;
285  }
286 
287  $filename = realpath($this->getTestArchive()) . DIRECTORY_SEPARATOR . 'participant_attempt_overview.html';
288  $this->html_generator->generateHTML(
290  $test,
291  $array_of_actives
292  ),
293  $filename
294  );
295  }
296 
297  public function ensureZipExportDirectoryExists(): void
298  {
299  if (!$this->hasZipExportDirectory()) {
300  $this->createZipExportDirectory();
301  }
302  }
303 
304  public function hasZipExportDirectory(): bool
305  {
306  return is_dir($this->getZipExportDirectory());
307  }
308 
309  protected function createZipExportDirectory(): void
310  {
311  mkdir($this->getZipExportDirectory(), 0777, true);
312  }
313 
314  public function getZipExportDirectory(): string
315  {
316  return $this->external_directory_path . DIRECTORY_SEPARATOR . $this->client_id . DIRECTORY_SEPARATOR . 'tst_data'
317  . DIRECTORY_SEPARATOR . self::EXPORT_DIRECTORY . DIRECTORY_SEPARATOR . 'tst_' . $this->test_obj_id;
318  }
319 
320  public function compressTestArchive(): void
321  {
322  $this->updateTestArchive();
324 
325  $zip_output_path = $this->getZipExportDirectory();
326  $zip_output_filename = 'test_archive_obj_' . $this->test_obj_id . '_' . time() . '.zip';
327 
328  ilFileUtils::zip($this->getTestArchive(), $zip_output_path . DIRECTORY_SEPARATOR . $zip_output_filename, true);
329  return;
330  }
331 
332  protected function hasPassDataDirectory(int $active_fi, int $pass): bool
333  {
334  return is_dir($this->getPassDataDirectory($active_fi, $pass));
335  }
336 
337  protected function createPassDataDirectory(int $active_fi, int $pass): void
338  {
339  mkdir($this->getPassDataDirectory($active_fi, $pass), 0777, true);
340  return;
341  }
342 
343  private function buildPassDataDirectory($active_fi, $pass): ?string
344  {
345  foreach ($this->archive_data_index as $data_index_entry) {
346  if ($data_index_entry != null && $data_index_entry['identifier'] == $active_fi . '|' . $pass) {
347  array_shift($data_index_entry);
348  return $this->getTestArchive() . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $data_index_entry);
349  }
350  }
351 
352  return null;
353  }
354 
355  protected function getPassDataDirectory(int $active_fi, int $pass): ?string
356  {
357  $pass_data_dir = $this->buildPassDataDirectory($active_fi, $pass);
358 
359  if ($pass_data_dir !== null) {
360  return $pass_data_dir;
361  }
362 
363  $test_obj = new ilObjTest($this->test_obj_id, false);
364  if ($test_obj->getAnonymity()) {
365  $firstname = $this->lng->txt('anonymous');
366  $lastname = '';
367  $matriculation = '';
368  } elseif ($this->getParticipantData()) {
369  $usr_data = $this->getParticipantData()->getUserDataByActiveId($active_fi);
370  $firstname = $usr_data['firstname'] ?? $this->lng->txt('deleted_user');
371  $lastname = $usr_data['lastname'] ?? '';
372  $matriculation = $usr_data['matriculation'] ?? '';
373  } else {
374  $firstname = $this->user->getFirstname();
375  $lastname = $this->user->getLastname();
376  $matriculation = $this->user->getMatriculation();
377  }
378 
380  date(DATE_ISO8601),
381  $active_fi,
382  $pass,
383  $firstname,
384  $lastname,
385  $matriculation
386  );
387 
388  return $this->buildPassDataDirectory($active_fi, $pass);
389  }
390 
391  protected function ensurePassDataDirectoryIsAvailable(int $active_fi, int $pass): void
392  {
393  if (!$this->hasPassDataDirectory($active_fi, $pass)) {
394  $this->createPassDataDirectory($active_fi, $pass);
395  }
396  return;
397  }
398 
399  protected function hasPassMaterialsDirectory(int $active_fi, int $pass): bool
400  {
401  if (is_dir($this->getPassMaterialsDirectory($active_fi, $pass))) {
402  return true;
403  }
404  return false;
405  }
406 
407  protected function createPassMaterialsDirectory(int $active_fi, int $pass): string
408  {
414  $user = $this->user;
415 
416  if ($this->getParticipantData()) {
417  $usrData = $this->getParticipantData()->getUserDataByActiveId($active_fi);
418  $user = new ilObjUser();
419  $user->setFirstname($usrData['firstname'] ?? $this->lng->txt('deleted_user'));
420  $user->setLastname($usrData['lastname'] ?? '');
421  $user->setMatriculation($usrData['matriculation'] ?? '');
422  }
423 
425  date('c'),
426  $active_fi,
427  $pass,
428  $user->getFirstname(),
429  $user->getLastname(),
430  $user->getMatriculation()
431  );
432  $material_directory = $this->getPassMaterialsDirectory($active_fi, $pass);
433  mkdir($material_directory, 0777, true);
434  return $material_directory;
435  }
436 
437  protected function getPassMaterialsDirectory(int $active_fi, int $pass): string
438  {
439  $pass_data_directory = $this->getPassDataDirectory($active_fi, $pass);
440  return $pass_data_directory . DIRECTORY_SEPARATOR . self::PASS_MATERIALS_PATH_COMPONENT;
441  }
442 
443  protected function ensurePassMaterialsDirectoryIsAvailable(int $active_fi, int $pass): void
444  {
445  if (!$this->hasPassMaterialsDirectory($active_fi, $pass)) {
446  $this->createPassMaterialsDirectory($active_fi, $pass);
447  }
448  }
449 
450  protected function readArchiveDataIndex(): array
451  {
456  $data_index_file = $this->getTestArchive() . DIRECTORY_SEPARATOR . self::DATA_INDEX_FILENAME;
457 
458  $contents = [];
459 
461  if (@file_exists($data_index_file)) {
462  $lines = explode("\n", file_get_contents($data_index_file));
463  foreach ($lines as $line) {
464  if (strlen($line) === 0) {
465  continue;
466  }
467  $line_items = explode('|', $line);
468  $line_data = [];
469  $line_data['identifier'] = $line_items[0] . '|' . $line_items[1];
470  $line_data['yyyy'] = $line_items[2];
471  $line_data['mm'] = $line_items[3];
472  $line_data['dd'] = $line_items[4];
473  $line_data['directory'] = $line_items[5];
474  $contents[] = $line_data;
475  }
476  }
477  return $contents;
478  }
479 
480  protected function appendToArchiveDataIndex(
481  string $date,
482  int $active_fi,
483  int $pass,
484  string $user_firstname,
485  string $user_lastname,
486  string $matriculation
487  ): void {
488  $line = $this->determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation);
489 
490  $this->archive_data_index[] = $line;
491  $output_contents = '';
492 
493  foreach ($this->archive_data_index as $line_data) {
494  if ($line_data['identifier'] == "|") {
495  continue;
496  }
497  $output_contents .= implode('|', $line_data) . "\n";
498  }
499 
500  file_put_contents($this->getTestArchive() . DIRECTORY_SEPARATOR . self::DATA_INDEX_FILENAME, $output_contents);
501  $this->readArchiveDataIndex();
502  return;
503  }
504 
505  private function determinePassDataPath(
506  string $date,
507  int $active_fi,
508  int $pass,
509  string $user_firstname,
510  string $user_lastname,
511  string $matriculation
512  ): array {
513  $parsed_date = date_create_from_format('Y-m-d\TH:i:sP', $date);
514  if (!$parsed_date) {
515  throw new Exception('Invalid date format. Expected ISO 8601 format.');
516  }
517 
518  $line = [
519  'identifier' => $active_fi . '|' . $pass,
520  'yyyy' => date_format($parsed_date, 'Y'),
521  'mm' => date_format($parsed_date, 'm'),
522  'dd' => date_format($parsed_date, 'd'),
523  'directory' => $active_fi . '_' . $pass . '_' . $user_firstname . '_' . $user_lastname . '_' . $matriculation
524  ];
525  return $line;
526  }
527 
528  private function createUserResultsForArchive(
529  \ilObjTest $test_obj,
530  array $active_ids,
531  ): string {
532  $template = new ilTemplate('tpl.il_as_tst_participants_result_output.html', true, true, 'components/ILIAS/Test');
533 
534  $participant_data = new ilTestParticipantData($this->db, $this->lng);
535  $participant_data->setParticipantAccessFilter(
536  $this->participant_access_filter_factory->getAccessResultsUserFilter($test_obj->getRefId())
537  );
538  $participant_data->setActiveIdsFilter($active_ids);
539  $participant_data->load($test_obj->getTestId());
540 
541  $test_session_factory = new ilTestSessionFactory($test_obj, $this->db, $this->user);
542 
543  $count = 0;
544  foreach ($active_ids as $active_id) {
545  if (!in_array($active_id, $participant_data->getActiveIds())) {
546  continue;
547  }
548 
549  $count++;
550  $results = '';
551  if ($active_id > 0) {
553  $test_obj,
554  $test_session_factory->getSession($active_id),
555  $participant_data->getUserDataByActiveId($active_id),
556  (int) $active_id,
557  ilObjTest::_getResultPass($active_id)
558  );
559  }
560  if ($count < count($active_ids)) {
561  $template->touchBlock('break');
562  }
563  $template->setCurrentBlock('user_result');
564  $template->setVariable('USER_RESULT', $results);
565  $template->parseCurrentBlock();
566  }
567 
568  return $template->get();
569  }
570 
571  public function getResultsOfUserOutput(
572  \ilObjTest $test_obj,
573  ilTestSession $test_session,
574  array $participant_data,
575  int $active_id,
576  int $attempt
577  ): string {
578  $template = new ilTemplate('tpl.il_as_tst_results_participant.html', true, true, 'components/ILIAS/Test');
579 
580  $uname = "{$participant_data['firstname']} {$participant_data['lastname']}";
581  if ($test_obj->getAnonymity()) {
582  $uname = $this->lng->txt('anonymous');
583  }
584 
585  $test_result_title_builder = new ResultsTitleBuilder($this->lng, $this->obj_cache);
586 
587  $result_array = $test_obj->getTestResult(
588  $active_id,
589  $attempt,
590  false,
591  true
592  );
593 
594  $table = $this->ui_factory->table()->data(
595  $this->getDataRetrievalForAttemptOverviewTable($result_array),
596  $test_result_title_builder->getPassDetailsHeaderLabel($attempt + 1),
598  )->withRequest($this->request);
599  $template->setVariable(
600  'PASS_DETAILS',
601  $this->ui_renderer->render($table)
602  );
603 
604  if ($test_obj->isShowExamIdInTestResultsEnabled()) {
605  $template->setCurrentBlock('exam_id_footer');
606  $template->setVariable('EXAM_ID_VAL', ilObjTest::lookupExamId(
607  $test_session->getActiveId(),
608  $attempt
609  ));
610  $template->setVariable('EXAM_ID_TXT', $this->lng->txt('exam_id'));
611  $template->parseCurrentBlock();
612  }
613 
614  $template->setCurrentBlock('participant_block_id');
615  $template->setVariable('PARTICIPANT_BLOCK_ID', "participant_active_{$active_id}");
616  $template->parseCurrentBlock();
617 
618  $template->setVariable('TEXT_HEADING', sprintf($this->lng->txt('tst_result_user_name'), $uname));
619 
620  if ($participant_data['matriculation'] !== '') {
621  $template->setVariable('USER_DATA', "{$this->lng->txt('matriculation')}: {$participant_data['matriculation']}");
622  }
623 
624  $results = $this->test_result_repository->getTestResult($active_id);
625 
626  $status = $this->lng->txt($results->isPassed() ? 'passed_official' : 'failed_official');
627  $template->setVariable(
628  'GRADING_MESSAGE',
629  "{$this->lng->txt('passed_status')}: {$status}<br>"
630  . "{$this->lng->txt('tst_mark')}: {$results->getMarkOfficial()}"
631  );
632 
633  $template->setVariable('PASS_FINISH_DATE_LABEL', $this->lng->txt('tst_pass_finished_on'));
634  $template->setVariable(
635  'PASS_FINISH_DATE_VALUE',
636  (new \DateTimeImmutable('@' . ilObjTest::lookupLastTestPassAccess($active_id, $attempt)))
637  ->setTimezone(new DateTimeZone($this->user->getTimeZone()))
638  ->format($this->user->getDateTimeFormat()->toString())
639  );
640 
641  return $template->get();
642  }
643 
644  private function getColumnsForAttemptOverviewTable(): array
645  {
646  $cf = $this->ui_factory->table()->column();
647  $columns = [
648  'order' => $cf->number($this->lng->txt('order')),
649  'question_id' => $cf->number($this->lng->txt('question_id')),
650  'title' => $cf->text($this->lng->txt('tst_question_title')),
651  'reachable_points' => $cf->number($this->lng->txt('tst_maximum_points')),
652  'reached_points' => $cf->number($this->lng->txt('tst_reached_points'))
653  ];
654  $columns['solved'] = $cf->text($this->lng->txt('tst_percent_solved'));
655  return $columns;
656  }
657 
658  private function getDataRetrievalForAttemptOverviewTable(array $result_data): DataRetrieval
659  {
660  return new class ($result_data) implements DataRetrieval {
661  public function __construct(
662  private readonly array $result_data
663  ) {
664  }
665 
666  public function getRows(
667  DataRowBuilder $row_builder,
668  array $visible_column_ids,
669  Range $range,
670  Order $order,
671  ?array $filter_data,
672  ?array $additional_parameters
673  ): \Generator {
674  $i = 1;
675  foreach ($this->result_data as $result) {
676  if (!isset($result['qid'])) {
677  continue;
678  }
679  yield $row_builder->buildDataRow(
680  (string) $result['qid'],
681  [
682  'order' => $i++,
683  'question_id' => $result['qid'],
684  'title' => $result['title'],
685  'reachable_points' => $result['max'],
686  'reached_points' => $result['reached'],
687  'solved' => $result['percent']
688  ]
689  );
690  }
691  }
692 
693  public function getTotalRowCount(
694  ?array $filter_data,
695  ?array $additional_parameters
696  ): ?int {
697  return count($this->result_data);
698  }
699  };
700  }
701 
702  private function logArchivingProcess(string $message): void
703  {
704  $archive = $this->getTestArchive() . DIRECTORY_SEPARATOR . self::ARCHIVE_LOG;
705  if (file_exists($archive)) {
706  $content = file_get_contents($archive) . "\n" . $message;
707  } else {
708  $content = $message;
709  }
710 
711  file_put_contents($archive, $content);
712  }
713 }
ilTestParticipantData $participant_data
createUserResultsForArchive(\ilObjTest $test_obj, array $active_ids,)
TestResultRepository $test_result_repository
handInParticipantQuestionMaterial(int $active_fi, int $pass, int $question_fi, string $original_filename, string $file_path)
getPassDataDirectory(int $active_fi, int $pass)
ensurePassDataDirectoryIsAvailable(int $active_fi, int $pass)
getTestId()
Gets the database id of the additional test data.
createPassMaterialsDirectory(int $active_fi, int $pass)
isShowExamIdInTestResultsEnabled()
getQuestionsOfPass(int $active_id, int $pass)
hasPassMaterialsDirectory(int $active_fi, int $pass)
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
Both the subject and the direction need to be specified when expressing an order. ...
Definition: Order.php:28
buildDataRow(string $id, array $record)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
setParticipantAccessFilter(Closure $participantAccessFilter)
const QUESTION_PATH_COMPONENT_PREFIX
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setActiveIdsFilter(array $active_ids_filter)
createPassDataDirectory(int $active_fi, int $pass)
static _getResultPass($active_id)
Retrieves the pass number that should be counted for a given user.
buildPassDataDirectory($active_fi, $pass)
getPassMaterialsDirectory(int $active_fi, int $pass)
handInTestBestSolution(string $best_solution)
const CLIENT_ID
Definition: constants.php:41
global $DIC
Definition: shib_login.php:26
handInParticipantUploadedResults(int $active_fi, int $pass, ilObjTest $tst_obj)
$results
determinePassDataPath(string $date, int $active_fi, int $pass, string $user_firstname, string $user_lastname, string $matriculation)
getResultsOfUserOutput(\ilObjTest $test_obj, ilTestSession $test_session, array $participant_data, int $active_id, int $attempt)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$filename
Definition: buildRTE.php:78
Class that handles PDF generation for test and assessment.
getQuestionDataset($question_id)
Returns the dataset for a given question id.
getTextAnswer($active_id, $question_id, $pass=null)
Returns the text answer of a given user for a given question.
getTestResult(int $active_id, ?int $attempt=null, bool $ordered_sequence=false, bool $consider_hidden_questions=true, bool $consider_optional_questions=true)
Calculates the results of a test for a given user and returns an array with all test results...
handInParticipantMisc(int $active_fi, int $pass, string $original_filename, string $file_path)
__construct(Container $dic, ilPlugin $plugin)
global $lng
Definition: privfeed.php:31
static zip(string $a_dir, string $a_file, bool $compress_content=false)
appendToArchiveDataIndex(string $date, int $active_fi, int $pass, string $user_firstname, string $user_lastname, string $matriculation)
const TEST_BEST_SOLUTION_PATH_COMPONENT
$message
Definition: xapiexit.php:31
handInTestResult(int $active_fi, int $pass, string $pdf_path)
setParticipantData(ilTestParticipantData $participant_data)
logArchivingProcess(string $message)
handInBestSolutionQuestionMaterial(int $question_fi, string $orginial_filename, string $file_path)
A simple class to express a naive range of whole positive numbers.
Definition: Range.php:28
ensurePassMaterialsDirectoryIsAvailable(int $active_fi, int $pass)
ilTestHTMLGenerator $html_generator
getDataRetrievalForAttemptOverviewTable(array $result_data)
static lookupExamId($active_id, $pass)
hasPassDataDirectory(int $active_fi, int $pass)