/** * Create a fake question to be displayed in place of a question that is blocked * until the previous question has been answered. * * @param int $slot int slot number of the question to replace. * @return question_definition the placeholde question. */ protected function make_blocked_question_placeholder($slot) { $replacedquestion = $this->get_question_attempt($slot)->get_question(); question_bank::load_question_definition_classes('description'); $question = new qtype_description_question(); $question->id = $replacedquestion->id; $question->category = null; $question->parent = 0; $question->qtype = question_bank::get_qtype('description'); $question->name = ''; $question->questiontext = get_string('questiondependsonprevious', 'quiz'); $question->questiontextformat = FORMAT_HTML; $question->generalfeedback = ''; $question->defaultmark = $this->quba->get_question_max_mark($slot); $question->length = $replacedquestion->length; $question->penalty = 0; $question->stamp = ''; $question->version = 0; $question->hidden = 0; $question->timecreated = null; $question->timemodified = null; $question->createdby = null; $question->modifiedby = null; $placeholderqa = new question_attempt($question, $this->quba->get_id(), null, $this->quba->get_question_max_mark($slot)); $placeholderqa->set_slot($slot); $placeholderqa->start($this->get_quiz()->preferredbehaviour, 1); $placeholderqa->set_flagged($this->is_question_flagged($slot)); return $placeholderqa; }
/** * Regrade a question in this usage. This replays the sequence of submitted * actions to recompute the outcomes. * @param int $slot the number used to identify this question within this usage. * @param bool $finished whether the question attempt should be forced to be finished * after the regrade, or whether it may still be in progress (default false). * @param number $newmaxmark (optional) if given, will change the max mark while regrading. */ public function regrade_question($slot, $finished = false, $newmaxmark = null) { $oldqa = $this->get_question_attempt($slot); if (is_null($newmaxmark)) { $newmaxmark = $oldqa->get_max_mark(); } $newqa = new question_attempt($oldqa->get_question(), $oldqa->get_usage_id(), $this->observer, $newmaxmark); $newqa->set_database_id($oldqa->get_database_id()); $newqa->set_slot($oldqa->get_slot()); $newqa->regrade($oldqa, $finished); $this->questionattempts[$slot] = $newqa; $this->observer->notify_attempt_modified($newqa); }
/** * Create a question_attempt_step from records loaded from the database. * * For internal use only. * * @param Iterator $records Raw records loaded from the database. * @param int $questionattemptid The id of the question_attempt to extract. * @return question_attempt The newly constructed question_attempt. */ public static function load_from_records($records, $questionattemptid, question_usage_observer $observer, $preferredbehaviour) { $record = $records->current(); while ($record->questionattemptid != $questionattemptid) { $record = $records->next(); if (!$records->valid()) { throw new coding_exception("Question attempt {$questionattemptid} not found in the database."); } $record = $records->current(); } try { $question = question_bank::load_question($record->questionid); } catch (Exception $e) { // The question must have been deleted somehow. Create a missing // question to use in its place. $question = question_bank::get_qtype('missingtype')->make_deleted_instance($record->questionid, $record->maxmark + 0); } $qa = new question_attempt($question, $record->questionusageid, null, $record->maxmark + 0); $qa->set_database_id($record->questionattemptid); $qa->set_slot($record->slot); $qa->variant = $record->variant + 0; $qa->minfraction = $record->minfraction + 0; $qa->maxfraction = $record->maxfraction + 0; $qa->set_flagged($record->flagged); $qa->questionsummary = $record->questionsummary; $qa->rightanswer = $record->rightanswer; $qa->responsesummary = $record->responsesummary; $qa->timemodified = $record->timemodified; $qa->behaviour = question_engine::make_behaviour($record->behaviour, $qa, $preferredbehaviour); $qa->observer = $observer; // If attemptstepid is null (which should not happen, but has happened // due to corrupt data, see MDL-34251) then the current pointer in $records // will not be advanced in the while loop below, and we get stuck in an // infinite loop, since this method is supposed to always consume at // least one record. Therefore, in this case, advance the record here. if (is_null($record->attemptstepid)) { $records->next(); } $i = 0; $autosavedstep = null; $autosavedsequencenumber = null; while ($record && $record->questionattemptid == $questionattemptid && !is_null($record->attemptstepid)) { $sequencenumber = $record->sequencenumber; $nextstep = question_attempt_step::load_from_records($records, $record->attemptstepid, $qa->get_question()->get_type_name()); if ($sequencenumber < 0) { if (!$autosavedstep) { $autosavedstep = $nextstep; $autosavedsequencenumber = -$sequencenumber; } else { // Old redundant data. Mark it for deletion. $qa->observer->notify_step_deleted($nextstep, $qa); } } else { $qa->steps[$i] = $nextstep; if ($i == 0) { $question->apply_attempt_state($qa->steps[0]); } $i++; } if ($records->valid()) { $record = $records->current(); } else { $record = false; } } if ($autosavedstep) { if ($autosavedsequencenumber >= $i) { $qa->autosavedstep = $autosavedstep; $qa->steps[$i] = $qa->autosavedstep; } else { $qa->observer->notify_step_deleted($autosavedstep, $qa); } } return $qa; }
/** * Create a question_attempt_step from records loaded from the database. * * For internal use only. * * @param Iterator $records Raw records loaded from the database. * @param int $questionattemptid The id of the question_attempt to extract. * @return question_attempt The newly constructed question_attempt. */ public static function load_from_records($records, $questionattemptid, question_usage_observer $observer, $preferredbehaviour) { $record = $records->current(); while ($record->questionattemptid != $questionattemptid) { $record = $records->next(); if (!$records->valid()) { throw new coding_exception("Question attempt $questionattemptid not found in the database."); } $record = $records->current(); } try { $question = question_bank::load_question($record->questionid); } catch (Exception $e) { // The question must have been deleted somehow. Create a missing // question to use in its place. $question = question_bank::get_qtype('missingtype')->make_deleted_instance( $record->questionid, $record->maxmark + 0); } $qa = new question_attempt($question, $record->questionusageid, null, $record->maxmark + 0); $qa->set_database_id($record->questionattemptid); $qa->set_slot($record->slot); $qa->variant = $record->variant + 0; $qa->minfraction = $record->minfraction + 0; $qa->set_flagged($record->flagged); $qa->questionsummary = $record->questionsummary; $qa->rightanswer = $record->rightanswer; $qa->responsesummary = $record->responsesummary; $qa->timemodified = $record->timemodified; $qa->behaviour = question_engine::make_behaviour( $record->behaviour, $qa, $preferredbehaviour); $i = 0; while ($record && $record->questionattemptid == $questionattemptid && !is_null($record->attemptstepid)) { $qa->steps[$i] = question_attempt_step::load_from_records($records, $record->attemptstepid); if ($i == 0) { $question->apply_attempt_state($qa->steps[0]); } $i++; if ($records->valid()) { $record = $records->current(); } else { $record = false; } } $qa->observer = $observer; return $qa; }