Skip to content

Commit

Permalink
MDL-56567 competency: Course module competency option to override grade
Browse files Browse the repository at this point in the history
Previously, if a course module had already been graded, course module
completion linked to the course module would not update. This
commit adds the option to enable overriding the grade at the course
module competency level.

This ensures that if enabled and a user completes a module associated
with a competency, that competency will be graded appropriately.
  • Loading branch information
matthewhilton committed Oct 16, 2022
1 parent 3bc792b commit cfb6432
Show file tree
Hide file tree
Showing 11 changed files with 418 additions and 17 deletions.
@@ -0,0 +1,69 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.


/**
* Course competency override grade element.
*
* @package tool_lp
* @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

defined('MOODLE_INTERNAL') || die();

global $CFG;

require_once($CFG->libdir . '/form/advcheckbox.php');

/**
* Course competency override grade element.
*
* @package tool_lp
* @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_lp_course_competency_overridegrade_form_element extends MoodleQuickForm_advcheckbox {

/**
* Constructor
*
* @param string $elementname Element name
* @param mixed $elementlabel Label(s) for an element
* @param array $options Options to control the element's display
*/
public function __construct($elementname=null, $elementlabel=null, $options=[]) {
if ($elementname == null) {
// This is broken quickforms messing with the constructors.
return;
}

if (!empty($options['cmid'])) {
$cmid = $options['cmid'];

$current = \core_competency\api::list_course_module_competencies_in_course_module($cmid);

// Note: We just pick the override grade value set on the first course_module_competency.
// Because in the UI we force them to be the same for all.
if (!empty($current)) {
$one = array_pop($current);
$this->setValue($one->get('overridegrade'));
}
}

parent::__construct($elementname, $elementlabel);
}
}
1 change: 1 addition & 0 deletions admin/tool/lp/lang/en/tool_lp.php
Expand Up @@ -165,6 +165,7 @@
$string['nouserplans'] = 'No learning plans have been created yet.';
$string['oneplanwascreated'] = 'A learning plan was created';
$string['outcome'] = 'Outcome';
$string['overridegrade'] = 'Override existing competency grade when completed.';
$string['path'] = 'Path:';
$string['parentcompetency'] = 'Parent';
$string['parentcompetency_edit'] = 'Edit parent';
Expand Down
11 changes: 10 additions & 1 deletion admin/tool/lp/lib.php
Expand Up @@ -161,6 +161,12 @@ function tool_lp_coursemodule_standard_elements($formwrapper, $mform) {
'tool_lp_course_competency_rule_form_element');
// Reuse the same options.
$mform->addElement('course_competency_rule', 'competency_rule', get_string('uponcoursemodulecompletion', 'tool_lp'), $options);

$overrideelementfile = "$CFG->dirroot/$CFG->admin/tool/lp/classes/course_competency_overridegrade_form_element.php";
MoodleQuickForm::registerElementType('course_competency_overridegrade', $overrideelementfile,
'tool_lp_course_competency_overridegrade_form_element');
$mform->addElement('course_competency_overridegrade', 'override_grade', get_string('overridegrade', 'tool_lp'), $options);
$mform->hideIf('override_grade', 'competency_rule', 'noteq', \core_competency\competency::OUTCOME_COMPLETE + 1);
}

/**
Expand Down Expand Up @@ -201,10 +207,13 @@ function tool_lp_coursemodule_edit_post_actions($data, $course) {
}

if (isset($data->competency_rule)) {
$overridegrade = isset($data->override_grade) ? $data->override_grade : false;

// Now update the rules for each course_module_competency.
$current = \core_competency\api::list_course_module_competencies_in_course_module($data->coursemodule);
foreach ($current as $coursemodulecompetency) {
\core_competency\api::set_course_module_competency_ruleoutcome($coursemodulecompetency, $data->competency_rule);
\core_competency\api::set_course_module_competency_ruleoutcome($coursemodulecompetency, $data->competency_rule,
$overridegrade);
}
}

Expand Down
4 changes: 2 additions & 2 deletions backup/moodle2/backup_stepslib.php
Expand Up @@ -1843,10 +1843,10 @@ protected function define_structure() {
$wrapper->add_child($competencies);

$competency = new backup_nested_element('competency', null, array('idnumber', 'ruleoutcome',
'sortorder', 'frameworkidnumber'));
'sortorder', 'frameworkidnumber', 'overridegrade'));
$competencies->add_child($competency);

$sql = 'SELECT c.idnumber, cmc.ruleoutcome, cmc.sortorder, f.idnumber AS frameworkidnumber
$sql = 'SELECT c.idnumber, cmc.ruleoutcome, cmc.overridegrade, cmc.sortorder, f.idnumber AS frameworkidnumber
FROM {' . \core_competency\course_module_competency::TABLE . '} cmc
JOIN {' . \core_competency\competency::TABLE . '} c ON c.id = cmc.competencyid
JOIN {' . \core_competency\competency_framework::TABLE . '} f ON f.id = c.competencyframeworkid
Expand Down
1 change: 1 addition & 0 deletions backup/moodle2/restore_stepslib.php
Expand Up @@ -3738,6 +3738,7 @@ public function process_course_module_competency($data) {
// Sortorder is ignored by precaution, anyway we should walk through the records in the right order.
$record = (object) $params;
$record->ruleoutcome = $data->ruleoutcome;
$record->overridegrade = $data->overridegrade;
$coursemodulecompetency = new \core_competency\course_module_competency(0, $record);
$coursemodulecompetency->create();
}
Expand Down
39 changes: 27 additions & 12 deletions competency/classes/api.php
Expand Up @@ -1582,9 +1582,11 @@ public static function reorder_course_module_competency($cmorid, $competencyidfr
*
* @param int|course_module_competency $coursemodulecompetencyorid The course_module_competency, or its ID.
* @param int $ruleoutcome The value of ruleoutcome.
* @param bool $overridegrade If true, will override existing grades in related competencies.
* @return bool True on success.
*/
public static function set_course_module_competency_ruleoutcome($coursemodulecompetencyorid, $ruleoutcome) {
public static function set_course_module_competency_ruleoutcome($coursemodulecompetencyorid, $ruleoutcome,
$overridegrade = false) {
static::require_enabled();
$coursemodulecompetency = $coursemodulecompetencyorid;
if (!is_object($coursemodulecompetency)) {
Expand All @@ -1599,6 +1601,8 @@ public static function set_course_module_competency_ruleoutcome($coursemodulecom
require_capability('moodle/competency:coursecompetencymanage', $context);

$coursemodulecompetency->set('ruleoutcome', $ruleoutcome);
$coursemodulecompetency->set('overridegrade', $overridegrade);

return $coursemodulecompetency->update();
}

Expand Down Expand Up @@ -4279,7 +4283,7 @@ public static function list_evidence_in_course($userid = 0, $courseid = 0, $comp
*/
public static function add_evidence($userid, $competencyorid, $contextorid, $action, $descidentifier, $desccomponent,
$desca = null, $recommend = false, $url = null, $grade = null, $actionuserid = null,
$note = null) {
$note = null, $overridegrade = false) {
global $DB;
static::require_enabled();

Expand Down Expand Up @@ -4350,8 +4354,8 @@ public static function add_evidence($userid, $competencyorid, $contextorid, $act
$usercompetencycourse = user_competency_course::create_relation($userid, $competencyid, $courseid);
$usercompetencycourse->create();
}
// Only update the grade and proficiency if there is not already a grade.
if ($usercompetencycourse->get('grade') === null) {
// Only update the grade and proficiency if there is not already a grade or the override option is enabled.
if ($usercompetencycourse->get('grade') === null || $overridegrade) {
// Set grade.
$usercompetencycourse->set('grade', $grade);
// Set proficiency.
Expand All @@ -4363,8 +4367,8 @@ public static function add_evidence($userid, $competencyorid, $contextorid, $act
$setucgrade = $coursesettings->get('pushratingstouserplans');

if ($setucgrade) {
// Only push to user plans if there is not already a grade.
if ($usercompetency->get('grade') !== null) {
// Only push to user plans if there is not already a grade or the override option is enabled.
if ($usercompetency->get('grade') !== null && !$overridegrade) {
$setucgrade = false;
} else {
$ucgrade = $grade;
Expand All @@ -4374,8 +4378,9 @@ public static function add_evidence($userid, $competencyorid, $contextorid, $act
} else {

// When completing the competency we fetch the default grade from the competency. But we only mark
// the user competency when a grade has not been set yet. Complete is an action to use with automated systems.
if ($usercompetency->get('grade') === null) {
// the user competency when a grade has not been set yet or if override option is enabled.
// Complete is an action to use with automated systems.
if ($usercompetency->get('grade') === null || $overridegrade) {
$setucgrade = true;
$ucgrade = $grade;
$ucproficiency = $proficiency;
Expand Down Expand Up @@ -4498,7 +4503,7 @@ public static function add_evidence($userid, $competencyorid, $contextorid, $act

// The competency was marked as completed, apply the rules.
if ($wascompleted) {
self::apply_competency_rules_from_usercompetency($usercompetency, $competency);
self::apply_competency_rules_from_usercompetency($usercompetency, $competency, $overridegrade);
}

return $evidence;
Expand Down Expand Up @@ -4557,7 +4562,7 @@ public static function delete_evidence($evidenceorid) {
* @return void
*/
protected static function apply_competency_rules_from_usercompetency(user_competency $usercompetency,
competency $competency = null) {
competency $competency = null, $overridegrade = false) {

// Perform some basic checks.
if (!$usercompetency->get('proficiency')) {
Expand Down Expand Up @@ -4624,7 +4629,12 @@ protected static function apply_competency_rules_from_usercompetency(user_compet
'evidence_competencyrule',
'core_competency',
null,
$recommend
$recommend,
null,
null,
null,
null,
$overridegrade
);
}

Expand Down Expand Up @@ -4659,6 +4669,7 @@ public static function observe_course_module_completion_updated(\core\event\cour
$action = null;
$recommend = false;
$strdesc = 'evidence_coursemodulecompleted';
$overridegrade = $coursemodulecompetency->get('overridegrade');

if ($outcome == course_module_competency::OUTCOME_NONE) {
continue;
Expand Down Expand Up @@ -4686,7 +4697,11 @@ public static function observe_course_module_completion_updated(\core\event\cour
'core_competency',
$cmname,
$recommend,
$url
$url,
null,
null,
null,
$overridegrade
);
}
}
Expand Down
4 changes: 4 additions & 0 deletions competency/classes/course_module_competency.php
Expand Up @@ -71,6 +71,10 @@ protected static function define_properties() {
'default' => self::OUTCOME_EVIDENCE,
'type' => PARAM_INT,
),
'overridegrade' => array(
'default' => false,
'type' => PARAM_BOOL
),
);
}

Expand Down

0 comments on commit cfb6432

Please sign in to comment.