/** * Start a normal, new, quiz attempt. * * @param quiz $quizobj the quiz object to start an attempt for. * @param question_usage_by_activity $quba * @param object $attempt * @param integer $attemptnumber starting from 1 * @param integer $timenow the attempt start time * @param array $questionids slot number => question id. Used for random questions, to force the choice * of a particular actual question. Intended for testing purposes only. * @param array $forcedvariantsbyslot slot number => variant. Used for questions with variants, * to force the choice of a particular variant. Intended for testing * purposes only. * @throws moodle_exception * @return object modified attempt object */ function quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptnumber, $timenow, $questionids = array(), $forcedvariantsbyslot = array()) { // Usages for this user's previous quiz attempts. $qubaids = new \mod_quiz\question\qubaids_for_users_attempts($quizobj->get_quizid(), $attempt->userid); // Fully load all the questions in this quiz. $quizobj->preload_questions(); $quizobj->load_questions(); // First load all the non-random questions. $randomfound = false; $slot = 0; $questions = array(); $maxmark = array(); $page = array(); foreach ($quizobj->get_questions() as $questiondata) { $slot += 1; $maxmark[$slot] = $questiondata->maxmark; $page[$slot] = $questiondata->page; if ($questiondata->qtype == 'random') { $randomfound = true; continue; } if (!$quizobj->get_quiz()->shuffleanswers) { $questiondata->options->shuffleanswers = false; } $questions[$slot] = question_bank::make_question($questiondata); } // Then find a question to go in place of each random question. if ($randomfound) { $slot = 0; $usedquestionids = array(); foreach ($questions as $question) { if (isset($usedquestions[$question->id])) { $usedquestionids[$question->id] += 1; } else { $usedquestionids[$question->id] = 1; } } $randomloader = new \core_question\bank\random_question_loader($qubaids, $usedquestionids); foreach ($quizobj->get_questions() as $questiondata) { $slot += 1; if ($questiondata->qtype != 'random') { continue; } // Deal with fixed random choices for testing. if (isset($questionids[$quba->next_slot_number()])) { if ($randomloader->is_question_available($questiondata->category, (bool) $questiondata->questiontext, $questionids[$quba->next_slot_number()])) { $questions[$slot] = question_bank::load_question($questionids[$quba->next_slot_number()], $quizobj->get_quiz()->shuffleanswers); continue; } else { throw new coding_exception('Forced question id not available.'); } } // Normal case, pick one at random. $questionid = $randomloader->get_next_question_id($questiondata->category, (bool) $questiondata->questiontext); if ($questionid === null) { throw new moodle_exception('notenoughrandomquestions', 'quiz', $quizobj->view_url(), $questiondata); } $questions[$slot] = question_bank::load_question($questionid, $quizobj->get_quiz()->shuffleanswers); } } // Finally add them all to the usage. ksort($questions); foreach ($questions as $slot => $question) { $newslot = $quba->add_question($question, $maxmark[$slot]); if ($newslot != $slot) { throw new coding_exception('Slot numbers have got confused.'); } } // Start all the questions. $variantstrategy = new core_question\engine\variants\least_used_strategy($quba, $qubaids); if (!empty($forcedvariantsbyslot)) { $forcedvariantsbyseed = question_variant_forced_choices_selection_strategy::prepare_forced_choices_array($forcedvariantsbyslot, $quba); $variantstrategy = new question_variant_forced_choices_selection_strategy($forcedvariantsbyseed, $variantstrategy); } $quba->start_all_questions($variantstrategy, $timenow); // Work out the attempt layout. $sections = $quizobj->get_sections(); foreach ($sections as $i => $section) { if (isset($sections[$i + 1])) { $sections[$i]->lastslot = $sections[$i + 1]->firstslot - 1; } else { $sections[$i]->lastslot = count($questions); } } $layout = array(); foreach ($sections as $section) { if ($section->shufflequestions) { $questionsinthissection = array(); for ($slot = $section->firstslot; $slot <= $section->lastslot; $slot += 1) { $questionsinthissection[] = $slot; } shuffle($questionsinthissection); $questionsonthispage = 0; foreach ($questionsinthissection as $slot) { if ($questionsonthispage && $questionsonthispage == $quizobj->get_quiz()->questionsperpage) { $layout[] = 0; $questionsonthispage = 0; } $layout[] = $slot; $questionsonthispage += 1; } } else { $currentpage = $page[$section->firstslot]; for ($slot = $section->firstslot; $slot <= $section->lastslot; $slot += 1) { if ($currentpage !== null && $page[$slot] != $currentpage) { $layout[] = 0; } $layout[] = $slot; $currentpage = $page[$slot]; } } // Each section ends with a page break. $layout[] = 0; } $attempt->layout = implode(',', $layout); return $attempt; }