Skip to content

Commit 42527ab

Browse files
author
Mahmoud Kassaei
committed
Qtype_drawlines: Implement shownumcorrect, showmisplaced #882022
1 parent 6fc86aa commit 42527ab

File tree

7 files changed

+173
-37
lines changed

7 files changed

+173
-37
lines changed

classes/line.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public static function is_item_positioned_correctly_on_axis($responsecoord, $lin
177177
* Parse the input and return the parts in a list of 'cx', 'cy' with or whothout 'r'.
178178
*
179179
* @param string $dropzone, the string in a given format with or whithout radius
180-
* @param bool $radius, if set to true, return the list with radius, otherwise with radius
180+
* @param bool $radius, if set to true, return the list with radius, otherwise without radius
181181
* @return int[], a list of 'cx', 'cy' with or whothout 'r'.
182182
*/
183183
public static function parse_into_cx_cy_with_or_without_radius(string $dropzone, bool $radius = false): array {

edit_drawlines_form.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,11 @@ protected function get_per_line_fields(MoodleQuickForm $mform, string $label, ar
172172
/**
173173
* Create the form elements required by one hint.
174174
*
175-
* @param string $withclearwrong whether this quesiton type uses the 'Clear wrong' option on hints.
175+
* @param string $withshowmisplaced whether this quesiton type uses the 'show misplaced' option on hints.
176176
* @param string $withshownumpartscorrect whether this quesiton type uses the 'Show num parts correct' option on hints.
177177
* @return array form field elements for one hint.
178178
*/
179-
protected function get_hint_fields($withclearwrong = false, $withshownumpartscorrect = false) {
179+
protected function get_hint_fields($withshowmisplaced = true, $withshownumpartscorrect = false) {
180180
$mform = $this->_form;
181181

182182
$repeated = [];
@@ -187,7 +187,7 @@ protected function get_hint_fields($withclearwrong = false, $withshownumpartscor
187187
$repeated[] = $mform->createElement('checkbox', 'hintshownumcorrect',
188188
get_string('options', 'question'),
189189
get_string('shownumpartscorrect', 'question'));
190-
$repeated[] = $mform->createElement('checkbox', 'hintoptions', '',
190+
$repeated[] = $mform->createElement('checkbox', 'hintshowmisplaced', '',
191191
get_string('showmisplaced', 'qtype_' . $this->qtype()));
192192

193193
return [$repeated, $repeatedoptions];
@@ -256,16 +256,18 @@ protected function data_preprocessing_lines(stdClass $question): object {
256256
}
257257

258258
#[\Override]
259-
protected function data_preprocessing_hints($question, $withclearwrong = false,
260-
$withshownumpartscorrect = false) {
259+
protected function data_preprocessing_hints($question, $withshowmisplaced = true,
260+
$withshownumpartscorrect = false) {
261261
if (empty($question->hints)) {
262262
return $question;
263263
}
264-
parent::data_preprocessing_hints($question, $withclearwrong, $withshownumpartscorrect);
264+
parent::data_preprocessing_hints($question, false, $withshownumpartscorrect);
265265

266266
$question->hintoptions = [];
267-
foreach ($question->hints as $hint) {
268-
$question->hintoptions[] = $hint->options;
267+
foreach ($question->hints as $key => $hint) {
268+
if ($withshowmisplaced) {
269+
$question->hintshowmisplaced[] = $hint->options;
270+
}
269271
}
270272

271273
return $question;

lang/en/qtype_drawlines.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
$string['correctanswersare'] = 'The correct answers are: {$a}';
3232
$string['dropbackground'] = 'Background image for Draw lines';
3333

34-
$string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.';
3534
$string['formerror_invalidimagesize'] = 'Image file should not be larger than 600x600 px. The uploaded image file size is {$a->width}x{$a->height} px.';
35+
$string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.';
3636
$string['formerror_nolines'] = 'You need to expand \'Line 1\' and fill the form for it';
3737
$string['formerror_notype'] = 'You have to select a type for Line {$a}';
3838
$string['formerror_zoneend'] = 'End zone coordinates should be in x,y;r format, where x,y are the coordinates of the centre of a circle and r is the radius.';
@@ -75,6 +75,8 @@
7575
$string['refresh'] = 'Refresh preview';
7676

7777
$string['showmisplaced'] = 'State which zones are incorrectly placed';
78+
$string['showmisplacedcoordinate'] = 'The coordinate {$a} is placed incorrectly.';
79+
$string['showmisplacedcoordinates'] = 'The coordinates {$a} are placed incorrectly.';
7880
$string['summarisechoice'] = '{$a->no}. {$a->text}';
7981
$string['summarisechoiceno'] = 'Item {$a}';
8082
$string['summariseplace'] = '{$a->no}. {$a->text}';
@@ -89,9 +91,9 @@
8991

9092
$string['xleft'] = 'Left';
9193

92-
$string['yougot1right'] = 'You have correctly selected one point.';
93-
$string['yougot1rightline'] = 'You have correctly selected one line.';
94-
$string['yougotnright'] = 'You have correctly selected {$a->num} points.';
94+
$string['yougot1right'] = 'You have correctly selected {$a->num} coordinate.';
95+
$string['yougot1rightline'] = 'You have correctly selected {$a->num} line.';
96+
$string['yougotnright'] = 'You have correctly selected {$a->num} coordinates.';
9597
$string['yougotnrightline'] = 'You have correctly selected {$a->num} lines.';
9698
$string['ytop'] = 'Top';
9799

questiontype.php

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
use qtype_drawlines\line;
1818

19+
defined('MOODLE_INTERNAL') || die();
20+
require_once($CFG->libdir.'/questionlib.php');
21+
1922
/**
2023
* The Draw lines question type class.
2124
*
@@ -115,32 +118,30 @@ public function save_hints($fromform, $withparts = false) {
115118
}
116119

117120
if ($withparts) {
118-
if (!empty($fromform->hintclearwrong)) {
119-
$numclears = max(array_keys($fromform->hintclearwrong)) + 1;
120-
} else {
121-
$numclears = 0;
122-
}
121+
$numclears = null;
123122
if (!empty($fromform->hintshownumcorrect)) {
124123
$numshows = max(array_keys($fromform->hintshownumcorrect)) + 1;
125124
} else {
126125
$numshows = 0;
127126
}
128-
$numhints = max($numhints, $numclears, $numshows);
129-
}
127+
if (!empty($fromform->hintshowmisplaced)) {
128+
$nummisplaced = max(array_keys($fromform->hintshowmisplaced)) + 1;
129+
} else {
130+
$nummisplaced = 0;
131+
}
130132

133+
$numhints = max($numhints, $nummisplaced, $numshows);
134+
}
131135
for ($i = 0; $i < $numhints; $i += 1) {
132136
if (html_is_blank($fromform->hint[$i]['text'])) {
133137
$fromform->hint[$i]['text'] = '';
134138
}
135139

136140
if ($withparts) {
137-
$clearwrong = !empty($fromform->hintclearwrong[$i]);
138141
$shownumcorrect = !empty($fromform->hintshownumcorrect[$i]);
139-
$statewhichincorrect = !empty($fromform->hintoptions[$i]);
142+
$showmisplaced = $fromform->hintshowmisplaced[$i] == 1 ? : 0;
140143
}
141-
142-
if (empty($fromform->hint[$i]['text']) && empty($clearwrong) &&
143-
empty($shownumcorrect) && empty($statewhichincorrect)) {
144+
if (empty($fromform->hint[$i]['text']) && empty($shownumcorrect) && empty($showmisplaced)) {
144145
continue;
145146
}
146147

@@ -157,9 +158,8 @@ public function save_hints($fromform, $withparts = false) {
157158
$context, 'question', 'hint', $hint->id);
158159
$hint->hintformat = $fromform->hint[$i]['format'];
159160
if ($withparts) {
160-
$hint->clearwrong = $clearwrong;
161161
$hint->shownumcorrect = $shownumcorrect;
162-
$hint->options = $statewhichincorrect;
162+
$hint->options = $showmisplaced;
163163
}
164164
$DB->update_record('question_hints', $hint);
165165
}
@@ -211,6 +211,11 @@ public function make_line(stdClass $line): line {
211211
$line->zonestart, $line->zoneend);
212212
}
213213

214+
#[\Override]
215+
protected function make_hint($hint) {
216+
return question_hint_drawlines::load_from_record($hint);
217+
}
218+
214219
#[\Override]
215220
public function delete_question($questionid, $contextid) {
216221
global $DB;
@@ -394,3 +399,45 @@ public function get_possible_responses($questiondata) {
394399
return $parts;
395400
}
396401
}
402+
403+
/**
404+
* Question hint for drawlines.
405+
*
406+
* An extension of {@link question_hint} for questions like match and multiple
407+
* choice with multiple answers, where there are options for whether to show the
408+
* number of parts right at each stage, and to reset the wrong parts.
409+
*
410+
* @package qtype_drawlines
411+
* @copyright 2025 The Open University
412+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
413+
*/
414+
class question_hint_drawlines extends question_hint_with_parts {
415+
416+
/** @var bool option to display the parts of the question that were wrong on retry. */
417+
public $showmisplaced;
418+
419+
/**
420+
* Constructor.
421+
* @param int the hint id from the database.
422+
* @param string $hint The hint text
423+
* @param int the corresponding text FORMAT_... type.
424+
* @param bool $shownumcorrect whether the number of right parts should be shown
425+
* @param bool $clearwrong whether the wrong parts should be reset.
426+
* @param bool $showmisplaced whether the show the wrong parts.
427+
*/
428+
public function __construct($id, $hint, $hintformat, $shownumcorrect,
429+
$clearwrong, $showmisplaced) {
430+
parent::__construct($id, $hint, $hintformat, $shownumcorrect, $clearwrong);
431+
$this->showmisplaced = $showmisplaced;
432+
}
433+
434+
/**
435+
* Create a basic hint from a row loaded from the question_hints table in the database.
436+
* @param object $row with property options as well as hint, shownumcorrect and clearwrong set.
437+
* @return question_hint_drawlines
438+
*/
439+
public static function load_from_record($row) {
440+
return new question_hint_drawlines($row->id, $row->hint, $row->hintformat,
441+
$row->shownumcorrect, $row->clearwrong, $row->options);
442+
}
443+
}

renderer.php

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,10 @@ public function formulation_and_controls(question_attempt $qa, question_display_
164164

165165
#[\Override]
166166
public function specific_feedback(question_attempt $qa) {
167-
return $this->combined_feedback($qa);
167+
$output = '';
168+
$output .= $this->combined_feedback($qa);
169+
$hint = $qa->get_applicable_hint();
170+
return $output;
168171
}
169172

170173
#[\Override]
@@ -193,6 +196,58 @@ public function correct_response(question_attempt $qa) {
193196
return $this->correct_choices($rightanswers);
194197
}
195198

199+
#[\Override]
200+
protected function hint(question_attempt $qa, question_hint $hint) {
201+
$output = '';
202+
$question = $qa->get_question();
203+
$response = $qa->get_last_qt_data();
204+
// Accumulate the wrong coords for each lines to be displayed and hint options.
205+
$wrongcoords = [];
206+
if ($hint->showmisplaced) {
207+
foreach ($question->lines as $key => $line) {
208+
if (in_array($question->field($key), array_keys($response))) {
209+
$coords = explode(' ', $response[$question->field($key)]);
210+
if ($question->grademethod === 'partial') {
211+
// Label the line.
212+
$linelabelstart = null;
213+
$linelabelend = null;
214+
if (!line::is_dragitem_in_the_right_place($coords[0], $line->zonestart)) {
215+
$linelabelstart = ' Line ' . $line->number;
216+
$wrongcoords[] = $linelabelstart . html_writer::tag('span',
217+
' start (' . $coords[0] . ')', ['class' => 'misplaced']);
218+
}
219+
if (!line::is_dragitem_in_the_right_place($coords[1], $line->zoneend)) {
220+
if (is_null($linelabelstart)) {
221+
// Do not repeat the line number for the end-prt of the line coordinates.
222+
$linelabelend = ' Line ' . $line->number;
223+
}
224+
$wrongcoords[] = $linelabelend . html_writer::tag('span',
225+
' end (' . $coords[1] . ')', ['class' => 'misplaced']);
226+
}
227+
} else {
228+
if (!(line::is_dragitem_in_the_right_place($coords[0], $line->zonestart) &&
229+
line::is_dragitem_in_the_right_place($coords[1], $line->zoneend))) {
230+
$wrongcoords[] = ' Line ' . $line->number . html_writer::tag('span',
231+
' start (' . $coords[0] . ') end (' . $coords[1] . ')', ['class' => 'misplaced']);
232+
}
233+
}
234+
}
235+
}
236+
if (empty($wrongcoords)) {
237+
$output .= '';
238+
} else if (count($wrongcoords) === 1) {
239+
$output .= html_writer::tag('div',
240+
get_string('showmisplacedcoordinate', 'qtype_drawlines', implode($wrongcoords)));
241+
} else {
242+
$output .= html_writer::tag('div',
243+
get_string('showmisplacedcoordinates', 'qtype_drawlines', implode($wrongcoords)));
244+
}
245+
}
246+
247+
$output .= parent::hint($qa, $hint);
248+
return $output;
249+
}
250+
196251
/**
197252
* Function returns string based on number of correct answers.
198253
*
@@ -214,16 +269,29 @@ protected function correct_choices(array $right): string {
214269
#[\Override]
215270
protected function num_parts_correct(question_attempt $qa): string {
216271
$a = new stdClass();
217-
list($a->num, $a->outof) = $qa->get_question()->get_num_parts_right(
218-
$qa->get_last_qt_data());
219-
if (is_null($a->outof)) {
272+
$grademethod = $qa->get_question()->grademethod;
273+
if ($grademethod === 'partial') {
274+
[$a->num, $a->outof] = $qa->get_question()->get_num_parts_right_grade_partial($qa->get_last_qt_data());
275+
} else {
276+
[$a->num, $a->outof] = $qa->get_question()->get_num_parts_right_grade_allornone($qa->get_last_qt_data());
277+
}
278+
if ($a->num === 0 || is_null($a->outof)) {
220279
return '';
221-
} else if ($a->num == 1) {
222-
return html_writer::tag('p', get_string('yougot1right', 'qtype_drawlines'));
280+
}
281+
if ($a->num == 1) {
282+
if ($grademethod === 'partial') {
283+
return html_writer::tag('p', get_string('yougot1right', 'qtype_drawlines', $a));
284+
} else {
285+
$f = new NumberFormatter(current_language(), NumberFormatter::SPELLOUT);
286+
$a->num = $f->format($a->num);
287+
return html_writer::tag('p', get_string('yougot1rightline', 'qtype_drawlines', $a));
288+
}
223289
} else {
224-
$f = new NumberFormatter(current_language(), NumberFormatter::SPELLOUT);
225-
$a->num = $f->format($a->num);
226-
return html_writer::tag('p', get_string('yougotnright', 'qtype_drawlines', $a));
290+
if ($grademethod === 'partial') {
291+
return html_writer::tag('p', get_string('yougotnright', 'qtype_drawlines', $a));
292+
} else {
293+
return html_writer::tag('p', get_string('yougotnrightline', 'qtype_drawlines', $a));
294+
}
227295
}
228296
}
229297
}

tests/behat/import.feature

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,20 @@ Feature: Test importing draw lines questions
2727
And I should see "1. Draw 2 lines on the map. A line segment from A (line starting point) to B (line Ending point), and another one from C to D."
2828
And I press "Continue"
2929
And I should see "Drawlines to edit"
30+
31+
@javascript @_file_upload
32+
Scenario: Verify the imported question for hints.
33+
Given I am on the "Course 1" "core_question > course question import" page logged in as teacher
34+
And I set the field "id_format_xml" to "1"
35+
And I upload "question/type/drawlines/tests/fixtures/testquestion_drawlines_mkmap_twolines.xml" file to "Import" filemanager
36+
And I expand all fieldsets
37+
And I press "id_submitbutton"
38+
And I should see "Parsing questions from import file."
39+
And I press "Continue"
40+
And I should see "Drawlines to edit"
41+
When I am on the "Drawlines to edit" "core_question > edit" page logged in as "teacher"
42+
And I expand all fieldsets
43+
Then the following fields match these values:
44+
| hintshownumcorrect[0] | Checked |
45+
| hintshownumcorrect[1] | Checked |
46+
| hintshowmisplaced[1] | Checked |

tests/helper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public function get_drawlines_question_form_data_mkmap_twolines(): stdClass {
168168
],
169169
];
170170
$fromform->hintshownumcorrect = [1, 1];
171-
$fromform->hintclearwrong = [0, 1];
171+
$fromform->hintmisplaced = [0, 1];
172172
$fromform->hintoptions = [0, 1];
173173

174174
$fromform->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;

0 commit comments

Comments
 (0)