From bc2bfead5c44716b56bdb2cab32ccf19fa347171 Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Tue, 7 Nov 2023 09:37:12 +0800 Subject: [PATCH] MDL-79876 qtype_ordering: Template to output the correct response Part of: MDL-79863 Creates an exporter class and a template to output the correct response to a given question attempt. --- .../classes/output/correct_response.php | 68 +++++++++ question/type/ordering/renderer.php | 42 +---- .../templates/correct_response.mustache | 59 ++++++++ .../tests/output/correct_response_test.php | 143 ++++++++++++++++++ 4 files changed, 274 insertions(+), 38 deletions(-) create mode 100644 question/type/ordering/classes/output/correct_response.php create mode 100644 question/type/ordering/templates/correct_response.mustache create mode 100644 question/type/ordering/tests/output/correct_response_test.php diff --git a/question/type/ordering/classes/output/correct_response.php b/question/type/ordering/classes/output/correct_response.php new file mode 100644 index 00000000000..cc02c7d99f3 --- /dev/null +++ b/question/type/ordering/classes/output/correct_response.php @@ -0,0 +1,68 @@ +. + +namespace qtype_ordering\output; + +/** + * Renderable class for the description of the correct response to a given question attempt. + * + * @package qtype_ordering + * @copyright 2023 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class correct_response extends renderable_base { + + /** + * Export the data for the mustache template. + * + * @param \renderer_base $output renderer to be used to render the action bar elements. + * @return array + */ + public function export_for_template(\renderer_base $output): array { + + $data = []; + $question = $this->qa->get_question(); + $correctresponse = $question->correctresponse; + $data['hascorrectresponse'] = !empty($correctresponse); + // Early return if a correct response does not exist. + if (!$data['hascorrectresponse']) { + return $data; + } + + $step = $this->qa->get_last_step(); + // The correct response should be displayed only for partially correct or incorrect answers. + $data['showcorrect'] = $step->get_state() == 'gradedpartial' || $step->get_state() == 'gradedwrong'; + // Early return if the correct response should not be displayed. + if (!$data['showcorrect']) { + return $data; + } + + $data['orderinglayoutclass'] = $question->get_ordering_layoutclass(); + $data['correctanswers'] = []; + + foreach ($correctresponse as $answerid) { + $answer = $question->answers[$answerid]; + $answertext = $question->format_text($answer->answer, $answer->answerformat, + $this->qa, 'question', 'answer', $answerid); + + $data['correctanswers'][] = [ + 'answertext' => $answertext + ]; + } + + return $data; + } +} diff --git a/question/type/ordering/renderer.php b/question/type/ordering/renderer.php index ba2328bf6d1..1fbc1e63f38 100644 --- a/question/type/ordering/renderer.php +++ b/question/type/ordering/renderer.php @@ -347,45 +347,11 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer { * @param question_attempt $qa the question attempt to display. * @return string HTML fragment. */ - public function correct_response(question_attempt $qa) { - global $DB; + public function correct_response(question_attempt $qa): string { + $correctresponse = new \qtype_ordering\output\correct_response($qa); - $output = ''; - - $showcorrect = false; - $question = $qa->get_question(); - if (empty($question->correctresponse)) { - $output .= html_writer::tag('p', get_string('noresponsedetails', 'qtype_ordering')); - } else { - if ($step = $qa->get_last_step()) { - switch ($step->get_state()) { - case 'gradedright': - $showcorrect = false; - break; - case 'gradedpartial': - $showcorrect = true; - break; - case 'gradedwrong': - $showcorrect = true; - break; - } - } - } - if ($showcorrect) { - $sortableitem = $question->get_ordering_layoutclass(); - $output .= html_writer::tag('p', get_string('correctorder', 'qtype_ordering')); - $output .= html_writer::start_tag('ol', array('class' => 'correctorder ' . $sortableitem)); - $correctresponse = $question->correctresponse; - foreach ($correctresponse as $position => $answerid) { - $answer = $question->answers[$answerid]; - $answertext = $question->format_text($answer->answer, $answer->answerformat, - $qa, 'question', 'answer', $answerid); - $output .= html_writer::tag('li', $answertext, array('class' => $sortableitem)); - } - $output .= html_writer::end_tag('ol'); - } - - return $output; + return $this->output->render_from_template('qtype_ordering/correct_response', + $correctresponse->export_for_template($this->output)); } // Custom methods. diff --git a/question/type/ordering/templates/correct_response.mustache b/question/type/ordering/templates/correct_response.mustache new file mode 100644 index 00000000000..cb89a2ae3e7 --- /dev/null +++ b/question/type/ordering/templates/correct_response.mustache @@ -0,0 +1,59 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template qtype_ordering/correct_response + + Renders the description of the correct response to a given question attempt. + + Context variables required for this template: + * hascorrectresponse - Whether correct response exist. + * showcorrect - Whether to show the correct response. + * orderinglayoutclass - The ordering layout CSS class. + * correctanswers - An array containing the correct answers. + * answertext - The formatted answer text. + + Example context (json): + { + "hascorrectresponse": true, + "showcorrect": "true", + "orderinglayoutclass": "vertical", + "correctanswers": [ + { + "answertext": "Correct answer 1" + }, + { + "answertext": "Correct answer 2" + } + ] + } +}} + +{{#hascorrectresponse}} + {{#showcorrect}} +

{{#str}} correctorder, qtype_ordering {{/str}}

+
    + {{#correctanswers}} +
  1. + {{answertext}} +
  2. + {{/correctanswers}} +
+ {{/showcorrect}} +{{/hascorrectresponse}} +{{^hascorrectresponse}} +

{{#str}} noresponsedetails, qtype_ordering {{/str}}

+{{/hascorrectresponse}} diff --git a/question/type/ordering/tests/output/correct_response_test.php b/question/type/ordering/tests/output/correct_response_test.php new file mode 100644 index 00000000000..91484fd536a --- /dev/null +++ b/question/type/ordering/tests/output/correct_response_test.php @@ -0,0 +1,143 @@ +. + +namespace qtype_ordering\output; + +use advanced_testcase; +use test_question_maker; +use qtype_ordering_question; +use qtype_ordering_test_helper; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); + +/** + * A test class used to test correct_response. + * + * @package qtype_ordering + * @copyright 2023 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_ordering\output\correct_response + */ +class correct_response_test extends advanced_testcase { + + /** + * Test the exported data for the template that renders the correct response to a given question attempt. + * + * @dataProvider export_for_template_provider + * @param array $currentresponse The array of items representing the current response. + * @param string $layouttype The type of the layout. + * @param array $expected The expected exported data. + * @return void + * @covers ::export_for_template + */ + public function test_export_for_template(array $currentresponse, string $layouttype, array $expected): void { + global $PAGE; + + $question = test_question_maker::make_question('ordering'); + // Set the grading type and layout type options. + $question->options->gradingtype = qtype_ordering_question::GRADING_ABSOLUTE_POSITION; + $question->options->layouttype = $layouttype === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL : + qtype_ordering_question::LAYOUT_VERTICAL; + // Create a question attempt. + $qa = new \testable_question_attempt($question, 0); + // Create a question attempt step and add it to the question attemp. + $step = new \question_attempt_step(); + $qa->add_step($step); + $question->start_attempt($qa->get_last_step(), 1); + // Get the grading state based on the correct response and the current response, and later set it in the question + // attempt step. + list($fraction, $state) = $question->grade_response(qtype_ordering_test_helper::get_response($question, $currentresponse)); + $qa->get_last_step()->set_state($state); + + $renderer = $PAGE->get_renderer('core'); + $correctresponse = new correct_response($qa); + // Validate the exported data for the template. + $this->assertEquals($expected, $correctresponse->export_for_template($renderer)); + } + + /** + * Data provider for the test_export_for_template test. + * + * @return array + */ + public function export_for_template_provider(): array { + + return [ + 'Correct question attempt.' => [ + ['Modular', 'Object', 'Oriented', 'Dynamic', 'Learning', 'Environment'], + 'horizontal', + [ + 'hascorrectresponse' => true, + 'showcorrect' => false, + ] + ], + 'Partially correct question attempt (horizontal layout).' => [ + ['Modular', 'Object', 'Dynamic', 'Learning', 'Oriented', 'Environment'], + 'horizontal', + [ + 'hascorrectresponse' => true, + 'showcorrect' => true, + 'orderinglayoutclass' => 'horizontal', + 'correctanswers' => [ + ['answertext' => 'Modular'], + ['answertext' => 'Object'], + ['answertext' => 'Oriented'], + ['answertext' => 'Dynamic'], + ['answertext' => 'Learning'], + ['answertext' => 'Environment'], + ], + ] + ], + 'Incorrect question attempt (horizontal layout).' => [ + ['Object', 'Dynamic', 'Modular', 'Learning', 'Environment', 'Oriented'], + 'horizontal', + [ + 'hascorrectresponse' => true, + 'showcorrect' => true, + 'orderinglayoutclass' => 'horizontal', + 'correctanswers' => [ + ['answertext' => 'Modular'], + ['answertext' => 'Object'], + ['answertext' => 'Oriented'], + ['answertext' => 'Dynamic'], + ['answertext' => 'Learning'], + ['answertext' => 'Environment'], + ], + ] + ], + 'Incorrect question attempt (vertical layout).' => [ + ['Object', 'Dynamic', 'Modular', 'Learning', 'Environment', 'Oriented'], + 'vertical', + [ + 'hascorrectresponse' => true, + 'showcorrect' => true, + 'orderinglayoutclass' => 'vertical', + 'correctanswers' => [ + ['answertext' => 'Modular'], + ['answertext' => 'Object'], + ['answertext' => 'Oriented'], + ['answertext' => 'Dynamic'], + ['answertext' => 'Learning'], + ['answertext' => 'Environment'], + ], + ] + ], + ]; + } +}