ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilTestSequenceDynamicQuestionSet.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
4 require_once 'Modules/Test/interfaces/interface.ilTestSequenceSummaryProvider.php';
5 
17 {
21  private $db = null;
22 
26  private $questionSet = null;
27 
31  private $activeId = null;
32 
37 
41  private $questionTracking = array();
42 
47 
52 
56  private $postponedQuestions = array();
57 
62 
67 
72 
77 
81  private $correctAnsweredQuestions = array();
82 
86  private $wrongAnsweredQuestions = array();
87 
92 
97 
102 
109  {
110  $this->db = $db;
111  $this->questionSet = $questionSet;
112  $this->activeId = $activeId;
113 
114  $this->newlyTrackedQuestion = null;
115  $this->newlyTrackedQuestionsStatus = null;
116 
117  $this->newlyPostponedQuestion = null;
118  $this->newlyPostponedQuestionsCount = null;
119 
120  $this->newlyAnsweredQuestion = null;
121  $this->newlyAnsweredQuestionsAnswerStatus = null;
122 
123  $this->alreadyCheckedQuestions = array();
124  $this->newlyCheckedQuestion = null;
125 
126  $this->preventCheckedQuestionsFromComingUpEnabled = false;
127 
128  $this->currentQuestionId = null;
129  }
130 
131  public function getActiveId()
132  {
133  return $this->activeId;
134  }
135 
137  {
138  $this->preventCheckedQuestionsFromComingUpEnabled = $preventCheckedQuestionsFromComingUpEnabled;
139  }
140 
142  {
144  }
145 
149  public function getCurrentQuestionId()
150  {
152  }
153 
158  {
159  $this->currentQuestionId = $currentQuestionId;
160  }
161 
162  public function loadFromDb()
163  {
164  $this->loadQuestionTracking();
165  $this->loadAnswerStatus();
166  $this->loadPostponedQuestions();
167  $this->loadCheckedQuestions();
168  }
169 
170  private function loadQuestionTracking()
171  {
172  $query = "
173  SELECT question_fi, status
174  FROM tst_seq_qst_tracking
175  WHERE active_fi = %s
176  AND pass = %s
177  ORDER BY orderindex ASC
178  ";
179 
180  $res = $this->db->queryF($query, array('integer','integer'), array($this->activeId, 0));
181 
182  $this->questionTracking = array();
183 
184  while ($row = $this->db->fetchAssoc($res)) {
185  $this->questionTracking[] = array(
186  'qid' => $row['question_fi'],
187  'status' => $row['status']
188  );
189  }
190  }
191 
192  private function loadAnswerStatus()
193  {
194  $query = "
195  SELECT question_fi, correctness
196  FROM tst_seq_qst_answstatus
197  WHERE active_fi = %s
198  AND pass = %s
199  ";
200 
201  $res = $this->db->queryF($query, array('integer','integer'), array($this->activeId, 0));
202 
203  $this->correctAnsweredQuestions = array();
204  $this->wrongAnsweredQuestions = array();
205 
206  while ($row = $this->db->fetchAssoc($res)) {
207  if ($row['correctness']) {
208  $this->correctAnsweredQuestions[ $row['question_fi'] ] = $row['question_fi'];
209  } else {
210  $this->wrongAnsweredQuestions[ $row['question_fi'] ] = $row['question_fi'];
211  }
212  }
213  }
214 
215  private function loadPostponedQuestions()
216  {
217  $query = "
218  SELECT question_fi, cnt
219  FROM tst_seq_qst_postponed
220  WHERE active_fi = %s
221  AND pass = %s
222  ";
223 
224  $res = $this->db->queryF($query, array('integer','integer'), array($this->activeId, 0));
225 
226  $this->postponedQuestions = array();
227 
228  while ($row = $this->db->fetchAssoc($res)) {
229  $this->postponedQuestions[ $row['question_fi'] ] = $row['cnt'];
230  }
231  }
232 
233  private function loadCheckedQuestions()
234  {
235  $res = $this->db->queryF(
236  "SELECT question_fi FROM tst_seq_qst_checked WHERE active_fi = %s AND pass = %s",
237  array('integer','integer'),
238  array($this->getActiveId(), 0)
239  );
240 
241  while ($row = $this->db->fetchAssoc($res)) {
242  $this->alreadyCheckedQuestions[ $row['question_fi'] ] = $row['question_fi'];
243  }
244  }
245 
246  public function saveToDb()
247  {
248  $this->db->manipulateF(
249  "DELETE FROM tst_sequence WHERE active_fi = %s AND pass = %s",
250  array('integer','integer'),
251  array($this->getActiveId(), 0)
252  );
253 
254  $this->db->insert('tst_sequence', array(
255  'active_fi' => array('integer', $this->getActiveId()),
256  'pass' => array('integer', 0),
257  'sequence' => array('clob', null),
258  'postponed' => array('text', null),
259  'hidden' => array('text', null),
260  'tstamp' => array('integer', time())
261  ));
262 
263  $this->saveNewlyTrackedQuestion();
267  $this->saveNewlyCheckedQuestion();
269  }
270 
271  private function saveNewlyTrackedQuestion()
272  {
273  if ((int) $this->newlyTrackedQuestion) {
274  $newOrderIndex = $this->getNewOrderIndexForQuestionTracking();
275 
276  $this->db->replace(
277  'tst_seq_qst_tracking',
278  array(
279  'active_fi' => array('integer', (int) $this->getActiveId()),
280  'pass' => array('integer', 0),
281  'question_fi' => array('integer', (int) $this->newlyTrackedQuestion)
282  ),
283  array(
284  'status' => array('text', $this->newlyTrackedQuestionsStatus),
285  'orderindex' => array('integer', $newOrderIndex)
286  )
287  );
288  }
289  }
290 
292  {
293  $query = "
294  SELECT (MAX(orderindex) + 1) new_order_index
295  FROM tst_seq_qst_tracking
296  WHERE active_fi = %s
297  AND pass = %s
298  ";
299 
300  $res = $this->db->queryF($query, array('integer','integer'), array($this->getActiveId(), 0));
301 
302  $row = $this->db->fetchAssoc($res);
303 
304  if ($row['new_order_index']) {
305  return $row['new_order_index'];
306  }
307 
308  return 1;
309  }
310 
312  {
313  if ((int) $this->newlyAnsweredQuestion) {
314  $this->db->replace(
315  'tst_seq_qst_answstatus',
316  array(
317  'active_fi' => array('integer', (int) $this->getActiveId()),
318  'pass' => array('integer', 0),
319  'question_fi' => array('integer', (int) $this->newlyAnsweredQuestion)
320  ),
321  array(
322  'correctness' => array('integer', (int) $this->newlyAnsweredQuestionsAnswerStatus)
323  )
324  );
325  }
326  }
327 
328  private function saveNewlyPostponedQuestion()
329  {
330  if ((int) $this->newlyPostponedQuestion) {
331  $this->db->replace(
332  'tst_seq_qst_postponed',
333  array(
334  'active_fi' => array('integer', (int) $this->getActiveId()),
335  'pass' => array('integer', 0),
336  'question_fi' => array('integer', (int) $this->newlyPostponedQuestion)
337  ),
338  array(
339  'cnt' => array('integer', (int) $this->newlyPostponedQuestionsCount)
340  )
341  );
342  }
343  }
344 
346  {
347  $INquestions = $this->db->in('question_fi', array_keys($this->postponedQuestions), true, 'integer');
348 
349  $query = "
350  DELETE FROM tst_seq_qst_postponed
351  WHERE active_fi = %s
352  AND pass = %s
353  AND $INquestions
354  ";
355 
356  $this->db->manipulateF($query, array('integer','integer'), array($this->getActiveId(), 0));
357  }
358 
359  private function saveNewlyCheckedQuestion()
360  {
361  if ((int) $this->newlyCheckedQuestion) {
362  $this->db->replace('tst_seq_qst_checked', array(
363  'active_fi' => array('integer', (int) $this->getActiveId()),
364  'pass' => array('integer', 0),
365  'question_fi' => array('integer', (int) $this->newlyCheckedQuestion)
366  ), array());
367  }
368  }
369 
371  {
372  $NOT_IN_checkedQuestions = $this->db->in('question_fi', $this->alreadyCheckedQuestions, true, 'integer');
373 
374  // BEGIN: FIX IN QUERY
375  if ($NOT_IN_checkedQuestions == ' 1=2 ') {
376  $NOT_IN_checkedQuestions = ' 1=1 ';
377  }
378  // END: FIX IN QUERY
379 
380  $query = "
381  DELETE FROM tst_seq_qst_checked
382  WHERE active_fi = %s
383  AND pass = %s
384  AND $NOT_IN_checkedQuestions
385  ";
386 
387  $this->db->manipulateF($query, array('integer', 'integer'), array((int) $this->getActiveId(), 0));
388  }
389 
390  public function loadQuestions(ilObjTestDynamicQuestionSetConfig $dynamicQuestionSetConfig, ilTestDynamicQuestionSetFilterSelection $filterSelection)
391  {
392  $this->questionSet->load($dynamicQuestionSetConfig, $filterSelection);
393 
394  // echo "<table><tr>";
395 // echo "<td width='200'><pre>".print_r($this->questionSet->getActualQuestionSequence(), 1)."</pre></td>";
396 // echo "<td width='200'><pre>".print_r($this->correctAnsweredQuestions, 1)."</pre></td>";
397 // echo "<td width='200'><pre>".print_r($this->wrongAnsweredQuestions, 1)."</pre></td>";
398 // echo "</tr></table>";
399  }
400 
401  // -----------------------------------------------------------------------------------------------------------------
402 
403  public function cleanupQuestions(ilTestSessionDynamicQuestionSet $testSession)
404  {
405  switch (true) {
406  case !$this->questionSet->questionExists($testSession->getCurrentQuestionId()):
407  case !$this->isFilteredQuestion($testSession->getCurrentQuestionId()):
408 
409  $testSession->setCurrentQuestionId(null);
410  }
411 
412  foreach ($this->postponedQuestions as $questionId) {
413  if (!$this->questionSet->questionExists($questionId)) {
414  unset($this->postponedQuestions[$questionId]);
415  }
416  }
417 
418  foreach ($this->wrongAnsweredQuestions as $questionId) {
419  if (!$this->questionSet->questionExists($questionId)) {
420  unset($this->wrongAnsweredQuestions[$questionId]);
421  }
422  }
423 
424  foreach ($this->correctAnsweredQuestions as $questionId) {
425  if (!$this->questionSet->questionExists($questionId)) {
426  unset($this->correctAnsweredQuestions[$questionId]);
427  }
428  }
429  }
430 
431  // -----------------------------------------------------------------------------------------------------------------
432 
433  public function getUpcomingQuestionId()
434  {
435  if ($questionId = $this->fetchUpcomingQuestionId(true, true)) {
436  return $questionId;
437  }
438 
439  if ($questionId = $this->fetchUpcomingQuestionId(false, true)) {
440  return $questionId;
441  }
442 
443  if ($questionId = $this->fetchUpcomingQuestionId(true, false)) {
444  return $questionId;
445  }
446 
447  if ($questionId = $this->fetchUpcomingQuestionId(false, false)) {
448  return $questionId;
449  }
450 
451  return null;
452  }
453 
454  private function fetchUpcomingQuestionId($excludePostponedQuestions, $forceNonAnsweredQuestion)
455  {
456  foreach ($this->questionSet->getActualQuestionSequence() as $level => $questions) {
457  $postponedQuestions = array();
458 
459  foreach ($questions as $pos => $qId) {
460  if (isset($this->correctAnsweredQuestions[$qId])) {
461  continue;
462  }
463 
465  continue;
466  }
467 
468  if ($forceNonAnsweredQuestion && isset($this->wrongAnsweredQuestions[$qId])) {
469  continue;
470  }
471 
472  if (isset($this->postponedQuestions[$qId])) {
473  $postponedQuestions[$qId] = $this->postponedQuestions[$qId];
474  continue;
475  }
476 
477  return $qId;
478  }
479 
480  if (!$excludePostponedQuestions && count($postponedQuestions)) {
482  }
483  }
484 
485  return null;
486  }
487 
488  public function isAnsweredQuestion($questionId)
489  {
490  return (
491  isset($this->correctAnsweredQuestions[$questionId])
492  || isset($this->wrongAnsweredQuestions[$questionId])
493  );
494  }
495 
496  public function isPostponedQuestion($questionId)
497  {
498  return isset($this->postponedQuestions[$questionId]);
499  }
500 
501  public function isFilteredQuestion($questionId)
502  {
503  foreach ($this->questionSet->getActualQuestionSequence() as $level => $questions) {
504  if (in_array($questionId, $questions)) {
505  return true;
506  }
507  }
508 
509  return false;
510  }
511 
512  public function trackedQuestionExists()
513  {
514  return (bool) count($this->questionTracking);
515  }
516 
518  {
519  $questionList = array();
520 
521  if ($currentQuestionId) {
522  $questionList[$currentQuestionId] = $this->questionSet->getQuestionData($currentQuestionId);
523  }
524 
525  foreach (array_reverse($this->questionTracking) as $trackedQuestion) {
526  if (!isset($questionList[ $trackedQuestion['qid'] ])) {
527  $questionList[ $trackedQuestion['qid'] ] = $this->questionSet->getQuestionData($trackedQuestion['qid']);
528  }
529  }
530 
531  return $questionList;
532  }
533 
534  public function resetTrackedQuestionList()
535  {
536  $this->questionTracking = array();
537  }
538 
539  public function openQuestionExists()
540  {
541  return count($this->getOpenQuestions()) > 0;
542  }
543 
544  public function getOpenQuestions()
545  {
546  $completeQuestionIds = array_keys($this->questionSet->getAllQuestionsData());
547 
548  $openQuestions = array_diff($completeQuestionIds, $this->correctAnsweredQuestions);
549 
550  return $openQuestions;
551  }
552 
553  public function getTrackedQuestionCount()
554  {
555  $uniqueQuestions = array();
556 
557  foreach ($this->questionTracking as $trackedQuestion) {
558  $uniqueQuestions[$trackedQuestion['qid']] = $trackedQuestion['qid'];
559  }
560 
561  return count($uniqueQuestions);
562  }
563 
564  public function getCurrentPositionIndex($questionId)
565  {
566  $i = 0;
567 
568  foreach ($this->getSelectionOrderedSequence() as $qId) {
569  $i++;
570 
571  if ($qId == $questionId) {
572  return $i;
573  }
574  }
575 
576  return null;
577  }
578 
579  public function getLastPositionIndex()
580  {
581  return count($this->getSelectionOrderedSequence());
582  }
583 
584  // -----------------------------------------------------------------------------------------------------------------
585 
586  public function setQuestionUnchecked($questionId)
587  {
588  unset($this->alreadyCheckedQuestions[$questionId]);
589  }
590 
591  public function setQuestionChecked($questionId)
592  {
593  $this->newlyCheckedQuestion = $questionId;
594  $this->alreadyCheckedQuestions[$questionId] = $questionId;
595  }
596 
597  public function isQuestionChecked($questionId)
598  {
599  return isset($this->alreadyCheckedQuestions[$questionId]);
600  }
601 
602  public function setQuestionPostponed($questionId)
603  {
604  $this->trackQuestion($questionId, 'postponed');
605 
606  if (!isset($this->postponedQuestions[$questionId])) {
607  $this->postponedQuestions[$questionId] = 0;
608  }
609 
610  $this->postponedQuestions[$questionId]++;
611 
612  $this->newlyPostponedQuestion = $questionId;
613  $this->newlyPostponedQuestionsCount = $this->postponedQuestions[$questionId];
614  }
615 
616  public function unsetQuestionPostponed($questionId)
617  {
618  if (isset($this->postponedQuestions[$questionId])) {
619  unset($this->postponedQuestions[$questionId]);
620  }
621  }
622 
623  public function setQuestionAnsweredCorrect($questionId)
624  {
625  $this->trackQuestion($questionId, 'correct');
626 
627  $this->correctAnsweredQuestions[$questionId] = $questionId;
628 
629  if (isset($this->wrongAnsweredQuestions[$questionId])) {
630  unset($this->wrongAnsweredQuestions[$questionId]);
631  }
632 
633  $this->newlyAnsweredQuestion = $questionId;
634  $this->newlyAnsweredQuestionsAnswerStatus = true;
635  }
636 
637  public function setQuestionAnsweredWrong($questionId)
638  {
639  $this->trackQuestion($questionId, 'wrong');
640 
641  $this->wrongAnsweredQuestions[$questionId] = $questionId;
642 
643  if (isset($this->correctAnsweredQuestions[$questionId])) {
644  unset($this->correctAnsweredQuestions[$questionId]);
645  }
646 
647  $this->newlyAnsweredQuestion = $questionId;
648  $this->newlyAnsweredQuestionsAnswerStatus = false;
649  }
650 
651  private function trackQuestion($questionId, $answerStatus)
652  {
653  $this->questionTracking[] = array(
654  'qid' => $questionId, 'status' => $answerStatus
655  );
656 
657  $this->newlyTrackedQuestion = $questionId;
658  $this->newlyTrackedQuestionsStatus = $answerStatus;
659  }
660 
661  // -----------------------------------------------------------------------------------------------------------------
662 
663  public function hasStarted()
664  {
665  return $this->trackedQuestionExists();
666  }
667 
668  // -----------------------------------------------------------------------------------------------------------------
669 
673  public function getQuestionSet()
674  {
675  return $this->questionSet;
676  }
677 
678  public function getCompleteQuestionsData()
679  {
680  return $this->questionSet->getCompleteQuestionList()->getQuestionDataArray();
681  }
682 
683  public function getFilteredQuestionsData()
684  {
685  return $this->questionSet->getFilteredQuestionList()->getQuestionDataArray();
686  }
687 
688  // -----------------------------------------------------------------------------------------------------------------
689 
690  public function getUserSequenceQuestions()
691  {
692  //return array_keys( $this->getTrackedQuestionList() );
693 
694  $questionSequence = array();
695 
696  foreach ($this->questionSet->getActualQuestionSequence() as $level => $questions) {
697  $questionSequence = array_merge($questionSequence, $questions);
698  }
699 
700  return $questionSequence;
701  }
702 
708  {
709  $minPostponeCount = null;
710  $minPostponeItem = null;
711 
712  foreach (array_reverse($postponedQuestions, true) as $qId => $postponeCount) {
713  if ($minPostponeCount === null || $postponeCount <= $minPostponeCount) {
714  $minPostponeCount = $postponeCount;
715  $minPostponeItem = $qId;
716  }
717  }
718  return $minPostponeItem;
719  }
720 
721  public function getPass()
722  {
723  return 0;
724  }
725 
726  // -----------------------------------------------------------------------------------------------------------------
727 
729  {
730  $maxPostponeCount = max($postponedQuestions);
731 
732  $orderedSequence = array();
733  $postponedCountDomain = array_flip($postponedQuestions);
734 
735  for ($i = 1; $i <= $maxPostponeCount; $i++) {
736  if (!isset($postponedCountDomain[$i])) {
737  continue;
738  }
739 
740  foreach ($postponedQuestions as $qId => $postponeCount) {
741  if ($postponeCount == $i) {
742  $orderedSequence[] = $qId;
743  }
744  }
745  }
746 
747  return $orderedSequence;
748  }
749 
750  private function fetchQuestionSequence($nonPostponedQuestions, $nonAnsweredQuestions)
751  {
752  $questionSequence = array();
753 
754  foreach ($this->questionSet->getActualQuestionSequence() as $level => $questions) {
755  $postponedQuestions = array();
756 
757  foreach ($questions as $pos => $qId) {
758  if (isset($this->correctAnsweredQuestions[$qId])) {
759  continue;
760  }
761 
762  if ($nonAnsweredQuestions && isset($this->wrongAnsweredQuestions[$qId])) {
763  continue;
764  } elseif (!$nonAnsweredQuestions && !isset($this->wrongAnsweredQuestions[$qId])) {
765  continue;
766  }
767 
768  if (!$nonPostponedQuestions && isset($this->postponedQuestions[$qId])) {
769  $postponedQuestions[$qId] = $this->postponedQuestions[$qId];
770  continue;
771  } elseif ($nonPostponedQuestions && !isset($this->postponedQuestions[$qId])) {
772  $questionSequence[] = $qId;
773  }
774  }
775 
776  if (!$nonPostponedQuestions && count($postponedQuestions)) {
777  $questionSequence = array_merge(
778  $questionSequence,
780  );
781  }
782  }
783 
784  return $questionSequence;
785  }
786 
788  {
789  $questionSequence = array();
790 
791  foreach ($this->questionTracking as $key => $question) {
792  $qId = $question['qid'];
793 
794  if (!isset($this->correctAnsweredQuestions[$qId])) {
795  continue;
796  }
797 
798  $questionSequence[] = $qId;
799  }
800 
801  return $questionSequence;
802  }
803 
804  private function getOrderedSequence()
805  {
807 
808  $nonAnsweredQuestions = $this->fetchQuestionSequence(
809  true,
810  true
811  );
812 
813  $postponedNonAnsweredQuestions = $this->fetchQuestionSequence(
814  false,
815  true
816  );
817 
819  true,
820  false
821  );
822 
823  $postponedWrongAnsweredQuestions = $this->fetchQuestionSequence(
824  false,
825  false
826  );
827 
828  $questionOrder = array_merge(
830  $nonAnsweredQuestions,
831  $postponedNonAnsweredQuestions,
833  $postponedWrongAnsweredQuestions
834  );
835 
836  return $questionOrder;
837  }
838 
839  public function getSelectionOrderedSequence()
840  {
841  $sequence = array();
842 
843  foreach ($this->getOrderedSequence() as $qId) {
844  if (!$this->getQuestionSet()->getSelectionQuestionList()->isInList($qId)) {
845  continue;
846  }
847 
848  $sequence[] = $qId;
849  }
850 
851  return $sequence;
852  }
853 
854  public function getSequenceSummary($obligationsFilterEnabled = false)
855  {
856  $questionOrder = $this->getSelectionOrderedSequence();
857 
858  $solved_questions = ilObjTest::_getSolvedQuestions($this->getActiveId());
859 
860  $key = 1;
861 
862  $summary = array();
863 
864  foreach ($questionOrder as $qId) {
865  $question = &ilObjTest::_instanciateQuestion($qId);
866  if (is_object($question)) {
867  $worked_through = $question->_isWorkedThrough($this->getActiveId(), $question->getId(), $this->getPass());
868  $solved = 0;
869  if (array_key_exists($question->getId(), $solved_questions)) {
870  $solved = $solved_questions[$question->getId()]["solved"];
871  }
872 
873  // do not show postponing, since this happens implicit on dircarding solutions (CTM only)
874  //$is_postponed = $this->isPostponedQuestion($question->getId());
875 
876  $row = array("nr" => "$key", "title" => $question->getTitle(), "qid" => $question->getId(), "visited" => $worked_through, "solved" => (($solved) ? "1" : "0"), "description" => $question->getComment(), "points" => $question->getMaximumPoints(), "worked_through" => $worked_through, "postponed" => $is_postponed, "sequence" => $qId, "obligatory" => ilObjTest::isQuestionObligatory($question->getId()), 'isAnswered' => $question->isAnswered($this->getActiveId(), $this->getPass()));
877 
878  if (!$obligationsFilterEnabled || $row['obligatory']) {
879  $summary[] = $row;
880  }
881 
882  $key++;
883  }
884  }
885 
886  return $summary;
887  }
888 
890  {
891  $filteredQuestions = $this->questionSet->getFilteredQuestionList()->getQuestionDataArray();
892 
893  foreach ($filteredQuestions as $filteredQuestion) {
894  if ($this->isQuestionChecked($filteredQuestion['question_id'])) {
895  return true;
896  }
897  }
898 
899  return false;
900  }
901 
903  {
904  $filteredQuestions = $this->questionSet->getFilteredQuestionList()->getQuestionDataArray();
905 
906  foreach ($filteredQuestions as $filteredQuestion) {
907  if ($this->isQuestionChecked($filteredQuestion['question_id'])) {
908  $this->setQuestionUnchecked($filteredQuestion['question_id']);
909  }
910  }
911  }
912 }
setPreventCheckedQuestionsFromComingUpEnabled($preventCheckedQuestionsFromComingUpEnabled)
cleanupQuestions(ilTestSessionDynamicQuestionSet $testSession)
$summary
Definition: cron.php:24
loadQuestions(ilObjTestDynamicQuestionSetConfig $dynamicQuestionSetConfig, ilTestDynamicQuestionSetFilterSelection $filterSelection)
static _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
foreach($_POST as $key=> $value) $res
static _getSolvedQuestions($active_id, $question_fi=null)
get solved questions
$query
__construct(ilDBInterface $db, ilTestDynamicQuestionSet $questionSet, $activeId)
Constructor.
$row
fetchQuestionSequence($nonPostponedQuestions, $nonAnsweredQuestions)
$i
Definition: disco.tpl.php:19
$key
Definition: croninfo.php:18
fetchUpcomingQuestionId($excludePostponedQuestions, $forceNonAnsweredQuestion)