ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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
4require_once 'Modules/Test/interfaces/interface.ilTestQuestionSequence.php';
5require_once 'Modules/Test/interfaces/interface.ilTestSequenceSummaryProvider.php';
6
17{
24
31
38
44 var $pass;
45
52
57
62
67
72
77
82
92 function ilTestSequence($active_id, $pass, $randomtest)
93 {
94 $this->active_id = $active_id;
95 $this->pass = $pass;
96 $this->isRandomTest = $randomtest;
97 $this->sequencedata = array(
98 "sequence" => array(),
99 "postponed" => array(),
100 "hidden" => array()
101 );
102
103 $this->alreadyCheckedQuestions = array();
104 $this->newlyCheckedQuestion = null;
105
106 $this->optionalQuestions = array();
107 $this->answeringOptionalQuestionsConfirmed = false;
108
109 $this->considerHiddenQuestionsEnabled = false;
110 $this->considerOptionalQuestionsEnabled = true;
111 }
112
113 function getActiveId()
114 {
115 return $this->active_id;
116 }
117
118 function createNewSequence($max, $shuffle)
119 {
120 $newsequence = array();
121 if ($max > 0)
122 {
123 for ($i = 1; $i <= $max; $i++)
124 {
125 array_push($newsequence, $i);
126 }
127 if ($shuffle) $newsequence = $this->pcArrayShuffle($newsequence);
128 }
129 $this->sequencedata["sequence"] = $newsequence;
130 }
131
135 public function loadQuestions(ilTestQuestionSetConfig $testQuestionSetConfig = null, $taxonomyFilterSelection = array())
136 {
137 global $ilDB;
138
139 $this->questions = array();
140
141 $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",
142 array('integer'),
143 array($this->active_id)
144 );
145
146 $index = 1;
147
148 while ($data = $ilDB->fetchAssoc($result))
149 {
150 $this->questions[$index++] = $data["question_fi"];
151 }
152 }
153
159 public function loadFromDb()
160 {
161 $this->loadQuestionSequence();
162 $this->loadCheckedQuestions();
163 $this->loadOptionalQuestions();
164 }
165
166 private function loadQuestionSequence()
167 {
168 global $ilDB;
169 $result = $ilDB->queryF("SELECT * FROM tst_sequence WHERE active_fi = %s AND pass = %s",
170 array('integer','integer'),
171 array($this->active_id, $this->pass)
172 );
173 if ($result->numRows())
174 {
175 $row = $ilDB->fetchAssoc($result);
176 $this->sequencedata = array(
177 "sequence" => unserialize($row["sequence"]),
178 "postponed" => unserialize($row["postponed"]),
179 "hidden" => unserialize($row["hidden"])
180 );
181 if (!is_array($this->sequencedata["sequence"])) $this->sequencedata["sequence"] = array();
182 if (!is_array($this->sequencedata["postponed"])) $this->sequencedata["postponed"] = array();
183 if (!is_array($this->sequencedata["hidden"])) $this->sequencedata["hidden"] = array();
184
185 $this->setAnsweringOptionalQuestionsConfirmed((bool)$row['ans_opt_confirmed']);
186 }
187 }
188
189 private function loadCheckedQuestions()
190 {
191 global $ilDB;
192
193 $res = $ilDB->queryF("SELECT question_fi FROM tst_seq_qst_checked WHERE active_fi = %s AND pass = %s",
194 array('integer','integer'), array($this->active_id, $this->pass)
195 );
196
197 while( $row = $ilDB->fetchAssoc($res) )
198 {
199 $this->alreadyCheckedQuestions[ $row['question_fi'] ] = $row['question_fi'];
200 }
201 }
202
203 private function loadOptionalQuestions()
204 {
205 global $ilDB;
206
207 $res = $ilDB->queryF("SELECT question_fi FROM tst_seq_qst_optional WHERE active_fi = %s AND pass = %s",
208 array('integer','integer'), array($this->active_id, $this->pass)
209 );
210
211 while( $row = $ilDB->fetchAssoc($res) )
212 {
213 $this->optionalQuestions[ $row['question_fi'] ] = $row['question_fi'];
214 }
215 }
216
222 public function saveToDb()
223 {
224 $this->saveQuestionSequence();
226 $this->saveOptionalQuestions();
227 }
228
229 private function saveQuestionSequence()
230 {
231 global $ilDB;
232
233 $postponed = NULL;
234 if ((is_array($this->sequencedata["postponed"])) && (count($this->sequencedata["postponed"])))
235 {
236 $postponed = serialize($this->sequencedata["postponed"]);
237 }
238 $hidden = NULL;
239 if ((is_array($this->sequencedata["hidden"])) && (count($this->sequencedata["hidden"])))
240 {
241 $hidden = serialize($this->sequencedata["hidden"]);
242 }
243
244 $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s AND pass = %s",
245 array('integer','integer'),
246 array($this->active_id, $this->pass)
247 );
248
249 $affectedRows = $ilDB->insert("tst_sequence", array(
250 "active_fi" => array("integer", $this->active_id),
251 "pass" => array("integer", $this->pass),
252 "sequence" => array("clob", serialize($this->sequencedata["sequence"])),
253 "postponed" => array("text", $postponed),
254 "hidden" => array("text", $hidden),
255 "tstamp" => array("integer", time()),
256 'ans_opt_confirmed' => array('integer', (int)$this->isAnsweringOptionalQuestionsConfirmed())
257 ));
258 }
259
263 private function saveNewlyCheckedQuestion()
264 {
265 if( (int)$this->newlyCheckedQuestion )
266 {
267 global $ilDB;
268
269 $ilDB->replace('tst_seq_qst_checked', array(
270 'active_fi' => array('integer', (int)$this->active_id),
271 'pass' => array('integer', (int)$this->pass),
272 'question_fi' => array('integer', (int)$this->newlyCheckedQuestion)
273 ), array());
274 }
275 }
276
280 private function saveOptionalQuestions()
281 {
282 global $ilDB;
283
284 $NOT_IN_questions = $ilDB->in('question_fi', $this->optionalQuestions, true, 'integer');
285
286 $ilDB->queryF(
287 "DELETE FROM tst_seq_qst_optional WHERE active_fi = %s AND pass = %s AND $NOT_IN_questions",
288 array('integer', 'integer'), array($this->active_id, $this->pass)
289 );
290
291 foreach($this->optionalQuestions as $questionId)
292 {
293 $ilDB->replace('tst_seq_qst_optional', array(
294 'active_fi' => array('integer', (int)$this->active_id),
295 'pass' => array('integer', (int)$this->pass),
296 'question_fi' => array('integer', (int)$questionId)
297 ), array());
298 }
299 }
300
301 function postponeQuestion($question_id)
302 {
303 if (!$this->isPostponedQuestion($question_id))
304 {
305 array_push($this->sequencedata["postponed"], intval($question_id));
306 }
307 }
308
309 function hideQuestion($question_id)
310 {
311 if (!$this->isHiddenQuestion($question_id))
312 {
313 array_push($this->sequencedata["hidden"], intval($question_id));
314 }
315 }
316
317 function isPostponedQuestion($question_id)
318 {
319 if (!is_array($this->sequencedata["postponed"])) return FALSE;
320 if (!in_array($question_id, $this->sequencedata["postponed"]))
321 {
322 return FALSE;
323 }
324 else
325 {
326 return TRUE;
327 }
328 }
329
330 function isHiddenQuestion($question_id)
331 {
332 if (!is_array($this->sequencedata["hidden"])) return FALSE;
333 if (!in_array($question_id, $this->sequencedata["hidden"]))
334 {
335 return FALSE;
336 }
337 else
338 {
339 return TRUE;
340 }
341 }
342
343 function isPostponedSequence($sequence)
344 {
345 if (!array_key_exists($sequence, $this->questions)) return FALSE;
346 if (!is_array($this->sequencedata["postponed"])) return FALSE;
347 if (!in_array($this->questions[$sequence], $this->sequencedata["postponed"]))
348 {
349 return FALSE;
350 }
351 else
352 {
353 return TRUE;
354 }
355 }
356
357 function isHiddenSequence($sequence)
358 {
359 if (!array_key_exists($sequence, $this->questions)) return FALSE;
360 if (!is_array($this->sequencedata["hidden"])) return FALSE;
361 if (!in_array($this->questions[$sequence], $this->sequencedata["hidden"]))
362 {
363 return FALSE;
364 }
365 else
366 {
367 return TRUE;
368 }
369 }
370
371 function postponeSequence($sequence)
372 {
373 if (!$this->isPostponedSequence($sequence))
374 {
375 if (array_key_exists($sequence, $this->questions))
376 {
377 if (!is_array($this->sequencedata["postponed"])) $this->sequencedata["postponed"] = array();
378 array_push($this->sequencedata["postponed"], intval($this->questions[$sequence]));
379 }
380 }
381 }
382
383 function hideSequence($sequence)
384 {
385 if (!$this->isHiddenSequence($sequence))
386 {
387 if (array_key_exists($sequence, $this->questions))
388 {
389 if (!is_array($this->sequencedata["hidden"])) $this->sequencedata["hidden"] = array();
390 array_push($this->sequencedata["hidden"], intval($this->questions[$sequence]));
391 }
392 }
393 }
394
395 public function setQuestionChecked($questionId)
396 {
397 $this->newlyCheckedQuestion = $questionId;
398 }
399
400 public function isQuestionChecked($questionId)
401 {
402 return isset($this->alreadyCheckedQuestions[$questionId]);
403 }
404
405 function getPositionOfSequence($sequence)
406 {
407 $correctedsequence = $this->getCorrectedSequence();
408 $sequencekey = array_search($sequence, $correctedsequence);
409 if ($sequencekey !== FALSE)
410 {
411 return $sequencekey + 1;
412 }
413 else
414 {
415 return "";
416 }
417 }
418
420 {
421 return count($this->getCorrectedSequence());
422 }
423
425 {
426 $sequenceKeys = array();
427
428 foreach(array_keys($this->questions) as $sequenceKey)
429 {
430 if( $this->isHiddenSequence($sequenceKey) && !$this->isConsiderHiddenQuestionsEnabled() )
431 {
432 continue;
433 }
434
435 if( $this->isSequenceOptional($sequenceKey) && !$this->isConsiderOptionalQuestionsEnabled() )
436 {
437 continue;
438 }
439
440 $sequenceKeys[] = $sequenceKey;
441 }
442
443 return $sequenceKeys;
444 }
445
447 {
448 $questions = array();
449
450 foreach($this->questions as $questionId)
451 {
452 if( $this->isHiddenQuestion($questionId) && !$this->isConsiderHiddenQuestionsEnabled() )
453 {
454 continue;
455 }
456
457 if( $this->isQuestionOptional($questionId) && !$this->isConsiderOptionalQuestionsEnabled() )
458 {
459 continue;
460 }
461
462 $questions[] = $questionId;
463 }
464
465 return $questions;
466 }
467
469 {
470 return $this->getCorrectedSequence();
471 }
472
474 {
475 $seq = $this->getCorrectedSequence();
476 $found = array();
477 foreach ($seq as $sequence)
478 {
479 array_push($found, $this->getQuestionForSequence($sequence));
480 }
481 return $found;
482 }
483
484 private function ensureQuestionNotInSequence($sequence, $questionId)
485 {
486 $questionKey = array_search($questionId, $this->questions);
487
488 if( $questionKey === false )
489 {
490 return $sequence;
491 }
492
493 $sequenceKey = array_search($questionKey, $sequence);
494
495 if( $sequenceKey === FALSE )
496 {
497 return $sequence;
498 }
499
500 unset($sequence[$sequenceKey]);
501
502 return $sequence;
503 }
504
505 protected function getCorrectedSequence()
506 {
507 $correctedsequence = $this->sequencedata["sequence"];
509 {
510 if (is_array($this->sequencedata["hidden"]))
511 {
512 foreach ($this->sequencedata["hidden"] as $question_id)
513 {
514 $correctedsequence = $this->ensureQuestionNotInSequence($correctedsequence, $question_id);
515 }
516 }
517 }
519 {
520 foreach($this->optionalQuestions as $questionId)
521 {
522 $correctedsequence = $this->ensureQuestionNotInSequence($correctedsequence, $questionId);
523 }
524 }
525 if (is_array($this->sequencedata["postponed"]))
526 {
527 foreach ($this->sequencedata["postponed"] as $question_id)
528 {
529 $foundsequence = array_search($question_id, $this->questions);
530 if ($foundsequence !== FALSE)
531 {
532 $sequencekey = array_search($foundsequence, $correctedsequence);
533 if ($sequencekey !== FALSE)
534 {
535 unset($correctedsequence[$sequencekey]);
536 array_push($correctedsequence, $foundsequence);
537 }
538 }
539 }
540 }
541 return array_values($correctedsequence);
542 }
543
544 function getSequenceForQuestion($question_id)
545 {
546 return array_search($question_id, $this->questions);
547 }
548
550 {
551 $correctedsequence = $this->getCorrectedSequence();
552 if (count($correctedsequence))
553 {
554 return reset($correctedsequence);
555 }
556 else
557 {
558 return FALSE;
559 }
560 }
561
563 {
564 $correctedsequence = $this->getCorrectedSequence();
565 if (count($correctedsequence))
566 {
567 return end($correctedsequence);
568 }
569 else
570 {
571 return FALSE;
572 }
573 }
574
575 function getNextSequence($sequence)
576 {
577 $correctedsequence = $this->getCorrectedSequence();
578 $sequencekey = array_search($sequence, $correctedsequence);
579 if ($sequencekey !== FALSE)
580 {
581 $nextsequencekey = $sequencekey + 1;
582 if (array_key_exists($nextsequencekey, $correctedsequence))
583 {
584 return $correctedsequence[$nextsequencekey];
585 }
586 }
587 return FALSE;
588 }
589
590 function getPreviousSequence($sequence)
591 {
592 $correctedsequence = $this->getCorrectedSequence();
593 $sequencekey = array_search($sequence, $correctedsequence);
594 if ($sequencekey !== FALSE)
595 {
596 $prevsequencekey = $sequencekey - 1;
597 if (($prevsequencekey >= 0) && (array_key_exists($prevsequencekey, $correctedsequence)))
598 {
599 return $correctedsequence[$prevsequencekey];
600 }
601 }
602 return FALSE;
603 }
604
613 function pcArrayShuffle($array)
614 {
615 $keys = array_keys($array);
616 shuffle($keys);
617 $result = array();
618 foreach ($keys as $key)
619 {
620 $result[$key] = $array[$key];
621 }
622 return $result;
623 }
624
625 function getQuestionForSequence($sequence)
626 {
627 if ($sequence < 1) return FALSE;
628 if (array_key_exists($sequence, $this->questions))
629 {
630 return $this->questions[$sequence];
631 }
632 else
633 {
634 return FALSE;
635 }
636 }
637
638 public function getSequenceSummary($obligationsFilterEnabled = false)
639 {
640 $correctedsequence = $this->getCorrectedSequence();
641 $result_array = array();
642 include_once "./Modules/Test/classes/class.ilObjTest.php";
643 $solved_questions = ilObjTest::_getSolvedQuestions($this->active_id);
644 $key = 1;
645 foreach ($correctedsequence as $sequence)
646 {
647 $question =& ilObjTest::_instanciateQuestion($this->getQuestionForSequence($sequence));
648 if (is_object($question))
649 {
650 $worked_through = $question->_isWorkedThrough($this->active_id, $question->getId(), $this->pass);
651 $solved = 0;
652 if (array_key_exists($question->getId(), $solved_questions))
653 {
654 $solved = $solved_questions[$question->getId()]["solved"];
655 }
656 $is_postponed = $this->isPostponedQuestion($question->getId());
657
658 $row = array(
659 "nr" => "$key",
660 "title" => $question->getTitle(),
661 "qid" => $question->getId(),
662 "visited" => $worked_through,
663 "solved" => (($solved)?"1":"0"),
664 "description" => $question->getComment(),
665 "points" => $question->getMaximumPoints(),
666 "worked_through" => $worked_through,
667 "postponed" => $is_postponed,
668 "sequence" => $sequence,
669 "obligatory" => ilObjTest::isQuestionObligatory($question->getId()),
670 'isAnswered' => $question->isAnswered($this->active_id, $this->pass)
671 );
672
673 if( !$obligationsFilter || $row['obligatory'] )
674 {
675 array_push($result_array, $row);
676 }
677
678 $key++;
679 }
680 }
681 return $result_array;
682 }
683
684 function getPass()
685 {
686 return $this->pass;
687 }
688
689 function setPass($pass)
690 {
691 $this->pass = $pass;
692 }
693
694 function hasSequence()
695 {
696 if ((is_array($this->sequencedata["sequence"])) && (count($this->sequencedata["sequence"]) > 0))
697 {
698 return TRUE;
699 }
700 else
701 {
702 return FALSE;
703 }
704 }
705
707 {
708 if ((is_array($this->sequencedata["hidden"])) && (count($this->sequencedata["hidden"]) > 0))
709 {
710 return TRUE;
711 }
712 else
713 {
714 return FALSE;
715 }
716 }
717
719 {
720 $this->sequencedata["hidden"] = array();
721 }
722
723 private function hideCorrectAnsweredQuestions(ilObjTest $testOBJ, $activeId, $pass)
724 {
725 if( $activeId > 0 )
726 {
727 $result = $testOBJ->getTestResult($activeId, $pass, TRUE);
728
729 foreach( $result as $sequence => $question )
730 {
731 if( is_numeric($sequence) )
732 {
733 if( $question['reached'] == $question['max'] )
734 {
735 $this->hideQuestion($question['qid']);
736 }
737 }
738 }
739
740 $this->saveToDb();
741 }
742 }
743
744 public function hasStarted(ilTestSession $testSession)
745 {
746 if( $testSession->getLastSequence() < 1 )
747 {
748 return false;
749 }
750
751 // WTF ?? heard about tests with only one question !?
752 if( $testSession->getLastSequence() == $this->getFirstSequence() )
753 {
754 return false;
755 }
756
757 return true;
758 }
759
760 public function openQuestionExists()
761 {
762 return $this->getFirstSequence() !== false;
763 }
764
765 public function getQuestionIds()
766 {
767 return array_values($this->questions);
768 }
769
770 public function questionExists($questionId)
771 {
772 return in_array($questionId, $this->questions);
773 }
774
775 public function setQuestionOptional($questionId)
776 {
777 $this->optionalQuestions[$questionId] = $questionId;
778 }
779
780 public function isQuestionOptional($questionId)
781 {
782 return isset($this->optionalQuestions[$questionId]);
783 }
784
785 public function hasOptionalQuestions()
786 {
787 return (bool)count($this->optionalQuestions);
788 }
789
790 public function getOptionalQuestions()
791 {
793 }
794
795 public function clearOptionalQuestions()
796 {
797 $this->optionalQuestions = array();
798 }
799
801 {
802 $optionalSequenceKeys = array();
803
804 foreach($this->sequencedata['sequence'] as $index => $sequenceKey)
805 {
806 if( $this->isQuestionOptional($this->getQuestionForSequence($sequenceKey)) )
807 {
808 $optionalSequenceKeys[$index] = $sequenceKey;
809 unset($this->sequencedata['sequence'][$index]);
810 }
811 }
812
813 foreach($optionalSequenceKeys as $index => $sequenceKey)
814 {
815 $this->sequencedata['sequence'][$index] = $sequenceKey;
816 }
817 }
818
823 {
825 }
826
831 {
832 $this->answeringOptionalQuestionsConfirmed = $answeringOptionalQuestionsConfirmed;
833 }
834
839 {
841 }
842
847 {
848 $this->considerHiddenQuestionsEnabled = $considerHiddenQuestionsEnabled;
849 }
850
855 {
857 }
858
863 {
864 $this->considerOptionalQuestionsEnabled = $considerOptionalQuestionsEnabled;
865 }
866}
867
868?>
$result
_getSolvedQuestions($active_id, $question_fi=null)
get solved questions
& getTestResult($active_id, $pass=NULL, $ordered_sequence=FALSE, $considerHiddenQuestions=true, $considerOptionalQuestions=true)
Calculates the results of a test for a given user and returns an array with all test results.
& _instanciateQuestion($question_id)
Creates an instance of a question with a given question id.
static isQuestionObligatory($question_id)
checks wether the question with given id is marked as obligatory or not
Test sequence handler.
questionExists($questionId)
isHiddenQuestion($question_id)
ilTestSequence($active_id, $pass, $randomtest)
ilTestSequence constructor
setAnsweringOptionalQuestionsConfirmed($answeringOptionalQuestionsConfirmed)
isPostponedSequence($sequence)
pcArrayShuffle($array)
Shuffles the values of a given array.
getQuestionForSequence($sequence)
hideQuestion($question_id)
getSequenceSummary($obligationsFilterEnabled=false)
setQuestionChecked($questionId)
isQuestionChecked($questionId)
postponeQuestion($question_id)
loadQuestions(ilTestQuestionSetConfig $testQuestionSetConfig=null, $taxonomyFilterSelection=array())
Loads the question mapping.
saveToDb()
Saves the sequence data for a given pass to the database.
saveNewlyCheckedQuestion()
@global ilDB $ilDB
isQuestionOptional($questionId)
saveOptionalQuestions()
@global ilDB $ilDB
hasStarted(ilTestSession $testSession)
hideCorrectAnsweredQuestions(ilObjTest $testOBJ, $activeId, $pass)
getPositionOfSequence($sequence)
createNewSequence($max, $shuffle)
getPreviousSequence($sequence)
setQuestionOptional($questionId)
setConsiderOptionalQuestionsEnabled($considerOptionalQuestionsEnabled)
loadFromDb()
Loads the sequence data for a given active id.
getSequenceForQuestion($question_id)
ensureQuestionNotInSequence($sequence, $questionId)
isPostponedQuestion($question_id)
setConsiderHiddenQuestionsEnabled($considerHiddenQuestionsEnabled)
Test session handler.
$data
global $ilDB