MDL-79877 qtype_ordering: Template to output correct parts

Part of: MDL-79863
Creates an exporter class and a template to output the correct
part  to a given question attempt.
This commit is contained in:
Ilya Tregubov
2024-04-04 16:00:52 +08:00
committed by Mathew May
parent 36822f0c0a
commit d00061da26
6 changed files with 204 additions and 69 deletions
@@ -0,0 +1,45 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace qtype_ordering\output;
/**
* Renderable class for the statement of how many sub-parts of the question the student got correct|partial|incorrect.
*
* @package qtype_ordering
* @copyright 2023 Ilya Tregubov <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class num_parts_correct extends renderable_base {
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output The output renderer.
* @return array
*/
public function export_for_template(\renderer_base $output): array {
list($numright, $numpartial, $numincorrect) = $this->qa->get_question()->get_num_parts_right(
$this->qa->get_last_qt_data());
return [
'numcorrect' => $numright,
'numpartial' => $numpartial,
'numincorrect' => $numincorrect,
];
}
}
@@ -71,9 +71,6 @@ $string['gradingtype_help'] = 'Choose the type of grading calculation.
$string['highlightresponse'] = 'Highlight response as correct or incorrect';
$string['horizontal'] = 'Horizontal';
$string['itemplural'] = 'items';
$string['itemsingular'] = 'item';
$string['layouttype_help'] = 'Choose whether to display the items vertically or horizontally.';
$string['layouttype'] = 'Layout of items';
$string['longestcontiguoussubset'] = 'Longest contiguous subset';
@@ -123,6 +120,6 @@ $string['showgrading'] = 'Grading details';
$string['showgrading_help'] = 'Choose whether to show or hide details of the score calculation when a student reviews a response to this Ordering question.';
$string['vertical'] = 'Vertical';
$string['yougotnright'] = 'You have {$a->numright} {$a->numrightplural} correct.';
$string['yougotnpartial'] = 'You have {$a->numpartial} {$a->numpartialplural} partially correct.';
$string['yougotnincorrect'] = 'You have {$a->numincorrect} {$a->numincorrectplural} incorrect.';
$string['correctitemsnumber'] = 'Correct items: {$a}';
$string['partialitemsnumber'] = 'Partially correct items: {$a}';
$string['incorrectitemsnumber'] = 'Incorrect items: {$a}';
+4 -23
View File
@@ -480,34 +480,15 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
}
/**
* Gereate a brief statement of how many sub-parts of this question the
* Generate a brief statement of how many sub-parts of this question the
* student got correct|partial|incorrect.
*
* @param question_attempt $qa The question attempt to display.
* @return string HTML fragment.
*/
protected function num_parts_correct(question_attempt $qa) {
$a = new stdClass();
$output = '';
list($a->numright, $a->numpartial, $a->numincorrect) = $qa->get_question()->get_num_parts_right(
$qa->get_last_qt_data());
if ($a->numright) {
$a->numrightplural = get_string($a->numright > 1 ? 'itemplural' : 'itemsingular', 'qtype_ordering');
$output .= html_writer::nonempty_tag('div', get_string('yougotnright', 'qtype_ordering', $a));
}
if ($a->numpartial) {
$a->numpartialplural = get_string($a->numpartial > 1 ? 'itemplural' : 'itemsingular', 'qtype_ordering');
$output .= html_writer::nonempty_tag('div', get_string('yougotnpartial', 'qtype_ordering', $a));
}
if ($a->numincorrect) {
$a->numincorrectplural = get_string($a->numincorrect > 1 ? 'itemplural' : 'itemsingular', 'qtype_ordering');
$output .= html_writer::nonempty_tag('div', get_string('yougotnincorrect', 'qtype_ordering', $a));
}
return $output;
$numpartscorrect = new \qtype_ordering\output\num_parts_correct($qa);
return $this->output->render_from_template('qtype_ordering/num_parts_correct',
$numpartscorrect->export_for_template($this->output));
}
}
@@ -0,0 +1,49 @@
{{!
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 <http://www.gnu.org/licenses/>.
}}
{{!
@template qtype_ordering/num_parts_correct
Renders a statement of how many sub-parts of the question the student got correct|partial|incorrect.
Context variables required for this template:
* numcorrect - The number of correct answers.
* numpartial - The number of partially correct answers.
* numincorrect - The number of incorrect answers.
Example context (json):
{
"numcorrect": 1,
"numpartial": 3,
"numincorrect": 0
}
}}
{{#numcorrect}}
<div>
{{#str}} correctitemsnumber, qtype_ordering, {{numcorrect}} {{/str}}
</div>
{{/numcorrect}}
{{#numpartial}}
<div>
{{#str}} partialitemsnumber, qtype_ordering, {{numpartial}} {{/str}}
</div>
{{/numpartial}}
{{#numincorrect}}
<div>
{{#str}} incorrectitemsnumber, qtype_ordering, {{numincorrect}} {{/str}}
</div>
{{/numincorrect}}
@@ -0,0 +1,99 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 specific_grade_detail_feedback.
*
* @package qtype_ordering
* @copyright 2023 Ilya Tregubov <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \qtype_ordering\output\specific_grade_detail_feedback
*/
class num_parts_correct_test extends advanced_testcase {
/**
* Test the exported data for the template that renders the specific grade detail feedback test to a given question attempt.
*
* @dataProvider export_for_template_provider
* @param array $answeritems The array of ordered answers.
* @param int $gradingtype Grading type.
* @param array $expected The expected exported data.
* @return void
* @covers ::export_for_template
*/
public function test_export_for_template(array $answeritems, int $gradingtype, array $expected): void {
global $PAGE;
$question = test_question_maker::make_question('ordering');
$qa = new \testable_question_attempt($question, 0);
$step = new \question_attempt_step();
$qa->add_step($step);
$question->start_attempt($step, 1);
$question->options->gradingtype = $gradingtype;
$keys = implode(',', array_keys($answeritems));
$step->set_qt_var('_currentresponse', $keys);
$renderer = $PAGE->get_renderer('core');
$specificgradedetailfeedback = new num_parts_correct($qa);
$actual = $specificgradedetailfeedback->export_for_template($renderer);
$this->assertEquals($expected, $actual);
}
/**
* Data provider for the test_export_for_template test.
*
* @return array
*/
public function export_for_template_provider(): array {
global $CFG;
require_once($CFG->dirroot . '/question/type/ordering/question.php');
return [
'Partially correct question attempt. Absolute position' => [
[13 => 'Modular', 14 => 'Object', 15 => 'Oriented', 17 => 'Learning', 16 => 'Dynamic', 18 => 'Environment'],
qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT,
[
'numcorrect' => 4,
'numpartial' => 2,
'numincorrect' => 0,
],
],
'Incorrect question attempt (horizontal layout). Relative to ALL the previous and next items' => [
[14 => 'Object', 16 => 'Dynamic', 13 => 'Modular', 17 => 'Learning', 18 => 'Environment', 15 => 'Oriented'],
qtype_ordering_question::GRADING_ABSOLUTE_POSITION,
[
'numcorrect' => 0,
'numpartial' => 0,
'numincorrect' => 6,
],
],
];
}
}
@@ -140,10 +140,13 @@ class walkthrough_test extends \qbehaviour_walkthrough_test_base {
$this->get_does_not_contain_submit_button_expectation(),
$this->get_contains_try_again_button_expectation(true),
$this->get_does_not_contain_correctness_expectation(),
$this->get_contains_num_parts_correct_ordering(0, 5, 1),
$this->get_contains_hint_expectation('This is the first hint')
);
$this->check_output_does_not_contain(get_string('correctitemsnumber', 'qtype_ordering', 0));
$this->check_output_contains(get_string('partialitemsnumber', 'qtype_ordering', 5));
$this->check_output_contains(get_string('incorrectitemsnumber', 'qtype_ordering', 1));
// Do try again.
$this->process_submission(['-tryagain' => 1]);
@@ -181,43 +184,4 @@ class walkthrough_test extends \qbehaviour_walkthrough_test_base {
$this->check_current_state(question_state::$gradedright);
$this->check_current_mark(2);
}
/**
* Return question pattern expectation.
*
* @param int $numright The number of item correct.
* @param int $numpartial The number of partial correct item.
* @param int $numincorrect The number of incorrect item.
* @return question_pattern_expectation
*/
protected function get_contains_num_parts_correct_ordering(
int $numright,
int $numpartial,
int $numincorrect
): question_pattern_expectation {
$a = new stdClass();
$a->numright = $numright;
$a->numpartial = $numpartial;
$a->numincorrect = $numincorrect;
$output = '';
if ($a->numright) {
$a->numrightplural = get_string($a->numright > 1 ? 'itemplural' : 'itemsingular', 'qtype_ordering');
$output .= '<div>' . get_string('yougotnright', 'qtype_ordering', $a) . '</div>';
}
if ($a->numpartial) {
$a->numpartialplural = get_string($a->numpartial > 1 ? 'itemplural' : 'itemsingular', 'qtype_ordering');
$output .= '<div>' . get_string('yougotnpartial', 'qtype_ordering', $a) . '</div>';
}
if ($a->numincorrect) {
$a->numincorrectplural = get_string($a->numincorrect > 1 ? 'itemplural' : 'itemsingular', 'qtype_ordering');
$output .= '<div>' . get_string('yougotnincorrect', 'qtype_ordering', $a) . '</div>';
}
$pattern = '/<div class="numpartscorrect">' . preg_quote($output, '/');
return new question_pattern_expectation($pattern . '/');
}
}