ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilTestResultsImportParser.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
24 
26 {
27  private $table;
30  private string $user_criteria_field = '';
31  private string $user_criteria_type = '';
32  private bool $user_criteria_checked = false;
33 
35 
36  private string $import_directory;
37 
41  public function __construct(
42  ?string $a_xml_file,
43  private ilObjTest $test_obj,
44  private ilDBInterface $db,
45  private TestLogger $log,
46  private ResourceStorage $irss
47  ) {
48  parent::__construct($a_xml_file, true);
49  $this->table = '';
50  $this->active_id_mapping = [];
51  $this->question_id_mapping = [];
52  $this->src_pool_def_id_mapping = [];
53  $this->import_directory = dirname($a_xml_file);
54  }
55 
59  public function getQuestionIdMapping(): array
60  {
62  }
63 
67  public function setQuestionIdMapping(array $question_id_mapping): void
68  {
69  $this->question_id_mapping = $question_id_mapping;
70  }
71 
75  public function getSrcPoolDefIdMapping(): array
76  {
78  }
79 
83  public function setSrcPoolDefIdMapping(array $src_pool_def_id_mapping): void
84  {
85  $this->src_pool_def_id_mapping = $src_pool_def_id_mapping;
86  }
87 
93  public function setHandlers($a_xml_parser): void
94  {
95  xml_set_element_handler($a_xml_parser, $this->handlerBeginTag(...), $this->handlerEndTag(...));
96  xml_set_character_data_handler($a_xml_parser, $this->handlerParseCharacterData(...));
97  }
98 
102  public function handlerBeginTag($a_xml_parser, $a_name, $a_attribs): void
103  {
104  switch (strtolower($a_name)) {
105  case "results":
106  break;
107  case "row":
108  switch ($this->table) {
109  case 'tst_active':
110  if (!$this->user_criteria_checked) {
111  $this->user_criteria_checked = true;
112  if (isset($a_attribs['user_criteria'])
113  && $this->db->tableColumnExists('usr_data', $a_attribs['user_criteria'])) {
114  $analyzer = new ilDBAnalyzer();
115  $info = $analyzer->getFieldInformation('usr_data');
116  $this->user_criteria_field = $a_attribs['user_criteria'];
117  $this->user_criteria_type = $info[$a_attribs['user_criteria']]['type'];
118  }
119  }
120  $usr_id = ANONYMOUS_USER_ID;
121  if ($this->user_criteria_field !== '') {
122  $result = $this->db->queryF(
123  'SELECT usr_id FROM usr_data WHERE '
124  . $this->user_criteria_field . ' = %s',
125  [$this->user_criteria_type],
126  [$a_attribs[$this->user_criteria_field]]
127  );
128  if ($result->numRows()) {
129  $row = $this->db->fetchAssoc($result);
130  $usr_id = $row['usr_id'];
131  }
132  }
133  $next_id = $this->db->nextId('tst_active');
134 
135  $this->db->insert('tst_active', [
136  'active_id' => ['integer', $next_id],
137  'user_fi' => ['integer', $usr_id],
138  'anonymous_id' => ['text', strlen($a_attribs['anonymous_id']) ? $a_attribs['anonymous_id'] : null],
139  'test_fi' => ['integer', $this->test_obj->getTestId()],
140  'lastindex' => ['integer', $a_attribs['lastindex']],
141  'tries' => ['integer', $a_attribs['tries']],
142  'submitted' => ['integer', $a_attribs['submitted']],
143  'submittimestamp' => ['timestamp', strlen($a_attribs['submittimestamp']) ? $a_attribs['submittimestamp'] : null],
144  'tstamp' => ['integer', $a_attribs['tstamp']],
145  'importname' => ['text', $a_attribs['fullname']],
146  'last_finished_pass' => ['integer', $this->fetchLastFinishedPass($a_attribs)],
147  'last_started_pass' => ['integer', $this->fetchLastStartedPass($a_attribs)],
148  'answerstatusfilter' => ['integer', $this->fetchAttribute($a_attribs, 'answer_status_filter')],
149  'objective_container' => ['integer', $this->fetchAttribute($a_attribs, 'objective_container')]
150  ]);
151  $this->active_id_mapping[$a_attribs['active_id']] = $next_id;
152  break;
153  case 'tst_test_rnd_qst':
154  $nextId = $this->db->nextId('tst_test_rnd_qst');
155  $newActiveId = $this->active_id_mapping[$a_attribs['active_fi']];
156  $newQuestionId = $this->question_id_mapping[$a_attribs['question_fi']];
157  $newSrcPoolDefId = $this->src_pool_def_id_mapping[$a_attribs['src_pool_def_fi']];
158  $this->db->insert('tst_test_rnd_qst', [
159  'test_random_question_id' => ['integer', $nextId],
160  'active_fi' => ['integer', $newActiveId],
161  'question_fi' => ['integer', $newQuestionId],
162  'sequence' => ['integer', $a_attribs['sequence']],
163  'pass' => ['integer', $a_attribs['pass']],
164  'tstamp' => ['integer', $a_attribs['tstamp']],
165  'src_pool_def_fi' => ['integer', $newSrcPoolDefId]
166  ]);
167  break;
168  case 'tst_pass_result':
169  $affectedRows = $this->db->manipulateF(
170  "INSERT INTO tst_pass_result (active_fi, pass, points, maxpoints, questioncount, answeredquestions, workingtime, tstamp) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
171  [
172  'integer',
173  'integer',
174  'float',
175  'float',
176  'integer',
177  'integer',
178  'integer',
179  'integer'
180  ],
181  [
182  $this->active_id_mapping[$a_attribs['active_fi']],
183  strlen($a_attribs['pass']) ? $a_attribs['pass'] : 0,
184  ($a_attribs["points"]) ? $a_attribs["points"] : 0,
185  ($a_attribs["maxpoints"]) ? $a_attribs["maxpoints"] : 0,
186  $a_attribs["questioncount"],
187  $a_attribs["answeredquestions"],
188  ($a_attribs["workingtime"]) ? $a_attribs["workingtime"] : 0,
189  $a_attribs["tstamp"]
190  ]
191  );
192  break;
193  case 'tst_result_cache':
194  $affectedRows = $this->db->manipulateF(
195  "INSERT INTO tst_result_cache (active_fi, pass, max_points, reached_points, mark_short, mark_official, passed, failed, tstamp) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)",
196  [
197  'integer',
198  'integer',
199  'float',
200  'float',
201  'text',
202  'text',
203  'integer',
204  'integer',
205  'integer'
206  ],
207  [
208  $this->active_id_mapping[$a_attribs['active_fi']],
209  strlen($a_attribs['pass']) ? $a_attribs['pass'] : 0,
210  ($a_attribs["max_points"]) ? $a_attribs["max_points"] : 0,
211  ($a_attribs["reached_points"]) ? $a_attribs["reached_points"] : 0,
212  strlen($a_attribs["mark_short"]) ? $a_attribs["mark_short"] : " ",
213  strlen($a_attribs["mark_official"]) ? $a_attribs["mark_official"] : " ",
214  ($a_attribs["passed"]) ? 1 : 0,
215  ($a_attribs["failed"]) ? 1 : 0,
216  $a_attribs["tstamp"]
217  ]
218  );
219  break;
220  case 'tst_sequence':
221  $affectedRows = $this->db->insert("tst_sequence", [
222  "active_fi" => ["integer", $this->active_id_mapping[$a_attribs['active_fi']]],
223  "pass" => ["integer", $a_attribs['pass']],
224  "sequence" => ["clob", $a_attribs['sequence']],
225  "postponed" => ["text", (strlen($a_attribs['postponed'])) ? $a_attribs['postponed'] : null],
226  "hidden" => ["text", (strlen($a_attribs['hidden'])) ? $a_attribs['hidden'] : null],
227  "tstamp" => ["integer", $a_attribs['tstamp']]
228  ]);
229  break;
230  case 'tst_solutions':
231  $next_id = $this->db->nextId('tst_solutions');
232  $affectedRows = $this->db->insert("tst_solutions", [
233  "solution_id" => ["integer", $next_id],
234  "active_fi" => ["integer", $this->active_id_mapping[$a_attribs['active_fi']]],
235  "question_fi" => ["integer", $this->question_id_mapping[$a_attribs['question_fi']]],
236  "value1" => ["clob", $this->importParticipantsUploadedFiles($a_attribs)],
237  "value2" => ["clob", (strlen($a_attribs['value2'])) ? $a_attribs['value2'] : null],
238  "pass" => ["integer", $a_attribs['pass']],
239  "tstamp" => ["integer", $a_attribs['tstamp']]
240  ]);
241  break;
242  case 'tst_test_result':
243  $next_id = $this->db->nextId('tst_test_result');
244  $affectedRows = $this->db->manipulateF(
245  "INSERT INTO tst_test_result (test_result_id, active_fi, question_fi, points, pass, manual, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s)",
246  ['integer', 'integer','integer', 'float', 'integer', 'integer','integer'],
247  [$next_id, $this->active_id_mapping[$a_attribs['active_fi']], $this->question_id_mapping[$a_attribs['question_fi']], $a_attribs['points'], $a_attribs['pass'], (strlen($a_attribs['manual'])) ? $a_attribs['manual'] : 0, $a_attribs['tstamp']]
248  );
249  break;
250  case 'tst_times':
251  $next_id = $this->db->nextId('tst_times');
252  $affectedRows = $this->db->manipulateF(
253  "INSERT INTO tst_times (times_id, active_fi, started, finished, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
254  ['integer', 'integer', 'timestamp', 'timestamp', 'integer', 'integer'],
255  [$next_id, $this->active_id_mapping[$a_attribs['active_fi']], $a_attribs['started'], $a_attribs['finished'], $a_attribs['pass'], $a_attribs['tstamp']]
256  );
257  break;
258  }
259  break;
260  default:
261  $this->table = $a_name;
262  break;
263  }
264  }
265 
269  public function handlerEndTag($a_xml_parser, $a_name): void
270  {
271  switch (strtolower($a_name)) {
272  case 'tst_active':
273  $this->log->info('active id mapping: ' . print_r($this->active_id_mapping, true));
274  break;
275  case 'tst_test_question':
276  $this->log->info('question id mapping: ' . print_r($this->question_id_mapping, true));
277  break;
278  }
279  }
280 
284  public function handlerParseCharacterData($a_xml_parser, $a_data): void
285  {
286  // do nothing
287  }
288 
289  private function fetchAttribute($attributes, $name)
290  {
291  if (isset($attributes[$name])) {
292  return $attributes[$name];
293  }
294 
295  return null;
296  }
297 
298  private function fetchLastFinishedPass($attribs): ?int
299  {
300  if (isset($attribs['last_finished_pass'])) {
301  return (int) $attribs['last_finished_pass'];
302  }
303 
304  if ($attribs['tries'] > 0) {
305  return $attribs['tries'] - 1;
306  }
307 
308  return null;
309  }
310 
311  private function fetchLastStartedPass($attribs): ?int
312  {
313  if (isset($attribs['last_started_pass'])) {
314  return (int) $attribs['last_started_pass'];
315  }
316 
317  if ($attribs['tries'] > 0) {
318  return (int) $attribs['tries'] - 1;
319  }
320 
321  return null;
322  }
323 
328  private function importParticipantsUploadedFiles(array $a_attribs): ?string
329  {
330  ['value1' => $value1, 'value2' => $value2] = $a_attribs;
331 
332  if ($value2 !== 'rid') {
333  return $value1 !== '' ? $value1 : null;
334  }
335 
336  $resource_directory = "$this->import_directory/objects/resources/$value1";
337  $file_name = $this->getFirstFileName($resource_directory);
338  if (!is_string($file_name)) {
339  return $value1 !== '' ? $value1 : null;
340  }
341 
342  $file_path = "$resource_directory/$file_name";
343  $new_rid = $this->irss->manage()->stream(
344  new Stream(fopen($file_path, 'rwb')),
346  basename($file_path),
347  );
348  return $new_rid->serialize();
349  }
350 
351  private function getFirstFileName(string $resource_directory): mixed
352  {
353  $entries = array_filter(
354  scandir($resource_directory),
355  static fn(string $entry): bool => is_file("$resource_directory/$entry") && !in_array($entry, ['.', '..']),
356  );
357  return array_shift($entries);
358  }
359 }
handlerParseCharacterData($a_xml_parser, $a_data)
handler for character data
__construct(?string $a_xml_file, private ilObjTest $test_obj, private ilDBInterface $db, private TestLogger $log, private ResourceStorage $irss)
Constructor.
const ANONYMOUS_USER_ID
Definition: constants.php:27
handlerEndTag($a_xml_parser, $a_name)
handler for end of element
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$log
Definition: result.php:32
handlerBeginTag($a_xml_parser, $a_name, $a_attribs)
handler for begin of element parser
__construct(Container $dic, ilPlugin $plugin)
$info
Definition: entry_point.php:21
This class gives all kind of DB information using the database manager and reverse module...
setQuestionIdMapping(array $question_id_mapping)
setSrcPoolDefIdMapping(array $src_pool_def_id_mapping)
setHandlers($a_xml_parser)
set event handler should be overwritten by inherited class private
getFirstFileName(string $resource_directory)