MDL-52206 core: Add completion based on pass grade

Add new completion based on pass grade to the completion criteria.
This commit is contained in:
Peter 2019-11-29 13:26:40 +08:00 committed by Peter Dias
parent 1a9bee69e6
commit 663137748e
11 changed files with 126 additions and 13 deletions

View File

@ -2806,12 +2806,13 @@ class backup_completion_defaults_structure_step extends backup_structure_step {
$cc = new backup_nested_element('course_completion_defaults');
$defaults = new backup_nested_element('course_completion_default', array('id'), array(
'modulename', 'completion', 'completionview', 'completionusegrade', 'completionexpected', 'customrules'
'modulename', 'completion', 'completionview', 'completionusegrade', 'completionpassgrade',
'completionexpected', 'customrules'
));
// Use module name instead of module id so we can insert into another site later.
$sourcesql = "SELECT d.id, m.name as modulename, d.completion, d.completionview, d.completionusegrade,
d.completionexpected, d.customrules
d.completionpassgrade, d.completionexpected, d.customrules
FROM {course_completion_defaults} d join {modules} m on d.module = m.id
WHERE d.course = ?";
$defaults->set_source_sql($sourcesql, array(backup::VAR_COURSEID));

View File

@ -137,7 +137,8 @@ class core_completion_bulkedit_form extends core_completion_edit_base_form {
// Completion: Don't let them choose automatic completion without turning
// on some conditions.
if (array_key_exists('completion', $data) &&
$data['completion'] == COMPLETION_TRACKING_AUTOMATIC && !empty($data['completionusegrade'])) {
$data['completion'] == COMPLETION_TRACKING_AUTOMATIC &&
(!empty($data['completionusegrade']) || !empty($data['completionpassgrade']))) {
require_once($CFG->libdir.'/gradelib.php');
$moduleswithoutgradeitem = [];
foreach ($this->cms as $cm) {

View File

@ -199,10 +199,19 @@ abstract class core_completion_edit_base_form extends moodleform {
// Automatic completion once it's graded.
if ($this->support_grades()) {
$mform->addElement('advcheckbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
$group = [];
$group[] = $mform->createElement('advcheckbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
get_string('completionusegrade_desc', 'completion'));
$mform->disabledIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
$mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion');
$mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion', '', true);
$group[] = $mform->createElement('advcheckbox', 'completionpassgrade', get_string('completionpassgrade', 'completion'),
get_string('completionpassgrade_desc', 'completion'));
$mform->addHelpButton('completionpassgrade', 'completionpassgrade', 'completion', '', true);
$mform->disabledIf('completionpassgrade', 'completionusegrade', 'notchecked');
$mform->addGroup($group, 'completionpassgroup', get_string('completionpassgrade', 'completion'), '   ', false);
$mform->disabledIf('completionpassgroup', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
$autocompletionpossible = true;
}
@ -247,7 +256,7 @@ abstract class core_completion_edit_base_form extends moodleform {
// on some conditions.
if (array_key_exists('completion', $data) &&
$data['completion'] == COMPLETION_TRACKING_AUTOMATIC) {
if (empty($data['completionview']) && empty($data['completionusegrade']) &&
if (empty($data['completionview']) && empty($data['completionusegrade']) && empty($data['completionpassgrade']) &&
!$this->completion_rule_enabled($data)) {
$errors['completion'] = get_string('badautocompletion', 'completion');
}

View File

@ -176,7 +176,13 @@ class manager {
}
if ($moduledata instanceof cm_info && !is_null($moduledata->completiongradeitemnumber) ||
($moduledata instanceof stdClass && !empty($moduledata->completionusegrade))) {
$activeruledescriptions[] = get_string('completionusegrade_desc', 'completion');
$description = 'completionusegrade_desc';
if (!empty($moduledata->completionpassgrade)) {
$description = 'completionpassgrade_desc';
}
$activeruledescriptions[] = get_string($description, 'completion');
}
// Now, ask the module to provide descriptions for its custom conditional completion rules.
@ -351,8 +357,11 @@ class manager {
protected function apply_completion_cm(\cm_info $cm, $data, $updateinstance) {
global $DB;
$defaults = ['completion' => COMPLETION_DISABLED, 'completionview' => COMPLETION_VIEW_NOT_REQUIRED,
'completionexpected' => 0, 'completiongradeitemnumber' => null];
$defaults = [
'completion' => COMPLETION_DISABLED, 'completionview' => COMPLETION_VIEW_NOT_REQUIRED,
'completionexpected' => 0, 'completiongradeitemnumber' => null,
'completionpassgrade' => 0
];
$data += ['completion' => $cm->completion,
'completionexpected' => $cm->completionexpected,

View File

@ -240,6 +240,10 @@ class completion_criteria_activity extends completion_criteria {
if (!is_null($cm->completiongradeitemnumber)) {
$details['requirement'][] = get_string('achievinggrade', 'completion');
}
if ($cm->completionpassgrade) {
$details['requirement'][] = get_string('achievingpassinggrade', 'completion');
}
}
$details['requirement'] = implode(', ', $details['requirement']);

View File

@ -370,6 +370,9 @@ abstract class moodleform_mod extends moodleform {
if ($mform->elementExists('completionusegrade')) {
$mform->freeze('completionusegrade');
}
if ($mform->elementExists('completionpassgrade')) {
$mform->freeze('completionpassgrade');
}
if ($mform->elementExists('completiongradeitemnumber')) {
$mform->freeze('completiongradeitemnumber');
}
@ -468,6 +471,28 @@ abstract class moodleform_mod extends moodleform {
);
}
}
if (isset($data['completionpassgrade']) && $data['completionpassgrade']) {
// We need to check whether there's a valid gradepass value.
// This can either be in completiongradeitemnumber when there are multiple options OR,
// The first grade item if completionusegrade is specified.
$validategradepass = false;
if (isset($data['completiongradeitemnumber'])) {
if ($data['completiongradeitemnumber'] == (string)$itemnumber) {
$validategradepass = true;
}
} else if (isset($data['completionusegrade']) && $data['completionusegrade']) {
$validategradepass = true;
}
// Confirm gradepass is a valid non-zero value.
if ($validategradepass && (!isset($data[$gradepassfieldname]) || grade_floatval($data[$gradepassfieldname]) == 0)) {
$errors['completionpassgrade'] = get_string(
'activitygradetopassnotset',
'completion'
);
}
}
}
// Completion: Don't let them choose automatic completion without turning
@ -482,7 +507,7 @@ abstract class moodleform_mod extends moodleform {
$rulesenabled = !empty($data['completionview']);
// Use grade to complete (only one grade item).
$rulesenabled = $rulesenabled || !empty($data['completionusegrade']);
$rulesenabled = $rulesenabled || !empty($data['completionusegrade']) || !empty($data['completionpassgrade']);
// Use grade to complete (specific grade item).
if (!$rulesenabled && isset($data['completiongradeitemnumber'])) {
@ -724,16 +749,28 @@ abstract class moodleform_mod extends moodleform {
$mform->hideIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
$mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion');
// Complete if the user has reached the pass grade.
$mform->addElement(
'checkbox',
'completionpassgrade', null,
get_string('completionpassgrade_desc', 'completion')
);
$mform->hideIf('completionpassgrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
$mform->disabledIf('completionpassgrade', 'completionusegrade', 'notchecked');
$mform->addHelpButton('completionpassgrade', 'completionpassgrade', 'completion');
// The disabledIf logic differs between ratings and other grade items due to different field types.
if ($this->_features->rating) {
// If using the rating system, there is no grade unless ratings are enabled.
$mform->disabledIf('completionusegrade', 'assessed', 'eq', 0);
$mform->disabledIf('completionpassgrade', 'assessed', 'eq', 0);
} else {
// All other field types use the '$gradefieldname' field's modgrade_type.
$itemnumbers = array_keys($itemnames);
$itemnumber = array_shift($itemnumbers);
$gradefieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'grade');
$mform->disabledIf('completionusegrade', "{$gradefieldname}[modgrade_type]", 'eq', 'none');
$mform->disabledIf('completionpassgrade', "{$gradefieldname}[modgrade_type]", 'eq', 'none');
}
} else if (count($itemnames) > 1) {
// There are multiple grade items in this activity.
@ -752,6 +789,16 @@ abstract class moodleform_mod extends moodleform {
$options
);
$mform->hideIf('completiongradeitemnumber', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
// Complete if the user has reached the pass grade.
$mform->addElement(
'checkbox',
'completionpassgrade', null,
get_string('completionpassgrade_desc', 'completion')
);
$mform->hideIf('completionpassgrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
$mform->disabledIf('completionpassgrade', 'completiongradeitemnumber', 'eq', '');
$mform->addHelpButton('completionpassgrade', 'completionpassgrade', 'completion');
}
}

View File

@ -25,6 +25,7 @@
*/
$string['achievinggrade'] = 'Achieving grade';
$string['achievingpassinggrade'] = 'Achieving passing grade';
$string['activities'] = 'Activities';
$string['activitieslabel'] = 'Activities / resources';
$string['activityaggregation'] = 'Condition requires';
@ -35,6 +36,7 @@ $string['activitiescompletednote'] = 'Note: Activity completion must be set for
$string['activitycompletion'] = 'Activity completion';
$string['activitycompletionupdated'] = 'Changes saved';
$string['activitygradenotrequired'] = 'Grade not required';
$string['activitygradetopassnotset'] = 'This activity does not have a valid grade to pass set. It may be set in the Grade section of the activity settings.';
$string['affectedactivities'] = 'The changes will affect the following <b>{$a}</b> activities or resources:';
$string['aggregationmethod'] = 'Aggregation method';
$string['all'] = 'All';
@ -106,6 +108,9 @@ $string['completionondate'] = 'Date';
$string['completionondatevalue'] = 'Date when course will be marked as complete';
$string['completionduration'] = 'Enrolment';
$string['completionsettingslocked'] = 'Completion settings locked';
$string['completionpassgrade'] = 'Require passing grade';
$string['completionpassgrade_desc'] = 'Student must receive a passing grade to complete this activity';
$string['completionpassgrade_help'] = 'If enabled, the activity is considered complete when a student receives a passing grade.';
$string['completionusegrade'] = 'Require grade';
$string['completionusegrade_desc'] = 'Student must receive a grade to complete this activity';
$string['completionusegrade_help'] = 'If enabled, the activity is considered complete when a student receives a grade. If a pass grade for the activity is set, then pass and fail icons are displayed in the activity completion report.';
@ -141,6 +146,7 @@ $string['deletecompletiondata'] = 'Delete completion data';
$string['dependencies'] = 'Dependencies';
$string['dependenciescompleted'] = 'Completion of other courses';
$string['detail_desc:receivegrade'] = 'Receive a grade';
$string['detail_desc:receivepassgrade'] = 'Receive a passing grade';
$string['detail_desc:view'] = 'View';
$string['hiddenrules'] = 'Some settings specific to <b>{$a}</b> have been hidden. To view unselect other activities';
$string['editcoursecompletionsettings'] = 'Edit course completion settings';

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20210728" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20211001" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@ -304,6 +304,7 @@
<FIELD NAME="completiongradeitemnumber" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Grade-item number used to track automatic completion, if applicable."/>
<FIELD NAME="completionview" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Controls whether a page view is part of the automatic completion requirements for this activity. 0 = view not required 1 = view required"/>
<FIELD NAME="completionexpected" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Date at which students are expected to complete this activity. This field is used when displaying student progress."/>
<FIELD NAME="completionpassgrade" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Enable completion check on passing grade."/>
<FIELD NAME="showdescription" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Some module types support a 'description' which shows within the module pages. This option controls whether it also displays on the course main page. 0 = does not display (default), 1 = displays"/>
<FIELD NAME="availability" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Availability restrictions for viewing this activity, in JSON format. Null if no restrictions."/>
<FIELD NAME="deletioninprogress" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
@ -3866,6 +3867,7 @@
<FIELD NAME="completion" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completionview" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completionusegrade" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completionpassgrade" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completionexpected" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="customrules" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>

View File

@ -2862,5 +2862,21 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2021092400.03);
}
if ($oldversion < 2021100300.01) {
$table = new xmldb_table('course_completion_defaults');
// Adding fields to table course_completion_defaults.
$field = new xmldb_field('completionpassgrade', XMLDB_TYPE_INTEGER, '1', null,
XMLDB_NOTNULL, null, '0', 'completionusegrade');
// Conditionally launch add field for course_completion_defaults.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2021100300.01);
}
return true;
}

View File

@ -32,6 +32,7 @@ require_once($CFG->dirroot . '/course/moodleform_mod.php');
require_once(__DIR__ . '/locallib.php');
require_once($CFG->libdir . '/filelib.php');
use core_grades\component_gradeitems;
/**
* Module settings form for Workshop instances
*/
@ -462,6 +463,23 @@ class mod_workshop_mod_form extends moodleform_mod {
}
}
// We need to do a custom completion validation because workshop grade items identifiers divert from standard.
// Refer to validation defined in moodleform_mod.php.
if (isset($data['completionpassgrade']) && $data['completionpassgrade'] &&
isset($data['completiongradeitemnumber'])) {
$itemnames = component_gradeitems::get_itemname_mapping_for_component('mod_workshop');
$gradepassfield = $itemnames[(int) $data['completiongradeitemnumber']] . 'gradepass';
if (!isset($data[$gradepassfield]) || grade_floatval($data[$gradepassfield]) == 0) {
$errors['completionpassgrade'] = get_string(
'activitygradetopassnotset',
'completion'
);
} else {
// We have validated grade pass. Unset any errors.
unset($errors['completionpassgrade']);
}
}
if (!$data['submissiontypetextavailable'] && !$data['submissiontypefileavailable']) {
// One submission type must be available.
$errors['submissiontypes'] = get_string('nosubmissiontype', 'workshop');

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2021100300.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2021100300.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.0dev (Build: 20211003)'; // Human-friendly version name