/** * Test update question flag */ public function test_core_question_update_flag() { $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); // Create a question category. $cat = $questiongenerator->create_question_category(); $quba = question_engine::make_questions_usage_by_activity('core_question_update_flag', context_system::instance()); $quba->set_preferred_behaviour('deferredfeedback'); $questiondata = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); $question = question_bank::load_question($questiondata->id); $slot = $quba->add_question($question); $qa = $quba->get_question_attempt($slot); self::setUser($this->student); $quba->start_all_questions(); question_engine::save_questions_usage_by_activity($quba); $qubaid = $quba->get_id(); $questionid = $question->id; $qaid = $qa->get_database_id(); $checksum = md5($qubaid . "_" . $this->student->secret . "_" . $questionid . "_" . $qaid . "_" . $slot); $flag = core_question_external::update_flag($qubaid, $questionid, $qaid, $slot, $checksum, true); $this->assertTrue($flag['status']); // Test invalid checksum. try { // Using random_string to force failing. $checksum = md5($qubaid . "_" . random_string(11) . "_" . $questionid . "_" . $qaid . "_" . $slot); core_question_external::update_flag($qubaid, $questionid, $qaid, $slot, $checksum, true); $this->fail('Exception expected due to invalid checksum.'); } catch (moodle_exception $e) { $this->assertEquals('errorsavingflags', $e->errorcode); } }
public function test_second_attempt_uses_other_dataset() { global $DB; $this->resetAfterTest(); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $generator->create_question_category(); $questiondata = $generator->create_question('calculated', null, array('category' => $cat->id)); // Create two dataset items. $adefinitionid = $DB->get_field_sql("\n SELECT qdd.id\n FROM {question_dataset_definitions} qdd\n JOIN {question_datasets} qd ON qd.datasetdefinition = qdd.id\n WHERE qd.question = ?\n AND qdd.name = ?", array($questiondata->id, 'a')); $bdefinitionid = $DB->get_field_sql("\n SELECT qdd.id\n FROM {question_dataset_definitions} qdd\n JOIN {question_datasets} qd ON qd.datasetdefinition = qdd.id\n WHERE qd.question = ?\n AND qdd.name = ?", array($questiondata->id, 'b')); $DB->set_field('question_dataset_definitions', 'itemcount', 2, array('id' => $adefinitionid)); $DB->set_field('question_dataset_definitions', 'itemcount', 2, array('id' => $bdefinitionid)); $DB->insert_record('question_dataset_items', array('definition' => $adefinitionid, 'itemnumber' => 1, 'value' => 3)); $DB->insert_record('question_dataset_items', array('definition' => $bdefinitionid, 'itemnumber' => 1, 'value' => 7)); $DB->insert_record('question_dataset_items', array('definition' => $adefinitionid, 'itemnumber' => 2, 'value' => 6)); $DB->insert_record('question_dataset_items', array('definition' => $bdefinitionid, 'itemnumber' => 2, 'value' => 4)); $question = question_bank::load_question($questiondata->id); $quba1 = question_engine::make_questions_usage_by_activity('test', context_system::instance()); $quba1->set_preferred_behaviour('deferredfeedback'); $slot1 = $quba1->add_question($question); $quba1->start_all_questions(new core_question\engine\variants\least_used_strategy($quba1, new qubaid_list(array()))); question_engine::save_questions_usage_by_activity($quba1); $variant1 = $quba1->get_variant($slot1); // Second attempt should use the other variant. $quba2 = question_engine::make_questions_usage_by_activity('test', context_system::instance()); $quba2->set_preferred_behaviour('deferredfeedback'); $slot2 = $quba2->add_question($question); $quba2->start_all_questions(new core_question\engine\variants\least_used_strategy($quba1, new qubaid_list(array($quba1->get_id())))); question_engine::save_questions_usage_by_activity($quba2); $variant2 = $quba2->get_variant($slot2); $this->assertNotEquals($variant1, $variant2); // Third attempt uses either variant at random. $quba3 = question_engine::make_questions_usage_by_activity('test', context_system::instance()); $quba3->set_preferred_behaviour('deferredfeedback'); $slot3 = $quba3->add_question($question); $quba3->start_all_questions(new core_question\engine\variants\least_used_strategy($quba1, new qubaid_list(array($quba1->get_id(), $quba2->get_id())))); $variant3 = $quba3->get_variant($slot3); $this->assertTrue($variant3 == $variant1 || $variant3 == $variant2); }
/** * Regrade a particular quiz attempt. Either for real ($dryrun = false), or * as a pretend regrade to see which fractions would change. The outcome is * stored in the quiz_overview_regrades table. * * Note, $attempt is not upgraded in the database. The caller needs to do that. * However, $attempt->sumgrades is updated, if this is not a dry run. * * @param object $attempt the quiz attempt to regrade. * @param bool $dryrun if true, do a pretend regrade, otherwise do it for real. * @param array $slots if null, regrade all questions, otherwise, just regrade * the quetsions with those slots. */ protected function regrade_attempt($attempt, $dryrun = false, $slots = null) { global $DB; // Need more time for a quiz with many questions. set_time_limit(300); $transaction = $DB->start_delegated_transaction(); $quba = question_engine::load_questions_usage_by_activity($attempt->uniqueid); if (is_null($slots)) { $slots = $quba->get_slots(); } $finished = $attempt->state == quiz_attempt::FINISHED; foreach ($slots as $slot) { $qqr = new stdClass(); $qqr->oldfraction = $quba->get_question_fraction($slot); $quba->regrade_question($slot, $finished); $qqr->newfraction = $quba->get_question_fraction($slot); if (abs($qqr->oldfraction - $qqr->newfraction) > 1.0E-7) { $qqr->questionusageid = $quba->get_id(); $qqr->slot = $slot; $qqr->regraded = empty($dryrun); $qqr->timemodified = time(); $DB->insert_record('quiz_overview_regrades', $qqr, false); } } if (!$dryrun) { question_engine::save_questions_usage_by_activity($quba); } $transaction->allow_commit(); // Really, PHP should not need this hint, but without this, we just run out of memory. $quba = null; $transaction = null; gc_collect_cycles(); }
/** * Makes an attempt for one student in this quiz, answering all the questions. * * @param $student * @param $quiz * @return int How many questions answers were made. */ public function make_student_quiz_atttempt($student, $quiz) { $submissioncount = 0; $attempt = $this->start_quiz_attempt($quiz->id, $student->id); $quba = question_engine::load_questions_usage_by_activity($attempt->uniqueid); // This bit strips out bits of quiz_attempt::process_submitted_actions() // Simulates data from the form. // TODO iterate over the questions. $formdata = array('answer' => 'Sample essay answer text', 'answerformat' => FORMAT_MOODLE); $slot = 1; // Only 1 question so far. $quba->process_action($slot, $formdata, time()); $submissioncount++; question_engine::save_questions_usage_by_activity($quba); $this->end_quiz_attmept($attempt); return $submissioncount; }
/** * The save started question usage and quiz attempt in db and log the started attempt. * * @param quiz $quizobj * @param question_usage_by_activity $quba * @param object $attempt * @return object attempt object with uniqueid and id set. */ function quiz_attempt_save_started($quizobj, $quba, $attempt) { global $DB; // Save the attempt in the database. question_engine::save_questions_usage_by_activity($quba); $attempt->uniqueid = $quba->get_id(); $attempt->id = $DB->insert_record('quiz_attempts', $attempt); // Params used by the events below. $params = array('objectid' => $attempt->id, 'relateduserid' => $attempt->userid, 'courseid' => $quizobj->get_courseid(), 'context' => $quizobj->get_context()); // Decide which event we are using. if ($attempt->preview) { $params['other'] = array('quizid' => $quizobj->get_quizid()); $event = \mod_quiz\event\attempt_preview_started::create($params); } else { $event = \mod_quiz\event\attempt_started::create($params); } // Trigger the event. $event->add_record_snapshot('quiz', $quizobj->get_quiz()); $event->add_record_snapshot('quiz_attempts', $attempt); $event->trigger(); return $attempt; }
public function finish_attempt($timestamp) { global $DB, $USER; $this->quba->process_all_actions($timestamp); $this->quba->finish_all_questions($timestamp); question_engine::save_questions_usage_by_activity($this->quba); $this->attempt->timemodified = $timestamp; $this->attempt->timefinish = $timestamp; $this->attempt->sumgrades = $this->quba->get_total_mark(); $this->attempt->persent = round((double) $this->quba->get_total_mark() / (double) $this->readerobj->quiz->sumgrades * 100); if ((int) $this->readerobj->reader->percentforreading <= (int) $this->attempt->persent) { $this->attempt->passed = 'true'; $passedlog = "Passed"; } else { $this->attempt->passed = 'false'; $passedlog = "Failed"; } //add_to_log($this->readerobj->course->id, "reader", "finish attempt: ".addslashes($this->readerobj->book->name), "view.php?id={$this->readerobj->reader->id}", $this->attempt->persent."%/".$this->attempt->passed); add_to_log($this->readerobj->course->id, "reader", "view attempt: " . addslashes($this->readerobj->book->name), "view.php?id={$this->attempt->id}", "readerID {$this->readerobj->reader->id}; reader quiz {$this->readerobj->book->id}; {$this->attempt->persent}/{$passedlog}"); $DB->update_record('reader_attempts', $this->attempt); }
public function process_finish($timestamp, $processsubmitted) { global $DB; $transaction = $DB->start_delegated_transaction(); if ($processsubmitted) { $this->quba->process_all_actions($timestamp); } $this->quba->finish_all_questions($timestamp); question_engine::save_questions_usage_by_activity($this->quba); $this->attempt->timemodified = $timestamp; $this->attempt->timefinish = $timestamp; $this->attempt->sumgrades = $this->quba->get_total_mark(); $this->attempt->state = self::FINISHED; $this->attempt->timecheckstate = null; $DB->update_record('quiz_attempts', $this->attempt); if (!$this->is_preview()) { quiz_save_best_grade($this->get_quiz(), $this->attempt->userid); // Trigger event. $this->fire_state_transition_event('\\mod_quiz\\event\\attempt_submitted', $timestamp); // Tell any access rules that care that the attempt is over. $this->get_access_manager($timestamp)->current_attempt_finished(); } $transaction->allow_commit(); }
$oldnumberstonew[$oldslot] = $newslot; } // Update attempt layout. $newlayout = array(); foreach (explode(',', $lastattempt->layout) as $oldslot) { if ($oldslot != 0) { $newlayout[] = $oldnumberstonew[$oldslot]; } else { $newlayout[] = 0; } } $attempt->layout = implode(',', $newlayout); } // Save the attempt in the database. $transaction = $DB->start_delegated_transaction(); question_engine::save_questions_usage_by_activity($quba); $attempt->uniqueid = $quba->get_id(); $attempt->id = $DB->insert_record('quiz_attempts', $attempt); // Log the new attempt. if ($attempt->preview) { add_to_log($course->id, 'quiz', 'preview', 'view.php?id=' . $quizobj->get_cmid(), $quizobj->get_quizid(), $quizobj->get_cmid()); } else { add_to_log($course->id, 'quiz', 'attempt', 'review.php?attempt=' . $attempt->id, $quizobj->get_quizid(), $quizobj->get_cmid()); } // Trigger event. $eventdata = new stdClass(); $eventdata->component = 'mod_quiz'; $eventdata->attemptid = $attempt->id; $eventdata->timestart = $attempt->timestart; $eventdata->timestamp = $attempt->timestart; $eventdata->userid = $attempt->userid;
/** * saves the current attempt class * * @return bool */ public function save() { global $DB; // first save the question usage by activity object \question_engine::save_questions_usage_by_activity($this->quba); // add the quba id as the questionengid // this is here because for new usages there is no id until we save it $this->attempt->questionengid = $this->quba->get_id(); $this->attempt->timemodified = time(); if (isset($this->attempt->id)) { // update the record try { $DB->update_record('activequiz_attempts', $this->attempt); } catch (\Exception $e) { error_log($e->getMessage()); return false; // return false on failure } } else { // insert new record try { $newid = $DB->insert_record('activequiz_attempts', $this->attempt); $this->attempt->id = $newid; } catch (\Exception $e) { return false; // return false on failure } } return true; // return true if we get here }
public function test_previously_used_question_not_returned_until_later() { $this->resetAfterTest(); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $generator->create_question_category(); $question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $question2 = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $quba = question_engine::make_questions_usage_by_activity('test', context_system::instance()); $quba->set_preferred_behaviour('deferredfeedback'); $question = question_bank::load_question($question2->id); $quba->add_question($question); $quba->add_question($question); $quba->start_all_questions(); question_engine::save_questions_usage_by_activity($quba); $loader = new \core_question\bank\random_question_loader(new qubaid_list(array($quba->get_id()))); $this->assertEquals($question1->id, $loader->get_next_question_id($cat->id, 0)); $this->assertEquals($question2->id, $loader->get_next_question_id($cat->id, 0)); $this->assertNull($loader->get_next_question_id($cat->id, 0)); }
public function get_clone($qinstances) { // The new quba doesn't have to be cloned, so we can use the parent class. $newquba = question_engine::make_questions_usage_by_activity($this->owningcomponent, $this->context); $newquba->set_preferred_behaviour('immediatefeedback'); foreach ($this->get_slots() as $slot) { $slotquestion = $this->get_question($slot); $attempt = $this->get_question_attempt($slot); // We have to check for the type because we might have old migrated templates // that could contain description questions. if ($slotquestion->get_type_name() == 'multichoice' || $slotquestion->get_type_name() == 'multichoiceset') { $order = $slotquestion->get_order($attempt); // Order of the answers. $order = implode(',', $order); $newslot = $newquba->add_question($slotquestion, $qinstances[$slotquestion->id]->maxmark); $qa = $newquba->get_question_attempt($newslot); $qa->start('immediatefeedback', 1, array('_order' => $order)); } } question_engine::save_questions_usage_by_activity($newquba); return $newquba; }
/** * Checks whether a given result is complete, i.e. all contributing scanned pages have been submitted. * Updates the result in the DB if it is complete. Also updates the scanned pages that were duplicates from * 'doublepage' to 'resultexists' * * @param object $offlinequiz * @param object $group * @param object $result * @return boolean */ function offlinequiz_check_result_completed($offlinequiz, $group, $result) { global $DB; $resultpages = $DB->get_records_sql("SELECT *\n FROM {offlinequiz_scanned_pages}\n WHERE resultid = :resultid\n AND status = 'submitted'", array('resultid' => $result->id)); if (count($resultpages) == $group->numberofpages) { $transaction = $DB->start_delegated_transaction(); $quba = question_engine::load_questions_usage_by_activity($result->usageid); $quba->finish_all_questions(time()); $totalmark = $quba->get_total_mark(); question_engine::save_questions_usage_by_activity($quba); $result->sumgrades = $totalmark; $result->status = 'complete'; $result->timestart = time(); $result->timefinish = time(); $result->timemodified = time(); $DB->update_record('offlinequiz_results', $result); $transaction->allow_commit(); offlinequiz_update_grades($offlinequiz, $result->userid); // Change the error of all submitted pages of the result to '' (was 'missingpages' before). foreach ($resultpages as $page) { $DB->set_field('offlinequiz_scanned_pages', 'error', '', array('id' => $page->id)); } // Change the status of all double pages of the user to 'resultexists'. $offlinequizconfig = get_config('offlinequiz'); $user = $DB->get_record('user', array('id' => $result->userid)); $sql = "SELECT id\n FROM {offlinequiz_scanned_pages}\n WHERE offlinequizid = :offlinequizid\n AND userkey = :userkey\n AND groupnumber = :groupnumber\n AND error = 'doublepage'"; $params = array('offlinequizid' => $offlinequiz->id, 'userkey' => $user->{$offlinequizconfig->ID_field}, 'groupnumber' => $group->number); $doublepages = $DB->get_records_sql($sql, $params); foreach ($doublepages as $page) { $DB->set_field('offlinequiz_scanned_pages', 'error', 'resultexists', array('id' => $page->id)); $DB->set_field('offlinequiz_scanned_pages', 'resultid', 0, array('id' => $page->id)); } return true; } return false; }
private static function quiz_attempt_process($id, $step, $forcenew, $page, $uid, $request, $key, $startedOn) { global $DB, $USER; $USER->id = $uid; $response = new CliniqueServiceResponce(); $quizobj = ''; //$timenow = time(); // Update time now, in case the server is running really slowly. $timenow = $startedOn; if (!($cm = get_coursemodule_from_id('quiz', $id))) { print_error('invalidcoursemodule'); } $quizobj = quiz::create($cm->instance, $uid); $attempts_allowed = self::attempts_allowed($attempt, $quizobj); // Create an object to manage all the other (non-roles) access rules. $accessmanager = $quizobj->get_access_manager($timenow); $attempt_nums = self::accessmanager_process($quizobj, $accessmanager, $forcenew, $uid); if ($attempts_allowed != 0) { if ($attempts_allowed < $attempt_nums->attemptnumber) { $block_res = self::get_blocked_response($request, '', $key); $response->response(false, "", $block_res); die; // $response->response(false, "", $request); } } $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); // Create the new attempt and initialize the question sessions $attempt = quiz_create_attempt($quizobj, $attempt_nums->attemptnumber, $attempt_nums->lastattempt, $timenow, $quizobj->is_preview_user()); self::quiz_attempt_stepone($attempt, $attempt_nums->lastattempt, $quizobj, $timenow, $quba, $attempt_nums->attemptnumber); // Save the attempt in the database. $transaction = $DB->start_delegated_transaction(); question_engine::save_questions_usage_by_activity($quba); $attempt->uniqueid = $quba->get_id(); $attempt->id = $DB->insert_record('quiz_attempts', $attempt); // Trigger event. $res = self::trigger_event($attempt, $quizobj); $transaction->allow_commit(); return $res; }
/** * The save started question usage and quiz attempt in db and log the started attempt. * * @param quiz $quizobj * @param question_usage_by_activity $quba * @param object $attempt * @return object attempt object with uniqueid and id set. */ function quiz_attempt_save_started($quizobj, $quba, $attempt) { global $DB; // Save the attempt in the database. question_engine::save_questions_usage_by_activity($quba); $attempt->uniqueid = $quba->get_id(); $attempt->id = $DB->insert_record('quiz_attempts', $attempt); // Log the new attempt. if ($attempt->preview) { add_to_log($quizobj->get_courseid(), 'quiz', 'preview', 'view.php?id='.$quizobj->get_cmid(), $quizobj->get_quizid(), $quizobj->get_cmid()); } else { add_to_log($quizobj->get_courseid(), 'quiz', 'attempt', 'review.php?attempt='.$attempt->id, $quizobj->get_quizid(), $quizobj->get_cmid()); } return $attempt; }
/** * Regrade a particular offlinequiz result. Either for real ($dryrun = false), or * as a pretend regrade to see which fractions would change. The outcome is * stored in the offlinequiz_overview_regrades table. * * Note, $result is not upgraded in the database. The caller needs to do that. * However, $result->sumgrades is updated, if this is not a dry run. * * @param object $result the offlinequiz result to regrade. * @param bool $dryrun if true, do a pretend regrade, otherwise do it for real. * @param array $slots if null, regrade all questions, otherwise, just regrade * the quetsions with those slots. */ protected function regrade_result($result, $questions, $dryrun = false, $slots = null) { global $DB; $transaction = $DB->start_delegated_transaction(); $quba = question_engine::load_questions_usage_by_activity($result->usageid); if (is_null($slots)) { $slots = $quba->get_slots(); } $changed = false; $finished = true; foreach ($slots as $slot) { $qqr = new stdClass(); $qqr->oldfraction = $quba->get_question_fraction($slot); $slotquestion = $quba->get_question($slot); $newmaxmark = $questions[$slotquestion->id]->maxmark; $quba->regrade_question($slot, $finished, $newmaxmark); $qqr->newfraction = $quba->get_question_fraction($slot); if (abs($qqr->oldfraction - $qqr->newfraction) > 1.0E-7) { $changed = true; } } if (!$dryrun) { question_engine::save_questions_usage_by_activity($quba); } $transaction->allow_commit(); return $changed; }
protected function save_quba(moodle_database $db = null) { question_engine::save_questions_usage_by_activity($this->quba, $db); }
/** * Creates a single printable copy of the given quiz. * * @return The ID of the created printable question usage. * */ protected function create_printable_copy($shuffle_mode = quiz_papercopy_shuffle_modes::MODE_SHUFFLE_IGNORE_PAGES, $fix_descriptions = false, $fix_first = false, $fix_last = false) { //get a reference to the current user global $USER; //create a new usage object, which will allow us to create a psuedoquiz in the same context as the online quiz $usage = question_engine::make_questions_usage_by_activity('mod_quiz', $this->context); //and set the grading mode to "deferred feedback", the standard for paper quizzes //this makes sense, since our paradigm is duriven by the idea that feedback is only offered once a paper quiz has been uploaded/graded $usage->set_preferred_behaviour('deferredfeedback'); //get an array of questions in the current quiz $quiz_questions = $this->quizobj->get_questions(); //randomize the question order, as requested $quiz_questions = self::shuffle_questions($quiz_questions, $this->quiz->questions, $shuffle_mode, $fix_descriptions, $fix_first, $fix_last); //for each question in our online quiz foreach ($quiz_questions as $slot => $qdata) { $question = question_bank::make_question($qdata); //add the new question instance to our new printable copy, keeping the maximum grade from the quiz //TODO: respect maximum marks $usage->add_question($question); } //initialize each of the questions $usage->start_all_questions(); //save the usage to the database question_engine::save_questions_usage_by_activity($usage); //return the ID of the newly created questions usage return $usage->get_id(); }
public function test_load_used_variants() { $this->resetAfterTest(); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $generator->create_question_category(); $questiondata1 = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $questiondata2 = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $questiondata3 = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $quba = question_engine::make_questions_usage_by_activity('test', context_system::instance()); $quba->set_preferred_behaviour('deferredfeedback'); $question1 = question_bank::load_question($questiondata1->id); $question3 = question_bank::load_question($questiondata3->id); $quba->add_question($question1); $quba->add_question($question1); $quba->add_question($question3); $quba->start_all_questions(); question_engine::save_questions_usage_by_activity($quba); $this->assertEquals(array($questiondata1->id => array(1 => 2), $questiondata2->id => array(), $questiondata3->id => array(1 => 1)), question_engine::load_used_variants(array($questiondata1->id, $questiondata2->id, $questiondata3->id), new qubaid_list(array($quba->get_id())))); }
public function finish_attempt($timestamp) { global $DB, $USER; $this->quba->process_all_actions($timestamp); $this->quba->finish_all_questions($timestamp); question_engine::save_questions_usage_by_activity($this->quba); $this->attempt->timemodified = $timestamp; $this->attempt->timefinish = $timestamp; $this->attempt->sumgrades = $this->quba->get_total_mark(); $DB->update_record('quiz_attempts', $this->attempt); if (!$this->is_preview()) { quiz_save_best_grade($this->get_quiz()); // Trigger event $eventdata = new stdClass(); $eventdata->component = 'mod_quiz'; $eventdata->attemptid = $this->attempt->id; $eventdata->timefinish = $this->attempt->timefinish; $eventdata->userid = $this->attempt->userid; $eventdata->submitterid = $USER->id; $eventdata->quizid = $this->get_quizid(); $eventdata->cmid = $this->get_cmid(); $eventdata->courseid = $this->get_courseid(); events_trigger('quiz_attempt_submitted', $eventdata); // Clear the password check flag in the session. $this->get_access_manager($timestamp)->clear_password_access(); } }
/** * Regrade a particular quiz attempt. Either for real ($dryrun = false), or * as a pretend regrade to see which fractions would change. The outcome is * stored in the quiz_overview_regrades table. * * Note, $attempt is not upgraded in the database. The caller needs to do that. * However, $attempt->sumgrades is updated, if this is not a dry run. * * @param object $attempt the quiz attempt to regrade. * @param bool $dryrun if true, do a pretend regrade, otherwise do it for real. * @param array $slots if null, regrade all questions, otherwise, just regrade * the quetsions with those slots. */ protected function regrade_attempt($attempt, $dryrun = false, $slots = null) { global $DB; $transaction = $DB->start_delegated_transaction(); $quba = question_engine::load_questions_usage_by_activity($attempt->uniqueid); if (is_null($slots)) { $slots = $quba->get_slots(); } $finished = $attempt->timefinish > 0; foreach ($slots as $slot) { $qqr = new stdClass(); $qqr->oldfraction = $quba->get_question_fraction($slot); $quba->regrade_question($slot, $finished); $qqr->newfraction = $quba->get_question_fraction($slot); if (abs($qqr->oldfraction - $qqr->newfraction) > 1.0E-7) { $qqr->questionusageid = $quba->get_id(); $qqr->slot = $slot; $qqr->regraded = empty($dryrun); $qqr->timemodified = time(); $DB->insert_record('quiz_overview_regrades', $qqr, false); } } if (!$dryrun) { question_engine::save_questions_usage_by_activity($quba); } $transaction->allow_commit(); }
public function finish_attempt($timestamp) { global $DB; $this->quba->process_all_actions($timestamp); $this->quba->finish_all_questions($timestamp); question_engine::save_questions_usage_by_activity($this->quba); $this->attempt->timemodified = $timestamp; $this->attempt->timefinish = $timestamp; $this->attempt->sumgrades = $this->quba->get_total_mark(); $DB->update_record('quiz_attempts', $this->attempt); if (!$this->is_preview()) { quiz_save_best_grade($this->get_quiz()); $this->quiz_send_notification_emails(); } }