ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
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 $filename = realpath($this->getTestArchive()) . self::DIR_SEP . 'participant_pass_overview.pdf';
399
400 return;
401 }
402
404 {
405 if (!$this->hasZipExportDirectory()) {
407 }
408 }
409
415 public function hasZipExportDirectory()
416 {
417 return is_dir($this->getZipExportDirectory());
418 }
419
420 protected function createZipExportDirectory()
421 {
422 mkdir($this->getZipExportDirectory(), 0777, true);
423 }
424
430 public function getZipExportDirectory()
431 {
432 return $this->external_directory_path . self::DIR_SEP . $this->client_id . self::DIR_SEP . 'tst_data'
433 . self::DIR_SEP . self::EXPORT_DIRECTORY . self::DIR_SEP . 'tst_' . $this->test_obj_id;
434 }
435
441 public function compressTestArchive()
442 {
443 $this->updateTestArchive();
445
446 $zip_output_path = $this->getZipExportDirectory();
447 $zip_output_filename = 'test_archive_obj_' . $this->test_obj_id . '_' . time() . '_.zip';
448
449 ilUtil::zip($this->getTestArchive(), $zip_output_path . self::DIR_SEP . $zip_output_filename, true);
450 return;
451 }
452
453 #endregion
454
455 #region PassDataDirectory
456 // The pass data directory contains all data relevant for a participants pass.
457 // In addition to the test-archive-directory, this directory lives here:
458 // .../<year>/<month>/<day>/<ActiveFi>_<Pass>[_<Lastname>][_<Firstname>][_<Matriculation>]/
459 // Lastname, Firstname and Matriculation are not mandatory in the directory name.
460
469 protected function hasPassDataDirectory($active_fi, $pass)
470 {
471 $pass_data_dir = $this->getPassDataDirectory($active_fi, $pass);
472 return is_dir($this->getPassDataDirectory($active_fi, $pass));
473 }
474
483 protected function createPassDataDirectory($active_fi, $pass)
484 {
485 mkdir($this->getPassDataDirectory($active_fi, $pass), 0777, true);
486 return;
487 }
488
489 private function buildPassDataDirectory($active_fi, $pass)
490 {
491 foreach ($this->archive_data_index as $data_index_entry) {
492 if ($data_index_entry != null && $data_index_entry['identifier'] == $active_fi . '|' . $pass) {
493 array_shift($data_index_entry);
494 return $this->getTestArchive() . self::DIR_SEP . implode(self::DIR_SEP, $data_index_entry);
495 }
496 }
497
498 return null;
499 }
500
509 protected function getPassDataDirectory($active_fi, $pass)
510 {
511 $passDataDir = $this->buildPassDataDirectory($active_fi, $pass);
512
513 if (!$passDataDir) {
514 if ($this->getParticipantData()) {
515 $usrData = $this->getParticipantData()->getUserDataByActiveId($active_fi);
516 $user = new ilObjUser();
517 $user->setFirstname($usrData['firstname']);
518 $user->setLastname($usrData['lastname']);
519 $user->setMatriculation($usrData['matriculation']);
520 $user->setFirstname($usrData['firstname']);
521 } else {
522 global $DIC;
523 $ilUser = $DIC['ilUser'];
524 $user = $ilUser;
525 }
526
528 date(DATE_ISO8601),
529 $active_fi,
530 $pass,
531 $user->getFirstname(),
532 $user->getLastname(),
533 $user->getMatriculation()
534 );
535
536 $passDataDir = $this->buildPassDataDirectory($active_fi, $pass);
537 }
538
539 return $passDataDir;
540 }
541
552 protected function ensurePassDataDirectoryIsAvailable($active_fi, $pass)
553 {
554 if (!$this->hasPassDataDirectory($active_fi, $pass)) {
555 $this->createPassDataDirectory($active_fi, $pass);
556 }
557 return;
558 }
559
560 #endregion
561
562 #region PassMaterialsDirectory
563
572 protected function hasPassMaterialsDirectory($active_fi, $pass)
573 {
575 if (@is_dir($this->getPassMaterialsDirectory($active_fi, $pass))) {
576 return true;
577 }
578 return false;
579 }
580
589 protected function createPassMaterialsDirectory($active_fi, $pass)
590 {
591 // Data are taken from the current user as the implementation expects the first interaction of the pass
592 // takes place from the usage/behaviour of the current user.
593
594 if ($this->getParticipantData()) {
595 $usrData = $this->getParticipantData()->getUserDataByActiveId($active_fi);
596 $user = new ilObjUser();
597 $user->setFirstname($usrData['firstname']);
598 $user->setLastname($usrData['lastname']);
599 $user->setMatriculation($usrData['matriculation']);
600 $user->setFirstname($usrData['firstname']);
601 } else {
602 global $DIC;
603 $ilUser = $DIC['ilUser'];
604 $user = $ilUser;
605 }
606
608 date('Y'),
609 $active_fi,
610 $pass,
611 $user->getFirstname(),
612 $user->getLastname(),
613 $user->getMatriculation()
614 );
615 mkdir($this->getPassMaterialsDirectory($active_fi, $pass), 0777, true);
616 }
617
626 protected function getPassMaterialsDirectory($active_fi, $pass)
627 {
628 $pass_data_directory = $this->getPassMaterialsDirectory($active_fi, $pass);
629 return $pass_data_directory . self::DIR_SEP . self::PASS_MATERIALS_PATH_COMPONENT;
630 }
631
641 protected function ensurePassMaterialsDirectoryIsAvailable($active_fi, $pass)
642 {
643 if (!$this->hasPassMaterialsDirectory($active_fi, $pass)) {
644 $this->createPassMaterialsDirectory($active_fi, $pass);
645 }
646 }
647
648 #endregion
649
655 protected function readArchiveDataIndex()
656 {
661 $data_index_file = $this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME;
662
663 $contents = array();
664
666 if (@file_exists($data_index_file)) {
667 $lines = explode("\n", file_get_contents($data_index_file));
668 foreach ($lines as $line) {
669 $line_items = explode('|', $line);
670 $line_data['identifier'] = $line_items[0] . '|' . $line_items[1];
671 $line_data['yyyy'] = $line_items[2];
672 $line_data['mm'] = $line_items[3];
673 $line_data['dd'] = $line_items[4];
674 $line_data['directory'] = $line_items[5];
675 $contents[] = $line_data;
676 }
677 }
678 return $contents;
679 }
680
693 protected function appendToArchiveDataIndex($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
694 {
695 $line = $this->determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation);
696
697 $this->archive_data_index[] = $line;
698 $output_contents = '';
699
700 foreach ($this->archive_data_index as $line_data) {
701 if ($line_data['identifier'] == "|") {
702 continue;
703 }
704 $output_contents .= implode('|', $line_data) . "\n";
705 }
706
707 file_put_contents($this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME, $output_contents);
708 $this->readArchiveDataIndex();
709 return;
710 }
711
724 protected function determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
725 {
726 $date = date_create_from_format(DATE_ISO8601, $date);
727 $line = array(
728 'identifier' => $active_fi . '|' . $pass,
729 'yyyy' => date_format($date, 'Y'),
730 'mm' => date_format($date, 'm'),
731 'dd' => date_format($date, 'd'),
732 'directory' => $active_fi . '_' . $pass . '_' . $user_firstname . '_' . $user_lastname . '_' . $matriculation
733 );
734 return $line;
735 }
736
744 protected function logArchivingProcess($message)
745 {
746 $archive = $this->getTestArchive() . self::DIR_SEP . self::ARCHIVE_LOG;
747 if (file_exists($archive)) {
748 $content = file_get_contents($archive) . "\n" . $message;
749 } else {
750 $content = $message;
751 }
752
753 file_put_contents($archive, $content);
754 }
755
764 protected function countFilesInDirectory($directory, $pattern = null)
765 {
766 $filecount = 0;
767
769 if ($handle = opendir($directory)) {
770 while (($file = readdir($handle)) !== false) {
771 if (!in_array($file, array( '.', '..' )) && !is_dir($directory . $file)) {
772 if ($pattern && strpos($file, $pattern) === 0) {
773 $filecount++;
774 }
775 }
776 }
777 }
778 return $filecount;
779 }
780}
$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.
const PDF_USER_RESULT
PDF Purposes.
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$query
$ilUser
Definition: imgupload.php:18
$message
Definition: xapiexit.php:14
$DIC
Definition: xapitoken.php:46