/** * Generate the display of a question in a particular state, and with certain * display options. Normally you do not call this method directly. Intsead * you call {@link question_usage_by_activity::render_question()} which will * call this method with appropriate arguments. * * @param question_attempt $qa the question attempt to display. * @param qbehaviour_renderer $behaviouroutput the renderer to output the behaviour * specific parts. * @param qtype_renderer $qtoutput the renderer to output the question type * specific parts. * @param question_display_options $options controls what should and should not be displayed. * @param string|null $number The question number to display. 'i' is a special * value that gets displayed as Information. Null means no number is displayed. * @return string HTML representation of the question. */ public function question(question_attempt $qa, qbehaviour_renderer $behaviouroutput, qtype_renderer $qtoutput, question_display_options $options, $number) { $output = ''; $output .= html_writer::start_tag('div', array('id' => 'q' . $qa->get_slot(), 'class' => implode(' ', array('que', $qa->get_question()->qtype->name(), $qa->get_behaviour_name(), $qa->get_state_class($options->correctness && $qa->has_marks()))))); $output .= html_writer::tag('div', $this->info($qa, $behaviouroutput, $qtoutput, $options, $number), array('class' => 'info')); $output .= html_writer::start_tag('div', array('class' => 'content')); $output .= html_writer::tag('div', $this->add_part_heading($qtoutput->formulation_heading(), $this->formulation($qa, $behaviouroutput, $qtoutput, $options)), array('class' => 'formulation clearfix')); $output .= html_writer::nonempty_tag('div', $this->add_part_heading(get_string('feedback', 'question'), $this->outcome($qa, $behaviouroutput, $qtoutput, $options)), array('class' => 'outcome clearfix')); $output .= html_writer::nonempty_tag('div', $this->add_part_heading(get_string('comments', 'question'), $this->manual_comment($qa, $behaviouroutput, $qtoutput, $options)), array('class' => 'comment clearfix')); $output .= html_writer::nonempty_tag('div', $this->response_history($qa, $behaviouroutput, $qtoutput, $options), array('class' => 'history clearfix')); $output .= html_writer::end_tag('div'); $output .= html_writer::end_tag('div'); return $output; }
/** * Store an entire {@link question_attempt} in the database, * including all the question_attempt_steps that comprise it. * @param question_attempt $qa the question attempt to store. * @param object $context the context of the owning question_usage_by_activity. */ public function insert_question_attempt(question_attempt $qa, $context) { $record = new stdClass(); $record->questionusageid = $qa->get_usage_id(); $record->slot = $qa->get_slot(); $record->behaviour = $qa->get_behaviour_name(); $record->questionid = $qa->get_question()->id; $record->variant = $qa->get_variant(); $record->maxmark = $qa->get_max_mark(); $record->minfraction = $qa->get_min_fraction(); $record->flagged = $qa->is_flagged(); $record->questionsummary = $qa->get_question_summary(); $record->rightanswer = $qa->get_right_answer_summary(); $record->responsesummary = $qa->get_response_summary(); $record->timemodified = time(); $record->id = $this->db->insert_record('question_attempts', $record); foreach ($qa->get_step_iterator() as $seq => $step) { $this->insert_question_attempt_step($step, $record->id, $seq, $context); } }
/** * Store an entire {@link question_attempt} in the database, * including all the question_attempt_steps that comprise it. * @param question_attempt $qa the question attempt to store. * @param context $context the context of the owning question_usage_by_activity. */ public function insert_question_attempt(question_attempt $qa, $context) { $record = new stdClass(); $record->questionusageid = $qa->get_usage_id(); $record->slot = $qa->get_slot(); $record->behaviour = $qa->get_behaviour_name(); $record->questionid = $qa->get_question()->id; $record->variant = $qa->get_variant(); $record->maxmark = $qa->get_max_mark(); $record->minfraction = $qa->get_min_fraction(); $record->maxfraction = $qa->get_max_fraction(); $record->flagged = $qa->is_flagged(); $record->questionsummary = $qa->get_question_summary(); if (core_text::strlen($record->questionsummary) > question_bank::MAX_SUMMARY_LENGTH) { // It seems some people write very long quesions! MDL-30760 $record->questionsummary = core_text::substr($record->questionsummary, 0, question_bank::MAX_SUMMARY_LENGTH - 3) . '...'; } $record->rightanswer = $qa->get_right_answer_summary(); $record->responsesummary = $qa->get_response_summary(); $record->timemodified = time(); $record->id = $this->db->insert_record('question_attempts', $record); $qa->set_database_id($record->id); foreach ($qa->get_step_iterator() as $seq => $step) { $this->insert_question_attempt_step($step, $record->id, $seq, $context); } }
/** * Store an entire {@link question_attempt} in the database, * including all the question_attempt_steps that comprise it. * * You should not call this method directly. You should use * @link question_engine::save_questions_usage_by_activity()}. * * @param question_attempt $qa the question attempt to store. * @param context $context the context of the owning question_usage_by_activity. * @return array of question_attempt_step_data rows, that still need to be inserted. */ public function insert_question_attempt(question_attempt $qa, $context) { $record = new stdClass(); $record->questionusageid = $qa->get_usage_id(); $record->slot = $qa->get_slot(); $record->behaviour = $qa->get_behaviour_name(); $record->questionid = $qa->get_question()->id; $record->variant = $qa->get_variant(); $record->maxmark = $qa->get_max_mark(); $record->minfraction = $qa->get_min_fraction(); $record->maxfraction = $qa->get_max_fraction(); $record->flagged = $qa->is_flagged(); $record->questionsummary = $qa->get_question_summary(); if (core_text::strlen($record->questionsummary) > question_bank::MAX_SUMMARY_LENGTH) { // It seems some people write very long quesions! MDL-30760 $record->questionsummary = core_text::substr($record->questionsummary, 0, question_bank::MAX_SUMMARY_LENGTH - 3) . '...'; } $record->rightanswer = $qa->get_right_answer_summary(); $record->responsesummary = $qa->get_response_summary(); $record->timemodified = time(); $record->id = $this->db->insert_record('question_attempts', $record); $qa->set_database_id($record->id); // Initially an array of array of question_attempt_step_objects. // Built as a nested array for efficiency, then flattened. $stepdata = array(); foreach ($qa->get_step_iterator() as $seq => $step) { $stepdata[] = $this->insert_question_attempt_step($step, $record->id, $seq, $context); } return call_user_func_array('array_merge', $stepdata); }
/** * Actually generate the display of the PRT feedback. * @param string $name the PRT name. * @param question_attempt $qa the question attempt to display. * @param question_definition $question the question being displayed. * @param stack_potentialresponse_tree_state $result the results to display. * @param question_display_options $options controls what should and should not be displayed. * @return string nicely formatted feedback, for display. */ protected function prt_feedback_display($name, question_attempt $qa, question_definition $question, stack_potentialresponse_tree_state $result, question_display_options $options, $includestandardfeedback) { $err = ''; if ($result->errors) { $err = $result->errors; } $feedback = ''; $feedbackbits = $result->get_feedback(); if ($feedbackbits) { $feedback = array(); $format = null; foreach ($feedbackbits as $bit) { $feedback[] = $qa->rewrite_pluginfile_urls($bit->feedback, 'qtype_stack', $bit->filearea, $bit->itemid); if (!is_null($bit->format)) { if (is_null($format)) { $format = $bit->format; } if ($bit->format != $format) { throw new coding_exception('Inconsistent feedback formats found in PRT ' . $name); } } } if (is_null($format)) { $format = FORMAT_HTML; } $feedback = $result->substitue_variables_in_feedback(implode(' ', $feedback)); $feedback = format_text(stack_maths::process_display_castext($feedback, $this), $format, array('noclean' => true, 'para' => false)); } $gradingdetails = ''; if (!$result->errors && $qa->get_behaviour_name() == 'adaptivemultipart') { // This is rather a hack, but it will probably work. $renderer = $this->page->get_renderer('qbehaviour_adaptivemultipart'); $gradingdetails = $renderer->render_adaptive_marks($qa->get_behaviour()->get_part_mark_details($name), $options); } if ($includestandardfeedback) { $standardfeedback = $this->standard_prt_feedback($qa, $question, $result); } else { $standardfeedback = ''; } return html_writer::nonempty_tag('div', $standardfeedback . $err . $feedback . $gradingdetails, array('class' => 'stackprtfeedback stackprtfeedback-' . $name)); }
/** * Create a question_attempt_with_restricted_history * @param question_attempt $baseqa The question_attempt to make a restricted version of. * @param int $lastseq the index of the last step to include. * @param string $preferredbehaviour the preferred behaviour. It is slightly * annoyting that this needs to be passed, but unavoidable for now. */ public function __construct(question_attempt $baseqa, $lastseq, $preferredbehaviour) { if ($lastseq < 0 || $lastseq >= $baseqa->get_num_steps()) { throw new coding_exception('$seq out of range', $seq); } $this->baseqa = $baseqa; $this->steps = array_slice($baseqa->steps, 0, $lastseq + 1); $this->observer = new question_usage_null_observer(); // This should be a straight copy of all the remaining fields. $this->id = $baseqa->id; $this->usageid = $baseqa->usageid; $this->slot = $baseqa->slot; $this->question = $baseqa->question; $this->maxmark = $baseqa->maxmark; $this->minfraction = $baseqa->minfraction; $this->questionsummary = $baseqa->questionsummary; $this->responsesummary = $baseqa->responsesummary; $this->rightanswer = $baseqa->rightanswer; $this->flagged = $baseqa->flagged; // Except behaviour, where we need to create a new one. $this->behaviour = question_engine::make_behaviour($baseqa->get_behaviour_name(), $this, $preferredbehaviour); }