From d54086f838c985cd60b6a67e2f91eaaec5664950 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 28 Nov 2023 10:08:31 +0100 Subject: [PATCH] MDL-79295 quiz: Return state class for questions via WS --- mod/quiz/classes/external.php | 9 +++++++-- mod/quiz/tests/external/external_test.php | 12 ++++++++++++ mod/quiz/upgrade.txt | 3 +++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/mod/quiz/classes/external.php b/mod/quiz/classes/external.php index 5c3b4098756..9a6ea9ae429 100644 --- a/mod/quiz/classes/external.php +++ b/mod/quiz/classes/external.php @@ -931,10 +931,13 @@ class mod_quiz_external extends external_api { 'hasautosavedstep' => new external_value(PARAM_BOOL, 'whether this question attempt has autosaved data', VALUE_OPTIONAL), 'flagged' => new external_value(PARAM_BOOL, 'whether the question is flagged or not'), - 'state' => new external_value(PARAM_ALPHA, 'the state where the question is in. + 'state' => new external_value(PARAM_ALPHA, 'the state where the question is in terms of correctness. It will not be returned if the user cannot see it due to the quiz display correctness settings.', VALUE_OPTIONAL), - 'status' => new external_value(PARAM_RAW, 'current formatted state of the question', VALUE_OPTIONAL), + 'stateclass' => new external_value(PARAM_NOTAGS, + 'A machine-readable class name for the state that this question attempt is in, as returned by question_usage_by_activity::get_question_state_class(). + Always returned.', VALUE_OPTIONAL), + 'status' => new external_value(PARAM_RAW, 'Human readable state of the question.', VALUE_OPTIONAL), 'blockedbyprevious' => new external_value(PARAM_BOOL, 'whether the question is blocked by the previous question', VALUE_OPTIONAL), 'mark' => new external_value(PARAM_RAW, 'the mark awarded. @@ -991,6 +994,8 @@ class mod_quiz_external extends external_api { if ($showcorrectness) { $question['state'] = (string) $attemptobj->get_question_state($slot); } + // The stateclass is used for CSS classes but also for the lang strings. + $question['stateclass'] = $attemptobj->get_question_state_class($slot, $displayoptions->correctness); $question['status'] = $attemptobj->get_question_status($slot, $displayoptions->correctness); $question['blockedbyprevious'] = $attemptobj->is_blocked_by_previous_question($slot); } diff --git a/mod/quiz/tests/external/external_test.php b/mod/quiz/tests/external/external_test.php index 5718aec9c85..f4768067858 100644 --- a/mod/quiz/tests/external/external_test.php +++ b/mod/quiz/tests/external/external_test.php @@ -1084,7 +1084,9 @@ class external_test extends externallib_advanced_testcase { $this->assertArrayNotHasKey('number', $result['questions'][0]); $this->assertEquals('1.a', $result['questions'][0]['questionnumber']); $this->assertEquals('numerical', $result['questions'][0]['type']); + $this->assertEquals('notyetanswered', $result['questions'][0]['stateclass']); $this->assertArrayNotHasKey('state', $result['questions'][0]); // We don't receive the state yet. + $this->assertEquals('notyetanswered', $result['questions'][0]['stateclass']); $this->assertEquals(get_string('notyetanswered', 'question'), $result['questions'][0]['status']); $this->assertFalse($result['questions'][0]['flagged']); $this->assertEquals(0, $result['questions'][0]['page']); @@ -1106,6 +1108,7 @@ class external_test extends externallib_advanced_testcase { $this->assertEquals(2, $result['questions'][0]['questionnumber']); $this->assertEquals(2, $result['questions'][0]['number']); $this->assertEquals('numerical', $result['questions'][0]['type']); + $this->assertEquals('notyetanswered', $result['questions'][0]['stateclass']); $this->assertArrayNotHasKey('state', $result['questions'][0]); // We don't receive the state yet. $this->assertEquals(get_string('notyetanswered', 'question'), $result['questions'][0]['status']); $this->assertFalse($result['questions'][0]['flagged']); @@ -1120,6 +1123,7 @@ class external_test extends externallib_advanced_testcase { // Now we should receive the question state. $result = mod_quiz_external::get_attempt_review($attempt->id, 1); $result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_review_returns(), $result); + $this->assertEquals('notanswered', $result['questions'][0]['stateclass']); $this->assertEquals('gaveup', $result['questions'][0]['state']); // Change setting and expect two pages. @@ -1216,7 +1220,9 @@ class external_test extends externallib_advanced_testcase { // Check the state, flagged and mark data is correct. $this->assertEquals('todo', $result['questions'][0]['state']); + $this->assertEquals('notyetanswered', $result['questions'][0]['stateclass']); $this->assertEquals('todo', $result['questions'][1]['state']); + $this->assertEquals('notyetanswered', $result['questions'][1]['stateclass']); $this->assertEquals(1, $result['questions'][0]['number']); $this->assertEquals(2, $result['questions'][1]['number']); $this->assertFalse($result['questions'][0]['flagged']); @@ -1243,7 +1249,9 @@ class external_test extends externallib_advanced_testcase { // Check it's marked as completed only the first one. $this->assertEquals('complete', $result['questions'][0]['state']); + $this->assertEquals('answersaved', $result['questions'][0]['stateclass']); $this->assertEquals('todo', $result['questions'][1]['state']); + $this->assertEquals('notyetanswered', $result['questions'][1]['stateclass']); $this->assertEquals(1, $result['questions'][0]['number']); $this->assertEquals(2, $result['questions'][1]['number']); $this->assertFalse($result['questions'][0]['flagged']); @@ -1289,7 +1297,9 @@ class external_test extends externallib_advanced_testcase { // Check it's marked as completed only the first one. $this->assertEquals('complete', $result['questions'][0]['state']); + $this->assertEquals('answersaved', $result['questions'][0]['stateclass']); $this->assertEquals('todo', $result['questions'][1]['state']); + $this->assertEquals('notyetanswered', $result['questions'][1]['stateclass']); $this->assertEquals(1, $result['questions'][0]['number']); $this->assertEquals(2, $result['questions'][1]['number']); $this->assertFalse($result['questions'][0]['flagged']); @@ -1322,8 +1332,10 @@ class external_test extends externallib_advanced_testcase { // Check it's marked as completed only the first one. $this->assertEquals('complete', $result['questions'][0]['state']); + $this->assertEquals('answersaved', $result['questions'][0]['stateclass']); $this->assertEquals(1, $result['questions'][0]['sequencecheck']); $this->assertEquals('complete', $result['questions'][1]['state']); + $this->assertEquals('answersaved', $result['questions'][1]['stateclass']); $this->assertEquals(1, $result['questions'][1]['sequencecheck']); } diff --git a/mod/quiz/upgrade.txt b/mod/quiz/upgrade.txt index ba58d8552ec..b9bb2ed5e0b 100644 --- a/mod/quiz/upgrade.txt +++ b/mod/quiz/upgrade.txt @@ -4,6 +4,9 @@ This files describes API changes in the quiz code. * A quiz_structure_modified callback has been added for quiz_ plugins, called from grade_calculator::recompute_quiz_sumgrades(). Plugins can implement this by creating a `quiz_structure_modified` class in their namespace with a static `callback` method, see quiz_statistics as an example. +* External functions returning question information, currently get_attempt_summary, get_attempt_data and get_attemp_review + now return a new field called "stateclass". A machine-readable class name for the state that this question attempt is in, + as returned by question_usage_by_activity::get_question_state_class(). === 4.3 ===