Commit c4e2b67c authored by Tim Hunt's avatar Tim Hunt

MDL-68597 qtype_essay: show the word count on the review screen

parent 6fc462ad
......@@ -69,3 +69,6 @@ $string['responseisrequired'] = 'Require the student to enter text';
$string['responsetemplate'] = 'Response template';
$string['responsetemplateheader'] = 'Response Template';
$string['responsetemplate_help'] = 'Any text entered here will be displayed in the response input box when a new attempt at the question starts.';
$string['wordcount'] = 'Word count: {$a}';
$string['wordcounttoofew'] = 'Word count: {$a->count}, less than the required {$a->limit} words.';
$string['wordcounttoomuch'] = 'Word count: {$a->count}, more than the limit of {$a->limit} words.';
......@@ -252,19 +252,52 @@ class qtype_essay_question extends question_with_responses {
if (!$this->responserequired) {
return null;
}
if ((isset($this->minwordlimit) && $this->minwordlimit > 0) ||
(isset($this->maxwordlimit) && $this->maxwordlimit > 0)) {
// Count the number of words in the response string.
$responsewords = count_words($responsestring);
if (isset($this->minwordlimit) && $this->minwordlimit > $responsewords) {
return get_string('minwordlimitboundary', 'qtype_essay',
['limit' => $this->minwordlimit, 'count' => $responsewords]);
}
if (isset($this->maxwordlimit) && $this->maxwordlimit < $responsewords) {
return get_string('maxwordlimitboundary', 'qtype_essay',
['limit' => $this->maxwordlimit, 'count' => $responsewords]);
}
if (!$this->minwordlimit && !$this->maxwordlimit) {
// This question does not care about the word count.
return null;
}
// Count the number of words in the response string.
$count = count_words($responsestring);
if ($this->maxwordlimit && $count > $this->maxwordlimit) {
return get_string('maxwordlimitboundary', 'qtype_essay',
['limit' => $this->maxwordlimit, 'count' => $count]);
} else if ($count < $this->minwordlimit) {
return get_string('minwordlimitboundary', 'qtype_essay',
['limit' => $this->minwordlimit, 'count' => $count]);
} else {
return null;
}
}
/**
* If this question uses word counts, then return a display of the current
* count, and whether it is within limit, for when the question is being reviewed.
*
* @param array $response responses, as returned by
* {@see question_attempt_step::get_qt_data()}.
* @return string If relevant to this question, a display of the word count.
*/
public function get_word_count_message_for_review(array $response): string {
if (!$this->minwordlimit && !$this->maxwordlimit) {
// This question does not care about the word count.
return '';
}
if (!array_key_exists('answer', $response) || ($response['answer'] === '')) {
// No response.
return '';
}
$count = count_words($response['answer']);
if ($this->maxwordlimit && $count > $this->maxwordlimit) {
return get_string('wordcounttoomuch', 'qtype_essay',
['limit' => $this->maxwordlimit, 'count' => $count]);
} else if ($count < $this->minwordlimit) {
return get_string('wordcounttoofew', 'qtype_essay',
['limit' => $this->minwordlimit, 'count' => $count]);
} else {
return get_string('wordcount', 'qtype_essay', $count);
}
return null;
}
}
......@@ -55,6 +55,8 @@ class qtype_essay_renderer extends qtype_renderer {
} else {
$answer = $responseoutput->response_area_read_only('answer', $qa,
$step, $question->responsefieldlines, $options->context);
$answer .= html_writer::nonempty_tag('p', $question->get_word_count_message_for_review($step->get_qt_data()));
}
$files = '';
......
......@@ -186,8 +186,8 @@ class qtype_essay_question_test extends advanced_testcase {
$this->assertTrue($essay->is_complete_response($response));
// Unset the minwordlimit/maxwordlimit variables to avoid the extra check in is_complete_response() for further tests.
unset($essay->minwordlimit);
unset($essay->maxwordlimit);
$essay->minwordlimit = null;
$essay->maxwordlimit = null;
// Test the case where two files are required.
$essay->attachmentsrequired = 2;
......@@ -284,8 +284,10 @@ class qtype_essay_question_test extends advanced_testcase {
/**
* Test get_validation_error when users submit their input text.
*
* (The tests are done with a fixed 14-word response.)
*
* @dataProvider get_min_max_wordlimit_test_cases()
* @param int $responserequired whether reponse required (yes = 1, no = 0)
* @param int $responserequired whether response required (yes = 1, no = 0)
* @param int $minwordlimit minimum word limit
* @param int $maxwordlimit maximum word limit
* @param string $expected error message | null
......@@ -303,8 +305,10 @@ class qtype_essay_question_test extends advanced_testcase {
/**
* Data provider for get_validation_error test.
*
* @return array the test cases.
*/
public function get_min_max_wordlimit_test_cases() {
public function get_min_max_wordlimit_test_cases(): array {
return [
'text input required, min/max word limit not set' => [1, 0, 0, ''],
'text input required, min/max word limit valid (within the boundaries)' => [1, 10, 25, ''],
......@@ -315,4 +319,49 @@ class qtype_essay_question_test extends advanced_testcase {
'text input not required, min/max word limit not set' => [0, 5, 12, ''],
];
}
/**
* Test get_word_count_message_for_review when users submit their input text.
*
* (The tests are done with a fixed 14-word response.)
*
* @dataProvider get_word_count_message_for_review_test_cases()
* @param int|null $minwordlimit minimum word limit
* @param int|null $maxwordlimit maximum word limit
* @param string $expected error message | null
*/
public function test_get_word_count_message_for_review(?int $minwordlimit, ?int $maxwordlimit, string $expected): void {
$question = test_question_maker::make_an_essay_question();
$question->minwordlimit = $minwordlimit;
$question->maxwordlimit = $maxwordlimit;
$response = ['answer' => 'One two three four five six seven eight nine ten eleven twelve thirteen fourteen.'];
$this->assertEquals($expected, $question->get_word_count_message_for_review($response));
}
/**
* Data provider for test_get_word_count_message_for_review.
*
* @return array the test cases.
*/
public function get_word_count_message_for_review_test_cases() {
return [
'No limit' =>
[null, null, ''],
'min and max, answer within range' =>
[10, 25, get_string('wordcount', 'qtype_essay', 14)],
'min and max, answer too short' =>
[15, 25, get_string('wordcounttoofew', 'qtype_essay', ['count' => 14, 'limit' => 15])],
'min and max, answer too long' =>
[5, 12, get_string('wordcounttoomuch', 'qtype_essay', ['count' => 14, 'limit' => 12])],
'min only, answer within range' =>
[14, null, get_string('wordcount', 'qtype_essay', 14)],
'min only, answer too short' =>
[15, null, get_string('wordcounttoofew', 'qtype_essay', ['count' => 14, 'limit' => 15])],
'max only, answer within range' =>
[null, 14, get_string('wordcount', 'qtype_essay', 14)],
'max only, answer too short' =>
[null, 13, get_string('wordcounttoomuch', 'qtype_essay', ['count' => 14, 'limit' => 13])],
];
}
}
......@@ -715,5 +715,17 @@ class qtype_essay_walkthrough_testcase extends qbehaviour_walkthrough_test_base
'attempting to submit 8 words. Please shorten your response and try again.',
$this->currentoutput);
// Now submit all and finish.
$this->finish();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->render();
$this->check_current_output(
$this->get_contains_question_text_expectation($q),
$this->get_contains_general_feedback_expectation($q));
$this->assertStringContainsString('Word count: 8, more than the limit of 7 words.',
$this->currentoutput);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment