Skip to content

Commit

Permalink
Merge branch 'MDL-79726-403' of https://github.com/sarjona/moodle int…
Browse files Browse the repository at this point in the history
…o MOODLE_403_STABLE
  • Loading branch information
junpataleta committed Dec 4, 2023
2 parents bea6119 + d29433d commit bc133f6
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 3 deletions.
26 changes: 26 additions & 0 deletions completion/classes/cm_completion_details.php
Expand Up @@ -193,6 +193,32 @@ public function get_overall_completion(): int {
return (int)$this->completiondata->completionstate;
}

/**
* Returns whether the overall completion state of this course module should be marked as complete or not.
* This is based on the completion settings of the course module, so when the course module requires a passing grade,
* it will only be marked as complete when the user has passed the course module. Otherwise, it will be marked as complete
* even when the user has failed the course module.
*
* @return bool True when the module can be marked as completed.
*/
public function is_overall_complete(): bool {
$completionstates = [];
if ($this->is_manual()) {
$completionstates = [COMPLETION_COMPLETE];
} else if ($this->is_automatic()) {
// Successfull completion states depend on the completion settings.
if (isset($this->completiondata->passgrade)) {
// Passing grade is required. Don't mark it as complete when state is COMPLETION_COMPLETE_FAIL.
$completionstates = [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS];
} else {
// Any grade is required. Mark it as complete even when state is COMPLETION_COMPLETE_FAIL.
$completionstates = [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS, COMPLETION_COMPLETE_FAIL];
}
}

return in_array($this->get_overall_completion(), $completionstates);
}

/**
* Whether this activity module has completion enabled.
*
Expand Down
135 changes: 135 additions & 0 deletions completion/tests/cm_completion_details_test.php
Expand Up @@ -223,6 +223,141 @@ public function test_get_overall_completion(int $state) {
$this->assertEquals($state, $cmcompletion->get_overall_completion());
}

/**
* Data provider for test_is_overall_complete().
* @return array[]
*/
public static function is_overall_complete_provider(): array {
return [
'Automatic, require view, not viewed' => [
'expected' => false,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionstate' => COMPLETION_INCOMPLETE,
'completionview' => COMPLETION_INCOMPLETE,
'completiongrade' => null,
'completionpassgrade' => null,
],
'Automatic, require view, viewed' => [
'expected' => true,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionstate' => COMPLETION_COMPLETE,
'completionview' => COMPLETION_COMPLETE,
'completiongrade' => null,
'completionpassgrade' => null,
],
'Automatic, require grade, not graded' => [
'expected' => false,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionstate' => COMPLETION_INCOMPLETE,
'completionview' => null,
'completiongrade' => COMPLETION_INCOMPLETE,
'completionpassgrade' => null,
],
'Automatic, require grade, graded with fail' => [
'expected' => true,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionstate' => COMPLETION_COMPLETE_FAIL,
'completionview' => null,
'completiongrade' => COMPLETION_COMPLETE_FAIL,
'completionpassgrade' => null,
],
'Automatic, require grade, graded with passing' => [
'expected' => true,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionstate' => COMPLETION_COMPLETE_PASS,
'completionview' => null,
'completiongrade' => COMPLETION_COMPLETE_PASS,
'completionpassgrade' => null,
],
'Automatic, require passgrade, not graded' => [
'expected' => false,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionstate' => COMPLETION_INCOMPLETE,
'completionview' => null,
'completiongrade' => null,
'completionpassgrade' => COMPLETION_INCOMPLETE,
],
'Automatic, require passgrade, graded with fail' => [
'expected' => false,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionstate' => COMPLETION_COMPLETE_FAIL,
'completionview' => null,
'completiongrade' => null,
'completionpassgrade' => COMPLETION_COMPLETE_FAIL,
],
'Automatic, require passgrade, graded with passing' => [
'expected' => true,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionstate' => COMPLETION_COMPLETE_PASS,
'completionview' => null,
'completiongrade' => null,
'completionpassgrade' => COMPLETION_COMPLETE_PASS,
],
'Manual, incomplete' => [
'expected' => false,
'completion' => COMPLETION_TRACKING_MANUAL,
'completionstate' => COMPLETION_INCOMPLETE,
],
'Manual, complete' => [
'expected' => true,
'completion' => COMPLETION_TRACKING_MANUAL,
'completionstate' => COMPLETION_COMPLETE,
],
'None, incomplete' => [
'expected' => false,
'completion' => COMPLETION_TRACKING_NONE,
'completionstate' => COMPLETION_INCOMPLETE,
],
'None, complete' => [
'expected' => false,
'completion' => COMPLETION_TRACKING_NONE,
'completionstate' => COMPLETION_COMPLETE,
],
];
}

/**
* Test for is_overall_complete().
*
* @covers ::is_overall_complete
* @dataProvider is_overall_complete_provider
* @param bool $expected Expected result returned by is_overall_complete().
* @param int $completion The completion tracking mode.
* @param int $completionstate The overall completion state.
* @param int|null $completionview Completion status of the "view" completion condition.
* @param int|null $completiongrade Completion status of the "must receive grade" completion condition.
* @param int|null $completionpassgrade Completion status of the "must receive passing grade" completion condition.
*/
public function test_is_overall_complete(
bool $expected,
int $completion,
int $completionstate,
?int $completionview = null,
?int $completiongrade = null,
?int $completionpassgrade = null,
): void {
$options = [];
$getdatareturn = (object)[
'completionstate' => $completionstate,
'viewed' => $completionview,
'completiongrade' => $completiongrade,
'passgrade' => $completionpassgrade,
];

if (!is_null($completionview)) {
$options['completionview'] = true;
}
if (!is_null($completiongrade)) {
$options['completionusegrade'] = true;
}
if (!is_null($completionpassgrade)) {
$options['completionpassgrade'] = true;
}

$cmcompletion = $this->setup_data($completion, $options, $getdatareturn);
$this->assertEquals($expected, $cmcompletion->is_overall_complete());
}

/**
* Data provider for test_get_details().
* @return array[]
Expand Down
4 changes: 4 additions & 0 deletions completion/upgrade.txt
@@ -1,6 +1,10 @@
This file describes API changes in /completion/* - completion,
information provided here is intended especially for developers.

=== 4.3.1 ===
* A new method, cm_completion_details::is_overall_complete() has been added to calculate whether a module should be considered or
not completed, based on their completion options and the current value for the overall complete state.

=== 4.3 ===
* A trait class, core_completion/form/form_trait has been added to reuse code for adding and validation completion settings to any
form.
Expand Down
5 changes: 3 additions & 2 deletions course/classes/output/activity_completion.php
Expand Up @@ -57,6 +57,7 @@ public function export_for_template(renderer_base $output): stdClass {
global $CFG;

$overallcompletion = $this->cmcompletion->get_overall_completion();
$isoverallcomplete = $this->cmcompletion->is_overall_complete();
$overrideby = $this->get_overrideby();
$course = $this->cminfo->get_course();

Expand All @@ -77,8 +78,8 @@ public function export_for_template(renderer_base $output): stdClass {
'ismanual' => $this->cmcompletion->is_manual(),
'showmanualcompletion' => $this->cmcompletion->show_manual_completion(),
'istrackeduser' => $this->cmcompletion->is_tracked_user(),
'overallcomplete' => $overallcompletion == COMPLETION_COMPLETE,
'overallincomplete' => $overallcompletion == COMPLETION_INCOMPLETE,
'overallcomplete' => $isoverallcomplete,
'overallincomplete' => !$isoverallcomplete,
'withavailability' => $withavailability ?? false,
'overrideby' => $overrideby,
'completiondetails' => $this->get_completion_details($overrideby),
Expand Down
72 changes: 71 additions & 1 deletion course/format/tests/behat/coursepage_completion.feature
Expand Up @@ -56,7 +56,7 @@ Feature: Course page activities completion
And I am on the "Course 1" course page
And "Completion" "button" should exist in the "Activity sample" "activity"

Scenario: Student should see the automatic completion criterias statuses of activities
Scenario: Student should see the automatic completion criterias statuses of activities with completion view
Given the following "activity" exists:
| activity | assign |
| name | Activity sample |
Expand All @@ -71,6 +71,76 @@ Feature: Course page activities completion
And "To do" "button" should not exist in the "Activity sample" "activity"
And the "View" item should exist in the "Done" dropdown of the "Activity sample" "activity"

Scenario: Student should see the automatic completion criterias statuses of activities with completion grade
Given the following "activities" exist:
| activity | name | course | idnumber | gradepass | completion | completionusegrade |
| quiz | Activity sample 1 | C1 | quiz1 | 5.00 | 2 | 1 |
| quiz | Activity sample 2 | C1 | quiz2 | 5.00 | 2 | 1 |
And the following "question categories" exist:
| contextlevel | reference | name |
| Course | C1 | Test questions |
And the following "questions" exist:
| questioncategory | qtype | name | questiontext |
| Test questions | truefalse | First question | Answer the first question |
And quiz "Activity sample 1" contains the following questions:
| question | page |
| First question | 1 |
And quiz "Activity sample 2" contains the following questions:
| question | page |
| First question | 1 |
When I am on the "C1" "Course" page logged in as "student1"
Then the "Receive a grade" item should exist in the "To do" dropdown of the "Activity sample 1" "activity"
And the "Receive a grade" item should exist in the "To do" dropdown of the "Activity sample 2" "activity"
# Pass grade.
And user "student1" has attempted "Activity sample 1" with responses:
| slot | response |
| 1 | True |
# Fail grade.
And user "student1" has attempted "Activity sample 2" with responses:
| slot | response |
| 1 | False |
# After receiving a grade, the completion criteria dropdown should display "Done" instead of "To do", regardless of pass/fail.
And I am on the "Course 1" course page
And "To do" "button" should not exist in the "Activity sample 1" "activity"
And the "Receive a grade" item should exist in the "Done" dropdown of the "Activity sample 1" "activity"
And "To do" "button" should not exist in the "Activity sample 2" "activity"
And the "Receive a grade" item should exist in the "Done" dropdown of the "Activity sample 2" "activity"

Scenario: Student should see the automatic completion criterias statuses of activities with completion passgrade
Given the following "activities" exist:
| activity | name | course | idnumber | gradepass | completion | completionusegrade | completionpassgrade |
| quiz | Activity sample 1 | C1 | quiz1 | 5.00 | 2 | 1 | 1 |
| quiz | Activity sample 2 | C1 | quiz2 | 5.00 | 2 | 1 | 1 |
And the following "question categories" exist:
| contextlevel | reference | name |
| Course | C1 | Test questions |
And the following "questions" exist:
| questioncategory | qtype | name | questiontext |
| Test questions | truefalse | First question | Answer the first question |
And quiz "Activity sample 1" contains the following questions:
| question | page |
| First question | 1 |
And quiz "Activity sample 2" contains the following questions:
| question | page |
| First question | 1 |
When I am on the "C1" "Course" page logged in as "student1"
Then the "Receive a grade" item should exist in the "To do" dropdown of the "Activity sample 1" "activity"
And the "Receive a grade" item should exist in the "To do" dropdown of the "Activity sample 2" "activity"
# Pass grade.
And user "student1" has attempted "Activity sample 1" with responses:
| slot | response |
| 1 | True |
# Fail grade.
And user "student1" has attempted "Activity sample 2" with responses:
| slot | response |
| 1 | False |
# After receiving a grade, the completion criteria dropdown should display "Done" only for the passing grade.
And I am on the "Course 1" course page
And "To do" "button" should not exist in the "Activity sample 1" "activity"
And the "Receive a grade" item should exist in the "Done" dropdown of the "Activity sample 1" "activity"
But "To do" "button" should exist in the "Activity sample 2" "activity"
And the "Receive a grade" item should exist in the "To do" dropdown of the "Activity sample 2" "activity"

Scenario: Teacher can edit activity completion using completion dialog link
Given the following "activity" exists:
| activity | assign |
Expand Down

0 comments on commit bc133f6

Please sign in to comment.