/** * 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; }
/** * Load a {@link question_attempt_step} from the database. * @param int $stepid the id of the step to load. * @param question_attempt_step the step that was loaded. */ public function load_question_attempt_step($stepid) { $records = $this->db->get_recordset_sql("\nSELECT\n quba.contextid,\n COALLESCE(q.qtype, 'missingtype') AS qtype,\n qas.id AS attemptstepid,\n qas.questionattemptid,\n qas.sequencenumber,\n qas.state,\n qas.fraction,\n qas.timecreated,\n qas.userid,\n qasd.name,\n qasd.value\n\nFROM {question_attempt_steps} qas\nJOIN {question_attempts} qa ON qa.id = qas.questionattemptid\nJOIN {question_usages} quba ON quba.id = qa.questionusageid\nLEFT JOIN {question} q ON q.id = qa.questionid\nLEFT JOIN {question_attempt_step_data} qasd ON qasd.attemptstepid = qas.id\n\nWHERE\n qas.id = :stepid\n ", array('stepid' => $stepid)); if (!$records->valid()) { throw new coding_exception('Failed to load question_attempt_step ' . $stepid); } $step = question_attempt_step::load_from_records($records, $stepid); $records->close(); return $step; }
/** * 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; }
public function test_load_dont_be_too_greedy() { $records = new question_test_recordset(array(array('attemptstepid', 'questionattemptid', 'sequencenumber', 'state', 'fraction', 'timecreated', 'userid', 'name', 'value', 'contextid'), array(1, 1, 0, 'todo', null, 1256228502, 13, 'x', 'right', 1), array(2, 2, 0, 'complete', null, 1256228505, 13, 'x', 'wrong', 1))); $step = question_attempt_step::load_from_records($records, 1, 'description'); $this->assertEquals(array('x' => 'right'), $step->get_all_data()); }
/** * Load a {@link question_attempt_step} from the database. * @param int $stepid the id of the step to load. * @param question_attempt_step the step that was loaded. */ public function load_question_attempt_step($stepid) { $records = $this->db->get_recordset_sql(" SELECT qas.id AS attemptstepid, qas.questionattemptid, qas.sequencenumber, qas.state, qas.fraction, qas.timecreated, qas.userid, qasd.name, qasd.value FROM {question_attempt_steps} qas LEFT JOIN {question_attempt_step_data} qasd ON qasd.attemptstepid = qas.id WHERE qas.id = :stepid ", array('stepid' => $stepid)); if (!$records->valid()) { throw new coding_exception('Failed to load question_attempt_step ' . $stepid); } $step = question_attempt_step::load_from_records($records, $stepid); $records->close(); return $step; }