diff --git a/mod/quiz/classes/external.php b/mod/quiz/classes/external.php index 9c38f7e69bc..f6ead59d3fb 100644 --- a/mod/quiz/classes/external.php +++ b/mod/quiz/classes/external.php @@ -520,7 +520,23 @@ class mod_quiz_external extends external_api { } $result = array(); - $grade = quiz_get_best_grade($quiz, $user->id); + + // This code was mostly copied from mod/quiz/view.php. We need to make the web service logic consistent. + // Get this user's attempts. + $attempts = quiz_get_user_attempts($quiz->id, $user->id, 'all'); + $canviewgrade = false; + if ($attempts) { + if ($USER->id != $user->id) { + // No need to check the permission here. We did it at by require_capability('mod/quiz:viewreports', $context). + $canviewgrade = true; + } else { + // Work out which columns we need, taking account what data is available in each attempt. + list($notused, $alloptions) = quiz_get_combined_reviewoptions($quiz, $attempts); + $canviewgrade = $alloptions->marks >= question_display_options::MARK_AND_MAX; + } + } + + $grade = $canviewgrade ? quiz_get_best_grade($quiz, $user->id) : null; if ($grade === null) { $result['hasgrade'] = false; diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index 07b82237eeb..706ae5b81c1 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -1172,7 +1172,7 @@ function quiz_review_option_form_to_db($fromform, $field) { $review = 0; foreach ($times as $whenname => $when) { $fieldname = $field . $whenname; - if (isset($fromform->$fieldname)) { + if (!empty($fromform->$fieldname)) { $review |= $when; unset($fromform->$fieldname); } diff --git a/mod/quiz/tests/external_test.php b/mod/quiz/tests/external_test.php index eef2b2bc30a..d20ccf3c78c 100644 --- a/mod/quiz/tests/external_test.php +++ b/mod/quiz/tests/external_test.php @@ -451,37 +451,101 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase { * Test get_user_best_grade */ public function test_get_user_best_grade() { - global $DB; + $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); + $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $questioncat = $questiongenerator->create_question_category(); + + // Create a new quiz. + $quizapi1 = $quizgenerator->create_instance([ + 'name' => 'Test Quiz API 1', + 'course' => $this->course->id, + 'sumgrades' => 1 + ]); + $quizapi2 = $quizgenerator->create_instance([ + 'name' => 'Test Quiz API 2', + 'course' => $this->course->id, + 'sumgrades' => 1, + 'marksduring' => 0, + 'marksimmediately' => 0, + 'marksopen' => 0, + 'marksclosed' => 0 + ]); + + // Create a question. + $question = $questiongenerator->create_question('numerical', null, ['category' => $questioncat->id]); + + // Add question to the quizzes. + quiz_add_quiz_question($question->id, $quizapi1); + quiz_add_quiz_question($question->id, $quizapi2); + + // Create quiz object. + $quizapiobj1 = quiz::create($quizapi1->id, $this->student->id); + $quizapiobj2 = quiz::create($quizapi2->id, $this->student->id); + + // Set grade to pass. + $item = grade_item::fetch([ + 'courseid' => $this->course->id, + 'itemtype' => 'mod', + 'itemmodule' => 'quiz', + 'iteminstance' => $quizapi1->id, + 'outcomeid' => null + ]); + $item->gradepass = 80; + $item->update(); + + $item = grade_item::fetch([ + 'courseid' => $this->course->id, + 'itemtype' => 'mod', + 'itemmodule' => 'quiz', + 'iteminstance' => $quizapi2->id, + 'outcomeid' => null + ]); + $item->gradepass = 80; + $item->update(); + + // Start the passing attempt. + $quba1 = question_engine::make_questions_usage_by_activity('mod_quiz', $quizapiobj1->get_context()); + $quba1->set_preferred_behaviour($quizapiobj1->get_quiz()->preferredbehaviour); + + $quba2 = question_engine::make_questions_usage_by_activity('mod_quiz', $quizapiobj2->get_context()); + $quba2->set_preferred_behaviour($quizapiobj2->get_quiz()->preferredbehaviour); + + // Start the testing for quizapi1 that allow the student to view the grade. $this->setUser($this->student); - - $result = mod_quiz_external::get_user_best_grade($this->quiz->id); + $result = mod_quiz_external::get_user_best_grade($quizapi1->id); $result = external_api::clean_returnvalue(mod_quiz_external::get_user_best_grade_returns(), $result); // No grades yet. $this->assertFalse($result['hasgrade']); $this->assertTrue(!isset($result['grade'])); - $grade = new stdClass(); - $grade->quiz = $this->quiz->id; - $grade->userid = $this->student->id; - $grade->grade = 8.9; - $grade->timemodified = time(); - $grade->id = $DB->insert_record('quiz_grades', $grade); + // Start the attempt. + $timenow = time(); + $attempt = quiz_create_attempt($quizapiobj1, 1, false, $timenow, false, $this->student->id); + quiz_start_new_attempt($quizapiobj1, $quba1, $attempt, 1, $timenow); + quiz_attempt_save_started($quizapiobj1, $quba1, $attempt); - $result = mod_quiz_external::get_user_best_grade($this->quiz->id); + // Process some responses from the student. + $attemptobj = quiz_attempt::create($attempt->id); + $attemptobj->process_submitted_actions($timenow, false, [1 => ['answer' => '3.14']]); + + // Finish the attempt. + $attemptobj->process_finish($timenow, false); + + $result = mod_quiz_external::get_user_best_grade($quizapi1->id); $result = external_api::clean_returnvalue(mod_quiz_external::get_user_best_grade_returns(), $result); // Now I have grades. $this->assertTrue($result['hasgrade']); - $this->assertEquals(8.9, $result['grade']); + $this->assertEquals(100.0, $result['grade']); // We should not see other users grades. $anotherstudent = self::getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($anotherstudent->id, $this->course->id, $this->studentrole->id, 'manual'); try { - mod_quiz_external::get_user_best_grade($this->quiz->id, $anotherstudent->id); + mod_quiz_external::get_user_best_grade($quizapi1->id, $anotherstudent->id); $this->fail('Exception expected due to missing capability.'); } catch (required_capability_exception $e) { $this->assertEquals('nopermissions', $e->errorcode); @@ -490,11 +554,11 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase { // Teacher must be able to see student grades. $this->setUser($this->teacher); - $result = mod_quiz_external::get_user_best_grade($this->quiz->id, $this->student->id); + $result = mod_quiz_external::get_user_best_grade($quizapi1->id, $this->student->id); $result = external_api::clean_returnvalue(mod_quiz_external::get_user_best_grade_returns(), $result); $this->assertTrue($result['hasgrade']); - $this->assertEquals(8.9, $result['grade']); + $this->assertEquals(100.0, $result['grade']); // Invalid user. try { @@ -504,8 +568,48 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase { $this->assertEquals('invaliduser', $e->errorcode); } - // Remove the created data. - $DB->delete_records('quiz_grades', array('id' => $grade->id)); + // End the testing for quizapi1 that allow the student to view the grade. + + // Start the testing for quizapi2 that do not allow the student to view the grade. + + $this->setUser($this->student); + $result = mod_quiz_external::get_user_best_grade($quizapi2->id); + $result = external_api::clean_returnvalue(mod_quiz_external::get_user_best_grade_returns(), $result); + + // No grades yet. + $this->assertFalse($result['hasgrade']); + $this->assertTrue(!isset($result['grade'])); + + // Start the attempt. + $timenow = time(); + $attempt = quiz_create_attempt($quizapiobj2, 1, false, $timenow, false, $this->student->id); + quiz_start_new_attempt($quizapiobj2, $quba2, $attempt, 1, $timenow); + quiz_attempt_save_started($quizapiobj2, $quba2, $attempt); + + // Process some responses from the student. + $attemptobj = quiz_attempt::create($attempt->id); + $attemptobj->process_submitted_actions($timenow, false, [1 => ['answer' => '3.14']]); + + // Finish the attempt. + $attemptobj->process_finish($timenow, false); + + $result = mod_quiz_external::get_user_best_grade($quizapi2->id); + $result = external_api::clean_returnvalue(mod_quiz_external::get_user_best_grade_returns(), $result); + + // Now I have grades but I will not be allowed to see it. + $this->assertFalse($result['hasgrade']); + $this->assertTrue(!isset($result['grade'])); + + // Teacher must be able to see student grades. + $this->setUser($this->teacher); + + $result = mod_quiz_external::get_user_best_grade($quizapi2->id, $this->student->id); + $result = external_api::clean_returnvalue(mod_quiz_external::get_user_best_grade_returns(), $result); + + $this->assertTrue($result['hasgrade']); + $this->assertEquals(100.0, $result['grade']); + + // End the testing for quizapi2 that do not allow the student to view the grade. } /**