ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilTestArchiver.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
16{
17 #region Constants / Config
18
19 const DIR_SEP = '/';
20
21 const HTML_SUBMISSION_FILENAME = 'test_submission.html';
22 const PDF_SUBMISSION_FILENAME = 'test_submission.pdf';
23 const PASS_MATERIALS_PATH_COMPONENT = 'materials';
25
26 const TEST_BEST_SOLUTION_PATH_COMPONENT = 'best_solution';
27 const HTML_BEST_SOLUTION_FILENAME = 'best_solution.html';
28 const PDF_BEST_SOLUTION_FILENAME = 'best_solution.pdf';
29 const TEST_MATERIALS_PATH_COMPONENT = 'materials';
30
31 const TEST_RESULT_FILENAME = 'test_result_v';
32 const TEST_RESULT_POSTFIX = '.pdf';
33
34 const TEST_OVERVIEW_PDF_FILENAME = 'results_overview_html_v';
36
37 const TEST_OVERVIEW_HTML_FILENAME = 'results_overview_pdf_v';
39
40 const LOG_DTSGROUP_FORMAT = 'D M j G:i:s T Y';
41 const LOG_ADDITION_STRING = ' Adding ';
42 const LOG_CREATION_STRING = ' Creating ';
43 const LOG_UPDATE_STRING = ' Updating ';
44 const LOG_DELETION_STRING = ' Deleting ';
45
46 const TEST_LOG_FILENAME = 'test.log';
47 const DATA_INDEX_FILENAME = 'data_index.csv';
48 const ARCHIVE_LOG = 'archive.log';
49
50 const EXPORT_DIRECTORY = 'archive_exports';
51
52 #endregion
53
54 /*
55 * Test-Archive Schema:
56 *
57 * <external directory>/<client>/tst_data/archive/tst_<obj_id>/
58 * - archive_data_index.dat
59 * - archive_log.log
60 *
61 * - test_log.log
62 *
63 * - test_results_v<n>.pdf
64 * - test_results_v<n>.csv
65 *
66 * -> best_solution/
67 * best_solution_v<n>.pdf
68 * -> /materials/q_<question_fi>/<n>_<filename>
69 *
70 * -> <year>/<month>/<day>/<ActiveFi>_<Pass>[_<Lastname>][_<Firstname>][_<Matriculation>]/
71 * -> test_submission.pdf
72 * -> test_submission.html
73 * -> test_submission.sig (et al)
74 * -> test_result_v<n>.pdf
75 * -> /materials_v<n>/<question_fi>/<n>_<filename>
76 */
77
78 #region Properties
79
81 protected $client_id;
82 protected $test_obj_id;
85 protected $ilDB;
91
92 #endregion
93
99 public function __construct($test_obj_id)
100 {
102 global $DIC;
103 $ilias = $DIC['ilias'];
104 $this->external_directory_path = $ilias->ini_ilias->readVariable('clients', 'datadir');
105 $this->client_id = $ilias->client_id;
106 $this->test_obj_id = $test_obj_id;
107 $this->ilDB = $ilias->db;
108
109 $this->archive_data_index = $this->readArchiveDataIndex();
110
111 $this->participantData = null;
112 }
113
117 public function getParticipantData()
118 {
120 }
121
126 {
127 $this->participantData = $participantData;
128 }
129
130 #region API methods
131
144 public function handInParticipantSubmission($active_fi, $pass, $pdf_path, $html_string)
145 {
147 $this->ensurePassDataDirectoryIsAvailable($active_fi, $pass);
148
149 $pdf_new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
151 copy($pdf_path, $pdf_new_path);
152 # /home/mbecker/public_html/ilias/trunk-primary/extern/default/tst_data/archive/tst_350/2013/09/19/80_1_root_user_/test_submission.pdf
153 $html_new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
155 file_put_contents($html_new_path, $html_string);
156
157 $this->logArchivingProcess(date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING . $pdf_new_path);
158 $this->logArchivingProcess(date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING . $html_new_path);
159 }
160
170 public function handInParticipantQuestionMaterial($active_fi, $pass, $question_fi, $original_filename, $file_path)
171 {
173 $this->ensurePassDataDirectoryIsAvailable($active_fi, $pass);
174
175 $pass_question_directory = $this->getPassDataDirectory($active_fi, $pass)
176 . self::DIR_SEP . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
177 if (!is_dir($pass_question_directory)) {
178 mkdir($pass_question_directory, 0777, true);
179 }
180
181 copy($file_path, $pass_question_directory . self::DIR_SEP . $original_filename);
182
183 $this->logArchivingProcess(
184 date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
185 . $pass_question_directory . self::DIR_SEP . $original_filename
186 );
187 }
188
199 public function handInParticipantMisc($active_fi, $pass, $original_filename, $file_path)
200 {
202 $this->ensurePassDataDirectoryIsAvailable($active_fi, $pass);
203 $new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP . $original_filename;
204 copy($file_path, $new_path);
205 $this->logArchivingProcess(date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING . $new_path);
206 }
207
214 public function handInTestBestSolution($html_string, $pdf_path)
215 {
217
218 $best_solution_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_BEST_SOLUTION_PATH_COMPONENT;
219 if (!is_dir($best_solution_path)) {
220 mkdir($best_solution_path, 0777, true);
221 }
222
223 file_put_contents($best_solution_path . self::DIR_SEP . self::HTML_BEST_SOLUTION_FILENAME, $html_string);
224
225 copy($pdf_path, $best_solution_path . self::DIR_SEP . self::PDF_BEST_SOLUTION_FILENAME);
226
227 $this->logArchivingProcess(
228 date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
229 . $best_solution_path . self::DIR_SEP . self::HTML_BEST_SOLUTION_FILENAME
230 );
231
232 $this->logArchivingProcess(
233 date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
234 . $best_solution_path . self::DIR_SEP . self::PDF_BEST_SOLUTION_FILENAME
235 );
236 }
237
245 public function handInBestSolutionQuestionMaterial($question_fi, $orginial_filename, $file_path)
246 {
248
249 $best_solution_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_BEST_SOLUTION_PATH_COMPONENT;
250 if (!is_dir($best_solution_path)) {
251 mkdir($best_solution_path, 0777, true);
252 }
253
254 $materials_path = $best_solution_path . self::DIR_SEP . self::TEST_MATERIALS_PATH_COMPONENT;
255 if (!is_dir($materials_path)) {
256 mkdir($materials_path, 0777, true);
257 }
258
259 $question_materials_path = $materials_path . self::DIR_SEP . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
260 if (!is_dir($question_materials_path)) {
261 mkdir($question_materials_path, 0777, true);
262 }
263
264 copy($file_path, $question_materials_path . self::DIR_SEP . $orginial_filename);
265
266 $this->logArchivingProcess(
267 date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
268 . $question_materials_path . self::DIR_SEP . $orginial_filename
269 );
270 }
271
281 public function handInTestResult($active_fi, $pass, $pdf_path)
282 {
284 $this->ensurePassDataDirectoryIsAvailable($active_fi, $pass);
285 $new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
286 . self::TEST_RESULT_FILENAME . ($this->countFilesInDirectory($this->getPassDataDirectory($active_fi, $pass), self::TEST_RESULT_FILENAME))
287 . self::TEST_RESULT_POSTFIX;
288 copy($pdf_path, $new_path);
289 $this->logArchivingProcess(date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING . $new_path);
290 }
291
298 public function handInTestResultsOverview($html_string, $pdf_path)
299 {
301 $new_pdf_path = $this->getTestArchive() . self::DIR_SEP
302 . self::TEST_OVERVIEW_PDF_FILENAME
303 . $this->countFilesInDirectory($this->getTestArchive(), self::TEST_OVERVIEW_PDF_FILENAME) . self::TEST_OVERVIEW_PDF_POSTFIX;
304 copy($pdf_path, $new_pdf_path);
305 $html_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_OVERVIEW_HTML_FILENAME
306 . $this->countFilesInDirectory($this->getTestArchive(), self::TEST_OVERVIEW_HTML_FILENAME) . self::TEST_OVERVIEW_HTML_POSTFIX;
307 file_put_contents($html_path, $html_string);
308
309 $this->logArchivingProcess(date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING . $new_pdf_path);
310 $this->logArchivingProcess(date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING . $html_path);
311 }
312
313 #endregion
314
315 #region TestArchive
316 // The TestArchive lives here: <external directory>/<client>/tst_data/archive/tst_<obj_id>/
317
323 protected function hasTestArchive()
324 {
325 return is_dir($this->getTestArchive());
326 }
327
331 protected function createArchiveForTest()
332 {
334 //mkdir( $this->getTestArchive(), 0777, true );
335 }
336
342 protected function getTestArchive()
343 {
344 $test_archive_directory = $this->external_directory_path . self::DIR_SEP . $this->client_id . self::DIR_SEP . 'tst_data'
345 . self::DIR_SEP . 'archive' . self::DIR_SEP . 'tst_' . $this->test_obj_id;
346 return $test_archive_directory;
347 }
348
356 protected function ensureTestArchiveIsAvailable()
357 {
358 if (!$this->hasTestArchive()) {
359 $this->createArchiveForTest();
360 }
361 return;
362 }
363
369 public function updateTestArchive()
370 {
371 $query = 'SELECT * FROM ass_log WHERE obj_fi = ' . $this->ilDB->quote($this->test_obj_id, 'integer');
372 $result = $this->ilDB->query($query);
373
374 $outfile_lines = '';
376 while ($row = $this->ilDB->fetchAssoc($result)) {
377 $outfile_lines .= "\r\n" . implode("\t", $row);
378 }
379 file_put_contents($this->getTestArchive() . self::DIR_SEP . self::TEST_LOG_FILENAME, $outfile_lines);
380
381 // Generate test pass overview
382 $test = new ilObjTest($this->test_obj_id, false);
383 require_once 'Modules/Test/classes/class.ilParticipantsTestResultsGUI.php';
384 $gui = new ilParticipantsTestResultsGUI();
385 $gui->setTestObj($test);
386 require_once 'Modules/Test/classes/class.ilTestObjectiveOrientedContainer.php';
387 $objectiveOrientedContainer = new ilTestObjectiveOrientedContainer();
388 $gui->setObjectiveParent($objectiveOrientedContainer);
389 $array_of_actives = array();
390 $participants = $test->getParticipants();
391
392 foreach ($participants as $key => $value) {
393 $array_of_actives[] = $key;
394 }
395 $output_template = $gui->createUserResults(true, false, true, $array_of_actives);
396
397 require_once 'Modules/Test/classes/class.ilTestPDFGenerator.php';
398 $filename = realpath($this->getTestArchive()) . self::DIR_SEP . 'participant_pass_overview.pdf';
400
401 return;
402 }
403
405 {
406 if (!$this->hasZipExportDirectory()) {
408 }
409 }
410
416 public function hasZipExportDirectory()
417 {
418 return is_dir($this->getZipExportDirectory());
419 }
420
421 protected function createZipExportDirectory()
422 {
423 mkdir($this->getZipExportDirectory(), 0777, true);
424 }
425
431 public function getZipExportDirectory()
432 {
433 return $this->external_directory_path . self::DIR_SEP . $this->client_id . self::DIR_SEP . 'tst_data'
434 . self::DIR_SEP . self::EXPORT_DIRECTORY . self::DIR_SEP . 'tst_' . $this->test_obj_id;
435 }
436
442 public function compressTestArchive()
443 {
444 $this->updateTestArchive();
446
447 $zip_output_path = $this->getZipExportDirectory();
448 $zip_output_filename = 'test_archive_obj_' . $this->test_obj_id . '_' . time() . '_.zip';
449
450 ilUtil::zip($this->getTestArchive(), $zip_output_path . self::DIR_SEP . $zip_output_filename, true);
451 return;
452 }
453
454 #endregion
455
456 #region PassDataDirectory
457 // The pass data directory contains all data relevant for a participants pass.
458 // In addition to the test-archive-directory, this directory lives here:
459 // .../<year>/<month>/<day>/<ActiveFi>_<Pass>[_<Lastname>][_<Firstname>][_<Matriculation>]/
460 // Lastname, Firstname and Matriculation are not mandatory in the directory name.
461
470 protected function hasPassDataDirectory($active_fi, $pass)
471 {
472 $pass_data_dir = $this->getPassDataDirectory($active_fi, $pass);
473 return is_dir($this->getPassDataDirectory($active_fi, $pass));
474 }
475
484 protected function createPassDataDirectory($active_fi, $pass)
485 {
486 mkdir($this->getPassDataDirectory($active_fi, $pass), 0777, true);
487 return;
488 }
489
490 private function buildPassDataDirectory($active_fi, $pass)
491 {
492 foreach ($this->archive_data_index as $data_index_entry) {
493 if ($data_index_entry != null && $data_index_entry['identifier'] == $active_fi . '|' . $pass) {
494 array_shift($data_index_entry);
495 return $this->getTestArchive() . self::DIR_SEP . implode(self::DIR_SEP, $data_index_entry);
496 }
497 }
498
499 return null;
500 }
501
510 protected function getPassDataDirectory($active_fi, $pass)
511 {
512 $passDataDir = $this->buildPassDataDirectory($active_fi, $pass);
513
514 if (!$passDataDir) {
515 if ($this->getParticipantData()) {
516 $usrData = $this->getParticipantData()->getUserDataByActiveId($active_fi);
517 $user = new ilObjUser();
518 $user->setFirstname($usrData['firstname']);
519 $user->setLastname($usrData['lastname']);
520 $user->setMatriculation($usrData['matriculation']);
521 $user->setFirstname($usrData['firstname']);
522 } else {
523 global $DIC;
524 $ilUser = $DIC['ilUser'];
525 $user = $ilUser;
526 }
527
529 date(DATE_ISO8601),
530 $active_fi,
531 $pass,
532 $user->getFirstname(),
533 $user->getLastname(),
534 $user->getMatriculation()
535 );
536
537 $passDataDir = $this->buildPassDataDirectory($active_fi, $pass);
538 }
539
540 return $passDataDir;
541 }
542
553 protected function ensurePassDataDirectoryIsAvailable($active_fi, $pass)
554 {
555 if (!$this->hasPassDataDirectory($active_fi, $pass)) {
556 $this->createPassDataDirectory($active_fi, $pass);
557 }
558 return;
559 }
560
561 #endregion
562
563 #region PassMaterialsDirectory
564
573 protected function hasPassMaterialsDirectory($active_fi, $pass)
574 {
576 if (@is_dir($this->getPassMaterialsDirectory($active_fi, $pass))) {
577 return true;
578 }
579 return false;
580 }
581
590 protected function createPassMaterialsDirectory($active_fi, $pass)
591 {
592 // Data are taken from the current user as the implementation expects the first interaction of the pass
593 // takes place from the usage/behaviour of the current user.
594
595 if ($this->getParticipantData()) {
596 $usrData = $this->getParticipantData()->getUserDataByActiveId($active_fi);
597 $user = new ilObjUser();
598 $user->setFirstname($usrData['firstname']);
599 $user->setLastname($usrData['lastname']);
600 $user->setMatriculation($usrData['matriculation']);
601 $user->setFirstname($usrData['firstname']);
602 } else {
603 global $DIC;
604 $ilUser = $DIC['ilUser'];
605 $user = $ilUser;
606 }
607
609 date('Y'),
610 $active_fi,
611 $pass,
612 $user->getFirstname(),
613 $user->getLastname(),
614 $user->getMatriculation()
615 );
616 mkdir($this->getPassMaterialsDirectory($active_fi, $pass), 0777, true);
617 }
618
627 protected function getPassMaterialsDirectory($active_fi, $pass)
628 {
629 $pass_data_directory = $this->getPassMaterialsDirectory($active_fi, $pass);
630 return $pass_data_directory . self::DIR_SEP . self::PASS_MATERIALS_PATH_COMPONENT;
631 }
632
642 protected function ensurePassMaterialsDirectoryIsAvailable($active_fi, $pass)
643 {
644 if (!$this->hasPassMaterialsDirectory($active_fi, $pass)) {
645 $this->createPassMaterialsDirectory($active_fi, $pass);
646 }
647 }
648
649 #endregion
650
656 protected function readArchiveDataIndex()
657 {
662 $data_index_file = $this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME;
663
664 $contents = array();
665
667 if (@file_exists($data_index_file)) {
668 $lines = explode("\n", file_get_contents($data_index_file));
669 foreach ($lines as $line) {
670 $line_items = explode('|', $line);
671 $line_data['identifier'] = $line_items[0] . '|' . $line_items[1];
672 $line_data['yyyy'] = $line_items[2];
673 $line_data['mm'] = $line_items[3];
674 $line_data['dd'] = $line_items[4];
675 $line_data['directory'] = $line_items[5];
676 $contents[] = $line_data;
677 }
678 }
679 return $contents;
680 }
681
694 protected function appendToArchiveDataIndex($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
695 {
696 $line = $this->determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation);
697
698 $this->archive_data_index[] = $line;
699 $output_contents = '';
700
701 foreach ($this->archive_data_index as $line_data) {
702 if ($line_data['identifier'] == "|") {
703 continue;
704 }
705 $output_contents .= implode('|', $line_data) . "\n";
706 }
707
708 file_put_contents($this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME, $output_contents);
709 $this->readArchiveDataIndex();
710 return;
711 }
712
725 protected function determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
726 {
727 $date = date_create_from_format(DATE_ISO8601, $date);
728 $line = array(
729 'identifier' => $active_fi . '|' . $pass,
730 'yyyy' => date_format($date, 'Y'),
731 'mm' => date_format($date, 'm'),
732 'dd' => date_format($date, 'd'),
733 'directory' => $active_fi . '_' . $pass . '_' . $user_firstname . '_' . $user_lastname . '_' . $matriculation
734 );
735 return $line;
736 }
737
745 protected function logArchivingProcess($message)
746 {
747 $archive = $this->getTestArchive() . self::DIR_SEP . self::ARCHIVE_LOG;
748 if (file_exists($archive)) {
749 $content = file_get_contents($archive) . "\n" . $message;
750 } else {
751 $content = $message;
752 }
753
754 file_put_contents($archive, $content);
755 }
756
765 protected function countFilesInDirectory($directory, $pattern = null)
766 {
767 $filecount = 0;
768
770 if ($handle = opendir($directory)) {
771 while (($file = readdir($handle)) !== false) {
772 if (!in_array($file, array( '.', '..' )) && !is_dir($directory . $file)) {
773 if ($pattern && strpos($file, $pattern) === 0) {
774 $filecount++;
775 }
776 }
777 }
778 }
779 return $filecount;
780 }
781}
$result
$test
Definition: Utf8Test.php:84
$filename
Definition: buildRTE.php:89
An exception for terminatinating execution or to throw for unit testing.
Class ilTestArchiver.
handInTestResult($active_fi, $pass, $pdf_path)
Hands in an individual test result for a pass.
handInParticipantSubmission($active_fi, $pass, $pdf_path, $html_string)
Hands in a participants test submission ("a completed test") for archiving.
handInBestSolutionQuestionMaterial($question_fi, $orginial_filename, $file_path)
Hands in a file related to a question in context of the best solution.
compressTestArchive()
Generate the test archive for download.
hasPassMaterialsDirectory($active_fi, $pass)
Returns if the pass materials directory exists for a given pass.
countFilesInDirectory($directory, $pattern=null)
Returns the count of files in a directory, eventually matching the given, optional,...
ensureTestArchiveIsAvailable()
Ensures the availability of the test archive directory.
getTestArchive()
Returns the (theoretical) path to the archive directory of the test, this object is created for.
getZipExportDirectory()
Return the export directory, where zips are placed.
handInParticipantQuestionMaterial($active_fi, $pass, $question_fi, $original_filename, $file_path)
Hands in a particpants question material, such as an upload or other binary content.
getPassMaterialsDirectory($active_fi, $pass)
Returns the pass materials directory.
handInTestResultsOverview($html_string, $pdf_path)
Hands in a test results overview.
handInTestBestSolution($html_string, $pdf_path)
Hands in the best solution for a test.
hasPassDataDirectory($active_fi, $pass)
Checks if the directory for pass data is available.
setParticipantData($participantData)
readArchiveDataIndex()
Reads the archive data index.
getPassDataDirectory($active_fi, $pass)
Returns the pass data directory.
hasTestArchive()
Returns if the archive directory structure for the test the object is created for exists.
handInParticipantMisc($active_fi, $pass, $original_filename, $file_path)
Hands in a participants file, which is relevant for archiving but an unspecified type.
createPassDataDirectory($active_fi, $pass)
Creates pass data directory.
const TEST_BEST_SOLUTION_PATH_COMPONENT
buildPassDataDirectory($active_fi, $pass)
appendToArchiveDataIndex($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
Appends a line to the archive data index.
createPassMaterialsDirectory($active_fi, $pass)
Creates pass materials directory.
ensurePassMaterialsDirectoryIsAvailable($active_fi, $pass)
Ensures the availability of the pass materials directory.
const QUESTION_PATH_COMPONENT_PREFIX
updateTestArchive()
Replaces the test-log with the current one.
logArchivingProcess($message)
Logs to the archive log.
hasZipExportDirectory()
Returns if the export directory for zips exists.
createArchiveForTest()
Creates the directory for the test archive.
ensurePassDataDirectoryIsAvailable($active_fi, $pass)
Ensures the availability of the participant data directory.
determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
Determines the pass data path.
static generatePDF($pdf_output, $output_mode, $filename=null, $purpose=null)
static zip($a_dir, $a_file, $compress_content=false)
zips given directory/file into given zip.file
static makeDirParents($a_dir)
Create a new directory and all parent directories.
$key
Definition: croninfo.php:18
const PDF_USER_RESULT
PDF Purposes.
catch(Exception $e) $message
$user
Definition: migrateto20.php:57
$row
$query
global $DIC
Definition: saml.php:7
$ilUser
Definition: imgupload.php:18