From 7e3d9db579004af86c8edfe5132658c8386e4089 Mon Sep 17 00:00:00 2001 From: Ilya Tregubov Date: Tue, 17 Jan 2023 09:37:14 +0800 Subject: [PATCH 1/4] MDL-75719 completion: Fix completion state for hidden grade items. For hidden grade items we used to mark students as completed if they have any grade. But this was not working correctly when we also set pass grade for activity and completion criteria based on pass grade. So we will have these completion states Competion criteria 'Receive grade': No grade - COMPLETION_INCOMPLETE Grade visible, achieved passing grade - COMPLETION_COMPLETE_PASS Grade visible, failed passing grade - COMPLETION_COMPLETE_FAIL Grade hidden - COMPLETION_COMPLETE Completion criteris 'Receive passing grade' No grade - COMPLETION_INCOMPLETE Grade visible, achieved passing grade - COMPLETION_COMPLETE_PASS Grade visible, failed passing grade - COMPLETION_COMPLETE_FAIL Grade hidden, achieved passing grade - COMPLETION_COMPLETE_PASS Grade hidden, failed passing grade - COMPLETION_COMPLETE_FAIL_HIDDEN --- lib/completionlib.php | 29 ++++++++++++++++++++++------- lib/upgrade.txt | 5 +++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/completionlib.php b/lib/completionlib.php index aae0552e349..597f1de389e 100644 --- a/lib/completionlib.php +++ b/lib/completionlib.php @@ -88,6 +88,11 @@ define('COMPLETION_COMPLETE_PASS', 2); */ define('COMPLETION_COMPLETE_FAIL', 3); +/** + * Indicates that the user has received a failing grade for a hidden grade item. + */ +define('COMPLETION_COMPLETE_FAIL_HIDDEN', 4); + /** * The effect of this change to completion status is unknown. * A completion effect changes (used only in update_state) @@ -784,7 +789,8 @@ class completion_info { $this->internal_systemerror("Unexpected result: multiple grades for item '{$item->id}', user '{$userid}'"); } - return self::internal_get_grade_state($item, reset($grades)); + $returnpassfail = !empty($cm->completionpassgrade); + return self::internal_get_grade_state($item, reset($grades), $returnpassfail); } return COMPLETION_INCOMPLETE; @@ -1171,12 +1177,16 @@ class completion_info { $newstate = COMPLETION_COMPLETE_PASS; } - // The activity is using 'passing grade' criteria therefore fail indication should be on this criteria. - // The user has received a (failing) grade so 'completiongrade' should properly indicate this. - if ($newstate == COMPLETION_COMPLETE_FAIL) { + // No need to show failing status for the completiongrade condition when passing grade condition is set. + if (in_array($newstate, [COMPLETION_COMPLETE_FAIL, COMPLETION_COMPLETE_FAIL_HIDDEN])) { $data['completiongrade'] = COMPLETION_COMPLETE; - } + // If the grade received by the user is a failing grade for a hidden grade item, + // the 'Require passing grade' criterion is considered incomplete. + if ($newstate == COMPLETION_COMPLETE_FAIL_HIDDEN) { + $newstate = COMPLETION_INCOMPLETE; + } + } $data['passgrade'] = $newstate; } } @@ -1547,9 +1557,10 @@ class completion_info { * * @param grade_item $item an instance of grade_item * @param grade_grade $grade an instance of grade_grade + * @param bool $returnpassfail If course module has pass grade completion criteria * @return int Completion state e.g. COMPLETION_INCOMPLETE */ - public static function internal_get_grade_state($item, $grade) { + public static function internal_get_grade_state($item, $grade, bool $returnpassfail = false) { // If no grade is supplied or the grade doesn't have an actual value, then // this is not complete. if (!$grade || (is_null($grade->finalgrade) && is_null($grade->rawgrade))) { @@ -1557,15 +1568,19 @@ class completion_info { } // Conditions to show pass/fail: + // a) Completion criteria to achieve pass grade is enabled + // or // a) Grade has pass mark (default is 0.00000 which is boolean true so be careful) // b) Grade is visible (neither hidden nor hidden-until) - if ($item->gradepass && $item->gradepass > 0.000009 && !$item->hidden) { + if ($item->gradepass && $item->gradepass > 0.000009 && ($returnpassfail || !$item->hidden)) { // Use final grade if set otherwise raw grade $score = !is_null($grade->finalgrade) ? $grade->finalgrade : $grade->rawgrade; // We are displaying and tracking pass/fail if ($score >= $item->gradepass) { return COMPLETION_COMPLETE_PASS; + } else if ($item->hidden) { + return COMPLETION_COMPLETE_FAIL_HIDDEN; } else { return COMPLETION_COMPLETE_FAIL; } diff --git a/lib/upgrade.txt b/lib/upgrade.txt index c6e67320200..7d7afd202eb 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -1,6 +1,11 @@ This files describes API changes in core libraries and APIs, information provided here is intended especially for developers. +=== 4.1.2 === +* A new constant COMPLETION_COMPLETE_FAIL_HIDDEN is introduced to mark that user has received a failing grade + for a hidden grade item. This state can be currently returned only by get_core_completion_state() and + internal_get_grade_state() so we don't store it in database (for now). + === 4.1.1 === * There is a new helper function mtrace_exception to help with reporting exceptions you have caught in scheduled tasks. From 3ebd25ff48ace51d5cfc7d657502195953573687 Mon Sep 17 00:00:00 2001 From: Ilya Tregubov Date: Tue, 17 Jan 2023 09:59:56 +0800 Subject: [PATCH 2/4] MDL-75719 completion: Fix viewed criteria. When we unlock completion criteria and save changes 'viewed' criteria still should be completed if user has viewed activity before. Seems that that was broken by recent changes. Fixing here. --- lib/completionlib.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/completionlib.php b/lib/completionlib.php index 597f1de389e..26056518181 100644 --- a/lib/completionlib.php +++ b/lib/completionlib.php @@ -1119,6 +1119,9 @@ class completion_info { if (empty($data->coursemoduleid)) { $cacheddata[$data->cmid] = $defaultdata; + if ($data->viewed) { + $cacheddata[$data->cmid]['viewed'] = $data->viewed; + } $cacheddata[$data->cmid]['coursemoduleid'] = $data->cmid; } else { unset($data->cmid); From 23915a35ae3cd7ad0a96fe26f71c76b70a633472 Mon Sep 17 00:00:00 2001 From: Ilya Tregubov Date: Tue, 17 Jan 2023 09:53:30 +0800 Subject: [PATCH 3/4] MDL-75719 completion: Fix unit tests and Behat tests. Fix tests since we changed behavior of completions (completion state an UI). --- lib/tests/completionlib_test.php | 24 +++++++++++++++++++ .../quiz_activity_completion_unlocked.feature | 17 +++++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/tests/completionlib_test.php b/lib/tests/completionlib_test.php index 43c0cfe151f..b0d963b6e0a 100644 --- a/lib/tests/completionlib_test.php +++ b/lib/tests/completionlib_test.php @@ -1319,6 +1319,30 @@ class completionlib_test extends advanced_testcase { $this->assertEquals( COMPLETION_COMPLETE, completion_info::internal_get_grade_state($item, $grade)); + + // Item is hidden, but returnpassfail is true and the grade is passing. + $item->hidden = 1; + $item->gradepass = 4; + $grade->finalgrade = 5.0; + $this->assertEquals( + COMPLETION_COMPLETE_PASS, + completion_info::internal_get_grade_state($item, $grade, true)); + + // Item is hidden, but returnpassfail is true and the grade is failing. + $item->hidden = 1; + $item->gradepass = 4; + $grade->finalgrade = 3.0; + $this->assertEquals( + COMPLETION_COMPLETE_FAIL_HIDDEN, + completion_info::internal_get_grade_state($item, $grade, true)); + + // Item is not hidden, but returnpassfail is true and the grade is failing. + $item->hidden = 0; + $item->gradepass = 4; + $grade->finalgrade = 3.0; + $this->assertEquals( + COMPLETION_COMPLETE_FAIL, + completion_info::internal_get_grade_state($item, $grade, true)); } /** diff --git a/mod/quiz/tests/behat/quiz_activity_completion_unlocked.feature b/mod/quiz/tests/behat/quiz_activity_completion_unlocked.feature index 0977e63965f..b1e35f074a1 100644 --- a/mod/quiz/tests/behat/quiz_activity_completion_unlocked.feature +++ b/mod/quiz/tests/behat/quiz_activity_completion_unlocked.feature @@ -31,7 +31,7 @@ Feature: Activity completion in the quiz activity with unlocked and re-grading. | idnumber | quiz1 | | name | Test quiz name | | section | 1 | - | gradepass | 10.00 | + | gradepass | 8 | | grade | 10 | | grademethod | 1 | | completion | 2 | @@ -54,14 +54,21 @@ Feature: Activity completion in the quiz activity with unlocked and re-grading. And I am on "Course 1" course homepage And I follow "Test quiz name" And the "Receive a grade" completion condition of "Test quiz name" is displayed as "done" - And the "Receive a passing grade" completion condition of "Test quiz name" is displayed as "failed" + And the "Receive a passing grade" completion condition of "Test quiz name" is displayed as "done" And I log out - When I am on the "Test quiz name" "quiz activity" page logged in as teacher1 + When I am on the "Course 1" course page logged in as teacher1 + And I navigate to "Reports > Activity completion" in current page administration + And "Completed (achieved pass grade)" "icon" should exist in the "Student 1" "table_row" + And I am on the "Test quiz name" "quiz activity" page And I navigate to "Settings" in current page administration And I expand all fieldsets And I press "Unlock completion settings" And I set the following fields to these values: - | gradepass | 8 | + | gradepass | 10 | And I press "Save and return to course" And I navigate to "Reports > Activity completion" in current page administration - Then "Completed (achieved pass grade)" "icon" should exist in the "Student 1" "table_row" + Then "Completed (achieved pass grade)" "icon" should not exist in the "Student 1" "table_row" + And I log out + And I am on the "Test quiz name" "quiz activity" page logged in as student1 + And the "Receive a grade" completion condition of "Test quiz name" is displayed as "done" + And the "Receive a passing grade" completion condition of "Test quiz name" is displayed as "failed" From 1e26496cdd69d9db2f5904b1ffa7369c817113a4 Mon Sep 17 00:00:00 2001 From: Ilya Tregubov Date: Fri, 20 Jan 2023 14:41:04 +0800 Subject: [PATCH 4/4] MDL-75719 completion: Add Behat scenario for completion states. --- ...tion_criteria_gradeitem_visibility.feature | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 completion/tests/behat/passgrade_completion_criteria_gradeitem_visibility.feature diff --git a/completion/tests/behat/passgrade_completion_criteria_gradeitem_visibility.feature b/completion/tests/behat/passgrade_completion_criteria_gradeitem_visibility.feature new file mode 100644 index 00000000000..0e948611732 --- /dev/null +++ b/completion/tests/behat/passgrade_completion_criteria_gradeitem_visibility.feature @@ -0,0 +1,137 @@ +@core @core_completion @javascript +Feature: Students will be shown relevant completion state based on grade item visibility. + In order to understand completion states of course modules + As a student + I need to see relevant completion information for various combination of activity passgrade settings + + Background: + Given the following "courses" exist: + | fullname | shortname | category | enablecompletion | + | Course 1 | C1 | 0 | 1 | + And the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | Frist | teacher1@example.com | + | student1 | Student | First | student1@example.com | + | student2 | Student | Second | student2@example.com | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + | student2 | C1 | student | + And the following "activity" exists: + | activity | assign | + | course | C1 | + | name | Test assignment name | + | intro | Submit your online text | + | assignsubmission_onlinetext_enabled | 1 | + | assignsubmission_file_enabled | 0 | + | completion | 2 | + | completionpassgrade | 1 | + | completionusegrade | 1 | + | gradepass | 50 | + And I am on the "Course 1" course page logged in as teacher1 + And "Student First" user has not completed "Test assignment name" activity + And I log out + And I am on the "Course 1" course page logged in as student1 + And the "Receive a grade" completion condition of "Test assignment name" is displayed as "todo" + And the "Receive a passing grade" completion condition of "Test assignment name" is displayed as "todo" + And I log out + + Scenario: Passing grade and receive a grade completions for visible grade item (passgrade completion enabled) + Given I am on the "Course 1" course page logged in as teacher1 + And I navigate to "View > Grader report" in the course gradebook + And I turn editing mode on + And I give the grade "21" to the user "Student First" for the grade item "Test assignment name" + And I give the grade "50" to the user "Student Second" for the grade item "Test assignment name" + And I press "Save changes" + And I am on "Course 1" course homepage + And "Student First" user has completed "Test assignment name" activity + And "Student Second" user has completed "Test assignment name" activity + And I log out + When I am on the "Course 1" course page logged in as student1 + And the "Receive a grade" completion condition of "Test assignment name" is displayed as "done" + And the "Receive a passing grade" completion condition of "Test assignment name" is displayed as "failed" + And I log out + And I am on the "Course 1" course page logged in as student2 + Then the "Receive a grade" completion condition of "Test assignment name" is displayed as "done" + And the "Receive a passing grade" completion condition of "Test assignment name" is displayed as "done" + + Scenario: Passing grade and receive a grade completions for hidden grade item (passgrade completion enabled) + Given I am on the "Course 1" course page logged in as teacher1 + And I navigate to "Setup > Gradebook setup" in the course gradebook + And I hide the grade item "Test assignment name" + And I navigate to "View > Grader report" in the course gradebook + And I turn editing mode on + And I give the grade "21" to the user "Student First" for the grade item "Test assignment name" + And I give the grade "50" to the user "Student Second" for the grade item "Test assignment name" + And I press "Save changes" + And I am on "Course 1" course homepage + And "Student First" user has not completed "Test assignment name" activity + And "Student Second" user has completed "Test assignment name" activity + And I log out + And I am on the "Course 1" course page logged in as student1 + And the "Receive a grade" completion condition of "Test assignment name" is displayed as "done" + And the "Receive a passing grade" completion condition of "Test assignment name" is displayed as "todo" + And I log out + And I am on the "Course 1" course page logged in as student2 + And the "Receive a grade" completion condition of "Test assignment name" is displayed as "done" + And the "Receive a passing grade" completion condition of "Test assignment name" is displayed as "done" + + Scenario: Receive a grade completion for visible grade item (passgrade completion disabled) + Given I am on the "Test assignment name" Activity page logged in as teacher1 + When I navigate to "Settings" in current page administration + And I set the following fields to these values: + | completionpassgrade | 0 | + And I press "Save and display" + And I log out + And I am on the "Course 1" course page logged in as student1 + And the "Receive a grade" completion condition of "Test assignment name" is displayed as "todo" + And I should not see "Receive a passing grade" + And I log out + And I am on the "Course 1" course page logged in as teacher1 + And I navigate to "View > Grader report" in the course gradebook + And I turn editing mode on + And I give the grade "21" to the user "Student First" for the grade item "Test assignment name" + And I give the grade "50" to the user "Student Second" for the grade item "Test assignment name" + And I press "Save changes" + And I am on "Course 1" course homepage + And "Student First" user has completed "Test assignment name" activity + And "Student Second" user has completed "Test assignment name" activity + And I log out + When I am on the "Course 1" course page logged in as student1 + # Once MDL-75582 is fixed "failed" should be changed to "done" + And the "Receive a grade" completion condition of "Test assignment name" is displayed as "failed" + And I should not see "Receive a passing grade" + And I log out + And I am on the "Course 1" course page logged in as student2 + Then the "Receive a grade" completion condition of "Test assignment name" is displayed as "done" + + Scenario: Receive a grade completion for hidden grade item (passgrade completion disabled) + Given I am on the "Test assignment name" Activity page logged in as teacher1 + When I navigate to "Settings" in current page administration + And I set the following fields to these values: + | completionpassgrade | 0 | + And I press "Save and display" + And I log out + And I am on the "Course 1" course page logged in as student1 + And the "Receive a grade" completion condition of "Test assignment name" is displayed as "todo" + And I should not see "Receive a passing grade" + And I log out + And I am on the "Course 1" course page logged in as teacher1 + And I navigate to "Setup > Gradebook setup" in the course gradebook + And I hide the grade item "Test assignment name" + And I navigate to "View > Grader report" in the course gradebook + And I turn editing mode on + And I give the grade "21" to the user "Student First" for the grade item "Test assignment name" + And I give the grade "50" to the user "Student Second" for the grade item "Test assignment name" + And I press "Save changes" + And I am on "Course 1" course homepage + And "Student First" user has completed "Test assignment name" activity + And "Student Second" user has completed "Test assignment name" activity + And I log out + When I am on the "Course 1" course page logged in as student1 + Then the "Receive a grade" completion condition of "Test assignment name" is displayed as "done" + And I should not see "Receive a passing grade" + And I log out + And I am on the "Course 1" course page logged in as student2 + And the "Receive a grade" completion condition of "Test assignment name" is displayed as "done"