MDL-74610 quiz: add extra grades to web services the mobile app uses
This commit is contained in:
@@ -412,6 +412,9 @@ class mod_quiz_external extends external_api {
|
||||
// Update quiz with override information.
|
||||
$quiz = quiz_update_effective_access($quiz, $params['userid']);
|
||||
$attempts = quiz_get_user_attempts($quiz->id, $user->id, $params['status'], $params['includepreviews']);
|
||||
$quizobj = new quiz_settings($quiz, $cm, $course);
|
||||
$gradeitemmarks = $quizobj->get_grade_calculator()->compute_grade_item_totals_for_attempts(
|
||||
array_column($attempts, 'uniqueid'));
|
||||
$attemptresponse = [];
|
||||
foreach ($attempts as $attempt) {
|
||||
$reviewoptions = quiz_get_review_options($quiz, $attempt, $context);
|
||||
@@ -419,6 +422,15 @@ class mod_quiz_external extends external_api {
|
||||
($reviewoptions->marks < question_display_options::MARK_AND_MAX || $attempt->state != quiz_attempt::FINISHED)) {
|
||||
// Blank the mark if the teacher does not allow it.
|
||||
$attempt->sumgrades = null;
|
||||
} else if (isset($gradeitemmarks[$attempt->uniqueid])) {
|
||||
$attempt->gradeitemmarks = [];
|
||||
foreach ($gradeitemmarks[$attempt->uniqueid] as $gradeitem) {
|
||||
$attempt->gradeitemmarks[] = [
|
||||
'name' => \core_external\util::format_string($gradeitem->name, $context),
|
||||
'grade' => $gradeitem->grade,
|
||||
'maxgrade' => $gradeitem->maxgrade,
|
||||
];
|
||||
}
|
||||
}
|
||||
$attemptresponse[] = $attempt;
|
||||
}
|
||||
@@ -459,6 +471,13 @@ class mod_quiz_external extends external_api {
|
||||
'timecheckstate' => new external_value(PARAM_INT, 'Next time quiz cron should check attempt for
|
||||
state changes. NULL means never check.', VALUE_OPTIONAL),
|
||||
'sumgrades' => new external_value(PARAM_FLOAT, 'Total marks for this attempt.', VALUE_OPTIONAL),
|
||||
'gradeitemmarks' => new external_multiple_structure(
|
||||
new external_single_structure([
|
||||
'name' => new external_value(PARAM_RAW, 'The name of this grade item.'),
|
||||
'grade' => new external_value(PARAM_FLOAT, 'The grade this attempt earned for this item.'),
|
||||
'maxgrade' => new external_value(PARAM_FLOAT, 'The total this grade is out of.'),
|
||||
], 'The grade for each grade item.'),
|
||||
'If the quiz has additional grades set up, the mark for each grade for this attempt.', VALUE_OPTIONAL),
|
||||
'gradednotificationsenttime' => new external_value(PARAM_INT,
|
||||
'Time when the student was notified that manual grading of their attempt was complete.', VALUE_OPTIONAL),
|
||||
]
|
||||
@@ -1455,7 +1474,6 @@ class mod_quiz_external extends external_api {
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function get_attempt_review($attemptid, $page = -1) {
|
||||
global $PAGE;
|
||||
|
||||
$warnings = [];
|
||||
|
||||
@@ -1465,7 +1483,7 @@ class mod_quiz_external extends external_api {
|
||||
];
|
||||
$params = self::validate_parameters(self::get_attempt_review_parameters(), $params);
|
||||
|
||||
list($attemptobj, $displayoptions) = self::validate_attempt_review($params);
|
||||
[$attemptobj, $displayoptions] = self::validate_attempt_review($params);
|
||||
|
||||
if ($params['page'] !== -1) {
|
||||
$page = $attemptobj->force_page_number_into_range($params['page']);
|
||||
@@ -1502,6 +1520,22 @@ class mod_quiz_external extends external_api {
|
||||
];
|
||||
}
|
||||
|
||||
if (!has_capability('mod/quiz:viewreports', $attemptobj->get_context()) &&
|
||||
($displayoptions->marks < question_display_options::MARK_AND_MAX ||
|
||||
$attemptobj->get_attempt()->state != quiz_attempt::FINISHED)) {
|
||||
// Blank the mark if the teacher does not allow it.
|
||||
$result['attempt']->sumgrades = null;
|
||||
} else {
|
||||
$result['attempt']->gradeitemmarks = [];
|
||||
foreach ($attemptobj->get_grade_item_totals() as $gradeitem) {
|
||||
$result['attempt']->gradeitemmarks[] = [
|
||||
'name' => \core_external\util::format_string($gradeitem->name, $attemptobj->get_context()),
|
||||
'grade' => $gradeitem->grade,
|
||||
'maxgrade' => $gradeitem->maxgrade,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$result['grade'] = $grade;
|
||||
$result['warnings'] = $warnings;
|
||||
return $result;
|
||||
|
||||
+97
-12
@@ -84,6 +84,7 @@ class testable_mod_quiz_external extends mod_quiz_external {
|
||||
* @copyright 2016 Juan Leyva <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 3.1
|
||||
* @covers \mod_quiz_external
|
||||
*/
|
||||
class external_test extends externallib_advanced_testcase {
|
||||
|
||||
@@ -423,13 +424,10 @@ class external_test extends externallib_advanced_testcase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_user_attempts
|
||||
*/
|
||||
public function test_get_user_attempts() {
|
||||
public function test_get_user_attempts(): void {
|
||||
|
||||
// Create a quiz with one attempt finished.
|
||||
list($quiz, $context, $quizobj, $attempt, $attemptobj) = $this->create_quiz_with_questions(true, true);
|
||||
[$quiz, $context, $quizobj, $attempt, $attemptobj] = $this->create_quiz_with_questions(true, true);
|
||||
|
||||
$this->setUser($this->student);
|
||||
$result = mod_quiz_external::get_user_attempts($quiz->id);
|
||||
@@ -507,6 +505,41 @@ class external_test extends externallib_advanced_testcase {
|
||||
}
|
||||
}
|
||||
|
||||
public function test_get_user_attempts_with_extra_grades(): void {
|
||||
global $DB;
|
||||
|
||||
// Create a quiz with one attempt finished.
|
||||
[$quiz, , , $attempt, $attemptobj] = $this->create_quiz_with_questions(true, true);
|
||||
|
||||
// Add some extra grade items.
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
$listeninggrade = $quizgenerator->create_grade_item(['quizid' => $attemptobj->get_quizid(), 'name' => 'Listening']);
|
||||
$readinggrade = $quizgenerator->create_grade_item(['quizid' => $attemptobj->get_quizid(), 'name' => 'Reading']);
|
||||
$structure = $attemptobj->get_quizobj()->get_structure();
|
||||
$structure->update_slot_grade_item($structure->get_slot_by_number(1), $listeninggrade->id);
|
||||
$structure->update_slot_grade_item($structure->get_slot_by_number(2), $readinggrade->id);
|
||||
|
||||
$this->setUser($this->student);
|
||||
$result = mod_quiz_external::get_user_attempts($quiz->id);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||
|
||||
$this->assertCount(1, $result['attempts']);
|
||||
$this->assertEquals($attempt->id, $result['attempts'][0]['id']);
|
||||
|
||||
// Verify additional grades.
|
||||
$this->assertEquals(['name' => 'Listening', 'grade' => 1, 'maxgrade' => 1], $result['attempts'][0]['gradeitemmarks'][0]);
|
||||
$this->assertEquals(['name' => 'Reading', 'grade' => 0, 'maxgrade' => 1], $result['attempts'][0]['gradeitemmarks'][1]);
|
||||
|
||||
// Now change the review options, so marks are not displayed, and check the result.
|
||||
$DB->set_field('quiz', 'reviewmarks', 0, ['id' => $quiz->id]);
|
||||
$result = mod_quiz_external::get_user_attempts($quiz->id);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||
|
||||
$this->assertCount(1, $result['attempts']);
|
||||
$this->assertEquals($attempt->id, $result['attempts'][0]['id']);
|
||||
$this->assertArrayNotHasKey('gradeitemmarks', $result['attempts'][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_user_attempts with marks hidden
|
||||
*/
|
||||
@@ -1664,15 +1697,14 @@ class external_test extends externallib_advanced_testcase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test get_attempt_review
|
||||
*/
|
||||
public function test_get_attempt_review() {
|
||||
public function test_get_attempt_review(): void {
|
||||
global $DB;
|
||||
|
||||
// Create a new quiz with two questions and one attempt finished.
|
||||
list($quiz, $context, $quizobj, $attempt, $attemptobj, $quba) = $this->create_quiz_with_questions(true, true);
|
||||
[$quiz, , , $attempt] = $this->create_quiz_with_questions(true, true);
|
||||
|
||||
// Add feedback to the quiz.
|
||||
$feedback = new \stdClass();
|
||||
@@ -1703,10 +1735,55 @@ class external_test extends externallib_advanced_testcase {
|
||||
$this->assertEquals('gaveup', $result['questions'][1]['state']);
|
||||
$this->assertEquals(2, $result['questions'][1]['slot']);
|
||||
|
||||
// Only first page.
|
||||
$result = mod_quiz_external::get_attempt_review($attempt->id, 0);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_review_returns(), $result);
|
||||
|
||||
$this->assertEquals(50, $result['grade']);
|
||||
$this->assertEquals(1, $result['attempt']['attempt']);
|
||||
$this->assertEquals('finished', $result['attempt']['state']);
|
||||
$this->assertEquals(1, $result['attempt']['sumgrades']);
|
||||
$this->assertCount(1, $result['questions']);
|
||||
$this->assertEquals('gradedright', $result['questions'][0]['state']);
|
||||
$this->assertEquals(1, $result['questions'][0]['slot']);
|
||||
|
||||
$this->assertCount(1, $result['additionaldata']);
|
||||
$this->assertEquals('feedback', $result['additionaldata'][0]['id']);
|
||||
$this->assertEquals('Feedback', $result['additionaldata'][0]['title']);
|
||||
$this->assertEquals('Feedback text 1', $result['additionaldata'][0]['content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_attempt_review
|
||||
*/
|
||||
public function test_get_attempt_review_with_extra_grades(): void {
|
||||
global $DB;
|
||||
|
||||
// Create a new quiz with two questions and one attempt finished.
|
||||
$this->setUser($this->student);
|
||||
[, , , $attempt, $attemptobj] = $this->create_quiz_with_questions(true, true);
|
||||
|
||||
// Add some extra grade items.
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
$listeninggrade = $quizgenerator->create_grade_item(['quizid' => $attemptobj->get_quizid(), 'name' => 'Listening']);
|
||||
$readinggrade = $quizgenerator->create_grade_item(['quizid' => $attemptobj->get_quizid(), 'name' => 'Reading']);
|
||||
$structure = $attemptobj->get_quizobj()->get_structure();
|
||||
$structure->update_slot_grade_item($structure->get_slot_by_number(1), $listeninggrade->id);
|
||||
$structure->update_slot_grade_item($structure->get_slot_by_number(2), $readinggrade->id);
|
||||
|
||||
$result = mod_quiz_external::get_attempt_review($attempt->id);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_review_returns(), $result);
|
||||
|
||||
// Two questions, one completed and correct, the other gave up.
|
||||
$this->assertEquals(50, $result['grade']);
|
||||
$this->assertEquals(1, $result['attempt']['attempt']);
|
||||
$this->assertEquals('finished', $result['attempt']['state']);
|
||||
$this->assertEquals(1, $result['attempt']['sumgrades']);
|
||||
$this->assertCount(2, $result['questions']);
|
||||
$this->assertEquals('gradedright', $result['questions'][0]['state']);
|
||||
$this->assertEquals(1, $result['questions'][0]['slot']);
|
||||
$this->assertEquals('gaveup', $result['questions'][1]['state']);
|
||||
$this->assertEquals(2, $result['questions'][1]['slot']);
|
||||
|
||||
// Only first page.
|
||||
$result = mod_quiz_external::get_attempt_review($attempt->id, 0);
|
||||
@@ -1720,11 +1797,19 @@ class external_test extends externallib_advanced_testcase {
|
||||
$this->assertEquals('gradedright', $result['questions'][0]['state']);
|
||||
$this->assertEquals(1, $result['questions'][0]['slot']);
|
||||
|
||||
$this->assertCount(1, $result['additionaldata']);
|
||||
$this->assertEquals('feedback', $result['additionaldata'][0]['id']);
|
||||
$this->assertEquals('Feedback', $result['additionaldata'][0]['title']);
|
||||
$this->assertEquals('Feedback text 1', $result['additionaldata'][0]['content']);
|
||||
// Verify additional grades.
|
||||
$this->assertEquals(['name' => 'Listening', 'grade' => 1, 'maxgrade' => 1], $result['attempt']['gradeitemmarks'][0]);
|
||||
$this->assertEquals(['name' => 'Reading', 'grade' => 0, 'maxgrade' => 1], $result['attempt']['gradeitemmarks'][1]);
|
||||
|
||||
// Now change the review options, so marks are not displayed, and check the result.
|
||||
$DB->set_field('quiz', 'reviewmarks', 0, ['id' => $attemptobj->get_quizid()]);
|
||||
$result = mod_quiz_external::get_attempt_review($attempt->id, 0);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_review_returns(), $result);
|
||||
|
||||
$this->assertEquals(1, $result['attempt']['attempt']);
|
||||
$this->assertEquals('finished', $result['attempt']['state']);
|
||||
$this->assertNull($result['attempt']['sumgrades']);
|
||||
$this->assertArrayNotHasKey('gradeitemmarks', $result['attempt']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user