Differences Between: [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 /** @var \stdClass course record. */ 29 protected $course; 30 31 /** @var \stdClass user record. */ 32 protected $user; 33 34 /** @var \stdClass block instance record. */ 35 protected $scale; 36 37 /** @var competency_framework loading competency frameworks from the DB. */ 38 protected $framework; 39 40 /** @var plan loading competency plans from the DB. */ 41 protected $plan; 42 43 /** @var competency loading competency from the DB. */ 44 protected $comp1; 45 46 /** @var competency loading competency from the DB. */ 47 protected $comp2; 48 49 /** @var \stdClass course module. */ 50 protected $cm; 51 52 /** @var \completion_info completion information. */ 53 protected $completion; 54 55 /** @var \context_course context course. */ 56 protected $context; 57 58 public function setUp(): void { 59 $this->resetAfterTest(true); 60 $this->setAdminUser(); 61 $dg = $this->getDataGenerator(); 62 $lpg = $dg->get_plugin_generator('core_competency'); 63 64 // Create user in course. 65 $c1 = $dg->create_course((object) ['enablecompletion' => true]); 66 $u1 = $dg->create_user(); 67 $dg->enrol_user($u1->id, $c1->id); 68 69 // Create framework with three values. 70 $scale = $dg->create_scale(["scale" => "not,partially,fully"]); 71 $scaleconfiguration = json_encode([ 72 ['scaleid' => $scale->id], 73 ['id' => 1, 'scaledefault' => 1, 'proficient' => 1] 74 ]); 75 $framework = $lpg->create_framework([ 76 'scaleid' => $scale->id, 77 'scaleconfiguration' => $scaleconfiguration 78 ]); 79 80 $plan = $lpg->create_plan(['userid' => $u1->id]); 81 82 $comp1 = $lpg->create_competency([ 83 'competencyframeworkid' => $framework->get('id'), 84 'scaleid' => $scale->id, 85 'scaleconfiguration' => $scaleconfiguration 86 ]); 87 88 $comp2 = $lpg->create_competency([ 89 'competencyframeworkid' => $framework->get('id'), 90 'scaleid' => $scale->id, 91 'scaleconfiguration' => $scaleconfiguration 92 ]); 93 94 api::add_competency_to_plan($plan->get('id'), $comp1->get('id')); 95 api::add_competency_to_plan($plan->get('id'), $comp2->get('id')); 96 97 $lpg->create_course_competency([ 98 'courseid' => $c1->id, 99 'competencyid' => $comp1->get('id'), 100 'ruleoutcome' => \core_competency\course_competency::OUTCOME_COMPLETE, 101 ]); 102 103 $lpg->create_course_competency([ 104 'courseid' => $c1->id, 105 'competencyid' => $comp2->get('id'), 106 'ruleoutcome' => \core_competency\course_competency::OUTCOME_COMPLETE, 107 ]); 108 109 $label = $dg->create_module('label', ['course' => $c1, 'completion' => COMPLETION_VIEWED, 'completionview' => 1]); 110 $cm = get_coursemodule_from_instance('label', $label->id); 111 $completion = new \completion_info($c1); 112 $this->assertEquals(COMPLETION_ENABLED, $completion->is_enabled($cm)); 113 114 // Link course module with the competency and setup a rule to complete the competency when the module is completed. 115 api::add_competency_to_course_module($cm, $comp1->get('id')); 116 api::add_competency_to_course_module($cm, $comp2->get('id')); 117 118 $coursemodulecomps = api::list_course_module_competencies_in_course_module($cm); 119 $this->assertCount(2, $coursemodulecomps); 120 api::set_course_module_competency_ruleoutcome($coursemodulecomps[0], \core_competency\course_competency::OUTCOME_COMPLETE); 121 api::set_course_module_competency_ruleoutcome($coursemodulecomps[1], \core_competency\course_competency::OUTCOME_COMPLETE); 122 123 $this->course = $c1; 124 $this->user = $u1; 125 $this->scale = $scale; 126 $this->framework = $framework; 127 $this->plan = $plan; 128 $this->comp1 = $comp1; 129 $this->comp2 = $comp2; 130 $this->cm = $cm; 131 $this->completion = new \completion_info($c1); 132 $this->context = \context_course::instance($this->course->id); 133 } 134 135 /** 136 * Test ruleoutcome overridegrade is correctly applied when coursemodule completion is processed. 137 * 138 * @covers \core_competency\api::set_course_module_competency_ruleoutcome 139 */ 140 public function test_ruleoutcome_overridegrade(): void { 141 // Initially the competency (and hence all the child competencies) should not be complete for the user. 142 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 143 $this->assertEquals(0, $plancomp->usercompetency->get('grade')); 144 $this->assertEquals(0, $usercomp->get('grade')); 145 $this->assertEquals(0, $coursecomp->get('grade')); 146 147 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 148 $this->assertEquals(0, $plancomp2->usercompetency->get('grade')); 149 $this->assertEquals(0, $usercomp2->get('grade')); 150 $this->assertEquals(0, $coursecomp2->get('grade')); 151 152 // Update the course module completion state to complete and trigger a competency update. 153 $data = $this->completion->get_data($this->cm, false, $this->user->id); 154 $data->completionstate = COMPLETION_COMPLETE; 155 $data->timemodified = time(); 156 $this->completion->internal_set_data($this->cm, $data); 157 158 // Comptency should now be complete for user, plan, and course now that the course module is completed. 159 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 160 $this->assertEquals(1, $plancomp->usercompetency->get('grade')); 161 $this->assertEquals(1, $usercomp->get('grade')); 162 $this->assertEquals(1, $coursecomp->get('grade')); 163 164 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 165 $this->assertEquals(1, $plancomp2->usercompetency->get('grade')); 166 $this->assertEquals(1, $usercomp2->get('grade')); 167 $this->assertEquals(1, $coursecomp2->get('grade')); 168 169 // Change the competency completion for the user by adding evidence. 170 api::add_evidence($this->user->id, $this->comp1, $this->context, 171 evidence::ACTION_OVERRIDE, 'commentincontext', 'core', null, false, null, 2); 172 api::add_evidence($this->user->id, $this->comp2, $this->context, 173 evidence::ACTION_OVERRIDE, 'commentincontext', 'core', null, false, null, 2); 174 175 // After adding evidence, the competencies should now reflect the new grade value. 176 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 177 $this->assertEquals(2, $plancomp->usercompetency->get('grade')); 178 $this->assertEquals(2, $usercomp->get('grade')); 179 $this->assertEquals(2, $coursecomp->get('grade')); 180 181 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 182 $this->assertEquals(2, $plancomp2->usercompetency->get('grade')); 183 $this->assertEquals(2, $usercomp2->get('grade')); 184 $this->assertEquals(2, $coursecomp2->get('grade')); 185 186 // Update the course module competency to incomplete. This will not change the competency status. 187 $data = $this->completion->get_data($this->cm, false, $this->user->id); 188 $data->completionstate = COMPLETION_INCOMPLETE; 189 $data->timemodified = time(); 190 $this->completion->internal_set_data($this->cm, $data); 191 192 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 193 $this->assertEquals(2, $plancomp->usercompetency->get('grade')); 194 $this->assertEquals(2, $usercomp->get('grade')); 195 $this->assertEquals(2, $coursecomp->get('grade')); 196 197 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 198 $this->assertEquals(2, $plancomp2->usercompetency->get('grade')); 199 $this->assertEquals(2, $usercomp2->get('grade')); 200 $this->assertEquals(2, $coursecomp2->get('grade')); 201 202 // Re-complete the course module, so that it attempts to re-complete the competencies. 203 $data = $this->completion->get_data($this->cm, false, $this->user->id); 204 $data->completionstate = COMPLETION_COMPLETE; 205 $data->timemodified = time(); 206 $this->completion->internal_set_data($this->cm, $data); 207 208 // By default, this will not override the existing grade, so it should remain the same as before. 209 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 210 $this->assertEquals(2, $plancomp->usercompetency->get('grade')); 211 $this->assertEquals(2, $usercomp->get('grade')); 212 $this->assertEquals(2, $coursecomp->get('grade')); 213 214 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 215 $this->assertEquals(2, $plancomp2->usercompetency->get('grade')); 216 $this->assertEquals(2, $usercomp2->get('grade')); 217 $this->assertEquals(2, $coursecomp2->get('grade')); 218 219 // Update the completion rule for only competency 1 to $overridegrade = true. 220 $coursemodulecomps = api::list_course_module_competencies_in_course_module($this->cm); 221 api::set_course_module_competency_ruleoutcome($coursemodulecomps[0], \core_competency\course_competency::OUTCOME_COMPLETE, 222 true); 223 224 // Mark as incomplete then re-complete the course module. 225 $data = $this->completion->get_data($this->cm, false, $this->user->id); 226 $data->completionstate = COMPLETION_INCOMPLETE; 227 $data->timemodified = time(); 228 $this->completion->internal_set_data($this->cm, $data); 229 230 $data = $this->completion->get_data($this->cm, false, $this->user->id); 231 $data->completionstate = COMPLETION_COMPLETE; 232 $data->timemodified = time(); 233 $this->completion->internal_set_data($this->cm, $data); 234 235 // Because the rule is now set to override existing grades, the grade should have now updated as per the ruleoutcome. 236 // However the second competency didn't have this rule set, so it will not be overriden. 237 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 238 $this->assertEquals(1, $plancomp->usercompetency->get('grade')); 239 $this->assertEquals(1, $usercomp->get('grade')); 240 $this->assertEquals(1, $coursecomp->get('grade')); 241 242 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 243 $this->assertEquals(2, $plancomp2->usercompetency->get('grade')); 244 $this->assertEquals(2, $usercomp2->get('grade')); 245 $this->assertEquals(2, $coursecomp2->get('grade')); 246 247 // If competency 2 is changed now to override and re-completed, it will update the same as competency 1. 248 api::set_course_module_competency_ruleoutcome($coursemodulecomps[1], \core_competency\course_competency::OUTCOME_COMPLETE, 249 true); 250 251 $data = $this->completion->get_data($this->cm, false, $this->user->id); 252 $data->completionstate = COMPLETION_INCOMPLETE; 253 $data->timemodified = time(); 254 $this->completion->internal_set_data($this->cm, $data); 255 256 $data = $this->completion->get_data($this->cm, false, $this->user->id); 257 $data->completionstate = COMPLETION_COMPLETE; 258 $data->timemodified = time(); 259 $this->completion->internal_set_data($this->cm, $data); 260 261 // Now both the competencies have $overridegrade = true, 262 // they should both reflect the ruleoutcome after the completion above was processed. 263 [$coursecomp, $plancomp, $usercomp] = $this->get_related_competencies($this->comp1->get('id')); 264 $this->assertEquals(1, $plancomp->usercompetency->get('grade')); 265 $this->assertEquals(1, $usercomp->get('grade')); 266 $this->assertEquals(1, $coursecomp->get('grade')); 267 268 [$coursecomp2, $plancomp2, $usercomp2] = $this->get_related_competencies($this->comp2->get('id')); 269 $this->assertEquals(1, $plancomp2->usercompetency->get('grade')); 270 $this->assertEquals(1, $usercomp2->get('grade')); 271 $this->assertEquals(1, $coursecomp2->get('grade')); 272 } 273 274 /** 275 * Test competency backup and restore correctly restores the ruleoutcome overridegrade value. 276 * 277 * @covers \core_competency\api::set_course_module_competency_ruleoutcome 278 */ 279 public function test_override_backup_restore(): void { 280 global $CFG; 281 require_once($CFG->dirroot . '/course/externallib.php'); 282 283 // Set one to override grade and another to not override grade. 284 $coursemodulecomps = api::list_course_module_competencies_in_course_module($this->cm); 285 api::set_course_module_competency_ruleoutcome($coursemodulecomps[0], \core_competency\course_competency::OUTCOME_COMPLETE, 286 false); 287 api::set_course_module_competency_ruleoutcome($coursemodulecomps[1], \core_competency\course_competency::OUTCOME_COMPLETE, 288 true); 289 290 // Duplicate the course (backup and restore). 291 $duplicated = \core_course_external::duplicate_course($this->course->id, 'test', 'test', $this->course->category); 292 293 // Get the new course modules. 294 $newcoursemodules = get_coursemodules_in_course('label', $duplicated['id']); 295 $this->assertCount(1, $newcoursemodules); 296 $cm = array_pop($newcoursemodules); 297 298 // Get the comeptencies for this cm. 299 $newcoursemodulecomps = api::list_course_module_competencies_in_course_module($cm); 300 $this->assertCount(2, $newcoursemodulecomps); 301 302 // Ensure the override grade settings are restored properly. 303 $this->assertEquals($coursemodulecomps[0]->get('overridegrade'), $newcoursemodulecomps[0]->get('overridegrade')); 304 $this->assertEquals($coursemodulecomps[1]->get('overridegrade'), $newcoursemodulecomps[1]->get('overridegrade')); 305 } 306 307 /** 308 * Gets the course, user and plan competency for the given competency ID 309 * 310 * @param int $compid ID of the competency. 311 * @return array array containing the three related competencies 312 */ 313 private function get_related_competencies(int $compid): array { 314 $coursecomp = api::get_user_competency_in_course($this->course->id, $this->user->id, $compid); 315 $usercomp = api::get_user_competency($this->user->id, $compid); 316 $plancomp = api::get_plan_competency($this->plan, $compid); 317 return [$coursecomp, $plancomp, $usercomp]; 318 } 319 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body