ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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 $ilias;
103 $this->external_directory_path = $ilias->ini_ilias->readVariable('clients','datadir');
104 $this->client_id = $ilias->client_id;
105 $this->test_obj_id = $test_obj_id;
106 $this->ilDB = $ilias->db;
107
108 $this->archive_data_index = $this->readArchiveDataIndex();
109
110 $this->participantData = null;
111 }
112
116 public function getParticipantData()
117 {
119 }
120
125 {
126 $this->participantData = $participantData;
127 }
128
129 #region API methods
130
143 public function handInParticipantSubmission($active_fi, $pass, $pdf_path, $html_string)
144 {
146 $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
147
148 $pdf_new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
150 copy( $pdf_path, $pdf_new_path );
151# /home/mbecker/public_html/ilias/trunk-primary/extern/default/tst_data/archive/tst_350/2013/09/19/80_1_root_user_/test_submission.pdf
152 $html_new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
154 file_put_contents($html_new_path, $html_string);
155
156 $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $pdf_new_path );
157 $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $html_new_path );
158 }
159
169 public function handInParticipantQuestionMaterial($active_fi, $pass, $question_fi, $original_filename, $file_path)
170 {
172 $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
173
174 $pass_question_directory = $this->getPassDataDirectory( $active_fi, $pass )
175 . self::DIR_SEP . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
176 if ( !is_dir( $pass_question_directory ) )
177 {
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 {
221 mkdir( $best_solution_path , 0777, true );
222 }
223
224 file_put_contents( $best_solution_path . self::DIR_SEP . self::HTML_BEST_SOLUTION_FILENAME, $html_string );
225
226 copy( $pdf_path, $best_solution_path . self::DIR_SEP . self::PDF_BEST_SOLUTION_FILENAME );
227
228 $this->logArchivingProcess(
229 date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
230 . $best_solution_path . self::DIR_SEP . self::HTML_BEST_SOLUTION_FILENAME);
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
244 public function handInBestSolutionQuestionMaterial($question_fi, $orginial_filename, $file_path)
245 {
247
248 $best_solution_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_BEST_SOLUTION_PATH_COMPONENT;
249 if ( !is_dir( $best_solution_path ) )
250 {
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 {
257 mkdir( $materials_path , 0777, true);
258 }
259
260 $question_materials_path = $materials_path . self::DIR_SEP . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
261 if ( !is_dir( $question_materials_path ) )
262 {
263 mkdir( $question_materials_path , 0777, true);
264 }
265
266 copy( $file_path, $question_materials_path . self::DIR_SEP . $orginial_filename );
267
268 $this->logArchivingProcess(
269 date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
270 . $question_materials_path . self::DIR_SEP . $orginial_filename);
271 }
272
282 public function handInTestResult($active_fi, $pass, $pdf_path)
283 {
285 $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
286 $new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
287 . self::TEST_RESULT_FILENAME . ($this->countFilesInDirectory( $this->getPassDataDirectory($active_fi, $pass), self::TEST_RESULT_FILENAME ))
288 . self::TEST_RESULT_POSTFIX;
289 copy( $pdf_path, $new_path );
290 $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $new_path );
291 }
292
299 public function handInTestResultsOverview($html_string, $pdf_path)
300 {
302 $new_pdf_path = $this->getTestArchive() . self::DIR_SEP
303 . self::TEST_OVERVIEW_PDF_FILENAME
304 . $this->countFilesInDirectory($this->getTestArchive(), self::TEST_OVERVIEW_PDF_FILENAME) . self::TEST_OVERVIEW_PDF_POSTFIX;
305 copy( $pdf_path, $new_pdf_path );
306 $html_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_OVERVIEW_HTML_FILENAME
307 . $this->countFilesInDirectory($this->getTestArchive(), self::TEST_OVERVIEW_HTML_FILENAME) . self::TEST_OVERVIEW_HTML_POSTFIX;
308 file_put_contents($html_path, $html_string);
309
310 $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $new_pdf_path );
311 $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $html_path );
312 }
313
314 #endregion
315
316 #region TestArchive
317 // The TestArchive lives here: <external directory>/<client>/tst_data/archive/tst_<obj_id>/
318
324 protected function hasTestArchive()
325 {
326 return is_dir($this->getTestArchive());
327 }
328
332 protected function createArchiveForTest()
333 {
335 //mkdir( $this->getTestArchive(), 0777, true );
336 }
337
343 protected function getTestArchive()
344 {
345 $test_archive_directory = $this->external_directory_path . self::DIR_SEP . $this->client_id . self::DIR_SEP . 'tst_data'
346 . self::DIR_SEP . 'archive' . self::DIR_SEP . 'tst_'.$this->test_obj_id;
347 return $test_archive_directory;
348 }
349
357 protected function ensureTestArchiveIsAvailable()
358 {
359 if (!$this->hasTestArchive())
360 {
361 $this->createArchiveForTest();
362 }
363 return;
364 }
365
371 public function updateTestArchive()
372 {
373 $query = 'SELECT * FROM ass_log WHERE obj_fi = ' . $this->ilDB->quote($this->test_obj_id, 'integer');
374 $result = $this->ilDB->query($query);
375
376 $outfile_lines = '';
378 while ($row = $this->ilDB->fetchAssoc($result))
379 {
380 $outfile_lines .= "\r\n" . implode("\t", $row);
381 }
382 file_put_contents($this->getTestArchive() . self::DIR_SEP . self::TEST_LOG_FILENAME, $outfile_lines);
383
384 // Generate test pass overview
385 $test = new ilObjTest($this->test_obj_id, false);
386 $gui = new ilObjTestGUI();
387 $gui->object = $test;
388 $array_of_actives = array();
389 $participants = $test->getParticipants();
390
391 foreach($participants as $key => $value)
392 {
393 $array_of_actives[] = $key;
394 }
395 $output_template = $gui->createUserResults(true, false, true, $array_of_actives);
396
397 require_once '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())
407 {
409 }
410 }
411
417 public function hasZipExportDirectory()
418 {
419 return is_dir($this->getZipExportDirectory());
420 }
421
422 protected function createZipExportDirectory()
423 {
424 mkdir($this->getZipExportDirectory(), 0777, true);
425 }
426
432 public function getZipExportDirectory()
433 {
434 return $this->external_directory_path . self::DIR_SEP . $this->client_id . self::DIR_SEP . 'tst_data'
435 . self::DIR_SEP . self::EXPORT_DIRECTORY . self::DIR_SEP . 'tst_' . $this->test_obj_id;
436 }
437
443 public function compressTestArchive()
444 {
445 $this->updateTestArchive();
447
448 $zip_output_path = $this->getZipExportDirectory();
449 $zip_output_filename = 'test_archive_obj_'.$this->test_obj_id . '_' . time() . '_.zip';
450
451 ilUtil::zip($this->getTestArchive(), $zip_output_path . self::DIR_SEP . $zip_output_filename, true);
452 return;
453 }
454
455 #endregion
456
457 #region PassDataDirectory
458 // The pass data directory contains all data relevant for a participants pass.
459 // In addition to the test-archive-directory, this directory lives here:
460 // .../<year>/<month>/<day>/<ActiveFi>_<Pass>[_<Lastname>][_<Firstname>][_<Matriculation>]/
461 // Lastname, Firstname and Matriculation are not mandatory in the directory name.
462
471 protected function hasPassDataDirectory($active_fi, $pass)
472 {
473 $pass_data_dir = $this->getPassDataDirectory($active_fi, $pass);
474 return is_dir($this->getPassDataDirectory($active_fi, $pass));
475 }
476
485 protected function createPassDataDirectory($active_fi, $pass)
486 {
487 mkdir($this->getPassDataDirectory($active_fi, $pass), 0777, true);
488 return;
489 }
490
491 private function buildPassDataDirectory($active_fi, $pass)
492 {
493 foreach ($this->archive_data_index as $data_index_entry)
494 {
495 if ( $data_index_entry != null && $data_index_entry['identifier'] == $active_fi.'|'.$pass )
496 {
497 array_shift($data_index_entry);
498 return $this->getTestArchive() . self::DIR_SEP . implode(self::DIR_SEP, $data_index_entry);
499 }
500 }
501
502 return null;
503 }
504
513 protected function getPassDataDirectory($active_fi, $pass)
514 {
515 $passDataDir = $this->buildPassDataDirectory($active_fi, $pass);
516
517 if( !$passDataDir )
518 {
519 if( $this->getParticipantData() )
520 {
521 $usrData = $this->getParticipantData()->getUserDataByActiveId($active_fi);
522 $user = new ilObjUser();
523 $user->setFirstname($usrData['firstname']);
524 $user->setLastname($usrData['lastname']);
525 $user->setMatriculation($usrData['matriculation']);
526 $user->setFirstname($usrData['firstname']);
527 }
528 else
529 {
530 global $ilUser;
531 $user = $ilUser;
532 }
533
535 date(DATE_ISO8601), $active_fi, $pass,
536 $user->getFirstname(), $user->getLastname(), $user->getMatriculation()
537 );
538
539 $passDataDir = $this->buildPassDataDirectory($active_fi, $pass);
540 }
541
542 return $passDataDir;
543 }
544
555 protected function ensurePassDataDirectoryIsAvailable($active_fi, $pass)
556 {
557 if (!$this->hasPassDataDirectory( $active_fi, $pass ))
558 {
559 $this->createPassDataDirectory( $active_fi, $pass );
560 }
561 return;
562 }
563
564 #endregion
565
566 #region PassMaterialsDirectory
567
576 protected function hasPassMaterialsDirectory($active_fi, $pass)
577 {
579 if ( @is_dir( $this->getPassMaterialsDirectory($active_fi, $pass)) )
580 {
581 return true;
582 }
583 return false;
584 }
585
594 protected function createPassMaterialsDirectory($active_fi, $pass)
595 {
596 // Data are taken from the current user as the implementation expects the first interaction of the pass
597 // takes place from the usage/behaviour of the current user.
598
599 if( $this->getParticipantData() )
600 {
601 $usrData = $this->getParticipantData()->getUserDataByActiveId($active_fi);
602 $user = new ilObjUser();
603 $user->setFirstname($usrData['firstname']);
604 $user->setLastname($usrData['lastname']);
605 $user->setMatriculation($usrData['matriculation']);
606 $user->setFirstname($usrData['firstname']);
607 }
608 else
609 {
610 global $ilUser;
611 $user = $ilUser;
612 }
613
615 date('Y'), $active_fi, $pass, $user->getFirstname(), $user->getLastname(), $user->getMatriculation()
616 );
617 mkdir($this->getPassMaterialsDirectory($active_fi, $pass) , 0777, true );
618 }
619
628 protected function getPassMaterialsDirectory($active_fi, $pass)
629 {
630 $pass_data_directory = $this->getPassMaterialsDirectory($active_fi, $pass);
631 return $pass_data_directory . self::DIR_SEP . self::PASS_MATERIALS_PATH_COMPONENT;
632 }
633
643 protected function ensurePassMaterialsDirectoryIsAvailable($active_fi, $pass)
644 {
645 if (!$this->hasPassMaterialsDirectory( $active_fi, $pass ))
646 {
647 $this->createPassMaterialsDirectory( $active_fi, $pass );
648 }
649 }
650
651 #endregion
652
658 protected function readArchiveDataIndex()
659 {
664 $data_index_file = $this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME;
665
666 $contents = array();
667
669 if ( @file_exists( $data_index_file ) )
670 {
671 $lines = explode( "\n", file_get_contents( $data_index_file ) );
672 foreach ($lines as $line)
673 {
674 $line_items = explode('|', $line);
675 $line_data['identifier'] = $line_items[0] . '|' . $line_items[1];
676 $line_data['yyyy'] = $line_items[2];
677 $line_data['mm'] = $line_items[3];
678 $line_data['dd'] = $line_items[4];
679 $line_data['directory'] = $line_items[5];
680 $contents[] = $line_data;
681 }
682 }
683 return $contents;
684 }
685
698 protected function appendToArchiveDataIndex($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
699 {
700 $line = $this->determinePassDataPath( $date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation );
701
702 $this->archive_data_index[] = $line;
703 $output_contents = '';
704
705 foreach ($this->archive_data_index as $line_data)
706 {
707 if($line_data['identifier'] == "|")
708 {
709 continue;
710 }
711 $output_contents .= implode('|', $line_data) . "\n";
712 }
713
714 file_put_contents($this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME, $output_contents);
715 $this->readArchiveDataIndex();
716 return;
717 }
718
731 protected function determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
732 {
733 $date = date_create_from_format(DATE_ISO8601, $date);
734 $line = array(
735 'identifier' => $active_fi . '|' . $pass,
736 'yyyy' => date_format( $date, 'Y' ),
737 'mm' => date_format( $date, 'm' ),
738 'dd' => date_format( $date, 'd' ),
739 'directory' => $active_fi . '_' . $pass . '_' . $user_firstname . '_' . $user_lastname . '_' . $matriculation
740 );
741 return $line;
742 }
743
751 protected function logArchivingProcess($message)
752 {
753 $archive = $this->getTestArchive() . self::DIR_SEP . self::ARCHIVE_LOG;
754 if( file_exists($archive) )
755 {
756 $content = file_get_contents($archive). "\n" . $message;
757 }
758 else
759 {
760 $content = $message;
761 }
762
763 file_put_contents($archive, $content);
764 }
765
774 protected function countFilesInDirectory($directory, $pattern = null)
775 {
776 $filecount = 0;
777
779 if ($handle = opendir( $directory ))
780 {
781 while (($file = readdir( $handle )) !== false)
782 {
783 if (!in_array( $file, array( '.', '..' ) ) && !is_dir( $directory . $file ))
784 {
785 if ($pattern && strpos($file, $pattern) === 0 )
786 {
787 $filecount++;
788 }
789 }
790 }
791 }
792 return $filecount;
793 }
794}
$result
print $file
$test
Definition: Utf8Test.php:85
$filename
Definition: buildRTE.php:89
Database Wrapper.
Definition: class.ilDB.php:29
query($sql, $a_handle_error=true)
Query.
fetchAssoc($a_set)
Fetch row as associative array from result set.
quote($a_query, $a_type=null)
Wrapper for quote method.
Class ilObjTestGUI.
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)
static zip($a_dir, $a_file, $compress_content=false)
static makeDirParents($a_dir)
Create a new directory and all parent directories.
global $ilUser
Definition: imgupload.php:15