/** * Start test * * This function is used to start a test. It gathers all the required information, creates * a new EfrontTest instance and stores it in the database. * <br/>Example: * <code> * $test = new EfrontTest(23); //Instantiate object for test with id 23 * $testInstance = $test -> start('jdoe); //start() returns a new EfrontTest object, different that the initial, for the user 'jdoe' * $testInstance -> ... //Do whatever with the new object. * </code> * * @param string $login The user to start test for * @return EfrontCompletedTest a new EfrontCompletedTest instance, which represents the test (to be) completed * @since 3.5.2 * @access public */ public function start($login) { $completedTest = new EfrontCompletedTest($this, $login); $completedTest->time['start'] = time(); //The time that this test has started $completedTest->time['resume'] = time(); //Initialize time that this test has 'resumed' $completedTest->time['end'] = null; //The time that this test ends $completedTest->time['spent'] = 0; //Initialize the time spent $completedTest->completedTest['status'] = 'incomplete'; //The test just started; So set its status to 'incomplete' // $completedTest -> completedTest['archive'] = '0'; //The test just started; So set its status to 'incomplete' $testQuestions = $this->getQuestions(true); // lines added for redo only wrong questions $resultCompleted = EfrontCompletedTest::retrieveCompletedTest("completed_tests ct join completed_tests_blob ctb on ct.id=ctb.completed_tests_ID", "ctb.test", "archive=1 AND users_LOGIN='******'s_login'] . "' AND tests_ID=" . $this->test['id'], "timestamp desc"); $recentlyCompleted = unserialize($resultCompleted[0]['test']); //1. Get the random pool questions if ($this->options['random_pool']) { if ($recentlyCompleted->redoOnlyWrong == false) { sizeof($testQuestions) >= $this->options['random_pool'] ? $poolSize = $this->options['random_pool'] : ($poolSize = sizeof($testQuestions)); shuffle($testQuestions); //shuffle available questions so that we don't take the same always if ($this->options['show_incomplete']) { $alreadyCompletedQuestions = array(); foreach ($resultCompleted as $value) { $previouslyCompletedTest = unserialize($value['test']); if ($previouslyCompletedTest instanceof EfrontCompletedTest) { $alreadyCompletedQuestions = array_merge($alreadyCompletedQuestions, array_keys($previouslyCompletedTest->questions)); } } $alreadyCompletedQuestions = array_unique($alreadyCompletedQuestions); $incompleteQuestions = array_diff(array_keys($this->questions), $alreadyCompletedQuestions); //Find out which questions haven't been answered yet //Keep only incomplete questions foreach ($testQuestions as $key => $value) { if (!in_array($value->question['id'], $incompleteQuestions) && sizeof($testQuestions) > $poolSize) { unset($testQuestions[$key]); } } } // Code for selecting questions depending on lessons_ID frequency $lesson_count = array(); $questions_per_lesson = array(); foreach ($testQuestions as $value) { $lesson_count[$value->question['lessons_ID']] += 1; $questions_per_lesson[$value->question['lessons_ID']][$value->question['id']] = $value; } $questionsTemp = array(); $randomQuestions = array(); foreach (array_keys($questions_per_lesson) as $key) { $questionsTemp = $questions_per_lesson[$key]; shuffle($questionsTemp); $questionsTemp = array_slice($questionsTemp, 0, floor($lesson_count[$key] * $poolSize / array_sum($lesson_count)), true); $randomQuestions = array_merge($randomQuestions, $questionsTemp); } $temp = array(); foreach ($randomQuestions as $value) { //Shuffling reindexed array, so we need to put back the correct keys $temp[$value->question['id']] = $value; } $randomQuestions = $temp; $temp2 = array(); foreach ($testQuestions as $value) { //Shuffling reindexed array, so we need to put back the correct keys $temp2[$value->question['id']] = $value; } $testQuestions = $temp2; foreach ($randomQuestions as $key => $value) { unset($testQuestions[$key]); } if (sizeof($randomQuestions) < $poolSize) { $additionalQuestions = array_slice($testQuestions, 0, $poolSize - sizeof($randomQuestions)); } $temp = array(); foreach ($additionalQuestions as $value) { //Shuffling reindexed array, so we need to put back the correct keys $temp[$value->question['id']] = $value; } $additionalQuestions = $temp; if (!empty($additionalQuestions)) { $randomQuestions = array_merge($randomQuestions, $additionalQuestions); } $temp = array(); foreach ($randomQuestions as $value) { //Shuffling reindexed array, so we need to put back the correct keys $temp[$value->question['id']] = $value; } $randomQuestions = $temp; $completedTest->questions = $randomQuestions; //End of code for selecting questions depending on lessons_ID frequency } else { $completedTest->questions = $recentlyCompleted->questions; //when redoing wrong answered, same questions must be selected } } else { $completedTest->questions = $testQuestions; } //2. Shuffle answers inside questions foreach ($completedTest->questions as $key => $question) { if ($this->options['shuffle_answers']) { $question->shuffle(); } $completedTest->questions[$key] = $question; } //3. Set questions in order if ($this->options['shuffle_questions']) { $completedTest->orderQuestions(); } //4. Get additional information that might be useful $completedTest->getUnit(); $completedTest->getLesson(); //5. When redo only wrong, set it if ($recentlyCompleted->redoOnlyWrong == true) { $completedTest->correctPrevious = true; } //6. Store test $completedTest->save(); try { $lesson = new EfrontLesson($this->test['lessons_ID']); $lesson_name = $lesson->lesson['name']; } catch (EfrontLessonException $e) { $lesson_name = _SKILLGAPTESTS; } EfrontEvent::triggerEvent(array("type" => EfrontEvent::TEST_START, "users_LOGIN" => $login, "lessons_ID" => $this->test['lessons_ID'], "lessons_name" => $lesson_name, "entity_ID" => $this->test['id'], "entity_name" => $this->test['name'])); return $completedTest; }