/** * Selects the next question base appropriate for the quiz attempt passed * @param Model_Quiz_QuizAttempt $vQuizAttempt * @param boolean $debug Being Phased out TODO * @throws Exception * @return Model_Quiz_QuestionBase|NULL NULL Should only be returned in exceptional circumstances */ public static function select_next_question($vQuizAttempt, $debug = false) { //Get all the concepts this quiz tests $vQuiz = $vQuizAttempt->getQuiz(); $vTestedConcepts = $vQuiz->getTestedConcepts(); if (sizeof($vTestedConcepts) < 1) { throw new Exception("This quiz is not ready yet - reason: No testedConcepts specified."); } //Foreach concept tested, see how many questions we've attempted foreach ($vTestedConcepts as $vTestedConcept) { Model_Shell_Debug::getInstance()->log("Looking at TestedConcept: " . $vTestedConcept->getConcept()->getConcept_name()); $vQuestionAttempts = Model_Quiz_QuestionAttempt::getAllFromQuizAttemptAndConcept($vQuizAttempt, $vTestedConcept->getConcept()); Model_Shell_Debug::getInstance()->log("Done " . sizeof($vQuestionAttempts) . "/" . $vTestedConcept->getNumber_tested()); //If we haven't attempted enough in this concept, we need to choose a question difficulty if (sizeof($vQuestionAttempts) < $vTestedConcept->getNumber_tested()) { //What difficulties are there being tested within this concept? $vLowest = $vTestedConcept->getLower_difficulty(); $vHighest = $vTestedConcept->getHigher_difficulty(); //Find out how many questions I should allocate for each difficulty $mQuestionsPerDifficultyLevel = floor(0.8 / ($vHighest + 1 - $vLowest) * $vTestedConcept->getNumber_tested()); $mBuffer = $vTestedConcept->getNumber_tested() - ($vHighest + 1 - $vLowest) * $mQuestionsPerDifficultyLevel; //If there's only one difficulty, that settles that if ($vLowest == $vHighest) { return Model_Shell_QuestionChooser::select_next_question_2($vQuizAttempt, $vTestedConcept->getConcept(), $vLowest); } //Were there any previous attempts at this concept? If not, we need to start a Q at the lowest specified difficulty $vQuestionAttempts = $vQuizAttempt->getQuestionAttempts($vTestedConcept->getConcept()); //echo "Size of vQuestionAttempts:".sizeof($vQuestionAttempts)."<br/>"; if (sizeof($vQuestionAttempts) == 0) { return Model_Shell_QuestionChooser::select_next_question_2($vQuizAttempt, $vTestedConcept->getConcept(), $vLowest); } //Find the highest attempted difficulty done so far $vHighestSoFar = $vQuizAttempt->getHighestDifficultyTestedSoFar(); //echo "The highest difficulty so far: $vHighestSoFar<br/>"; if ($vHighestSoFar >= $vHighest) { //Already at the highest difficulty, the next question will be at this difficulty too return Model_Shell_QuestionChooser::select_next_question_2($vQuizAttempt, $vTestedConcept->getConcept(), $vHighest); } //Look at the last attempts in this difficulty. Were they below-par (use a percentage?)? $vTotal = 0; $vTotalRight = 0; Model_Shell_Debug::getInstance()->log("Checking your previous attempts...Size of vQuestionAttempts:" . sizeof($vQuestionAttempts)); foreach ($vQuestionAttempts as $vQA) { $vQB = $vQA->getQuestion_base(); //echo "Looking at " . $vQB->getDifficulty() . " vs $vHighestSoFar<br/>"; if ($vQB->getDifficulty() == $vHighestSoFar) { $vTotal++; if ($vQB->getEstimated_time() > $vQA->getTime_finished() - $vQA->getTime_started()) { //You did this question in a reasonable time... did you get it right first time? if ($vQA->getInitial_result() == "1") { $vTotalRight++; } } } } //Note, a divide by 0 happened here before... if ($vTotal == 0 || $vTotalRight / $vTotal > 0.8) { //OK. If they WERE good attempts, I should move on if I've hit my quota for this difficulty if ($vTotal >= $mQuestionsPerDifficultyLevel) { //Reached our minumum quota for this difficulty level. NEXT! return Model_Shell_QuestionChooser::select_next_question_2($vQuizAttempt, $vTestedConcept->getConcept(), $vHighestSoFar + 1); } else { //We've done well so far, but we need to do more in this difficulty return Model_Shell_QuestionChooser::select_next_question_2($vQuizAttempt, $vTestedConcept->getConcept(), $vHighestSoFar); } } else { //Your previous attempts weren't all that good. //Figure out how much buffer we have left. Can we afford to give you a question at $vHighestSoFar? Or do we have to move onto something harder? $mBuffer = $vTestedConcept->getNumber_tested() - ($vHighest - $vHighestSoFar) * $mQuestionsPerDifficultyLevel - sizeof($vQuestionAttempts); if ($mBuffer > 1) { //We can afford to give you another easier question return Model_Shell_QuestionChooser::select_next_question_2($vQuizAttempt, $vTestedConcept->getConcept(), $vHighestSoFar); } else { //Sorry.. buffer is dry. You weren't going too well, but you gotta do the harder stuff return Model_Shell_QuestionChooser::select_next_question_2($vQuizAttempt, $vTestedConcept->getConcept(), $vHighestSoFar + 1); } } } //End If } //End Foreach testedConcept //We shouldn't really ever get here, but in case we do -> null return null; }