ILIAS  Release_4_4_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilTestSequence.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
14 {
21 
28 
35 
41  var $pass;
42 
49 
59  function ilTestSequence($active_id, $pass, $randomtest)
60  {
61  $this->active_id = $active_id;
62  $this->pass = $pass;
63  $this->isRandomTest = $randomtest;
64  $this->sequencedata = array(
65  "sequence" => array(),
66  "postponed" => array(),
67  "hidden" => array()
68  );
69  }
70 
71  function getActiveId()
72  {
73  return $this->active_id;
74  }
75 
76  function createNewSequence($max, $shuffle)
77  {
78  $newsequence = array();
79  if ($max > 0)
80  {
81  for ($i = 1; $i <= $max; $i++)
82  {
83  array_push($newsequence, $i);
84  }
85  if ($shuffle) $newsequence = $this->pcArrayShuffle($newsequence);
86  }
87  $this->sequencedata["sequence"] = $newsequence;
88  }
89 
93  public function loadQuestions(ilTestQuestionSetConfig $testQuestionSetConfig = null, $taxonomyFilterSelection = array())
94  {
95  global $ilDB;
96 
97  $this->questions = array();
98  if ($this->isRandomTest)
99  {
100  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.* FROM tst_test_rnd_qst, qpl_questions WHERE tst_test_rnd_qst.active_fi = %s AND qpl_questions.question_id = tst_test_rnd_qst.question_fi AND tst_test_rnd_qst.pass = %s ORDER BY sequence",
101  array('integer','integer'),
102  array($this->active_id, $this->pass)
103  );
104  // The following is a fix for random tests prior to ILIAS 3.8. If someone started a random test in ILIAS < 3.8, there
105  // is only one test pass (pass = 0) in tst_test_rnd_qst while with ILIAS 3.8 there are questions for every test pass.
106  // To prevent problems with tests started in an older version and continued in ILIAS 3.8, the first pass should be taken if
107  // no questions are present for a newer pass.
108  if ($result->numRows() == 0)
109  {
110  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.* FROM tst_test_rnd_qst, qpl_questions WHERE tst_test_rnd_qst.active_fi = %s AND qpl_questions.question_id = tst_test_rnd_qst.question_fi AND tst_test_rnd_qst.pass = 0 ORDER BY sequence",
111  array('integer'),
112  array($this->active_id)
113  );
114  }
115  }
116  else
117  {
118  $result = $ilDB->queryF("SELECT tst_test_question.* FROM tst_test_question, qpl_questions, tst_active WHERE tst_active.active_id = %s AND tst_test_question.test_fi = tst_active.test_fi AND qpl_questions.question_id = tst_test_question.question_fi ORDER BY tst_test_question.sequence",
119  array('integer'),
120  array($this->active_id)
121  );
122  }
123  $index = 1;
124  while ($data = $ilDB->fetchAssoc($result))
125  {
126  $this->questions[$index++] = $data["question_fi"];
127  }
128  }
129 
135  public function loadFromDb()
136  {
137  global $ilDB;
138  $result = $ilDB->queryF("SELECT * FROM tst_sequence WHERE active_fi = %s AND pass = %s",
139  array('integer','integer'),
140  array($this->active_id, $this->pass)
141  );
142  if ($result->numRows())
143  {
144  $row = $ilDB->fetchAssoc($result);
145  $this->sequencedata = array(
146  "sequence" => unserialize($row["sequence"]),
147  "postponed" => unserialize($row["postponed"]),
148  "hidden" => unserialize($row["hidden"])
149  );
150  if (!is_array($this->sequencedata["sequence"])) $this->sequencedata["sequence"] = array();
151  if (!is_array($this->sequencedata["postponed"])) $this->sequencedata["postponed"] = array();
152  if (!is_array($this->sequencedata["hidden"])) $this->sequencedata["hidden"] = array();
153  }
154  }
155 
161  public function saveToDb()
162  {
163  global $ilDB;
164 
165  $postponed = NULL;
166  if ((is_array($this->sequencedata["postponed"])) && (count($this->sequencedata["postponed"])))
167  {
168  $postponed = serialize($this->sequencedata["postponed"]);
169  }
170  $hidden = NULL;
171  if ((is_array($this->sequencedata["hidden"])) && (count($this->sequencedata["hidden"])))
172  {
173  $hidden = serialize($this->sequencedata["hidden"]);
174  }
175 
176  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s AND pass = %s",
177  array('integer','integer'),
178  array($this->active_id, $this->pass)
179  );
180 
181  $affectedRows = $ilDB->insert("tst_sequence", array(
182  "active_fi" => array("integer", $this->active_id),
183  "pass" => array("integer", $this->pass),
184  "sequence" => array("clob", serialize($this->sequencedata["sequence"])),
185  "postponed" => array("text", $postponed),
186  "hidden" => array("text", $hidden),
187  "tstamp" => array("integer", time())
188  ));
189  }
190 
191  function postponeQuestion($question_id)
192  {
193  if (!$this->isPostponedQuestion($question_id))
194  {
195  array_push($this->sequencedata["postponed"], intval($question_id));
196  }
197  }
198 
199  function hideQuestion($question_id)
200  {
201  if (!$this->isHiddenQuestion($question_id))
202  {
203  array_push($this->sequencedata["hidden"], intval($question_id));
204  }
205  }
206 
207  function isPostponedQuestion($question_id)
208  {
209  if (!is_array($this->sequencedata["postponed"])) return FALSE;
210  if (!in_array($question_id, $this->sequencedata["postponed"]))
211  {
212  return FALSE;
213  }
214  else
215  {
216  return TRUE;
217  }
218  }
219 
220  function isHiddenQuestion($question_id)
221  {
222  if (!is_array($this->sequencedata["hidden"])) return FALSE;
223  if (!in_array($question_id, $this->sequencedata["hidden"]))
224  {
225  return FALSE;
226  }
227  else
228  {
229  return TRUE;
230  }
231  }
232 
233  function isPostponedSequence($sequence)
234  {
235  if (!array_key_exists($sequence, $this->questions)) return FALSE;
236  if (!is_array($this->sequencedata["postponed"])) return FALSE;
237  if (!in_array($this->questions[$sequence], $this->sequencedata["postponed"]))
238  {
239  return FALSE;
240  }
241  else
242  {
243  return TRUE;
244  }
245  }
246 
247  function isHiddenSequence($sequence)
248  {
249  if (!array_key_exists($sequence, $this->questions)) return FALSE;
250  if (!is_array($this->sequencedata["hidden"])) return FALSE;
251  if (!in_array($this->questions[$sequence], $this->sequencedata["hidden"]))
252  {
253  return FALSE;
254  }
255  else
256  {
257  return TRUE;
258  }
259  }
260 
261  function postponeSequence($sequence)
262  {
263  if (!$this->isPostponedSequence($sequence))
264  {
265  if (array_key_exists($sequence, $this->questions))
266  {
267  if (!is_array($this->sequencedata["postponed"])) $this->sequencedata["postponed"] = array();
268  array_push($this->sequencedata["postponed"], intval($this->questions[$sequence]));
269  }
270  }
271  }
272 
273  function hideSequence($sequence)
274  {
275  if (!$this->isHiddenSequence($sequence))
276  {
277  if (array_key_exists($sequence, $this->questions))
278  {
279  if (!is_array($this->sequencedata["hidden"])) $this->sequencedata["hidden"] = array();
280  array_push($this->sequencedata["hidden"], intval($this->questions[$sequence]));
281  }
282  }
283  }
284 
285  function getPositionOfSequence($sequence)
286  {
287  $correctedsequence = $this->getCorrectedSequence();
288  $sequencekey = array_search($sequence, $correctedsequence);
289  if ($sequencekey !== FALSE)
290  {
291  return $sequencekey + 1;
292  }
293  else
294  {
295  return "";
296  }
297  }
298 
300  {
301  return count($this->getCorrectedSequence());
302  }
303 
305  {
306  return array_keys($this->questions);
307  }
308 
310  {
311  return $this->questions;
312  }
313 
314  function getUserSequence()
315  {
316  return $this->getCorrectedSequence(TRUE);
317  }
318 
320  {
321  $seq = $this->getCorrectedSequence(TRUE);
322  $found = array();
323  foreach ($seq as $sequence)
324  {
325  array_push($found, $this->getQuestionForSequence($sequence));
326  }
327  return $found;
328  }
329 
330  protected function getCorrectedSequence($with_hidden_questions = FALSE)
331  {
332  $correctedsequence = $this->sequencedata["sequence"];
333  if (!$with_hidden_questions)
334  {
335  if (is_array($this->sequencedata["hidden"]))
336  {
337  foreach ($this->sequencedata["hidden"] as $question_id)
338  {
339  $foundsequence = array_search($question_id, $this->questions);
340  if ($foundsequence !== FALSE)
341  {
342  $sequencekey = array_search($foundsequence, $correctedsequence);
343  if ($sequencekey !== FALSE)
344  {
345  unset($correctedsequence[$sequencekey]);
346  }
347  }
348  }
349  }
350  }
351  if (is_array($this->sequencedata["postponed"]))
352  {
353  foreach ($this->sequencedata["postponed"] as $question_id)
354  {
355  $foundsequence = array_search($question_id, $this->questions);
356  if ($foundsequence !== FALSE)
357  {
358  $sequencekey = array_search($foundsequence, $correctedsequence);
359  if ($sequencekey !== FALSE)
360  {
361  unset($correctedsequence[$sequencekey]);
362  array_push($correctedsequence, $foundsequence);
363  }
364  }
365  }
366  }
367  return array_values($correctedsequence);
368  }
369 
370  function getSequenceForQuestion($question_id)
371  {
372  return array_search($question_id, $this->questions);
373  }
374 
375  function getFirstSequence()
376  {
377  $correctedsequence = $this->getCorrectedSequence();
378  if (count($correctedsequence))
379  {
380  return reset($correctedsequence);
381  }
382  else
383  {
384  return FALSE;
385  }
386  }
387 
388  function getLastSequence()
389  {
390  $correctedsequence = $this->getCorrectedSequence();
391  if (count($correctedsequence))
392  {
393  return end($correctedsequence);
394  }
395  else
396  {
397  return FALSE;
398  }
399  }
400 
401  function getNextSequence($sequence)
402  {
403  $correctedsequence = $this->getCorrectedSequence();
404  $sequencekey = array_search($sequence, $correctedsequence);
405  if ($sequencekey !== FALSE)
406  {
407  $nextsequencekey = $sequencekey + 1;
408  if (array_key_exists($nextsequencekey, $correctedsequence))
409  {
410  return $correctedsequence[$nextsequencekey];
411  }
412  }
413  return FALSE;
414  }
415 
416  function getPreviousSequence($sequence)
417  {
418  $correctedsequence = $this->getCorrectedSequence();
419  $sequencekey = array_search($sequence, $correctedsequence);
420  if ($sequencekey !== FALSE)
421  {
422  $prevsequencekey = $sequencekey - 1;
423  if (($prevsequencekey >= 0) && (array_key_exists($prevsequencekey, $correctedsequence)))
424  {
425  return $correctedsequence[$prevsequencekey];
426  }
427  }
428  return FALSE;
429  }
430 
439  function pcArrayShuffle($array)
440  {
441  $keys = array_keys($array);
442  shuffle($keys);
443  $result = array();
444  foreach ($keys as $key)
445  {
446  $result[$key] = $array[$key];
447  }
448  return $result;
449  }
450 
451  function getQuestionForSequence($sequence)
452  {
453  if ($sequence < 1) return FALSE;
454  if (array_key_exists($sequence, $this->questions))
455  {
456  return $this->questions[$sequence];
457  }
458  else
459  {
460  return FALSE;
461  }
462  }
463 
464  function &getSequenceSummary($obligationsFilter = false)
465  {
466  $correctedsequence = $this->getCorrectedSequence();
467  $result_array = array();
468  include_once "./Modules/Test/classes/class.ilObjTest.php";
469  $solved_questions = ilObjTest::_getSolvedQuestions($this->active_id);
470  $key = 1;
471  foreach ($correctedsequence as $sequence)
472  {
473  $question =& ilObjTest::_instanciateQuestion($this->getQuestionForSequence($sequence));
474  if (is_object($question))
475  {
476  $worked_through = $question->_isWorkedThrough($this->active_id, $question->getId(), $this->pass);
477  $solved = 0;
478  if (array_key_exists($question->getId(), $solved_questions))
479  {
480  $solved = $solved_questions[$question->getId()]["solved"];
481  }
482  $is_postponed = $this->isPostponedQuestion($question->getId());
483 
484  $row = array(
485  "nr" => "$key",
486  "title" => $question->getTitle(),
487  "qid" => $question->getId(),
488  "visited" => $worked_through,
489  "solved" => (($solved)?"1":"0"),
490  "description" => $question->getComment(),
491  "points" => $question->getMaximumPoints(),
492  "worked_through" => $worked_through,
493  "postponed" => $is_postponed,
494  "sequence" => $sequence,
495  "obligatory" => ilObjTest::isQuestionObligatory($question->getId()),
496  'isAnswered' => $question->isAnswered($this->active_id, $this->pass)
497  );
498 
499  if( !$obligationsFilter || $row['obligatory'] )
500  {
501  array_push($result_array, $row);
502  }
503 
504  $key++;
505  }
506  }
507  return $result_array;
508  }
509 
510  function getPass()
511  {
512  return $this->pass;
513  }
514 
515  function setPass($pass)
516  {
517  $this->pass = $pass;
518  }
519 
520  function hasSequence()
521  {
522  if ((is_array($this->sequencedata["sequence"])) && (count($this->sequencedata["sequence"]) > 0))
523  {
524  return TRUE;
525  }
526  else
527  {
528  return FALSE;
529  }
530  }
531 
533  {
534  if ((is_array($this->sequencedata["hidden"])) && (count($this->sequencedata["hidden"]) > 0))
535  {
536  return TRUE;
537  }
538  else
539  {
540  return FALSE;
541  }
542  }
543 
545  {
546  $this->sequencedata["hidden"] = array();
547  }
548 
549  private function hideCorrectAnsweredQuestions(ilObjTest $testOBJ, $activeId, $pass)
550  {
551  if( $activeId > 0 )
552  {
553  $result = $testOBJ->getTestResult($activeId, $pass, TRUE);
554 
555  foreach( $result as $sequence => $question )
556  {
557  if( is_numeric($sequence) )
558  {
559  if( $question['reached'] == $question['max'] )
560  {
561  $this->hideQuestion($question['qid']);
562  }
563  }
564  }
565 
566  $this->saveToDb();
567  }
568  }
569 
570  public function hasStarted(ilTestSession $testSession)
571  {
572  if( $testSession->getLastSequence() < 1 )
573  {
574  return false;
575  }
576 
577  // WTF ?? heard about tests with only one question !?
578  if( $testSession->getLastSequence() == $this->getFirstSequence() )
579  {
580  return false;
581  }
582 
583  return true;
584  }
585 
586  public function openQuestionExists()
587  {
588  return $this->getFirstSequence() !== false;
589  }
590 
591  public function questionExists($questionId)
592  {
593  return in_array($questionId, $this->questions);
594  }
595 }
596 
597 ?>