ILIAS  Release_4_4_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
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';
35  const TEST_OVERVIEW_PDF_POSTFIX = '.pdf';
36 
37  const TEST_OVERVIEW_HTML_FILENAME = 'results_overview_pdf_v';
38  const TEST_OVERVIEW_HTML_POSTFIX = '.html';
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;
83  protected $archive_data_index;
85  protected $ilDB;
87  #endregion
88 
94  public function __construct($test_obj_id)
95  {
97  global $ilias;
98  $this->external_directory_path = $ilias->ini_ilias->readVariable('clients','datadir');
99  $this->client_id = $ilias->client_id;
100  $this->test_obj_id = $test_obj_id;
101  $this->ilDB = $ilias->db;
102 
103  $this->archive_data_index = $this->readArchiveDataIndex();
104  }
105 
106  #region API methods
107 
120  public function handInParticipantSubmission($active_fi, $pass, $pdf_path, $html_string)
121  {
123  $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
124 
125  $pdf_new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
127  copy( $pdf_path, $pdf_new_path );
128 # /home/mbecker/public_html/ilias/trunk-primary/extern/default/tst_data/archive/tst_350/2013/09/19/80_1_root_user_/test_submission.pdf
129  $html_new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
131  file_put_contents($html_new_path, $html_string);
132 
133  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $pdf_new_path );
134  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $html_new_path );
135  }
136 
146  public function handInParticipantQuestionMaterial($active_fi, $pass, $question_fi, $original_filename, $file_path)
147  {
149  $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
150 
151  $pass_question_directory = $this->getPassDataDirectory( $active_fi, $pass )
152  . self::DIR_SEP . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
153  if ( !is_dir( $pass_question_directory ) )
154  {
155  mkdir( $pass_question_directory , 0777, true );
156  }
157 
158  copy( $file_path, $pass_question_directory . self::DIR_SEP. $original_filename );
159 
160  $this->logArchivingProcess(
161  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
162  . $pass_question_directory . self::DIR_SEP. $original_filename
163  );
164  }
165 
176  public function handInParticipantMisc($active_fi, $pass, $original_filename, $file_path)
177  {
179  $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
180  $new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP . $original_filename;
181  copy( $file_path, $new_path );
182  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $new_path );
183  }
184 
191  public function handInTestBestSolution($html_string, $pdf_path)
192  {
194 
195  $best_solution_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_BEST_SOLUTION_PATH_COMPONENT;
196  if ( !is_dir( $best_solution_path ) )
197  {
198  mkdir( $best_solution_path , 0777, true );
199  }
200 
201  file_put_contents( $best_solution_path . self::DIR_SEP . self::HTML_BEST_SOLUTION_FILENAME, $html_string );
202 
203  copy( $pdf_path, $best_solution_path . self::DIR_SEP . self::PDF_BEST_SOLUTION_FILENAME );
204 
205  $this->logArchivingProcess(
206  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
207  . $best_solution_path . self::DIR_SEP . self::HTML_BEST_SOLUTION_FILENAME);
208 
209  $this->logArchivingProcess(
210  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
211  . $best_solution_path . self::DIR_SEP . self::PDF_BEST_SOLUTION_FILENAME);
212  }
213 
221  public function handInBestSolutionQuestionMaterial($question_fi, $orginial_filename, $file_path)
222  {
224 
225  $best_solution_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_BEST_SOLUTION_PATH_COMPONENT;
226  if ( !is_dir( $best_solution_path ) )
227  {
228  mkdir( $best_solution_path , 0777, true);
229  }
230 
231  $materials_path = $best_solution_path . self::DIR_SEP . self::TEST_MATERIALS_PATH_COMPONENT;
232  if ( !is_dir( $materials_path ) )
233  {
234  mkdir( $materials_path , 0777, true);
235  }
236 
237  $question_materials_path = $materials_path . self::DIR_SEP . self::QUESTION_PATH_COMPONENT_PREFIX . $question_fi;
238  if ( !is_dir( $question_materials_path ) )
239  {
240  mkdir( $question_materials_path , 0777, true);
241  }
242 
243  copy( $file_path, $question_materials_path . self::DIR_SEP . $orginial_filename );
244 
245  $this->logArchivingProcess(
246  date(self::LOG_DTSGROUP_FORMAT) . self::LOG_ADDITION_STRING
247  . $question_materials_path . self::DIR_SEP . $orginial_filename);
248  }
249 
259  public function handInTestResult($active_fi, $pass, $pdf_path)
260  {
262  $this->ensurePassDataDirectoryIsAvailable( $active_fi, $pass );
263  $new_path = $this->getPassDataDirectory($active_fi, $pass) . self::DIR_SEP
264  . self::TEST_RESULT_FILENAME . ($this->countFilesInDirectory( $this->getPassDataDirectory($active_fi, $pass), self::TEST_RESULT_FILENAME ))
265  . self::TEST_RESULT_POSTFIX;
266  copy( $pdf_path, $new_path );
267  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $new_path );
268  }
269 
276  public function handInTestResultsOverview($html_string, $pdf_path)
277  {
279  $new_pdf_path = $this->getTestArchive() . self::DIR_SEP
280  . self::TEST_OVERVIEW_PDF_FILENAME
281  . $this->countFilesInDirectory($this->getTestArchive(), self::TEST_OVERVIEW_PDF_FILENAME) . self::TEST_OVERVIEW_PDF_POSTFIX;
282  copy( $pdf_path, $new_pdf_path );
283  $html_path = $this->getTestArchive() . self::DIR_SEP . self::TEST_OVERVIEW_HTML_FILENAME
284  . $this->countFilesInDirectory($this->getTestArchive(), self::TEST_OVERVIEW_HTML_FILENAME) . self::TEST_OVERVIEW_HTML_POSTFIX;
285  file_put_contents($html_path, $html_string);
286 
287  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $new_pdf_path );
288  $this->logArchivingProcess( date( self::LOG_DTSGROUP_FORMAT ) . self::LOG_ADDITION_STRING . $html_path );
289  }
290 
291  #endregion
292 
293  #region TestArchive
294  // The TestArchive lives here: <external directory>/<client>/tst_data/archive/tst_<obj_id>/
295 
301  protected function hasTestArchive()
302  {
303  return is_dir($this->getTestArchive());
304  }
305 
309  protected function createArchiveForTest()
310  {
311  mkdir( $this->getTestArchive(), 0777, true );
312  }
313 
319  protected function getTestArchive()
320  {
321  $test_archive_directory = $this->external_directory_path . self::DIR_SEP . $this->client_id . self::DIR_SEP . 'tst_data'
322  . self::DIR_SEP . 'archive' . self::DIR_SEP . 'tst_'.$this->test_obj_id;
323  return $test_archive_directory;
324  }
325 
333  protected function ensureTestArchiveIsAvailable()
334  {
335  if (!$this->hasTestArchive())
336  {
337  $this->createArchiveForTest();
338  }
339  return;
340  }
341 
347  public function updateTestArchive()
348  {
349  $query = 'SELECT * FROM ass_log WHERE obj_fi = ' . $this->ilDB->quote($this->test_obj_id, 'integer');
350  $result = $this->ilDB->query($query);
351 
352  $outfile_lines = '';
354  while ($row = $this->ilDB->fetchAssoc($result))
355  {
356  $outfile_lines .= "\r\n" . implode("\t", $row);
357  }
358  file_put_contents($this->getTestArchive() . self::DIR_SEP . self::TEST_LOG_FILENAME, $outfile_lines);
359 
360  // Generate test pass overview
361  $test = new ilObjTest($this->test_obj_id, false);
362  $gui = new ilObjTestGUI();
363  $gui->object = $test;
364  $array_of_actives = array();
365  $participants = $test->getParticipants();
366 
367  foreach($participants as $key => $value)
368  {
369  $array_of_actives[] = $key;
370  }
371  $output_template = $gui->createUserResults(true, false, true, $array_of_actives);
372 
373  require_once 'class.ilTestPDFGenerator.php';
374  $filename = $this->getTestArchive() . self::DIR_SEP . 'participant_pass_overview.pdf';
376 
377  return;
378  }
379 
381  {
382  if (!$this->hasZipExportDirectory())
383  {
384  $this->createZipExportDirectory();
385  }
386  }
387 
393  public function hasZipExportDirectory()
394  {
395  return is_dir($this->getZipExportDirectory());
396  }
397 
398  protected function createZipExportDirectory()
399  {
400  mkdir($this->getZipExportDirectory(), 0777, true);
401  }
402 
408  public function getZipExportDirectory()
409  {
410  return $this->external_directory_path . self::DIR_SEP . $this->client_id . self::DIR_SEP . 'tst_data'
411  . self::DIR_SEP . self::EXPORT_DIRECTORY . self::DIR_SEP . 'tst_' . $this->test_obj_id;
412  }
413 
419  public function compressTestArchive()
420  {
421  $this->updateTestArchive();
423 
424  $zip_output_path = $this->getZipExportDirectory();
425  $zip_output_filename = 'test_archive_obj_'.$this->test_obj_id . '_' . time() . '_.zip';
426 
427  ilUtil::zip($this->getTestArchive(), $zip_output_path . self::DIR_SEP . $zip_output_filename, true);
428  return;
429  }
430 
431  #endregion
432 
433  #region PassDataDirectory
434  // The pass data directory contains all data relevant for a participants pass.
435  // In addition to the test-archive-directory, this directory lives here:
436  // .../<year>/<month>/<day>/<ActiveFi>_<Pass>[_<Lastname>][_<Firstname>][_<Matriculation>]/
437  // Lastname, Firstname and Matriculation are not mandatory in the directory name.
438 
447  protected function hasPassDataDirectory($active_fi, $pass)
448  {
449  $pass_data_dir = $this->getPassDataDirectory($active_fi, $pass);
450  return is_dir($this->getPassDataDirectory($active_fi, $pass));
451  }
452 
461  protected function createPassDataDirectory($active_fi, $pass)
462  {
463  mkdir($this->getPassDataDirectory($active_fi, $pass), 0777, true);
464  return;
465  }
466 
475  protected function getPassDataDirectory($active_fi, $pass)
476  {
477  foreach ($this->archive_data_index as $data_index_entry)
478  {
479  if ( $data_index_entry != null && $data_index_entry['identifier'] == $active_fi.'|'.$pass )
480  {
481  array_shift($data_index_entry);
482  return $this->getTestArchive() . self::DIR_SEP . implode(self::DIR_SEP, $data_index_entry);
483  }
484  }
485  global $ilUser;
486  $this->appendToArchiveDataIndex(date(DATE_ISO8601), $active_fi, $pass, $ilUser->getFirstname(), $ilUser->getLastname(), $ilUser->getMatriculation());
487  $data_index_entry = $this->getPassDataDirectory($active_fi, $pass);
488  @array_shift($data_index_entry);
489  return $this->getTestArchive() . self::DIR_SEP . implode(self::DIR_SEP, $data_index_entry);
490  }
491 
502  protected function ensurePassDataDirectoryIsAvailable($active_fi, $pass)
503  {
504  if (!$this->hasPassDataDirectory( $active_fi, $pass ))
505  {
506  $this->createPassDataDirectory( $active_fi, $pass );
507  }
508  return;
509  }
510 
511  #endregion
512 
513  #region PassMaterialsDirectory
514 
523  protected function hasPassMaterialsDirectory($active_fi, $pass)
524  {
526  if ( @is_dir( $this->getPassMaterialsDirectory($active_fi, $pass)) )
527  {
528  return true;
529  }
530  return false;
531  }
532 
541  protected function createPassMaterialsDirectory($active_fi, $pass)
542  {
543  // Data are taken from the current user as the implementation expects the first interaction of the pass
544  // takes place from the usage/behaviour of the current user.
545  global $ilUser;
546 
547  $this->appendToArchiveDataIndex(date('Y'), $active_fi, $pass, $ilUser->getFirstname(), $ilUser->getLastname(), $ilUser->getMatriculation());
548  mkdir($this->getPassMaterialsDirectory($active_fi, $pass) , 0777, true );
549  }
550 
559  protected function getPassMaterialsDirectory($active_fi, $pass)
560  {
561  $pass_data_directory = $this->getPassMaterialsDirectory($active_fi, $pass);
562  return $pass_data_directory . self::DIR_SEP . self::PASS_MATERIALS_PATH_COMPONENT;
563  }
564 
574  protected function ensurePassMaterialsDirectoryIsAvailable($active_fi, $pass)
575  {
576  if (!$this->hasPassMaterialsDirectory( $active_fi, $pass ))
577  {
578  $this->createPassMaterialsDirectory( $active_fi, $pass );
579  }
580  }
581 
582  #endregion
583 
589  protected function readArchiveDataIndex()
590  {
595  $data_index_file = $this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME;
596 
597  $contents = array();
598 
600  if ( @file_exists( $data_index_file ) )
601  {
602  $lines = explode( "\n", file_get_contents( $data_index_file ) );
603  foreach ($lines as $line)
604  {
605  $line_items = explode('|', $line);
606  $line_data['identifier'] = $line_items[0] . '|' . $line_items[1];
607  $line_data['yyyy'] = $line_items[2];
608  $line_data['mm'] = $line_items[3];
609  $line_data['dd'] = $line_items[4];
610  $line_data['directory'] = $line_items[5];
611  $contents[] = $line_data;
612  }
613  }
614  return $contents;
615  }
616 
629  protected function appendToArchiveDataIndex($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
630  {
631  $line = $this->determinePassDataPath( $date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation );
632 
633  $this->archive_data_index[] = $line;
634  $output_contents = '';
635 
636  foreach ($this->archive_data_index as $line_data)
637  {
638  if($line_data['identifier'] == "|")
639  {
640  continue;
641  }
642  $output_contents .= implode('|', $line_data) . "\n";
643  }
644 
645  file_put_contents($this->getTestArchive() . self::DIR_SEP . self::DATA_INDEX_FILENAME, $output_contents);
646  $this->readArchiveDataIndex();
647  return;
648  }
649 
662  protected function determinePassDataPath($date, $active_fi, $pass, $user_firstname, $user_lastname, $matriculation)
663  {
664  $date = date_create_from_format(DATE_ISO8601, $date);
665  $line = array(
666  'identifier' => $active_fi . '|' . $pass,
667  'yyyy' => date_format( $date, 'Y' ),
668  'mm' => date_format( $date, 'm' ),
669  'dd' => date_format( $date, 'd' ),
670  'directory' => $active_fi . '_' . $pass . '_' . $user_firstname . '_' . $user_lastname . '_' . $matriculation
671  );
672  return $line;
673  }
674 
682  protected function logArchivingProcess($message)
683  {
684  $archive = $this->getTestArchive() . self::DIR_SEP . self::ARCHIVE_LOG;
685  file_put_contents($archive, file_get_contents($archive) . "\n" . $message);
686 
687  return;
688  }
689 
698  protected function countFilesInDirectory($directory, $pattern = null)
699  {
700  $filecount = 0;
701 
703  if ($handle = opendir( $directory ))
704  {
705  while (($file = readdir( $handle )) !== false)
706  {
707  if (!in_array( $file, array( '.', '..' ) ) && !is_dir( $directory . $file ))
708  {
709  if ($pattern && strpos($file, $pattern) === 0 )
710  {
711  $filecount++;
712  }
713  }
714  }
715  }
716  return $filecount;
717  }
718 }