See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core_competency; 18 19 /** 20 * Competency ruleoutcome override grade tests 21 * 22 * @package core_competency 23 * @copyright 2022 Matthew Hilton <matthewhilton@catalyst-au.net> 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 class competency_override_test extends \advanced_testcase { 27 28 public function setUp(): void { 29 $this->resetAfterTest(true); 30 $this->setAdminUser(); 31 $dg = $this->getDataGenerator(); 32 $lpg = $dg->get_plugin_generator('core_competency'); 33 34 // Create user in course. 35 $c1 = $dg->create_course((object) ['enablecompletion' => true]); 36 $u1 = $dg->create_user(); 37 $dg->enrol_user($u1->id, $c1->id); 38 39 // Create framework with three values. 40 $scale = $dg->create_scale(["scale" => "not,partially,fully"]); 41 $scaleconfiguration = json_encode([ 42 ['scaleid' => $scale->id], 43 ['id' => 1, 'scaledefault' => 1, 'proficient' => 1] 44 ]); 45 $framework = $lpg->create_framework([ 46 'scaleid' => $scale->id, 47 'scaleconfiguration' => $scaleconfiguration 48 ]); 49 50 $plan = $lpg->create_plan(['userid' => $u1->id]); 51 52 $comp1 = $lpg->create_competency([ 53 'competencyframeworkid' => $framework->get('id'), 54 'scaleid' => $scale->id, 55 'scaleconfiguration' => $scaleconfiguration 56 ]); 57 58 $comp2 = $lpg->create_competency([ 59 'competencyframeworkid' => $framework->get('id'), 60 'scaleid' => $scale->id, 61 'scaleconfiguration' => $scaleconfiguration 62 ]); 63 64 api::add_competency_to_plan($plan->get('id'), $comp1->get('id')); 65 api::add_competency_to_plan($plan->get('id'), $comp2->get('id')); 66 67 $lpg->create_course_competency([ 68 'courseid' => $c1->id, 69 'competencyid' => $comp1->get('id'), 70 'ruleoutcome' => \core_competency\course_competency::OUTCOME_COMPLETE, 71 ]); 72 73 $lpg->create_course_competency([ 74 'courseid' => $c1->id, 75 'competencyid' => $comp2->get('id'), 76 'ruleoutcome' => \core_competency\course_competency::OUTCOME_COMPLETE, 77 ]); 78 79 $label = $dg->create_module('label', ['course' => $c1, 'completion' => COMPLETION_VIEWED, 'completionview' => 1]); 80 $cm = get_coursemodule_from_instance('label', $label->id); 81 $completion = new \completion_info($c1); 82 $this->assertEquals(COMPLETION_ENABLED, $completion->is_enabled($cm)); 83 84 // Link course module with the competency and setup a rule to complete the competency when the module is completed. 85 api::add_competency_to_course_module($cm, $comp1->get('id')); 86 api::add_competency_to_course_module($cm, $comp2->get('id')); 87 88 $coursemodulecomps = api::list_course_module_competencies_in_course_module($cm); 89 $this->assertCount(2, $coursemodulecomps); 90 api::set_course_module_competency_ruleoutcome($coursemodulecomps[0], \core_competency\course_competency::OUTCOME_COMPLETE); 91 api::set_course_module_competency_ruleoutcome($coursemodulecomps[1], \core_competency\course_competency::OUTCOME_COMPLETE); 92 93 $this->course = $c1; 94 $this->user = $u1; 95 $this->scale = $scale; 96 $this->framework = $framework; 97 $this->plan = $plan; 98 $this->comp1 = $comp1; 99 $this->comp2 = $comp2; 100 $this->cm = $cm; 101 $this->completion = new \completion_info($c1); 102 $this->context = \context_course::instance($this->course->id); 103 } 104 105 /** 106 * Test ruleoutcome overridegrade is correctly applied when coursemodule completion is processed. 107 * 108 * @covers \core_competency\api::set_course_module_competency_ruleoutcome 109 */ 110 public function test_ruleoutcome_overridegrade(): void { 111 // Initially the competency (and hence all the child competencies) should not be complete for the user. 112 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 113 $this->assertEquals(0, $plancomp->usercompetency->get('grade')); 114 $this->assertEquals(0, $usercomp->get('grade')); 115 $this->assertEquals(0, $coursecomp->get('grade')); 116 117 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 118 $this->assertEquals(0, $plancomp2->usercompetency->get('grade')); 119 $this->assertEquals(0, $usercomp2->get('grade')); 120 $this->assertEquals(0, $coursecomp2->get('grade')); 121 122 // Update the course module completion state to complete and trigger a competency update. 123 $data = $this->completion->get_data($this->cm, false, $this->user->id); 124 $data->completionstate = COMPLETION_COMPLETE; 125 $data->timemodified = time(); 126 $this->completion->internal_set_data($this->cm, $data); 127 128 // Comptency should now be complete for user, plan, and course now that the course module is completed. 129 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 130 $this->assertEquals(1, $plancomp->usercompetency->get('grade')); 131 $this->assertEquals(1, $usercomp->get('grade')); 132 $this->assertEquals(1, $coursecomp->get('grade')); 133 134 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 135 $this->assertEquals(1, $plancomp2->usercompetency->get('grade')); 136 $this->assertEquals(1, $usercomp2->get('grade')); 137 $this->assertEquals(1, $coursecomp2->get('grade')); 138 139 // Change the competency completion for the user by adding evidence. 140 api::add_evidence($this->user->id, $this->comp1, $this->context, 141 evidence::ACTION_OVERRIDE, 'commentincontext', 'core', null, false, null, 2); 142 api::add_evidence($this->user->id, $this->comp2, $this->context, 143 evidence::ACTION_OVERRIDE, 'commentincontext', 'core', null, false, null, 2); 144 145 // After adding evidence, the competencies should now reflect the new grade value. 146 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 147 $this->assertEquals(2, $plancomp->usercompetency->get('grade')); 148 $this->assertEquals(2, $usercomp->get('grade')); 149 $this->assertEquals(2, $coursecomp->get('grade')); 150 151 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 152 $this->assertEquals(2, $plancomp2->usercompetency->get('grade')); 153 $this->assertEquals(2, $usercomp2->get('grade')); 154 $this->assertEquals(2, $coursecomp2->get('grade')); 155 156 // Update the course module competency to incomplete. This will not change the competency status. 157 $data = $this->completion->get_data($this->cm, false, $this->user->id); 158 $data->completionstate = COMPLETION_INCOMPLETE; 159 $data->timemodified = time(); 160 $this->completion->internal_set_data($this->cm, $data); 161 162 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 163 $this->assertEquals(2, $plancomp->usercompetency->get('grade')); 164 $this->assertEquals(2, $usercomp->get('grade')); 165 $this->assertEquals(2, $coursecomp->get('grade')); 166 167 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 168 $this->assertEquals(2, $plancomp2->usercompetency->get('grade')); 169 $this->assertEquals(2, $usercomp2->get('grade')); 170 $this->assertEquals(2, $coursecomp2->get('grade')); 171 172 // Re-complete the course module, so that it attempts to re-complete the competencies. 173 $data = $this->completion->get_data($this->cm, false, $this->user->id); 174 $data->completionstate = COMPLETION_COMPLETE; 175 $data->timemodified = time(); 176 $this->completion->internal_set_data($this->cm, $data); 177 178 // By default, this will not override the existing grade, so it should remain the same as before. 179 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 180 $this->assertEquals(2, $plancomp->usercompetency->get('grade')); 181 $this->assertEquals(2, $usercomp->get('grade')); 182 $this->assertEquals(2, $coursecomp->get('grade')); 183 184 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 185 $this->assertEquals(2, $plancomp2->usercompetency->get('grade')); 186 $this->assertEquals(2, $usercomp2->get('grade')); 187 $this->assertEquals(2, $coursecomp2->get('grade')); 188 189 // Update the completion rule for only competency 1 to $overridegrade = true. 190 $coursemodulecomps = api::list_course_module_competencies_in_course_module($this->cm); 191 api::set_course_module_competency_ruleoutcome($coursemodulecomps[0], \core_competency\course_competency::OUTCOME_COMPLETE, 192 true); 193 194 // Mark as incomplete then re-complete the course module. 195 $data = $this->completion->get_data($this->cm, false, $this->user->id); 196 $data->completionstate = COMPLETION_INCOMPLETE; 197 $data->timemodified = time(); 198 $this->completion->internal_set_data($this->cm, $data); 199 200 $data = $this->completion->get_data($this->cm, false, $this->user->id); 201 $data->completionstate = COMPLETION_COMPLETE; 202 $data->timemodified = time(); 203 $this->completion->internal_set_data($this->cm, $data); 204 205 // Because the rule is now set to override existing grades, the grade should have now updated as per the ruleoutcome. 206 // However the second competency didn't have this rule set, so it will not be overriden. 207 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 208 $this->assertEquals(1, $plancomp->usercompetency->get('grade')); 209 $this->assertEquals(1, $usercomp->get('grade')); 210 $this->assertEquals(1, $coursecomp->get('grade')); 211 212 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 213 $this->assertEquals(2, $plancomp2->usercompetency->get('grade')); 214 $this->assertEquals(2, $usercomp2->get('grade')); 215 $this->assertEquals(2, $coursecomp2->get('grade')); 216 217 // If competency 2 is changed now to override and re-completed, it will update the same as competency 1. 218 api::set_course_module_competency_ruleoutcome($coursemodulecomps[1], \core_competency\course_competency::OUTCOME_COMPLETE, 219 true); 220 221 $data = $this->completion->get_data($this->cm, false, $this->user->id); 222 $data->completionstate = COMPLETION_INCOMPLETE; 223 $data->timemodified = time(); 224 $this->completion->internal_set_data($this->cm, $data); 225 226 $data = $this->completion->get_data($this->cm, false, $this->user->id); 227 $data->completionstate = COMPLETION_COMPLETE; 228 $data->timemodified = time(); 229 $this->completion->internal_set_data($this->cm, $data); 230 231 // Now both the competencies have $overridegrade = true, 232 // they should both reflect the ruleoutcome after the completion above was processed. 233 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 234 $this->assertEquals(1, $plancomp->usercompetency->get('grade')); 235 $this->assertEquals(1, $usercomp->get('grade')); 236 $this->assertEquals(1, $coursecomp->get('grade')); 237 238 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 239 $this->assertEquals(1, $plancomp2->usercompetency->get('grade')); 240 $this->assertEquals(1, $usercomp2->get('grade')); 241 $this->assertEquals(1, $coursecomp2->get('grade')); 242 } 243 244 /** 245 * Test competency backup and restore correctly restores the ruleoutcome overridegrade value. 246 * 247 * @covers \core_competency\api::set_course_module_competency_ruleoutcome 248 */ 249 public function test_override_backup_restore(): void { 250 global $CFG; 251 require_once($CFG->dirroot . '/course/externallib.php'); 252 253 // Set one to override grade and another to not override grade. 254 $coursemodulecomps = api::list_course_module_competencies_in_course_module($this->cm); 255 api::set_course_module_competency_ruleoutcome($coursemodulecomps[0], \core_competency\course_competency::OUTCOME_COMPLETE, 256 false); 257 api::set_course_module_competency_ruleoutcome($coursemodulecomps[1], \core_competency\course_competency::OUTCOME_COMPLETE, 258 true); 259 260 // Duplicate the course (backup and restore). 261 $duplicated = \core_course_external::duplicate_course($this->course->id, 'test', 'test', $this->course->category); 262 263 // Get the new course modules. 264 $newcoursemodules = get_coursemodules_in_course('label', $duplicated['id']); 265 $this->assertCount(1, $newcoursemodules); 266 $cm = array_pop($newcoursemodules); 267 268 // Get the comeptencies for this cm. 269 $newcoursemodulecomps = api::list_course_module_competencies_in_course_module($cm); 270 $this->assertCount(2, $newcoursemodulecomps); 271 272 // Ensure the override grade settings are restored properly. 273 $this->assertEquals($coursemodulecomps[0]->get('overridegrade'), $newcoursemodulecomps[0]->get('overridegrade')); 274 $this->assertEquals($coursemodulecomps[1]->get('overridegrade'), $newcoursemodulecomps[1]->get('overridegrade')); 275 } 276 277 /** 278 * Gets the course, user and plan competency for the given competency ID 279 * 280 * @param int $compid ID of the competency. 281 * @return array array containing the three related competencies 282 */ 283 private function get_related_competencies(int $compid): array { 284 $coursecomp = api::get_user_competency_in_course($this->course->id, $this->user->id, $compid); 285 $usercomp = api::get_user_competency($this->user->id, $compid); 286 $plancomp = api::get_plan_competency($this->plan, $compid); 287 return [$coursecomp, $plancomp, $usercomp]; 288 } 289 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body