MDL-72827 completion: Revert completion flow logic
- Reverts the custom flow logic introduced in the 52206 - Expand unit tests to cover different grade based completion settings
This commit is contained in:
@@ -4,15 +4,8 @@ information provided here is intended especially for developers.
|
||||
=== 4.0 ===
|
||||
* New method mark_course_completions_activity_criteria() has been added to mark course completions instantly. It is
|
||||
based on cron for completion_criteria_activity.php which is refactored to use it as well.
|
||||
* Modified completion criteria to allow plugins to override core completion logic.
|
||||
* Core now passes an additional parameter to '_get_completion_state'. This is an array representation of the completion results that have already been
|
||||
tested. Currently contains - viewed, usegrade, passgrade. Any plugin that are dependent on these criteria can now check this array instead of retesting it.
|
||||
* Introduced a new plugin function - '_get_completion_aggregation_state', that would indicate the aggregation type/relationship between the plugin and core
|
||||
completion criteria. This callback should either return a COMPLETION_STANDARD_FLOW / COMPLETION_CUSTOM_MODULE_FLOW. The former for default existing core
|
||||
behaviour while the latter enforces the override logic from the plugin. Defaults to COMPLETION_STANDARD_FLOW if not defined. This is useful when plugins
|
||||
need to override the core completion criteria in cases where it may be dependent on them. In these cases, the 'source of truth' would be the response
|
||||
from the plugin's 'get_completion_state' function. e.g. Quiz's completion defines a criteria of 'requires passing grade OR all attempts AND min attempts
|
||||
reached.' In these cases, even if a passing grade has not been achieved, the activity should be marked as completed if the no.of attempts have been reached.
|
||||
* The method \completion_criteria_completion::mark_complete() now has the optional $timecompleted parameter to specify when the
|
||||
criteria was completed.
|
||||
|
||||
|
||||
+7
-23
@@ -132,16 +132,6 @@ define('COMPLETION_OR', false);
|
||||
*/
|
||||
define('COMPLETION_AND', true);
|
||||
|
||||
/**
|
||||
* When a module implements this, completion state is dependent to the
|
||||
* module's _get_completion_state callback and activity_custom_completion class.
|
||||
*/
|
||||
define('COMPLETION_CUSTOM_MODULE_FLOW', true);
|
||||
/**
|
||||
* Standard flow indicates ALL conditions need to be met for completion to be marked as done.
|
||||
*/
|
||||
define('COMPLETION_STANDARD_FLOW', false);
|
||||
|
||||
/**
|
||||
* Course completion criteria aggregation method.
|
||||
*/
|
||||
@@ -733,28 +723,22 @@ class completion_info {
|
||||
$response = $function($this->course, $cm, $userid, COMPLETION_AND, $completionstate);
|
||||
}
|
||||
|
||||
// Get the relationship between the core_completion and plugin_completion criteria.
|
||||
$aggregationtype = COMPLETION_STANDARD_FLOW;
|
||||
if ($aggregationfn = component_callback_exists("mod_$cminfo->modname", 'get_completion_aggregation_state')) {
|
||||
$aggregationtype = $aggregationfn();
|
||||
}
|
||||
// If the module aggregates using COMPLETION_STANDARD_FLOW, it requires ALL conditions to be met.
|
||||
// If the aggregation type is COMPLETION_CUSTOM_MODULE_FLOW, completion can be overridden by the plugin.
|
||||
if (!$response && $aggregationtype == COMPLETION_STANDARD_FLOW) {
|
||||
if (!$response) {
|
||||
return COMPLETION_INCOMPLETE;
|
||||
} else if ($aggregationtype == COMPLETION_CUSTOM_MODULE_FLOW) {
|
||||
return ($response ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
if ($completionstate) {
|
||||
// We have allowed the plugins to do it's thing and run their own checks.
|
||||
// We have now reached a state where we need to AND all the calculated results.
|
||||
// Preference for COMPLETION_COMPLETE_PASS over COMPLETION_COMPLETE for proper indication in reports.
|
||||
$newstate = array_reduce($completionstate, function($carry, $value) {
|
||||
if ($carry == COMPLETION_INCOMPLETE) {
|
||||
return $carry;
|
||||
if (in_array(COMPLETION_INCOMPLETE, [$carry, $value])) {
|
||||
return COMPLETION_INCOMPLETE;
|
||||
} else if (in_array(COMPLETION_COMPLETE_FAIL, [$carry, $value])) {
|
||||
return COMPLETION_COMPLETE_FAIL;
|
||||
} else {
|
||||
return $value;
|
||||
return in_array(COMPLETION_COMPLETE_PASS, [$carry, $value]) ? COMPLETION_COMPLETE_PASS : $value;
|
||||
}
|
||||
|
||||
}, COMPLETION_COMPLETE);
|
||||
|
||||
@@ -375,6 +375,101 @@ class core_completionlib_testcase extends advanced_testcase {
|
||||
$this->assertEquals($expectedstate, $completioninfo->internal_get_state($cm, $userid, $current));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for the test_internal_get_state_with_grade_criteria.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function test_internal_get_state_with_grade_criteria_provider() {
|
||||
return [
|
||||
"Passing grade enabled and achieve. State should be COMPLETION_COMPLETE_PASS" => [
|
||||
[
|
||||
'completionusegrade' => 1,
|
||||
'completionpassgrade' => 1,
|
||||
'gradepass' => 50,
|
||||
],
|
||||
50,
|
||||
COMPLETION_COMPLETE_PASS
|
||||
],
|
||||
"Passing grade enabled and not achieve. State should be COMPLETION_COMPLETE_FAIL" => [
|
||||
[
|
||||
'completionusegrade' => 1,
|
||||
'completionpassgrade' => 1,
|
||||
'gradepass' => 50,
|
||||
],
|
||||
40,
|
||||
COMPLETION_COMPLETE_FAIL
|
||||
],
|
||||
"Passing grade not enabled with passing grade set." => [
|
||||
[
|
||||
'completionusegrade' => 1,
|
||||
'gradepass' => 50,
|
||||
],
|
||||
50,
|
||||
COMPLETION_COMPLETE_PASS
|
||||
],
|
||||
"Passing grade not enabled with passing grade not set." => [
|
||||
[
|
||||
'completionusegrade' => 1,
|
||||
],
|
||||
90,
|
||||
COMPLETION_COMPLETE
|
||||
],
|
||||
"Passing grade not enabled with passing grade not set. No submission made." => [
|
||||
[
|
||||
'completionusegrade' => 1,
|
||||
],
|
||||
null,
|
||||
COMPLETION_INCOMPLETE
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the right completion state is being set based on the grade criteria.
|
||||
*
|
||||
* @dataProvider test_internal_get_state_with_grade_criteria_provider
|
||||
* @param array $completioncriteria The completion criteria to use
|
||||
* @param int|null $studentgrade Grade to assign to student
|
||||
* @param int $expectedstate Expected completion state
|
||||
*/
|
||||
public function test_internal_get_state_with_grade_criteria(array $completioncriteria, ?int $studentgrade, int $expectedstate) {
|
||||
$this->setup_data();
|
||||
|
||||
/** @var \mod_assign_generator $assigngenerator */
|
||||
$assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
|
||||
$assign = $assigngenerator->create_instance([
|
||||
'course' => $this->course->id,
|
||||
'completion' => COMPLETION_ENABLED,
|
||||
] + $completioncriteria);
|
||||
|
||||
$userid = $this->user->id;
|
||||
|
||||
$cm = get_coursemodule_from_instance('assign', $assign->id);
|
||||
$usercm = cm_info::create($cm, $userid);
|
||||
|
||||
// Create a teacher account.
|
||||
$teacher = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($teacher->id, $this->course->id, 'editingteacher');
|
||||
// Log in as the teacher.
|
||||
$this->setUser($teacher);
|
||||
|
||||
// Grade the student for this assignment.
|
||||
$assign = new assign($usercm->context, $cm, $cm->course);
|
||||
if ($studentgrade) {
|
||||
$data = (object)[
|
||||
'sendstudentnotifications' => false,
|
||||
'attemptnumber' => 1,
|
||||
'grade' => $studentgrade,
|
||||
];
|
||||
$assign->save_grade($userid, $data);
|
||||
}
|
||||
|
||||
// The target user already received a grade, so internal_get_state should be already complete.
|
||||
$completioninfo = new completion_info($this->course);
|
||||
$this->assertEquals($expectedstate, $completioninfo->internal_get_state($cm, $userid, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Covers the case where internal_get_state() is being called for a user different from the logged in user.
|
||||
*/
|
||||
|
||||
@@ -1908,15 +1908,6 @@ function quiz_get_navigation_options() {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the aggregation state for the module.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function quiz_get_completion_aggregation_state() {
|
||||
return COMPLETION_CUSTOM_MODULE_FLOW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the module has any update that affects the current user since a given time.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user