weekly release 4.5dev+
[moodle.git] / competency / tests / api_test.php
blobabab660795a39022f0141fc3d481d05cbd2d7b7c
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.
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/>.
17 namespace core_competency;
19 /**
20 * API tests.
22 * @package core_competency
23 * @copyright 2015 Frédéric Massart - FMCorz.net
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 class api_test extends \advanced_testcase {
28 public function test_get_framework_related_contexts(): void {
29 $this->resetAfterTest(true);
30 $dg = $this->getDataGenerator();
31 $cat1 = $dg->create_category();
32 $cat2 = $dg->create_category(array('parent' => $cat1->id));
33 $cat3 = $dg->create_category(array('parent' => $cat2->id));
34 $c1 = $dg->create_course(array('category' => $cat2->id)); // This context should not be returned.
36 $cat1ctx = \context_coursecat::instance($cat1->id);
37 $cat2ctx = \context_coursecat::instance($cat2->id);
38 $cat3ctx = \context_coursecat::instance($cat3->id);
39 $sysctx = \context_system::instance();
41 $expected = array($cat1ctx->id => $cat1ctx);
42 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'self'));
44 $expected = array($cat1ctx->id => $cat1ctx, $cat2ctx->id => $cat2ctx, $cat3ctx->id => $cat3ctx);
45 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'children'));
47 $expected = array($sysctx->id => $sysctx, $cat1ctx->id => $cat1ctx, $cat2ctx->id => $cat2ctx);
48 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'parents'));
51 public function test_get_framework_related_contexts_with_capabilities(): void {
52 $this->resetAfterTest(true);
53 $dg = $this->getDataGenerator();
54 $user = $dg->create_user();
55 $cat1 = $dg->create_category();
56 $cat2 = $dg->create_category(array('parent' => $cat1->id));
57 $cat3 = $dg->create_category(array('parent' => $cat2->id));
58 $c1 = $dg->create_course(array('category' => $cat2->id)); // This context should not be returned.
60 $cat1ctx = \context_coursecat::instance($cat1->id);
61 $cat2ctx = \context_coursecat::instance($cat2->id);
62 $cat3ctx = \context_coursecat::instance($cat3->id);
63 $sysctx = \context_system::instance();
65 $roleallow = create_role('Allow', 'allow', 'Allow read');
66 assign_capability('moodle/competency:competencyview', CAP_ALLOW, $roleallow, $sysctx->id);
67 role_assign($roleallow, $user->id, $sysctx->id);
69 $roleprevent = create_role('Prevent', 'prevent', 'Prevent read');
70 assign_capability('moodle/competency:competencyview', CAP_PROHIBIT, $roleprevent, $sysctx->id);
71 role_assign($roleprevent, $user->id, $cat2ctx->id);
73 accesslib_clear_all_caches_for_unit_testing();
74 $this->setUser($user);
75 $this->assertFalse(has_capability('moodle/competency:competencyview', $cat2ctx));
77 $requiredcap = array('moodle/competency:competencyview');
79 $expected = array();
80 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'self', $requiredcap));
82 $expected = array($cat1ctx->id => $cat1ctx);
83 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'children', $requiredcap));
85 $expected = array($sysctx->id => $sysctx, $cat1ctx->id => $cat1ctx);
86 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'parents', $requiredcap));
89 public function test_get_template_related_contexts(): void {
90 $this->resetAfterTest(true);
91 $dg = $this->getDataGenerator();
92 $cat1 = $dg->create_category();
93 $cat2 = $dg->create_category(array('parent' => $cat1->id));
94 $cat3 = $dg->create_category(array('parent' => $cat2->id));
95 $c1 = $dg->create_course(array('category' => $cat2->id)); // This context should not be returned.
97 $cat1ctx = \context_coursecat::instance($cat1->id);
98 $cat2ctx = \context_coursecat::instance($cat2->id);
99 $cat3ctx = \context_coursecat::instance($cat3->id);
100 $sysctx = \context_system::instance();
102 $expected = array($cat1ctx->id => $cat1ctx);
103 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'self'));
105 $expected = array($cat1ctx->id => $cat1ctx, $cat2ctx->id => $cat2ctx, $cat3ctx->id => $cat3ctx);
106 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'children'));
108 $expected = array($sysctx->id => $sysctx, $cat1ctx->id => $cat1ctx, $cat2ctx->id => $cat2ctx);
109 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'parents'));
112 public function test_get_template_related_contexts_with_capabilities(): void {
113 $this->resetAfterTest(true);
114 $dg = $this->getDataGenerator();
115 $user = $dg->create_user();
116 $cat1 = $dg->create_category();
117 $cat2 = $dg->create_category(array('parent' => $cat1->id));
118 $cat3 = $dg->create_category(array('parent' => $cat2->id));
119 $c1 = $dg->create_course(array('category' => $cat2->id)); // This context should not be returned.
121 $cat1ctx = \context_coursecat::instance($cat1->id);
122 $cat2ctx = \context_coursecat::instance($cat2->id);
123 $cat3ctx = \context_coursecat::instance($cat3->id);
124 $sysctx = \context_system::instance();
126 $roleallow = create_role('Allow', 'allow', 'Allow read');
127 assign_capability('moodle/competency:templateview', CAP_ALLOW, $roleallow, $sysctx->id);
128 role_assign($roleallow, $user->id, $sysctx->id);
130 $roleprevent = create_role('Prevent', 'prevent', 'Prevent read');
131 assign_capability('moodle/competency:templateview', CAP_PROHIBIT, $roleprevent, $sysctx->id);
132 role_assign($roleprevent, $user->id, $cat2ctx->id);
134 accesslib_clear_all_caches_for_unit_testing();
135 $this->setUser($user);
136 $this->assertFalse(has_capability('moodle/competency:templateview', $cat2ctx));
138 $requiredcap = array('moodle/competency:templateview');
140 $expected = array();
141 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'self', $requiredcap));
143 $expected = array($cat1ctx->id => $cat1ctx);
144 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'children', $requiredcap));
146 $expected = array($sysctx->id => $sysctx, $cat1ctx->id => $cat1ctx);
147 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'parents', $requiredcap));
151 * Test updating a template.
153 public function test_update_template(): void {
154 $cat = $this->getDataGenerator()->create_category();
155 $this->resetAfterTest(true);
156 $this->setAdminUser();
158 $syscontext = \context_system::instance();
159 $template = api::create_template((object) array('shortname' => 'testing', 'contextid' => $syscontext->id));
161 $this->assertEquals('testing', $template->get('shortname'));
162 $this->assertEquals($syscontext->id, $template->get('contextid'));
164 // Simple update.
165 api::update_template((object) array('id' => $template->get('id'), 'shortname' => 'success'));
166 $template = api::read_template($template->get('id'));
167 $this->assertEquals('success', $template->get('shortname'));
169 // Trying to change the context.
170 $this->expectException(\coding_exception::class);
171 api::update_template((object) ['id' => $template->get('id'), 'contextid' => \context_coursecat::instance($cat->id)->id]);
175 * Test listing framework with order param.
177 public function test_list_frameworks(): void {
178 $this->resetAfterTest(true);
179 $this->setAdminUser();
180 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
182 // Create a list of frameworks.
183 $framework1 = $lpg->create_framework(array(
184 'shortname' => 'shortname_alpha',
185 'idnumber' => 'idnumber_cinnamon',
186 'description' => 'description',
187 'descriptionformat' => FORMAT_HTML,
188 'visible' => true,
189 'contextid' => \context_system::instance()->id
192 $framework2 = $lpg->create_framework(array(
193 'shortname' => 'shortname_beetroot',
194 'idnumber' => 'idnumber_apple',
195 'description' => 'description',
196 'descriptionformat' => FORMAT_HTML,
197 'visible' => true,
198 'contextid' => \context_system::instance()->id
201 $framework3 = $lpg->create_framework(array(
202 'shortname' => 'shortname_crisps',
203 'idnumber' => 'idnumber_beer',
204 'description' => 'description',
205 'descriptionformat' => FORMAT_HTML,
206 'visible' => false,
207 'contextid' => \context_system::instance()->id
210 // Get frameworks list order by shortname desc.
211 $result = api::list_frameworks('shortname', 'DESC', null, 3, \context_system::instance());
213 $f = (object) array_shift($result);
214 $this->assertEquals($framework3->get('id'), $f->get('id'));
215 $f = (object) array_shift($result);
216 $this->assertEquals($framework2->get('id'), $f->get('id'));
217 $f = (object) array_shift($result);
218 $this->assertEquals($framework1->get('id'), $f->get('id'));
220 // Get frameworks list order by idnumber asc.
221 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance());
223 $f = (object) array_shift($result);
224 $this->assertEquals($framework2->get('id'), $f->get('id'));
225 $f = (object) array_shift($result);
226 $this->assertEquals($framework3->get('id'), $f->get('id'));
227 $f = (object) array_shift($result);
228 $this->assertEquals($framework1->get('id'), $f->get('id'));
230 // Repeat excluding the non-visible ones.
231 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', true);
232 $this->assertCount(2, $result);
233 $f = (object) array_shift($result);
234 $this->assertEquals($framework2->get('id'), $f->get('id'));
235 $f = (object) array_shift($result);
236 $this->assertEquals($framework1->get('id'), $f->get('id'));
238 // Search by query string, trying match on shortname.
239 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', false, 'crisp');
240 $this->assertCount(1, $result);
241 $f = (object) array_shift($result);
242 $this->assertEquals($framework3->get('id'), $f->get('id'));
244 // Search by query string, trying match on shortname, but hidden.
245 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', true, 'crisp');
246 $this->assertCount(0, $result);
248 // Search by query string, trying match on ID number.
249 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', false, 'apple');
250 $this->assertCount(1, $result);
251 $f = (object) array_shift($result);
252 $this->assertEquals($framework2->get('id'), $f->get('id'));
254 // Search by query string, trying match on both.
255 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', false, 'bee');
256 $this->assertCount(2, $result);
257 $f = (object) array_shift($result);
258 $this->assertEquals($framework2->get('id'), $f->get('id'));
259 $f = (object) array_shift($result);
260 $this->assertEquals($framework3->get('id'), $f->get('id'));
264 * Test duplicate a framework.
266 public function test_duplicate_framework(): void {
267 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
268 $this->resetAfterTest(true);
269 $this->setAdminUser();
271 $syscontext = \context_system::instance();
272 $params = array(
273 'shortname' => 'shortname_a',
274 'idnumber' => 'idnumber_c',
275 'description' => 'description',
276 'descriptionformat' => FORMAT_HTML,
277 'visible' => true,
278 'contextid' => $syscontext->id
280 $framework = $lpg->create_framework($params);
281 $competency1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
282 $competency2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
283 $competency3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
284 $competency4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
285 $competency41 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'),
286 'parentid' => $competency4->get('id'))
288 $competency42 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'),
289 'parentid' => $competency4->get('id'))
291 $competencyidnumbers = array($competency1->get('idnumber'),
292 $competency2->get('idnumber'),
293 $competency3->get('idnumber'),
294 $competency4->get('idnumber'),
295 $competency41->get('idnumber'),
296 $competency42->get('idnumber')
299 $config = json_encode(array(
300 'base' => array('points' => 4),
301 'competencies' => array(
302 array('id' => $competency41->get('id'), 'points' => 3, 'required' => 0),
303 array('id' => $competency42->get('id'), 'points' => 2, 'required' => 1),
306 $competency4->set('ruletype', 'core_competency\competency_rule_points');
307 $competency4->set('ruleoutcome', \core_competency\competency::OUTCOME_EVIDENCE);
308 $competency4->set('ruleconfig', $config);
309 $competency4->update();
311 api::add_related_competency($competency1->get('id'), $competency2->get('id'));
312 api::add_related_competency($competency3->get('id'), $competency4->get('id'));
314 $frameworkduplicated1 = api::duplicate_framework($framework->get('id'));
315 $frameworkduplicated2 = api::duplicate_framework($framework->get('id'));
317 $this->assertEquals($framework->get('idnumber').'_1', $frameworkduplicated1->get('idnumber'));
318 $this->assertEquals($framework->get('idnumber').'_2', $frameworkduplicated2->get('idnumber'));
320 $competenciesfr1 = api::list_competencies(array('competencyframeworkid' => $frameworkduplicated1->get('id')));
321 $competenciesfr2 = api::list_competencies(array('competencyframeworkid' => $frameworkduplicated2->get('id')));
323 $competencyidsfr1 = array();
324 $competencyidsfr2 = array();
326 foreach ($competenciesfr1 as $cmp) {
327 $competencyidsfr1[] = $cmp->get('idnumber');
329 foreach ($competenciesfr2 as $cmp) {
330 $competencyidsfr2[] = $cmp->get('idnumber');
333 $this->assertEmpty(array_diff($competencyidsfr1, $competencyidnumbers));
334 $this->assertEmpty(array_diff($competencyidsfr2, $competencyidnumbers));
335 $this->assertCount(6, $competenciesfr1);
336 $this->assertCount(6, $competenciesfr2);
338 // Test the related competencies.
339 reset($competenciesfr1);
340 $compduplicated1 = current($competenciesfr1);
341 $relatedcompetencies = $compduplicated1->get_related_competencies();
342 $comprelated = current($relatedcompetencies);
343 $this->assertEquals($comprelated->get('idnumber'), $competency2->get('idnumber'));
345 // Check if config rule have been ported correctly.
346 $competency4duplicated = competency::get_record(array(
347 'idnumber' => $competency4->get('idnumber'),
348 'competencyframeworkid' => $frameworkduplicated2->get('id')
350 $configduplicated = json_decode($competency4duplicated->get('ruleconfig'), true);
351 $configorigin = json_decode($config, true);
352 // Check that the 2 config have the same base.
353 $this->assertEquals($configorigin['base'], $configduplicated['base']);
354 $this->assertEquals(count($configorigin['competencies']), count($configduplicated['competencies']));
355 $competencyidsrules = array();
356 foreach ($configduplicated['competencies'] as $key => $value) {
357 // Check that the only difference between the 2 config is id competency.
358 $this->assertEquals(1, count(array_diff($value, $configorigin['competencies'][$key])));
359 $competencyidsrules[] = $value['id'];
361 $this->assertTrue($competency4duplicated->is_parent_of($competencyidsrules));
363 // Test duplicate an empty framework.
364 $emptyfrm = $lpg->create_framework();
365 $emptyfrmduplicated = api::duplicate_framework($emptyfrm->get('id'));
366 $this->assertEquals($emptyfrm->get('idnumber').'_1', $emptyfrmduplicated->get('idnumber'));
367 $nbcomp = api::count_competencies(array('competencyframeworkid' => $emptyfrmduplicated->get('id')));
368 $this->assertEquals(0, $nbcomp);
373 * Test update plan.
375 public function test_update_plan(): void {
376 $this->resetAfterTest(true);
377 $dg = $this->getDataGenerator();
378 $usermanageowndraft = $dg->create_user();
379 $usermanageown = $dg->create_user();
380 $usermanagedraft = $dg->create_user();
381 $usermanage = $dg->create_user();
383 $syscontext = \context_system::instance();
385 // Creating specific roles.
386 $manageowndraftrole = $dg->create_role(array(
387 'name' => 'User manage own draft',
388 'shortname' => 'manage-own-draft'
390 $manageownrole = $dg->create_role(array(
391 'name' => 'User manage own',
392 'shortname' => 'manage-own'
394 $managedraftrole = $dg->create_role(array(
395 'name' => 'User manage draft',
396 'shortname' => 'manage-draft'
398 $managerole = $dg->create_role(array(
399 'name' => 'User manage',
400 'shortname' => 'manage'
403 assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $manageowndraftrole, $syscontext->id);
404 assign_capability('moodle/competency:planviewowndraft', CAP_ALLOW, $manageowndraftrole, $syscontext->id);
406 assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $manageownrole, $syscontext->id);
407 assign_capability('moodle/competency:planviewown', CAP_ALLOW, $manageownrole, $syscontext->id);
409 assign_capability('moodle/competency:planmanagedraft', CAP_ALLOW, $managedraftrole, $syscontext->id);
410 assign_capability('moodle/competency:planviewdraft', CAP_ALLOW, $managedraftrole, $syscontext->id);
412 assign_capability('moodle/competency:planmanage', CAP_ALLOW, $managerole, $syscontext->id);
413 assign_capability('moodle/competency:planview', CAP_ALLOW, $managerole, $syscontext->id);
415 $dg->role_assign($manageowndraftrole, $usermanageowndraft->id, $syscontext->id);
416 $dg->role_assign($manageownrole, $usermanageown->id, $syscontext->id);
417 $dg->role_assign($managedraftrole, $usermanagedraft->id, $syscontext->id);
418 $dg->role_assign($managerole, $usermanage->id, $syscontext->id);
420 // Create first learning plan with user create draft.
421 $this->setUser($usermanageowndraft);
422 $plan = array (
423 'name' => 'plan own draft',
424 'description' => 'plan own draft',
425 'userid' => $usermanageowndraft->id
427 $plan = api::create_plan((object)$plan);
428 $record = $plan->to_record();
429 $record->name = 'plan own draft modified';
431 // Check if user create draft can edit the plan name.
432 $plan = api::update_plan($record);
433 $this->assertInstanceOf('\core_competency\plan', $plan);
435 // The status cannot be changed in this method.
436 $record->status = \core_competency\plan::STATUS_ACTIVE;
437 try {
438 $plan = api::update_plan($record);
439 $this->fail('Updating the status is not allowed.');
440 } catch (\coding_exception $e) {
441 $this->assertMatchesRegularExpression('/To change the status of a plan use the appropriate methods./',
442 $e->getMessage());
445 // Test when user with manage own plan capability try to edit other user plan.
446 $record->status = \core_competency\plan::STATUS_DRAFT;
447 $record->name = 'plan create draft modified 2';
448 $this->setUser($usermanageown);
449 try {
450 $plan = api::update_plan($record);
451 $this->fail('User with manage own plan capability can only edit his own plan.');
452 } catch (\required_capability_exception $e) {
453 $this->assertTrue(true);
456 // User with manage plan capability cannot edit the other user plans with status draft.
457 $this->setUser($usermanage);
458 $record->name = 'plan create draft modified 3';
459 try {
460 $plan = api::update_plan($record);
461 $this->fail('User with manage plan capability cannot edit the other user plans with status draft');
462 } catch (\required_capability_exception $e) {
463 $this->assertTrue(true);
466 // User with manage draft capability can edit other user's learning plan if the status is draft.
467 $this->setUser($usermanagedraft);
468 $record->status = \core_competency\plan::STATUS_DRAFT;
469 $record->name = 'plan manage draft modified 3';
470 $plan = api::update_plan($record);
471 $this->assertInstanceOf('\core_competency\plan', $plan);
473 // User with manage plan capability can create/edit learning plan if status is active/complete.
474 $this->setUser($usermanage);
475 $plan = array (
476 'name' => 'plan create',
477 'description' => 'plan create',
478 'userid' => $usermanage->id,
479 'status' => \core_competency\plan::STATUS_ACTIVE
481 $plan = api::create_plan((object)$plan);
483 // Silently transition to complete status to avoid errors about transitioning to complete.
484 $plan->set('status', \core_competency\plan::STATUS_COMPLETE);
485 $plan->update();
487 $record = $plan->to_record();
488 $record->name = 'plan create own modified';
489 try {
490 api::update_plan($record);
491 $this->fail('Completed plan can not be edited');
492 } catch (\coding_exception $e) {
493 $this->assertTrue(true);
497 public function test_create_plan_from_template(): void {
498 $this->resetAfterTest(true);
499 $this->setAdminUser();
501 $u1 = $this->getDataGenerator()->create_user();
502 $tpl = $this->getDataGenerator()->get_plugin_generator('core_competency')->create_template();
504 // Creating a new plan.
505 $plan = api::create_plan_from_template($tpl, $u1->id);
506 $record = $plan->to_record();
507 $this->assertInstanceOf('\core_competency\plan', $plan);
508 $this->assertTrue(\core_competency\plan::record_exists($plan->get('id')));
509 $this->assertEquals($tpl->get('id'), $plan->get('templateid'));
510 $this->assertEquals($u1->id, $plan->get('userid'));
511 $this->assertTrue($plan->is_based_on_template());
513 // Creating a plan that already exists.
514 $plan = api::create_plan_from_template($tpl, $u1->id);
515 $this->assertFalse($plan);
517 // Check that api::create_plan cannot be used.
518 unset($record->id);
519 $this->expectException(\coding_exception::class);
520 $plan = api::create_plan($record);
523 public function test_update_plan_based_on_template(): void {
524 $this->resetAfterTest(true);
525 $dg = $this->getDataGenerator();
526 $lpg = $dg->get_plugin_generator('core_competency');
527 $u1 = $dg->create_user();
528 $u2 = $dg->create_user();
530 $this->setAdminUser();
531 $tpl1 = $lpg->create_template();
532 $tpl2 = $lpg->create_template();
533 $up1 = $lpg->create_plan(array('userid' => $u1->id, 'templateid' => $tpl1->get('id')));
534 $up2 = $lpg->create_plan(array('userid' => $u2->id, 'templateid' => null));
536 try {
537 // Trying to remove the template dependency.
538 $record = $up1->to_record();
539 $record->templateid = null;
540 api::update_plan($record);
541 $this->fail('A plan cannot be unlinked using api::update_plan()');
542 } catch (\coding_exception $e) {
543 // All good.
546 try {
547 // Trying to switch to another template.
548 $record = $up1->to_record();
549 $record->templateid = $tpl2->get('id');
550 api::update_plan($record);
551 $this->fail('A plan cannot be moved to another template.');
552 } catch (\coding_exception $e) {
553 // All good.
556 try {
557 // Trying to switch to using a template.
558 $record = $up2->to_record();
559 $record->templateid = $tpl1->get('id');
560 api::update_plan($record);
561 $this->fail('A plan cannot be update to use a template.');
562 } catch (\coding_exception $e) {
563 // All good.
567 public function test_unlink_plan_from_template(): void {
568 $this->resetAfterTest(true);
569 $dg = $this->getDataGenerator();
570 $lpg = $dg->get_plugin_generator('core_competency');
571 $u1 = $dg->create_user();
572 $u2 = $dg->create_user();
574 $this->setAdminUser();
575 $f1 = $lpg->create_framework();
576 $f2 = $lpg->create_framework();
577 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
578 $c2a = $lpg->create_competency(array('competencyframeworkid' => $f2->get('id')));
579 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
581 $tpl1 = $lpg->create_template();
582 $tpl2 = $lpg->create_template();
584 $tplc1a = $lpg->create_template_competency(array('templateid' => $tpl1->get('id'), 'competencyid' => $c1a->get('id'),
585 'sortorder' => 9));
586 $tplc1b = $lpg->create_template_competency(array('templateid' => $tpl1->get('id'), 'competencyid' => $c1b->get('id'),
587 'sortorder' => 8));
588 $tplc2a = $lpg->create_template_competency(array('templateid' => $tpl2->get('id'), 'competencyid' => $c2a->get('id')));
590 $plan1 = $lpg->create_plan(array('userid' => $u1->id, 'templateid' => $tpl1->get('id'), 'status' => plan::STATUS_ACTIVE));
591 $plan2 = $lpg->create_plan(array('userid' => $u2->id, 'templateid' => $tpl2->get('id')));
592 $plan3 = $lpg->create_plan(array('userid' => $u1->id, 'templateid' => $tpl1->get('id'), 'status' => plan::STATUS_COMPLETE));
594 // Check that we have what we expect at this stage.
595 $this->assertEquals(2, \core_competency\template_competency::count_records(array('templateid' => $tpl1->get('id'))));
596 $this->assertEquals(1, \core_competency\template_competency::count_records(array('templateid' => $tpl2->get('id'))));
597 $this->assertEquals(0, \core_competency\plan_competency::count_records(array('planid' => $plan1->get('id'))));
598 $this->assertEquals(0, \core_competency\plan_competency::count_records(array('planid' => $plan2->get('id'))));
599 $this->assertTrue($plan1->is_based_on_template());
600 $this->assertTrue($plan2->is_based_on_template());
602 // Let's do this!
603 $tpl1comps = \core_competency\template_competency::list_competencies($tpl1->get('id'), true);
604 $tpl2comps = \core_competency\template_competency::list_competencies($tpl2->get('id'), true);
606 api::unlink_plan_from_template($plan1);
608 $plan1->read();
609 $plan2->read();
610 $this->assertCount(2, $tpl1comps);
611 $this->assertCount(1, $tpl2comps);
612 $this->assertEquals(2, \core_competency\template_competency::count_records(array('templateid' => $tpl1->get('id'))));
613 $this->assertEquals(1, \core_competency\template_competency::count_records(array('templateid' => $tpl2->get('id'))));
614 $this->assertEquals(2, \core_competency\plan_competency::count_records(array('planid' => $plan1->get('id'))));
615 $this->assertEquals(0, \core_competency\plan_competency::count_records(array('planid' => $plan2->get('id'))));
616 $this->assertFalse($plan1->is_based_on_template());
617 $this->assertEquals($tpl1->get('id'), $plan1->get('origtemplateid'));
618 $this->assertTrue($plan2->is_based_on_template());
619 $this->assertEquals(null, $plan2->get('origtemplateid'));
621 // Check we can unlink draft plan.
622 try {
623 api::unlink_plan_from_template($plan2);
624 } catch (\coding_exception $e) {
625 $this->fail('Fail to unlink draft plan.');
628 // Check we can not unlink completed plan.
629 try {
630 api::unlink_plan_from_template($plan3);
631 $this->fail('We can not unlink completed plan.');
632 } catch (\coding_exception $e) {
633 // All good.
636 // Even the order remains.
637 $plan1comps = \core_competency\plan_competency::list_competencies($plan1->get('id'));
638 $before = reset($tpl1comps);
639 $after = reset($plan1comps);
640 $this->assertEquals($before->get('id'), $after->get('id'));
641 $this->assertEquals($before->get('sortorder'), $after->get('sortorder'));
642 $before = next($tpl1comps);
643 $after = next($plan1comps);
644 $this->assertEquals($before->get('id'), $after->get('id'));
645 $this->assertEquals($before->get('sortorder'), $after->get('sortorder'));
648 public function test_update_template_updates_plans(): void {
649 $this->resetAfterTest(true);
650 $this->setAdminUser();
652 $dg = $this->getDataGenerator();
653 $u1 = $dg->create_user();
654 $u2 = $dg->create_user();
655 $lpg = $dg->get_plugin_generator('core_competency');
656 $tpl1 = $lpg->create_template();
657 $tpl2 = $lpg->create_template();
659 // Create plans with data not matching templates.
660 $time = time();
661 $plan1 = $lpg->create_plan(array('templateid' => $tpl1->get('id'), 'userid' => $u1->id,
662 'name' => 'Not good name', 'duedate' => $time + 3600, 'description' => 'Ahah', 'descriptionformat' => FORMAT_MARKDOWN));
663 $plan2 = $lpg->create_plan(array('templateid' => $tpl1->get('id'), 'userid' => $u2->id,
664 'name' => 'Not right name', 'duedate' => $time + 3601, 'description' => 'Ahah', 'descriptionformat' => FORMAT_PLAIN));
665 $plan3 = $lpg->create_plan(array('templateid' => $tpl2->get('id'), 'userid' => $u1->id,
666 'name' => 'Not sweet name', 'duedate' => $time + 3602, 'description' => 'Ahah', 'descriptionformat' => FORMAT_PLAIN));
668 // Prepare our expectations.
669 $plan1->read();
670 $plan2->read();
671 $plan3->read();
673 $this->assertEquals($tpl1->get('id'), $plan1->get('templateid'));
674 $this->assertEquals($tpl1->get('id'), $plan2->get('templateid'));
675 $this->assertEquals($tpl2->get('id'), $plan3->get('templateid'));
676 $this->assertNotEquals($tpl1->get('shortname'), $plan1->get('name'));
677 $this->assertNotEquals($tpl1->get('shortname'), $plan2->get('name'));
678 $this->assertNotEquals($tpl2->get('shortname'), $plan3->get('name'));
679 $this->assertNotEquals($tpl1->get('description'), $plan1->get('description'));
680 $this->assertNotEquals($tpl1->get('description'), $plan2->get('description'));
681 $this->assertNotEquals($tpl2->get('description'), $plan3->get('description'));
682 $this->assertNotEquals($tpl1->get('descriptionformat'), $plan1->get('descriptionformat'));
683 $this->assertNotEquals($tpl1->get('descriptionformat'), $plan2->get('descriptionformat'));
684 $this->assertNotEquals($tpl2->get('descriptionformat'), $plan3->get('descriptionformat'));
685 $this->assertNotEquals($tpl1->get('duedate'), $plan1->get('duedate'));
686 $this->assertNotEquals($tpl1->get('duedate'), $plan2->get('duedate'));
687 $this->assertNotEquals($tpl2->get('duedate'), $plan3->get('duedate'));
689 // Update the template without changing critical fields does not update the plans.
690 $data = $tpl1->to_record();
691 $data->visible = 0;
692 api::update_template($data);
693 $this->assertNotEquals($tpl1->get('shortname'), $plan1->get('name'));
694 $this->assertNotEquals($tpl1->get('shortname'), $plan2->get('name'));
695 $this->assertNotEquals($tpl2->get('shortname'), $plan3->get('name'));
696 $this->assertNotEquals($tpl1->get('description'), $plan1->get('description'));
697 $this->assertNotEquals($tpl1->get('description'), $plan2->get('description'));
698 $this->assertNotEquals($tpl2->get('description'), $plan3->get('description'));
699 $this->assertNotEquals($tpl1->get('descriptionformat'), $plan1->get('descriptionformat'));
700 $this->assertNotEquals($tpl1->get('descriptionformat'), $plan2->get('descriptionformat'));
701 $this->assertNotEquals($tpl2->get('descriptionformat'), $plan3->get('descriptionformat'));
702 $this->assertNotEquals($tpl1->get('duedate'), $plan1->get('duedate'));
703 $this->assertNotEquals($tpl1->get('duedate'), $plan2->get('duedate'));
704 $this->assertNotEquals($tpl2->get('duedate'), $plan3->get('duedate'));
706 // Now really update the template.
707 $data = $tpl1->to_record();
708 $data->shortname = 'Awesome!';
709 $data->description = 'This is too awesome!';
710 $data->descriptionformat = FORMAT_HTML;
711 $data->duedate = $time + 200;
712 api::update_template($data);
713 $tpl1->read();
715 // Now confirm that the right plans were updated.
716 $plan1->read();
717 $plan2->read();
718 $plan3->read();
720 $this->assertEquals($tpl1->get('id'), $plan1->get('templateid'));
721 $this->assertEquals($tpl1->get('id'), $plan2->get('templateid'));
722 $this->assertEquals($tpl2->get('id'), $plan3->get('templateid'));
724 $this->assertEquals($tpl1->get('shortname'), $plan1->get('name'));
725 $this->assertEquals($tpl1->get('shortname'), $plan2->get('name'));
726 $this->assertNotEquals($tpl2->get('shortname'), $plan3->get('name'));
727 $this->assertEquals($tpl1->get('description'), $plan1->get('description'));
728 $this->assertEquals($tpl1->get('description'), $plan2->get('description'));
729 $this->assertNotEquals($tpl2->get('description'), $plan3->get('description'));
730 $this->assertEquals($tpl1->get('descriptionformat'), $plan1->get('descriptionformat'));
731 $this->assertEquals($tpl1->get('descriptionformat'), $plan2->get('descriptionformat'));
732 $this->assertNotEquals($tpl2->get('descriptionformat'), $plan3->get('descriptionformat'));
733 $this->assertEquals($tpl1->get('duedate'), $plan1->get('duedate'));
734 $this->assertEquals($tpl1->get('duedate'), $plan2->get('duedate'));
735 $this->assertNotEquals($tpl2->get('duedate'), $plan3->get('duedate'));
739 * Test that the method to complete a plan.
741 public function test_complete_plan(): void {
742 global $DB;
744 $this->resetAfterTest(true);
745 $this->setAdminUser();
746 $dg = $this->getDataGenerator();
747 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
748 $user = $dg->create_user();
750 // Create a framework and assign competencies.
751 $framework = $lpg->create_framework();
752 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
753 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
754 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
755 $c4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
757 // Create two plans and assign competencies.
758 $plan = $lpg->create_plan(array('userid' => $user->id));
759 $otherplan = $lpg->create_plan(array('userid' => $user->id));
761 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c1->get('id')));
762 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c2->get('id')));
763 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c3->get('id')));
764 $lpg->create_plan_competency(array('planid' => $otherplan->get('id'), 'competencyid' => $c1->get('id')));
766 $uclist = array(
767 $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id'),
768 'proficiency' => true, 'grade' => 1 )),
769 $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c2->get('id'),
770 'proficiency' => false, 'grade' => 2 ))
773 $this->assertEquals(2, \core_competency\user_competency::count_records());
774 $this->assertEquals(0, \core_competency\user_competency_plan::count_records());
776 // Change status of the plan to complete.
777 api::complete_plan($plan);
779 // Check that user competencies are now in user_competency_plan objects and still in user_competency.
780 $this->assertEquals(2, \core_competency\user_competency::count_records());
781 $this->assertEquals(3, \core_competency\user_competency_plan::count_records());
783 $usercompetenciesplan = \core_competency\user_competency_plan::get_records();
785 $this->assertEquals($uclist[0]->get('userid'), $usercompetenciesplan[0]->get('userid'));
786 $this->assertEquals($uclist[0]->get('competencyid'), $usercompetenciesplan[0]->get('competencyid'));
787 $this->assertEquals($uclist[0]->get('proficiency'), (bool) $usercompetenciesplan[0]->get('proficiency'));
788 $this->assertEquals($uclist[0]->get('grade'), $usercompetenciesplan[0]->get('grade'));
789 $this->assertEquals($plan->get('id'), $usercompetenciesplan[0]->get('planid'));
791 $this->assertEquals($uclist[1]->get('userid'), $usercompetenciesplan[1]->get('userid'));
792 $this->assertEquals($uclist[1]->get('competencyid'), $usercompetenciesplan[1]->get('competencyid'));
793 $this->assertEquals($uclist[1]->get('proficiency'), (bool) $usercompetenciesplan[1]->get('proficiency'));
794 $this->assertEquals($uclist[1]->get('grade'), $usercompetenciesplan[1]->get('grade'));
795 $this->assertEquals($plan->get('id'), $usercompetenciesplan[1]->get('planid'));
797 $this->assertEquals($user->id, $usercompetenciesplan[2]->get('userid'));
798 $this->assertEquals($c3->get('id'), $usercompetenciesplan[2]->get('competencyid'));
799 $this->assertNull($usercompetenciesplan[2]->get('proficiency'));
800 $this->assertNull($usercompetenciesplan[2]->get('grade'));
801 $this->assertEquals($plan->get('id'), $usercompetenciesplan[2]->get('planid'));
803 // Check we can not add competency to completed plan.
804 try {
805 api::add_competency_to_plan($plan->get('id'), $c4->get('id'));
806 $this->fail('We can not add competency to completed plan.');
807 } catch (\coding_exception $e) {
808 // All good.
811 // Check we can not remove competency to completed plan.
812 try {
813 api::remove_competency_from_plan($plan->get('id'), $c3->get('id'));
814 $this->fail('We can not remove competency to completed plan.');
815 } catch (\coding_exception $e) {
816 // All good.
819 // Completing a plan that is completed throws an exception.
820 $this->expectException(\coding_exception::class);
821 api::complete_plan($plan);
825 * Set-up the workflow data (review, active, ...).
827 * @return array
829 protected function setup_workflow_data() {
830 $this->resetAfterTest();
832 $dg = $this->getDataGenerator();
833 $user = $dg->create_user();
834 $reviewer = $dg->create_user();
835 $otheruser = $dg->create_user();
837 $syscontext = \context_system::instance();
838 $userrole = $dg->create_role();
839 $reviewerrole = $dg->create_role();
840 $otheruserrole = $dg->create_role();
842 assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $userrole, $syscontext->id);
843 assign_capability('moodle/competency:planmanage', CAP_ALLOW, $reviewerrole, $syscontext->id);
844 assign_capability('moodle/competency:planviewdraft', CAP_ALLOW, $reviewerrole, $syscontext->id);
845 $dg->role_assign($userrole, $user->id, $syscontext->id);
846 $dg->role_assign($reviewerrole, $reviewer->id, $syscontext->id);
847 accesslib_clear_all_caches_for_unit_testing();
849 $lpg = $dg->get_plugin_generator('core_competency');
850 $tpl = $lpg->create_template();
851 $plan = $lpg->create_plan(array('userid' => $user->id));
852 $tplplan = $lpg->create_plan(array('userid' => $user->id, 'templateid' => $tpl->get('id')));
854 return array(
855 'dg' => $dg,
856 'lpg' => $lpg,
857 'user' => $user,
858 'reviewer' => $reviewer,
859 'otheruser' => $otheruser,
860 'plan' => $plan,
861 'tplplan' => $tplplan,
866 * Testing requesting the review of a plan.
868 public function test_plan_request_review(): void {
869 $data = $this->setup_workflow_data();
870 $dg = $data['dg'];
871 $lpg = $data['lpg'];
872 $user = $data['user'];
873 $reviewer = $data['reviewer'];
874 $otheruser = $data['otheruser'];
875 $plan = $data['plan'];
876 $tplplan = $data['tplplan'];
878 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status'));
879 $this->assertEquals(plan::STATUS_DRAFT, $tplplan->get('status'));
881 // Foreign user cannot do anything.
882 $this->setUser($otheruser);
883 try {
884 api::plan_request_review($plan);
885 $this->fail('The user can not read the plan.');
886 } catch (\required_capability_exception $e) {
887 $this->assertEquals('nopermissions', $e->errorcode);
890 // Can not change a plan based on a template.
891 $this->setUser($user);
892 try {
893 api::plan_request_review($tplplan);
894 $this->fail('The plan is based on a template.');
895 } catch (\coding_exception $e) {
896 $this->assertMatchesRegularExpression('/Template plans cannot be reviewed./', $e->getMessage());
899 // Can not send for review when not draft.
900 $this->setUser($user);
901 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
902 try {
903 api::plan_request_review($plan);
904 $this->fail('The plan cannot be sent for review at this stage.');
905 } catch (\coding_exception $e) {
906 $this->assertMatchesRegularExpression('/The plan cannot be sent for review at this stage./', $e->getMessage());
909 // Can not send for review when not draft.
910 $this->setUser($user);
911 $plan->set('status', plan::STATUS_ACTIVE);
912 try {
913 api::plan_request_review($plan);
914 $this->fail('The plan cannot be sent for review at this stage.');
915 } catch (\coding_exception $e) {
916 $this->assertMatchesRegularExpression('/The plan cannot be sent for review at this stage./', $e->getMessage());
919 // Can not send for review when not draft.
920 $this->setUser($user);
921 $plan->set('status', plan::STATUS_IN_REVIEW);
922 try {
923 api::plan_request_review($plan);
924 $this->fail('The plan cannot be sent for review at this stage.');
925 } catch (\coding_exception $e) {
926 $this->assertMatchesRegularExpression('/The plan cannot be sent for review at this stage./', $e->getMessage());
929 // Can not send for review when not draft.
930 $this->setUser($user);
931 $plan->set('status', plan::STATUS_COMPLETE);
932 try {
933 api::plan_request_review($plan);
934 $this->fail('The plan cannot be sent for review at this stage.');
935 } catch (\coding_exception $e) {
936 $this->assertMatchesRegularExpression('/The plan cannot be sent for review at this stage./', $e->getMessage());
939 // Sending for review as a reviewer.
940 $this->setUser($reviewer);
941 $plan->set('status', plan::STATUS_DRAFT);
942 try {
943 api::plan_request_review($plan);
944 $this->fail('The user can not request a review.');
945 } catch (\required_capability_exception $e) {
946 $this->assertEquals('nopermissions', $e->errorcode);
949 // Sending for review.
950 $this->setUser($user);
951 api::plan_request_review($plan);
952 $plan->read();
953 $this->assertEquals(plan::STATUS_WAITING_FOR_REVIEW, $plan->get('status'));
955 // Sending for review by ID.
956 $plan->set('status', plan::STATUS_DRAFT);
957 $plan->update();
958 api::plan_request_review($plan->get('id'));
959 $plan->read();
960 $this->assertEquals(plan::STATUS_WAITING_FOR_REVIEW, $plan->get('status'));
964 * Testing cancelling the review request.
966 public function test_plan_cancel_review_request(): void {
967 $data = $this->setup_workflow_data();
968 $dg = $data['dg'];
969 $lpg = $data['lpg'];
970 $user = $data['user'];
971 $reviewer = $data['reviewer'];
972 $otheruser = $data['otheruser'];
973 $plan = $data['plan'];
974 $tplplan = $data['tplplan'];
976 // Set waiting for review.
977 $tplplan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
978 $tplplan->update();
979 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
980 $plan->update();
982 // Foreign user cannot do anything.
983 $this->setUser($otheruser);
984 try {
985 api::plan_cancel_review_request($plan);
986 $this->fail('The user can not read the plan.');
987 } catch (\required_capability_exception $e) {
988 $this->assertEquals('nopermissions', $e->errorcode);
991 // Can not change a plan based on a template.
992 $this->setUser($user);
993 try {
994 api::plan_cancel_review_request($tplplan);
995 $this->fail('The plan is based on a template.');
996 } catch (\coding_exception $e) {
997 $this->assertMatchesRegularExpression('/Template plans cannot be reviewed./', $e->getMessage());
1000 // Can not cancel review request when not waiting for review.
1001 $this->setUser($user);
1002 $plan->set('status', plan::STATUS_DRAFT);
1003 try {
1004 api::plan_cancel_review_request($plan);
1005 $this->fail('The plan cannot be sent for review at this stage.');
1006 } catch (\coding_exception $e) {
1007 $this->assertMatchesRegularExpression('/The plan review cannot be cancelled at this stage./', $e->getMessage());
1010 // Can not cancel review request when not waiting for review.
1011 $this->setUser($user);
1012 $plan->set('status', plan::STATUS_IN_REVIEW);
1013 try {
1014 api::plan_cancel_review_request($plan);
1015 $this->fail('The plan review cannot be cancelled at this stage.');
1016 } catch (\coding_exception $e) {
1017 $this->assertMatchesRegularExpression('/The plan review cannot be cancelled at this stage./', $e->getMessage());
1020 // Can not cancel review request when not waiting for review.
1021 $this->setUser($user);
1022 $plan->set('status', plan::STATUS_ACTIVE);
1023 try {
1024 api::plan_cancel_review_request($plan);
1025 $this->fail('The plan review cannot be cancelled at this stage.');
1026 } catch (\coding_exception $e) {
1027 $this->assertMatchesRegularExpression('/The plan review cannot be cancelled at this stage./', $e->getMessage());
1030 // Can not cancel review request when not waiting for review.
1031 $this->setUser($user);
1032 $plan->set('status', plan::STATUS_COMPLETE);
1033 try {
1034 api::plan_cancel_review_request($plan);
1035 $this->fail('The plan review cannot be cancelled at this stage.');
1036 } catch (\coding_exception $e) {
1037 $this->assertMatchesRegularExpression('/The plan review cannot be cancelled at this stage./', $e->getMessage());
1040 // Cancelling as a reviewer.
1041 $this->setUser($reviewer);
1042 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
1043 try {
1044 api::plan_cancel_review_request($plan);
1045 $this->fail('The user can not cancel a review request.');
1046 } catch (\required_capability_exception $e) {
1047 $this->assertEquals('nopermissions', $e->errorcode);
1050 // Cancelling review request.
1051 $this->setUser($user);
1052 api::plan_cancel_review_request($plan);
1053 $plan->read();
1054 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status'));
1056 // Cancelling review request by ID.
1057 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
1058 $plan->update();
1059 api::plan_cancel_review_request($plan->get('id'));
1060 $plan->read();
1061 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status'));
1065 * Testing starting the review.
1067 public function test_plan_start_review(): void {
1068 $data = $this->setup_workflow_data();
1069 $dg = $data['dg'];
1070 $lpg = $data['lpg'];
1071 $user = $data['user'];
1072 $reviewer = $data['reviewer'];
1073 $otheruser = $data['otheruser'];
1074 $plan = $data['plan'];
1075 $tplplan = $data['tplplan'];
1077 // Set waiting for review.
1078 $tplplan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
1079 $tplplan->update();
1080 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
1081 $plan->update();
1083 // Foreign user cannot do anything.
1084 $this->setUser($otheruser);
1085 try {
1086 api::plan_start_review($plan);
1087 $this->fail('The user can not read the plan.');
1088 } catch (\required_capability_exception $e) {
1089 $this->assertEquals('nopermissions', $e->errorcode);
1092 // Can not change a plan based on a template.
1093 $this->setUser($reviewer);
1094 try {
1095 api::plan_start_review($tplplan);
1096 $this->fail('The plan is based on a template.');
1097 } catch (\coding_exception $e) {
1098 $this->assertMatchesRegularExpression('/Template plans cannot be reviewed./', $e->getMessage());
1101 // Can not start a review when not waiting for review.
1102 $this->setUser($reviewer);
1103 $plan->set('status', plan::STATUS_DRAFT);
1104 try {
1105 api::plan_start_review($plan);
1106 $this->fail('The plan review cannot be started at this stage.');
1107 } catch (\coding_exception $e) {
1108 $this->assertMatchesRegularExpression('/The plan review cannot be started at this stage./', $e->getMessage());
1111 // Can not start a review when not waiting for review.
1112 $this->setUser($reviewer);
1113 $plan->set('status', plan::STATUS_IN_REVIEW);
1114 try {
1115 api::plan_start_review($plan);
1116 $this->fail('The plan review cannot be started at this stage.');
1117 } catch (\coding_exception $e) {
1118 $this->assertMatchesRegularExpression('/The plan review cannot be started at this stage./', $e->getMessage());
1121 // Can not start a review when not waiting for review.
1122 $this->setUser($reviewer);
1123 $plan->set('status', plan::STATUS_ACTIVE);
1124 try {
1125 api::plan_start_review($plan);
1126 $this->fail('The plan review cannot be started at this stage.');
1127 } catch (\coding_exception $e) {
1128 $this->assertMatchesRegularExpression('/The plan review cannot be started at this stage./', $e->getMessage());
1131 // Can not start a review when not waiting for review.
1132 $this->setUser($reviewer);
1133 $plan->set('status', plan::STATUS_COMPLETE);
1134 try {
1135 api::plan_start_review($plan);
1136 $this->fail('The plan review cannot be started at this stage.');
1137 } catch (\coding_exception $e) {
1138 $this->assertMatchesRegularExpression('/The plan review cannot be started at this stage./', $e->getMessage());
1141 // Starting as the owner.
1142 $this->setUser($user);
1143 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
1144 try {
1145 api::plan_start_review($plan);
1146 $this->fail('The user can not start a review.');
1147 } catch (\required_capability_exception $e) {
1148 $this->assertEquals('nopermissions', $e->errorcode);
1151 // Starting review.
1152 $this->setUser($reviewer);
1153 api::plan_start_review($plan);
1154 $plan->read();
1155 $this->assertEquals(plan::STATUS_IN_REVIEW, $plan->get('status'));
1156 $this->assertEquals($reviewer->id, $plan->get('reviewerid'));
1158 // Starting review by ID.
1159 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
1160 $plan->set('reviewerid', null);
1161 $plan->update();
1162 api::plan_start_review($plan->get('id'));
1163 $plan->read();
1164 $this->assertEquals(plan::STATUS_IN_REVIEW, $plan->get('status'));
1165 $this->assertEquals($reviewer->id, $plan->get('reviewerid'));
1169 * Testing stopping the review.
1171 public function test_plan_stop_review(): void {
1172 $data = $this->setup_workflow_data();
1173 $dg = $data['dg'];
1174 $lpg = $data['lpg'];
1175 $user = $data['user'];
1176 $reviewer = $data['reviewer'];
1177 $otheruser = $data['otheruser'];
1178 $plan = $data['plan'];
1179 $tplplan = $data['tplplan'];
1181 // Set waiting for review.
1182 $tplplan->set('status', plan::STATUS_IN_REVIEW);
1183 $tplplan->update();
1184 $plan->set('status', plan::STATUS_IN_REVIEW);
1185 $plan->update();
1187 // Foreign user cannot do anything.
1188 $this->setUser($otheruser);
1189 try {
1190 api::plan_stop_review($plan);
1191 $this->fail('The user can not read the plan.');
1192 } catch (\required_capability_exception $e) {
1193 $this->assertEquals('nopermissions', $e->errorcode);
1196 // Can not change a plan based on a template.
1197 $this->setUser($reviewer);
1198 try {
1199 api::plan_stop_review($tplplan);
1200 $this->fail('The plan is based on a template.');
1201 } catch (\coding_exception $e) {
1202 $this->assertMatchesRegularExpression('/Template plans cannot be reviewed./', $e->getMessage());
1205 // Can not stop a review whe not in review.
1206 $this->setUser($reviewer);
1207 $plan->set('status', plan::STATUS_DRAFT);
1208 try {
1209 api::plan_stop_review($plan);
1210 $this->fail('The plan review cannot be stopped at this stage.');
1211 } catch (\coding_exception $e) {
1212 $this->assertMatchesRegularExpression('/The plan review cannot be stopped at this stage./', $e->getMessage());
1215 // Can not stop a review whe not in review.
1216 $this->setUser($reviewer);
1217 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
1218 try {
1219 api::plan_stop_review($plan);
1220 $this->fail('The plan review cannot be stopped at this stage.');
1221 } catch (\coding_exception $e) {
1222 $this->assertMatchesRegularExpression('/The plan review cannot be stopped at this stage./', $e->getMessage());
1225 // Can not stop a review whe not in review.
1226 $this->setUser($reviewer);
1227 $plan->set('status', plan::STATUS_ACTIVE);
1228 try {
1229 api::plan_stop_review($plan);
1230 $this->fail('The plan review cannot be stopped at this stage.');
1231 } catch (\coding_exception $e) {
1232 $this->assertMatchesRegularExpression('/The plan review cannot be stopped at this stage./', $e->getMessage());
1235 // Can not stop a review whe not in review.
1236 $this->setUser($reviewer);
1237 $plan->set('status', plan::STATUS_COMPLETE);
1238 try {
1239 api::plan_stop_review($plan);
1240 $this->fail('The plan review cannot be stopped at this stage.');
1241 } catch (\coding_exception $e) {
1242 $this->assertMatchesRegularExpression('/The plan review cannot be stopped at this stage./', $e->getMessage());
1245 // Stopping as the owner.
1246 $this->setUser($user);
1247 $plan->set('status', plan::STATUS_IN_REVIEW);
1248 try {
1249 api::plan_stop_review($plan);
1250 $this->fail('The user can not stop a review.');
1251 } catch (\required_capability_exception $e) {
1252 $this->assertEquals('nopermissions', $e->errorcode);
1255 // Stopping review.
1256 $this->setUser($reviewer);
1257 api::plan_stop_review($plan);
1258 $plan->read();
1259 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status'));
1261 // Stopping review by ID.
1262 $plan->set('status', plan::STATUS_IN_REVIEW);
1263 $plan->update();
1264 api::plan_stop_review($plan->get('id'));
1265 $plan->read();
1266 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status'));
1270 * Testing approving the plan.
1272 public function test_approve_plan(): void {
1273 $data = $this->setup_workflow_data();
1274 $dg = $data['dg'];
1275 $lpg = $data['lpg'];
1276 $user = $data['user'];
1277 $reviewer = $data['reviewer'];
1278 $otheruser = $data['otheruser'];
1279 $plan = $data['plan'];
1280 $tplplan = $data['tplplan'];
1282 // Set waiting for review.
1283 $tplplan->set('status', plan::STATUS_IN_REVIEW);
1284 $tplplan->update();
1285 $plan->set('status', plan::STATUS_IN_REVIEW);
1286 $plan->update();
1288 // Foreign user cannot do anything.
1289 $this->setUser($otheruser);
1290 try {
1291 api::approve_plan($plan);
1292 $this->fail('The user can not read the plan.');
1293 } catch (\required_capability_exception $e) {
1294 $this->assertEquals('nopermissions', $e->errorcode);
1297 // Can not change a plan based on a template.
1298 $this->setUser($reviewer);
1299 try {
1300 api::approve_plan($tplplan);
1301 $this->fail('The plan is based on a template.');
1302 } catch (\coding_exception $e) {
1303 $this->assertMatchesRegularExpression('/Template plans are already approved./', $e->getMessage());
1306 // Can not approve a plan already approved.
1307 $this->setUser($reviewer);
1308 $plan->set('status', plan::STATUS_ACTIVE);
1309 try {
1310 api::approve_plan($plan);
1311 $this->fail('The plan cannot be approved at this stage.');
1312 } catch (\coding_exception $e) {
1313 $this->assertMatchesRegularExpression('/The plan cannot be approved at this stage./', $e->getMessage());
1316 // Can not approve a plan already approved.
1317 $this->setUser($reviewer);
1318 $plan->set('status', plan::STATUS_COMPLETE);
1319 try {
1320 api::approve_plan($plan);
1321 $this->fail('The plan cannot be approved at this stage.');
1322 } catch (\coding_exception $e) {
1323 $this->assertMatchesRegularExpression('/The plan cannot be approved at this stage./', $e->getMessage());
1326 // Approve as the owner.
1327 $this->setUser($user);
1328 $plan->set('status', plan::STATUS_IN_REVIEW);
1329 try {
1330 api::approve_plan($plan);
1331 $this->fail('The user can not approve the plan.');
1332 } catch (\required_capability_exception $e) {
1333 $this->assertEquals('nopermissions', $e->errorcode);
1336 // Approve plan from in review.
1337 $this->setUser($reviewer);
1338 api::approve_plan($plan);
1339 $plan->read();
1340 $this->assertEquals(plan::STATUS_ACTIVE, $plan->get('status'));
1342 // Approve plan by ID.
1343 $plan->set('status', plan::STATUS_IN_REVIEW);
1344 $plan->update();
1345 api::approve_plan($plan->get('id'));
1346 $plan->read();
1347 $this->assertEquals(plan::STATUS_ACTIVE, $plan->get('status'));
1349 // Approve plan from draft.
1350 $plan->set('status', plan::STATUS_DRAFT);
1351 $plan->update();
1352 api::approve_plan($plan);
1353 $plan->read();
1354 $this->assertEquals(plan::STATUS_ACTIVE, $plan->get('status'));
1356 // Approve plan from waiting for review.
1357 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
1358 $plan->update();
1359 api::approve_plan($plan);
1360 $plan->read();
1361 $this->assertEquals(plan::STATUS_ACTIVE, $plan->get('status'));
1365 * Testing stopping the review.
1367 public function test_unapprove_plan(): void {
1368 $data = $this->setup_workflow_data();
1369 $dg = $data['dg'];
1370 $lpg = $data['lpg'];
1371 $user = $data['user'];
1372 $reviewer = $data['reviewer'];
1373 $otheruser = $data['otheruser'];
1374 $plan = $data['plan'];
1375 $tplplan = $data['tplplan'];
1377 // Set waiting for review.
1378 $tplplan->set('status', plan::STATUS_ACTIVE);
1379 $tplplan->update();
1380 $plan->set('status', plan::STATUS_ACTIVE);
1381 $plan->update();
1383 // Foreign user cannot do anything.
1384 $this->setUser($otheruser);
1385 try {
1386 api::unapprove_plan($plan);
1387 $this->fail('The user can not read the plan.');
1388 } catch (\required_capability_exception $e) {
1389 $this->assertEquals('nopermissions', $e->errorcode);
1392 // Can not change a plan based on a template.
1393 $this->setUser($reviewer);
1394 try {
1395 api::unapprove_plan($tplplan);
1396 $this->fail('The plan is based on a template.');
1397 } catch (\coding_exception $e) {
1398 $this->assertMatchesRegularExpression('/Template plans are always approved./', $e->getMessage());
1401 // Can not unapprove a non-draft plan.
1402 $this->setUser($reviewer);
1403 $plan->set('status', plan::STATUS_DRAFT);
1404 try {
1405 api::unapprove_plan($plan);
1406 $this->fail('The plan cannot be sent back to draft at this stage.');
1407 } catch (\coding_exception $e) {
1408 $this->assertMatchesRegularExpression('/The plan cannot be sent back to draft at this stage./', $e->getMessage());
1411 // Can not unapprove a non-draft plan.
1412 $this->setUser($reviewer);
1413 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW);
1414 try {
1415 api::unapprove_plan($plan);
1416 $this->fail('The plan cannot be sent back to draft at this stage.');
1417 } catch (\coding_exception $e) {
1418 $this->assertMatchesRegularExpression('/The plan cannot be sent back to draft at this stage./', $e->getMessage());
1421 // Can not unapprove a non-draft plan.
1422 $this->setUser($reviewer);
1423 $plan->set('status', plan::STATUS_IN_REVIEW);
1424 try {
1425 api::unapprove_plan($plan);
1426 $this->fail('The plan cannot be sent back to draft at this stage.');
1427 } catch (\coding_exception $e) {
1428 $this->assertMatchesRegularExpression('/The plan cannot be sent back to draft at this stage./', $e->getMessage());
1431 // Can not unapprove a non-draft plan.
1432 $this->setUser($reviewer);
1433 $plan->set('status', plan::STATUS_COMPLETE);
1434 try {
1435 api::unapprove_plan($plan);
1436 $this->fail('The plan cannot be sent back to draft at this stage.');
1437 } catch (\coding_exception $e) {
1438 $this->assertMatchesRegularExpression('/The plan cannot be sent back to draft at this stage./', $e->getMessage());
1441 // Unapprove as the owner.
1442 $this->setUser($user);
1443 $plan->set('status', plan::STATUS_ACTIVE);
1444 try {
1445 api::unapprove_plan($plan);
1446 $this->fail('The user can not unapprove the plan.');
1447 } catch (\required_capability_exception $e) {
1448 $this->assertEquals('nopermissions', $e->errorcode);
1451 // Unapprove plan.
1452 $this->setUser($reviewer);
1453 api::unapprove_plan($plan);
1454 $plan->read();
1455 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status'));
1457 // Unapprove plan by ID.
1458 $plan->set('status', plan::STATUS_ACTIVE);
1459 $plan->update();
1460 api::unapprove_plan($plan->get('id'));
1461 $plan->read();
1462 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status'));
1466 * Test update plan and the managing of archived user competencies.
1468 public function test_update_plan_manage_archived_competencies(): void {
1469 global $DB;
1471 $this->resetAfterTest(true);
1472 $dg = $this->getDataGenerator();
1473 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
1475 $syscontext = \context_system::instance();
1477 // Create users and roles for the test.
1478 $user = $dg->create_user();
1479 $manageownrole = $dg->create_role(array(
1480 'name' => 'User manage own',
1481 'shortname' => 'manageown'
1483 assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $manageownrole, $syscontext->id);
1484 assign_capability('moodle/competency:planviewowndraft', CAP_ALLOW, $manageownrole, $syscontext->id);
1485 assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $manageownrole, $syscontext->id);
1486 assign_capability('moodle/competency:planviewown', CAP_ALLOW, $manageownrole, $syscontext->id);
1487 $dg->role_assign($manageownrole, $user->id, $syscontext->id);
1488 $this->setUser($user);
1490 // Create a framework and assign competencies.
1491 $framework = $lpg->create_framework();
1492 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1493 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1494 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1496 // Create two plans and assign competencies.
1497 $plan = $lpg->create_plan(array('userid' => $user->id));
1498 $otherplan = $lpg->create_plan(array('userid' => $user->id));
1500 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c1->get('id')));
1501 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c2->get('id')));
1502 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c3->get('id')));
1503 $lpg->create_plan_competency(array('planid' => $otherplan->get('id'), 'competencyid' => $c1->get('id')));
1505 $uclist = array(
1506 $lpg->create_user_competency(array(
1507 'userid' => $user->id,
1508 'competencyid' => $c1->get('id'),
1509 'proficiency' => true,
1510 'grade' => 1
1512 $lpg->create_user_competency(array(
1513 'userid' => $user->id,
1514 'competencyid' => $c2->get('id'),
1515 'proficiency' => false,
1516 'grade' => 2
1520 // Change status of the plan to complete.
1521 $record = $plan->to_record();
1522 $record->status = \core_competency\plan::STATUS_COMPLETE;
1524 try {
1525 $plan = api::update_plan($record);
1526 $this->fail('We cannot complete a plan using api::update_plan().');
1527 } catch (\coding_exception $e) {
1528 // All good.
1530 api::complete_plan($plan);
1532 // Check that user compretencies are now in user_competency_plan objects and still in user_competency.
1533 $this->assertEquals(2, \core_competency\user_competency::count_records());
1534 $this->assertEquals(3, \core_competency\user_competency_plan::count_records());
1536 $usercompetenciesplan = \core_competency\user_competency_plan::get_records();
1538 $this->assertEquals($uclist[0]->get('userid'), $usercompetenciesplan[0]->get('userid'));
1539 $this->assertEquals($uclist[0]->get('competencyid'), $usercompetenciesplan[0]->get('competencyid'));
1540 $this->assertEquals($uclist[0]->get('proficiency'), (bool) $usercompetenciesplan[0]->get('proficiency'));
1541 $this->assertEquals($uclist[0]->get('grade'), $usercompetenciesplan[0]->get('grade'));
1542 $this->assertEquals($plan->get('id'), $usercompetenciesplan[0]->get('planid'));
1544 $this->assertEquals($uclist[1]->get('userid'), $usercompetenciesplan[1]->get('userid'));
1545 $this->assertEquals($uclist[1]->get('competencyid'), $usercompetenciesplan[1]->get('competencyid'));
1546 $this->assertEquals($uclist[1]->get('proficiency'), (bool) $usercompetenciesplan[1]->get('proficiency'));
1547 $this->assertEquals($uclist[1]->get('grade'), $usercompetenciesplan[1]->get('grade'));
1548 $this->assertEquals($plan->get('id'), $usercompetenciesplan[1]->get('planid'));
1550 $this->assertEquals($user->id, $usercompetenciesplan[2]->get('userid'));
1551 $this->assertEquals($c3->get('id'), $usercompetenciesplan[2]->get('competencyid'));
1552 $this->assertNull($usercompetenciesplan[2]->get('proficiency'));
1553 $this->assertNull($usercompetenciesplan[2]->get('grade'));
1554 $this->assertEquals($plan->get('id'), $usercompetenciesplan[2]->get('planid'));
1556 // Change status of the plan to active.
1557 $record = $plan->to_record();
1558 $record->status = \core_competency\plan::STATUS_ACTIVE;
1560 try {
1561 api::update_plan($record);
1562 $this->fail('Completed plan can not be edited');
1563 } catch (\coding_exception $e) {
1564 // All good.
1567 api::reopen_plan($record->id);
1568 // Check that user_competency_plan objects are deleted if the plan status is changed to another status.
1569 $this->assertEquals(2, \core_competency\user_competency::count_records());
1570 $this->assertEquals(0, \core_competency\user_competency_plan::count_records());
1574 * Test completing plan does not change the order of competencies.
1576 public function test_complete_plan_doesnot_change_order(): void {
1577 global $DB;
1579 $this->resetAfterTest(true);
1580 $this->setAdminUser();
1581 $dg = $this->getDataGenerator();
1582 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
1584 $syscontext = \context_system::instance();
1586 // Create users and roles for the test.
1587 $user = $dg->create_user();
1589 // Create a framework and assign competencies.
1590 $framework = $lpg->create_framework();
1591 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1592 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1593 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1595 // Create two plans and assign competencies.
1596 $plan = $lpg->create_plan(array('userid' => $user->id));
1598 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c1->get('id')));
1599 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c2->get('id')));
1600 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c3->get('id')));
1602 // Changing competencies order in plan competency.
1603 api::reorder_plan_competency($plan->get('id'), $c1->get('id'), $c3->get('id'));
1605 $competencies = api::list_plan_competencies($plan);
1606 $this->assertEquals($c2->get('id'), $competencies[0]->competency->get('id'));
1607 $this->assertEquals($c3->get('id'), $competencies[1]->competency->get('id'));
1608 $this->assertEquals($c1->get('id'), $competencies[2]->competency->get('id'));
1610 // Completing plan.
1611 api::complete_plan($plan);
1613 $competencies = api::list_plan_competencies($plan);
1615 // Completing plan does not change order.
1616 $this->assertEquals($c2->get('id'), $competencies[0]->competency->get('id'));
1617 $this->assertEquals($c3->get('id'), $competencies[1]->competency->get('id'));
1618 $this->assertEquals($c1->get('id'), $competencies[2]->competency->get('id'));
1620 // Testing plan based on template.
1621 $template = $lpg->create_template();
1622 $framework = $lpg->create_framework();
1623 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1624 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1625 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1627 $lpg->create_template_competency(array(
1628 'templateid' => $template->get('id'),
1629 'competencyid' => $c1->get('id')
1631 $lpg->create_template_competency(array(
1632 'templateid' => $template->get('id'),
1633 'competencyid' => $c2->get('id')
1635 $lpg->create_template_competency(array(
1636 'templateid' => $template->get('id'),
1637 'competencyid' => $c3->get('id')
1639 // Reorder competencies in template.
1640 api::reorder_template_competency($template->get('id'), $c1->get('id'), $c3->get('id'));
1642 // Create plan from template.
1643 $plan = api::create_plan_from_template($template->get('id'), $user->id);
1645 $competencies = api::list_plan_competencies($plan);
1647 // Completing plan does not change order.
1648 $this->assertEquals($c2->get('id'), $competencies[0]->competency->get('id'));
1649 $this->assertEquals($c3->get('id'), $competencies[1]->competency->get('id'));
1650 $this->assertEquals($c1->get('id'), $competencies[2]->competency->get('id'));
1652 // Completing plan.
1653 api::complete_plan($plan);
1655 $competencies = api::list_plan_competencies($plan);
1657 // Completing plan does not change order.
1658 $this->assertEquals($c2->get('id'), $competencies[0]->competency->get('id'));
1659 $this->assertEquals($c3->get('id'), $competencies[1]->competency->get('id'));
1660 $this->assertEquals($c1->get('id'), $competencies[2]->competency->get('id'));
1664 * Test remove plan and the managing of archived user competencies.
1666 public function test_delete_plan_manage_archived_competencies(): void {
1667 $this->resetAfterTest(true);
1668 $dg = $this->getDataGenerator();
1669 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
1671 $syscontext = \context_system::instance();
1673 // Create user and role for the test.
1674 $user = $dg->create_user();
1675 $managerole = $dg->create_role(array(
1676 'name' => 'User manage own',
1677 'shortname' => 'manageown'
1679 assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $managerole, $syscontext->id);
1680 assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $managerole, $syscontext->id);
1681 $dg->role_assign($managerole, $user->id, $syscontext->id);
1682 $this->setUser($user);
1684 // Create a framework and assign competencies.
1685 $framework = $lpg->create_framework();
1686 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1687 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1688 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1690 // Create completed plan with records in user_competency.
1691 $completedplan = $lpg->create_plan(array('userid' => $user->id, 'status' => \core_competency\plan::STATUS_COMPLETE));
1693 $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c1->get('id')));
1694 $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c2->get('id')));
1696 $uc1 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id')));
1697 $uc2 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c2->get('id')));
1699 $ucp1 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c1->get('id'),
1700 'planid' => $completedplan->get('id')));
1701 $ucp2 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c2->get('id'),
1702 'planid' => $completedplan->get('id')));
1704 api::delete_plan($completedplan->get('id'));
1706 // Check that achived user competencies are deleted.
1707 $this->assertEquals(0, \core_competency\plan::count_records());
1708 $this->assertEquals(2, \core_competency\user_competency::count_records());
1709 $this->assertEquals(0, \core_competency\user_competency_plan::count_records());
1713 * Test listing of plan competencies.
1715 public function test_list_plan_competencies_manage_archived_competencies(): void {
1716 $this->resetAfterTest(true);
1717 $dg = $this->getDataGenerator();
1718 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
1720 $syscontext = \context_system::instance();
1722 // Create user and role for the test.
1723 $user = $dg->create_user();
1724 $viewrole = $dg->create_role(array(
1725 'name' => 'User view',
1726 'shortname' => 'view'
1728 assign_capability('moodle/competency:planviewdraft', CAP_ALLOW, $viewrole, $syscontext->id);
1729 assign_capability('moodle/competency:planview', CAP_ALLOW, $viewrole, $syscontext->id);
1730 $dg->role_assign($viewrole, $user->id, $syscontext->id);
1731 $this->setUser($user);
1733 // Create a framework and assign competencies.
1734 $framework = $lpg->create_framework();
1735 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1736 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1737 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1739 // Create draft plan with records in user_competency.
1740 $draftplan = $lpg->create_plan(array('userid' => $user->id));
1742 $lpg->create_plan_competency(array('planid' => $draftplan->get('id'), 'competencyid' => $c1->get('id')));
1743 $lpg->create_plan_competency(array('planid' => $draftplan->get('id'), 'competencyid' => $c2->get('id')));
1744 $lpg->create_plan_competency(array('planid' => $draftplan->get('id'), 'competencyid' => $c3->get('id')));
1746 $uc1 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id')));
1747 $uc2 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c2->get('id')));
1749 // Check that user_competency objects are returned when plan status is not complete.
1750 $plancompetencies = api::list_plan_competencies($draftplan);
1752 $this->assertCount(3, $plancompetencies);
1753 $this->assertInstanceOf('\core_competency\user_competency', $plancompetencies[0]->usercompetency);
1754 $this->assertEquals($uc1->get('id'), $plancompetencies[0]->usercompetency->get('id'));
1755 $this->assertNull($plancompetencies[0]->usercompetencyplan);
1757 $this->assertInstanceOf('\core_competency\user_competency', $plancompetencies[1]->usercompetency);
1758 $this->assertEquals($uc2->get('id'), $plancompetencies[1]->usercompetency->get('id'));
1759 $this->assertNull($plancompetencies[1]->usercompetencyplan);
1761 $this->assertInstanceOf('\core_competency\user_competency', $plancompetencies[2]->usercompetency);
1762 $this->assertEquals(0, $plancompetencies[2]->usercompetency->get('id'));
1763 $this->assertNull($plancompetencies[2]->usercompetencyplan);
1765 // Create completed plan with records in user_competency_plan.
1766 $completedplan = $lpg->create_plan(array('userid' => $user->id, 'status' => \core_competency\plan::STATUS_COMPLETE));
1768 $pc1 = $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c1->get('id')));
1769 $pc2 = $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c2->get('id')));
1770 $pc3 = $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c3->get('id')));
1772 $ucp1 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c1->get('id'),
1773 'planid' => $completedplan->get('id')));
1774 $ucp2 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c2->get('id'),
1775 'planid' => $completedplan->get('id')));
1776 $ucp3 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c3->get('id'),
1777 'planid' => $completedplan->get('id')));
1779 // Check that user_competency_plan objects are returned when plan status is complete.
1780 $plancompetencies = api::list_plan_competencies($completedplan);
1782 $this->assertCount(3, $plancompetencies);
1783 $this->assertInstanceOf('\core_competency\user_competency_plan', $plancompetencies[0]->usercompetencyplan);
1784 $this->assertEquals($ucp1->get('id'), $plancompetencies[0]->usercompetencyplan->get('id'));
1785 $this->assertNull($plancompetencies[0]->usercompetency);
1786 $this->assertInstanceOf('\core_competency\user_competency_plan', $plancompetencies[1]->usercompetencyplan);
1787 $this->assertEquals($ucp2->get('id'), $plancompetencies[1]->usercompetencyplan->get('id'));
1788 $this->assertNull($plancompetencies[1]->usercompetency);
1789 $this->assertInstanceOf('\core_competency\user_competency_plan', $plancompetencies[2]->usercompetencyplan);
1790 $this->assertEquals($ucp3->get('id'), $plancompetencies[2]->usercompetencyplan->get('id'));
1791 $this->assertNull($plancompetencies[2]->usercompetency);
1794 public function test_create_template_cohort(): void {
1795 $this->resetAfterTest(true);
1796 $this->setAdminUser();
1798 $dg = $this->getDataGenerator();
1799 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
1801 $c1 = $dg->create_cohort();
1802 $c2 = $dg->create_cohort();
1803 $t1 = $lpg->create_template();
1804 $t2 = $lpg->create_template();
1806 $this->assertEquals(0, \core_competency\template_cohort::count_records());
1808 // Create two relations with mixed parameters.
1809 $result = api::create_template_cohort($t1->get('id'), $c1->id);
1810 $result = api::create_template_cohort($t1, $c2);
1812 $this->assertEquals(2, \core_competency\template_cohort::count_records());
1813 $this->assertInstanceOf('core_competency\template_cohort', $result);
1814 $this->assertEquals($c2->id, $result->get('cohortid'));
1815 $this->assertEquals($t1->get('id'), $result->get('templateid'));
1816 $this->assertEquals(2, \core_competency\template_cohort::count_records_select('templateid = :id',
1817 array('id' => $t1->get('id'))));
1818 $this->assertEquals(0, \core_competency\template_cohort::count_records_select('templateid = :id',
1819 array('id' => $t2->get('id'))));
1822 public function test_create_template_cohort_permissions(): void {
1823 $this->resetAfterTest(true);
1825 $dg = $this->getDataGenerator();
1826 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
1827 $cat = $dg->create_category();
1828 $catcontext = \context_coursecat::instance($cat->id);
1829 $syscontext = \context_system::instance();
1831 $user = $dg->create_user();
1832 $role = $dg->create_role();
1833 assign_capability('moodle/competency:templatemanage', CAP_ALLOW, $role, $syscontext->id, true);
1834 $dg->role_assign($role, $user->id, $syscontext->id);
1836 $cohortrole = $dg->create_role();
1837 assign_capability('moodle/cohort:view', CAP_ALLOW, $cohortrole, $syscontext->id, true);
1839 accesslib_clear_all_caches_for_unit_testing();
1841 $c1 = $dg->create_cohort();
1842 $c2 = $dg->create_cohort(array('visible' => 0, 'contextid' => $catcontext->id));
1843 $t1 = $lpg->create_template();
1845 $this->assertEquals(0, \core_competency\template_cohort::count_records());
1847 $this->setUser($user);
1848 $result = api::create_template_cohort($t1, $c1);
1849 $this->assertInstanceOf('core_competency\\template_cohort', $result);
1851 try {
1852 $result = api::create_template_cohort($t1, $c2);
1853 $this->fail('Permission required.');
1854 } catch (\required_capability_exception $e) {
1855 // That's what should happen.
1858 // Try again with the right permissions.
1859 $dg->role_assign($cohortrole, $user->id, $catcontext->id);
1860 accesslib_clear_all_caches_for_unit_testing();
1862 $result = api::create_template_cohort($t1, $c2);
1863 $this->assertInstanceOf('core_competency\\template_cohort', $result);
1866 public function test_reorder_template_competencies_permissions(): void {
1867 $this->resetAfterTest(true);
1869 $dg = $this->getDataGenerator();
1870 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
1871 $cat = $dg->create_category();
1872 $catcontext = \context_coursecat::instance($cat->id);
1873 $syscontext = \context_system::instance();
1875 $user = $dg->create_user();
1876 $role = $dg->create_role();
1877 assign_capability('moodle/competency:templatemanage', CAP_ALLOW, $role, $syscontext->id, true);
1878 $dg->role_assign($role, $user->id, $syscontext->id);
1880 // Create a template.
1881 $template = $lpg->create_template(array('contextid' => $catcontext->id));
1883 // Create a competency framework.
1884 $framework = $lpg->create_framework(array('contextid' => $catcontext->id));
1886 // Create competencies.
1887 $competency1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1888 $competency2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
1890 // Add the competencies.
1891 $lpg->create_template_competency(array(
1892 'templateid' => $template->get('id'),
1893 'competencyid' => $competency1->get('id')
1895 $lpg->create_template_competency(array(
1896 'templateid' => $template->get('id'),
1897 'competencyid' => $competency2->get('id')
1899 $this->setUser($user);
1900 // Can reorder competencies with system context permissions in category context.
1901 $result = api::reorder_template_competency($template->get('id'), $competency2->get('id'), $competency1->get('id'));
1902 $this->assertTrue($result);
1903 unassign_capability('moodle/competency:templatemanage', $role, $syscontext->id);
1904 accesslib_clear_all_caches_for_unit_testing();
1906 try {
1907 api::reorder_template_competency($template->get('id'), $competency2->get('id'), $competency1->get('id'));
1908 $this->fail('Exception expected due to not permissions to manage template competencies');
1909 } catch (\required_capability_exception $e) {
1910 $this->assertEquals('nopermissions', $e->errorcode);
1913 // Giving permissions in category context.
1914 assign_capability('moodle/competency:templatemanage', CAP_ALLOW, $role, $catcontext->id, true);
1915 $dg->role_assign($role, $user->id, $catcontext->id);
1916 // User with templatemanage capability in category context can reorder competencies in temple.
1917 $result = api::reorder_template_competency($template->get('id'), $competency1->get('id'), $competency2->get('id'));
1918 $this->assertTrue($result);
1919 // Removing templatemanage capability in category context.
1920 unassign_capability('moodle/competency:templatemanage', $role, $catcontext->id);
1921 accesslib_clear_all_caches_for_unit_testing();
1923 try {
1924 api::reorder_template_competency($template->get('id'), $competency2->get('id'), $competency1->get('id'));
1925 $this->fail('Exception expected due to not permissions to manage template competencies');
1926 } catch (\required_capability_exception $e) {
1927 $this->assertEquals('nopermissions', $e->errorcode);
1931 public function test_delete_template(): void {
1932 $this->resetAfterTest(true);
1933 $this->setAdminUser();
1935 $dg = $this->getDataGenerator();
1936 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
1938 $c1 = $dg->create_cohort();
1939 $c2 = $dg->create_cohort();
1940 $template = $lpg->create_template();
1941 $id = $template->get('id');
1943 // Create 2 template cohorts.
1944 $tc1 = $lpg->create_template_cohort(array('templateid' => $template->get('id'), 'cohortid' => $c1->id));
1945 $tc1 = $lpg->create_template_cohort(array('templateid' => $template->get('id'), 'cohortid' => $c2->id));
1947 // Check pre-test.
1948 $this->assertTrue(\core_competency\template::record_exists($id));
1949 $this->assertEquals(2, \core_competency\template_cohort::count_records(array('templateid' => $id)));
1951 $result = api::delete_template($template->get('id'));
1952 $this->assertTrue($result);
1954 // Check that the template deos not exist anymore.
1955 $this->assertFalse(\core_competency\template::record_exists($id));
1957 // Test if associated cohorts are also deleted.
1958 $this->assertEquals(0, \core_competency\template_cohort::count_records(array('templateid' => $id)));
1961 public function test_delete_template_cohort(): void {
1962 $this->resetAfterTest(true);
1963 $this->setAdminUser();
1965 $dg = $this->getDataGenerator();
1966 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
1968 $c1 = $dg->create_cohort();
1969 $c2 = $dg->create_cohort();
1970 $t1 = $lpg->create_template();
1971 $t2 = $lpg->create_template();
1972 $tc1 = $lpg->create_template_cohort(array('templateid' => $t1->get('id'), 'cohortid' => $c1->id));
1973 $tc1 = $lpg->create_template_cohort(array('templateid' => $t2->get('id'), 'cohortid' => $c2->id));
1975 $this->assertEquals(2, \core_competency\template_cohort::count_records());
1976 $this->assertEquals(1, \core_competency\template_cohort::count_records_select('templateid = :id',
1977 array('id' => $t1->get('id'))));
1978 $this->assertEquals(1, \core_competency\template_cohort::count_records_select('templateid = :id',
1979 array('id' => $t2->get('id'))));
1981 // Delete existing.
1982 $result = api::delete_template_cohort($t1->get('id'), $c1->id);
1983 $this->assertTrue($result);
1984 $this->assertEquals(1, \core_competency\template_cohort::count_records());
1985 $this->assertEquals(0, \core_competency\template_cohort::count_records_select('templateid = :id',
1986 array('id' => $t1->get('id'))));
1987 $this->assertEquals(1, \core_competency\template_cohort::count_records_select('templateid = :id',
1988 array('id' => $t2->get('id'))));
1990 // Delete non-existant.
1991 $result = api::delete_template_cohort($t1->get('id'), $c1->id);
1992 $this->assertTrue($result);
1993 $this->assertEquals(1, \core_competency\template_cohort::count_records());
1994 $this->assertEquals(0, \core_competency\template_cohort::count_records_select('templateid = :id',
1995 array('id' => $t1->get('id'))));
1996 $this->assertEquals(1, \core_competency\template_cohort::count_records_select('templateid = :id',
1997 array('id' => $t2->get('id'))));
2000 public function test_add_evidence_log(): void {
2001 $this->resetAfterTest(true);
2002 $dg = $this->getDataGenerator();
2003 $lpg = $dg->get_plugin_generator('core_competency');
2005 $u1 = $dg->create_user();
2006 $u1ctx = \context_user::instance($u1->id);
2007 $f1 = $lpg->create_framework();
2008 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2009 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2011 // Creating a standard evidence with minimal information.
2012 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG,
2013 'invaliddata', 'error');
2014 $evidence->read();
2015 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2016 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status'));
2017 $this->assertSame(null, $uc->get('grade'));
2018 $this->assertSame(null, $uc->get('proficiency'));
2019 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2020 $this->assertEquals($u1ctx->id, $evidence->get('contextid'));
2021 $this->assertEquals(\core_competency\evidence::ACTION_LOG, $evidence->get('action'));
2022 $this->assertEquals('invaliddata', $evidence->get('descidentifier'));
2023 $this->assertEquals('error', $evidence->get('desccomponent'));
2024 $this->assertSame(null, $evidence->get('desca'));
2025 $this->assertSame(null, $evidence->get('url'));
2026 $this->assertSame(null, $evidence->get('grade'));
2027 $this->assertSame(null, $evidence->get('actionuserid'));
2029 // Creating a standard evidence with more information.
2030 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata',
2031 'error', '$a', false, 'http://moodle.org', null, 2, 'The evidence of prior learning were reviewed.');
2032 $evidence->read();
2033 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2034 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status'));
2035 $this->assertSame(null, $uc->get('grade'));
2036 $this->assertSame(null, $uc->get('proficiency'));
2037 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2038 $this->assertEquals($u1ctx->id, $evidence->get('contextid'));
2039 $this->assertEquals(\core_competency\evidence::ACTION_LOG, $evidence->get('action'));
2040 $this->assertEquals('invaliddata', $evidence->get('descidentifier'));
2041 $this->assertEquals('error', $evidence->get('desccomponent'));
2042 $this->assertEquals('$a', $evidence->get('desca'));
2043 $this->assertEquals('http://moodle.org', $evidence->get('url'));
2044 $this->assertSame(null, $evidence->get('grade'));
2045 $this->assertEquals(2, $evidence->get('actionuserid'));
2046 $this->assertSame('The evidence of prior learning were reviewed.', $evidence->get('note'));
2048 // Creating a standard evidence and send for review.
2049 $evidence = api::add_evidence($u1->id, $c2->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata',
2050 'error', null, true);
2051 $evidence->read();
2052 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c2->get('id')));
2053 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status'));
2055 // Trying to pass a grade should fail.
2056 try {
2057 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata',
2058 'error', null, false, null, 1);
2059 $this->fail('A grade can not be set');
2060 } catch (\coding_exception $e) {
2061 $this->assertMatchesRegularExpression('/grade MUST NOT be set/', $e->getMessage());
2065 public function test_add_evidence_complete(): void {
2066 $this->resetAfterTest(true);
2067 $dg = $this->getDataGenerator();
2068 $lpg = $dg->get_plugin_generator('core_competency');
2070 $u1 = $dg->create_user();
2071 $u1ctx = \context_user::instance($u1->id);
2072 $scale = $dg->create_scale(array('scale' => 'A,B,C,D'));
2073 $scaleconfig = array(array('scaleid' => $scale->id));
2074 $scaleconfig[] = array('name' => 'B', 'id' => 2, 'scaledefault' => 1, 'proficient' => 0);
2075 $scaleconfig[] = array('name' => 'C', 'id' => 3, 'scaledefault' => 0, 'proficient' => 1);
2076 $scaleconfig[] = array('name' => 'D', 'id' => 4, 'scaledefault' => 0, 'proficient' => 1);
2077 $c2scaleconfig = array(array('scaleid' => $scale->id));
2078 $c2scaleconfig[] = array('name' => 'B', 'id' => 2, 'scaledefault' => 0, 'proficient' => 1);
2079 $c2scaleconfig[] = array('name' => 'C', 'id' => 3, 'scaledefault' => 0, 'proficient' => 0);
2080 $c2scaleconfig[] = array('name' => 'D', 'id' => 4, 'scaledefault' => 1, 'proficient' => 1);
2081 $f1 = $lpg->create_framework(array('scaleid' => $scale->id, 'scaleconfiguration' => $scaleconfig));
2082 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2083 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'scaleid' => $scale->id,
2084 'scaleconfiguration' => $c2scaleconfig));
2085 $c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2087 // Creating an evidence with minimal information.
2088 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE,
2089 'invaliddata', 'error');
2090 $evidence->read();
2091 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2092 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status'));
2093 $this->assertEquals(2, $uc->get('grade')); // The grade has been set automatically to the framework default.
2094 $this->assertEquals(0, $uc->get('proficiency'));
2095 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2096 $this->assertEquals($u1ctx->id, $evidence->get('contextid'));
2097 $this->assertEquals(\core_competency\evidence::ACTION_COMPLETE, $evidence->get('action'));
2098 $this->assertEquals('invaliddata', $evidence->get('descidentifier'));
2099 $this->assertEquals('error', $evidence->get('desccomponent'));
2100 $this->assertSame(null, $evidence->get('desca'));
2101 $this->assertSame(null, $evidence->get('url'));
2102 $this->assertEquals(2, $evidence->get('grade'));
2103 $this->assertSame(null, $evidence->get('actionuserid'));
2105 // Creating an evidence complete on competency with custom scale.
2106 $evidence = api::add_evidence($u1->id, $c2->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE,
2107 'invaliddata', 'error');
2108 $evidence->read();
2109 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c2->get('id')));
2110 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status'));
2111 $this->assertEquals(4, $uc->get('grade')); // The grade has been set automatically to the competency default.
2112 $this->assertEquals(true, $uc->get('proficiency'));
2113 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2114 $this->assertEquals($u1ctx->id, $evidence->get('contextid'));
2115 $this->assertEquals(\core_competency\evidence::ACTION_COMPLETE, $evidence->get('action'));
2116 $this->assertEquals('invaliddata', $evidence->get('descidentifier'));
2117 $this->assertEquals('error', $evidence->get('desccomponent'));
2118 $this->assertSame(null, $evidence->get('desca'));
2119 $this->assertSame(null, $evidence->get('url'));
2120 $this->assertEquals(4, $evidence->get('grade'));
2121 $this->assertSame(null, $evidence->get('actionuserid'));
2123 // Creating an evidence complete on a user competency with an existing grade.
2124 $uc = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c3->get('id'), 'grade' => 1,
2125 'proficiency' => 0));
2126 $this->assertEquals(1, $uc->get('grade'));
2127 $this->assertEquals(0, $uc->get('proficiency'));
2128 $evidence = api::add_evidence($u1->id, $c3->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE,
2129 'invaliddata', 'error');
2130 $evidence->read();
2131 $uc->read();
2132 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status'));
2133 $this->assertEquals(1, $uc->get('grade')); // The grade has not been changed.
2134 $this->assertEquals(0, $uc->get('proficiency'));
2135 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2136 $this->assertEquals($u1ctx->id, $evidence->get('contextid'));
2137 $this->assertEquals(\core_competency\evidence::ACTION_COMPLETE, $evidence->get('action'));
2138 $this->assertEquals('invaliddata', $evidence->get('descidentifier'));
2139 $this->assertEquals('error', $evidence->get('desccomponent'));
2140 $this->assertSame(null, $evidence->get('desca'));
2141 $this->assertSame(null, $evidence->get('url'));
2142 $this->assertEquals(2, $evidence->get('grade')); // The complete grade has been set.
2143 $this->assertSame(null, $evidence->get('actionuserid'));
2145 // Creating a standard evidence and send for review.
2146 $evidence = api::add_evidence($u1->id, $c2->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE,
2147 'invaliddata', 'error', null, true);
2148 $evidence->read();
2149 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c2->get('id')));
2150 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status'));
2152 // Trying to pass a grade should throw an exception.
2153 try {
2154 api::add_evidence($u1->id, $c2->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE, 'invaliddata',
2155 'error', null, false, null, 1);
2156 } catch (\coding_exception $e) {
2157 $this->assertMatchesRegularExpression('/grade MUST NOT be set/', $e->getMessage());
2161 public function test_add_evidence_override(): void {
2162 $this->resetAfterTest(true);
2163 $dg = $this->getDataGenerator();
2164 $lpg = $dg->get_plugin_generator('core_competency');
2166 $u1 = $dg->create_user();
2167 $u1ctx = \context_user::instance($u1->id);
2168 $f1 = $lpg->create_framework();
2169 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2171 // Creating an evidence with minimal information.
2172 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_OVERRIDE,
2173 'invaliddata', 'error');
2174 $evidence->read();
2175 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2176 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status'));
2177 $this->assertSame(null, $uc->get('grade')); // We overrode with 'null'.
2178 $this->assertSame(null, $uc->get('proficiency'));
2179 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2180 $this->assertEquals($u1ctx->id, $evidence->get('contextid'));
2181 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action'));
2182 $this->assertEquals('invaliddata', $evidence->get('descidentifier'));
2183 $this->assertEquals('error', $evidence->get('desccomponent'));
2184 $this->assertSame(null, $evidence->get('desca'));
2185 $this->assertSame(null, $evidence->get('url'));
2186 $this->assertSame(null, $evidence->get('grade')); // We overrode with 'null'.
2187 $this->assertSame(null, $evidence->get('actionuserid'));
2189 // Creating an evidence with a grade information.
2190 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_OVERRIDE,
2191 'invaliddata', 'error', null, false, null, 3);
2192 $evidence->read();
2193 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2194 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status'));
2195 $this->assertEquals(3, $uc->get('grade'));
2196 $this->assertEquals(true, $uc->get('proficiency'));
2197 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2198 $this->assertEquals($u1ctx->id, $evidence->get('contextid'));
2199 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action'));
2200 $this->assertEquals('invaliddata', $evidence->get('descidentifier'));
2201 $this->assertEquals('error', $evidence->get('desccomponent'));
2202 $this->assertSame(null, $evidence->get('desca'));
2203 $this->assertSame(null, $evidence->get('url'));
2204 $this->assertEquals(3, $evidence->get('grade'));
2205 $this->assertSame(null, $evidence->get('actionuserid'));
2207 // Creating an evidence with another grade information.
2208 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_OVERRIDE,
2209 'invaliddata', 'error', null, false, null, 1);
2210 $evidence->read();
2211 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2212 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status'));
2213 $this->assertEquals(1, $uc->get('grade'));
2214 $this->assertEquals(0, $uc->get('proficiency'));
2215 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2216 $this->assertEquals($u1ctx->id, $evidence->get('contextid'));
2217 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action'));
2218 $this->assertEquals('invaliddata', $evidence->get('descidentifier'));
2219 $this->assertEquals('error', $evidence->get('desccomponent'));
2220 $this->assertSame(null, $evidence->get('desca'));
2221 $this->assertSame(null, $evidence->get('url'));
2222 $this->assertEquals(1, $evidence->get('grade'));
2223 $this->assertSame(null, $evidence->get('actionuserid'));
2225 // Creating reverting the grade and send for review.
2226 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_OVERRIDE,
2227 'invaliddata', 'error', null, true);
2228 $evidence->read();
2229 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2230 $this->assertSame(null, $uc->get('grade'));
2231 $this->assertSame(null, $uc->get('proficiency'));
2232 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status'));
2233 $this->assertSame(null, $evidence->get('grade'));
2236 public function test_add_evidence_and_send_for_review(): void {
2237 $this->resetAfterTest(true);
2238 $dg = $this->getDataGenerator();
2239 $lpg = $dg->get_plugin_generator('core_competency');
2241 $u1 = $dg->create_user();
2242 $u1ctx = \context_user::instance($u1->id);
2243 $f1 = $lpg->create_framework();
2244 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2246 // Non-existing user competencies are created up for review.
2247 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata',
2248 'error', null, true);
2249 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2250 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status'));
2252 // Existing user competencies sent for review don't change.
2253 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata',
2254 'error', null, true);
2255 $uc->read();
2256 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status'));
2258 // A user competency with a status non-idle won't change.
2259 $uc->set('status', \core_competency\user_competency::STATUS_IN_REVIEW);
2260 $uc->update();
2261 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata',
2262 'error', null, true);
2263 $uc->read();
2264 $this->assertEquals(\core_competency\user_competency::STATUS_IN_REVIEW, $uc->get('status'));
2268 * Test add evidence for existing user_competency.
2270 public function test_add_evidence_existing_user_competency(): void {
2271 $this->resetAfterTest(true);
2272 $dg = $this->getDataGenerator();
2273 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
2275 $syscontext = \context_system::instance();
2277 // Create users.
2278 $user = $dg->create_user();
2279 $this->setUser($user);
2281 // Create a framework and assign competencies.
2282 $framework = $lpg->create_framework();
2283 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2284 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2285 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2286 $uc = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id')));
2287 $this->assertSame(null, $uc->get('grade'));
2288 $this->assertSame(null, $uc->get('proficiency'));
2290 // Create an evidence and check it was created with the right usercomptencyid and information.
2291 $evidence = api::add_evidence($user->id, $c1->get('id'), $syscontext->id, \core_competency\evidence::ACTION_OVERRIDE,
2292 'invalidevidencedesc', 'core_competency', array('a' => 'b'), false, 'http://moodle.org', 1, 2);
2293 $this->assertEquals(1, \core_competency\evidence::count_records());
2295 $evidence->read();
2296 $uc->read();
2297 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2298 $this->assertEquals('invalidevidencedesc', $evidence->get('descidentifier'));
2299 $this->assertEquals('core_competency', $evidence->get('desccomponent'));
2300 $this->assertEquals((object) array('a' => 'b'), $evidence->get('desca'));
2301 $this->assertEquals('http://moodle.org', $evidence->get('url'));
2302 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action'));
2303 $this->assertEquals(2, $evidence->get('actionuserid'));
2304 $this->assertEquals(1, $evidence->get('grade'));
2305 $this->assertEquals(1, $uc->get('grade'));
2306 $this->assertEquals(0, $uc->get('proficiency'));
2310 * Test add evidence for non-existing user_competency.
2312 public function test_add_evidence_no_existing_user_competency(): void {
2313 $this->resetAfterTest(true);
2314 $dg = $this->getDataGenerator();
2315 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
2317 $syscontext = \context_system::instance();
2319 // Create users.
2320 $user = $dg->create_user();
2321 $this->setUser($user);
2323 // Create a framework and assign competencies.
2324 $framework = $lpg->create_framework();
2325 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2326 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2327 $this->assertEquals(0, \core_competency\user_competency::count_records());
2329 // Create an evidence without a user competency record.
2330 $evidence = api::add_evidence($user->id, $c1->get('id'), $syscontext->id, \core_competency\evidence::ACTION_OVERRIDE,
2331 'invalidevidencedesc', 'core_competency', 'Hello world!', false, 'http://moodle.org', 1, 2);
2332 $this->assertEquals(1, \core_competency\evidence::count_records());
2333 $this->assertEquals(1, \core_competency\user_competency::count_records());
2335 $uc = \core_competency\user_competency::get_record(array('userid' => $user->id, 'competencyid' => $c1->get('id')));
2336 $evidence->read();
2337 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
2338 $this->assertEquals('invalidevidencedesc', $evidence->get('descidentifier'));
2339 $this->assertEquals('core_competency', $evidence->get('desccomponent'));
2340 $this->assertEquals('Hello world!', $evidence->get('desca'));
2341 $this->assertEquals('http://moodle.org', $evidence->get('url'));
2342 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action'));
2343 $this->assertEquals(2, $evidence->get('actionuserid'));
2344 $this->assertEquals(1, $evidence->get('grade'));
2345 $this->assertEquals(1, $uc->get('grade'));
2346 $this->assertEquals(0, $uc->get('proficiency'));
2349 public function test_add_evidence_applies_competency_rules(): void {
2350 $this->resetAfterTest(true);
2351 $dg = $this->getDataGenerator();
2352 $lpg = $dg->get_plugin_generator('core_competency');
2353 $syscontext = \context_system::instance();
2354 $ctxid = $syscontext->id;
2356 $u1 = $dg->create_user();
2358 // Setting up the framework.
2359 $f1 = $lpg->create_framework();
2360 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2361 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
2362 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
2363 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2364 $c2a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c2->get('id')));
2365 $c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2366 $c3a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c3->get('id')));
2367 $c4 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2368 $c4a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c4->get('id')));
2369 $c5 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2371 // Setting up the rules.
2372 $c1->set('ruletype', 'core_competency\\competency_rule_all');
2373 $c1->set('ruleoutcome', \core_competency\competency::OUTCOME_COMPLETE);
2374 $c1->update();
2375 $c2->set('ruletype', 'core_competency\\competency_rule_all');
2376 $c2->set('ruleoutcome', \core_competency\competency::OUTCOME_RECOMMEND);
2377 $c2->update();
2378 $c3->set('ruletype', 'core_competency\\competency_rule_all');
2379 $c3->set('ruleoutcome', \core_competency\competency::OUTCOME_EVIDENCE);
2380 $c3->update();
2381 $c4->set('ruletype', 'core_competency\\competency_rule_all');
2382 $c4->set('ruleoutcome', \core_competency\competency::OUTCOME_NONE);
2383 $c4->update();
2385 // Confirm the current data.
2386 $this->assertEquals(0, user_competency::count_records());
2387 $this->assertEquals(0, evidence::count_records());
2389 // Let's do this!
2390 // First let's confirm that evidence not marking a completion have no impact.
2391 api::add_evidence($u1->id, $c1a, $ctxid, evidence::ACTION_LOG, 'commentincontext', 'core');
2392 $uc1a = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1a->get('id')));
2393 $this->assertSame(null, $uc1a->get('proficiency'));
2394 $this->assertFalse(user_competency::record_exists_select('userid = ? AND competencyid = ?',
2395 array($u1->id, $c1->get('id'))));
2397 // Now let's try complete a competency but the rule won't match (not all children are complete).
2398 // The parent (the thing with the rule) will be created but won't have any evidence attached, and not
2399 // not be marked as completed.
2400 api::add_evidence($u1->id, $c1a, $ctxid, evidence::ACTION_COMPLETE, 'commentincontext', 'core');
2401 $uc1a = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1a->get('id')));
2402 $this->assertEquals(true, $uc1a->get('proficiency'));
2403 $uc1 = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2404 $this->assertSame(null, $uc1->get('proficiency'));
2405 $this->assertEquals(0, evidence::count_records(array('usercompetencyid' => $uc1->get('id'))));
2407 // Now we complete the other child. That will mark the parent as complete with an evidence.
2408 api::add_evidence($u1->id, $c1b, $ctxid, evidence::ACTION_COMPLETE, 'commentincontext', 'core');
2409 $uc1b = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1b->get('id')));
2410 $this->assertEquals(true, $uc1a->get('proficiency'));
2411 $uc1 = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id')));
2412 $this->assertEquals(true, $uc1->get('proficiency'));
2413 $this->assertEquals(user_competency::STATUS_IDLE, $uc1->get('status'));
2414 $this->assertEquals(1, evidence::count_records(array('usercompetencyid' => $uc1->get('id'))));
2416 // Check rule recommending.
2417 api::add_evidence($u1->id, $c2a, $ctxid, evidence::ACTION_COMPLETE, 'commentincontext', 'core');
2418 $uc2a = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c2a->get('id')));
2419 $this->assertEquals(true, $uc1a->get('proficiency'));
2420 $uc2 = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c2->get('id')));
2421 $this->assertSame(null, $uc2->get('proficiency'));
2422 $this->assertEquals(user_competency::STATUS_WAITING_FOR_REVIEW, $uc2->get('status'));
2423 $this->assertEquals(1, evidence::count_records(array('usercompetencyid' => $uc2->get('id'))));
2425 // Check rule evidence.
2426 api::add_evidence($u1->id, $c3a, $ctxid, evidence::ACTION_COMPLETE, 'commentincontext', 'core');
2427 $uc3a = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c3a->get('id')));
2428 $this->assertEquals(true, $uc1a->get('proficiency'));
2429 $uc3 = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c3->get('id')));
2430 $this->assertSame(null, $uc3->get('proficiency'));
2431 $this->assertEquals(user_competency::STATUS_IDLE, $uc3->get('status'));
2432 $this->assertEquals(1, evidence::count_records(array('usercompetencyid' => $uc3->get('id'))));
2434 // Check rule nothing.
2435 api::add_evidence($u1->id, $c4a, $ctxid, evidence::ACTION_COMPLETE, 'commentincontext', 'core');
2436 $uc4a = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c4a->get('id')));
2437 $this->assertEquals(true, $uc1a->get('proficiency'));
2438 $this->assertFalse(user_competency::record_exists_select('userid = ? AND competencyid = ?',
2439 array($u1->id, $c4->get('id'))));
2441 // Check marking on something that has no parent. This just checks that nothing breaks.
2442 api::add_evidence($u1->id, $c5, $ctxid, evidence::ACTION_COMPLETE, 'commentincontext', 'core');
2446 * Tests for the user_competency_course data when api::add_evidence() is invoked when
2447 * grading a user competency in the system context.
2449 public function test_add_evidence_for_user_competency_course_grade_outside_course(): void {
2450 $this->resetAfterTest(true);
2451 $dg = $this->getDataGenerator();
2452 $syscontext = \context_system::instance();
2454 // Create a student.
2455 $student = $dg->create_user();
2457 // Create a competency for the course.
2458 $lpg = $dg->get_plugin_generator('core_competency');
2459 $framework = $lpg->create_framework();
2460 $comp = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2462 // Add evidence.
2463 api::add_evidence($student->id, $comp, $syscontext, evidence::ACTION_OVERRIDE,
2464 'commentincontext', 'core', null, false, null, 1);
2466 // Query for user_competency_course data.
2467 $filterparams = array(
2468 'userid' => $student->id,
2469 'competencyid' => $comp->get('id'),
2471 $usercompcourse = \core_competency\user_competency_course::get_record($filterparams);
2472 // There should be no user_competency_course object created when grading.
2473 $this->assertFalse($usercompcourse);
2477 * Tests for the user_competency_course data when api::add_evidence() is invoked when
2478 * grading a user competency in a course.
2480 public function test_add_evidence_user_competency_course_grade_in_course(): void {
2481 global $USER;
2483 $this->resetAfterTest(true);
2484 $dg = $this->getDataGenerator();
2486 // Create and assign a current user.
2487 $currentuser = $dg->create_user();
2488 $this->setUser($currentuser);
2490 // Create a course.
2491 $course = $dg->create_course();
2492 $record = array('courseid' => $course->id, 'pushratingstouserplans' => false);
2493 $settings = new course_competency_settings(0, (object) $record);
2494 $settings->create();
2495 $coursecontext = \context_course::instance($course->id);
2497 // Create a student and enrol into the course.
2498 $student = $dg->create_user();
2499 $studentarch = get_archetype_roles('student');
2500 $studentrole = array_shift($studentarch);
2501 $dg->role_assign($studentrole->id, $student->id, $coursecontext->id);
2502 $dg->enrol_user($student->id, $course->id, $studentrole->id);
2504 // Create a competency for the course.
2505 $lpg = $dg->get_plugin_generator('core_competency');
2506 $framework = $lpg->create_framework();
2507 // Do not push ratings from course to user plans.
2508 $comp = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2509 $lpg->create_course_competency(array('courseid' => $course->id, 'competencyid' => $comp->get('id')));
2511 // Query for user_competency_course data.
2512 $filterparams = array(
2513 'userid' => $student->id,
2514 'competencyid' => $comp->get('id'),
2515 'courseid' => $course->id
2518 // Add evidence that sets a grade to the course.
2519 $evidence = api::add_evidence($student->id, $comp, $coursecontext, evidence::ACTION_OVERRIDE,
2520 'commentincontext', 'core', null, false, null, 3, $USER->id);
2521 // Get user competency course record.
2522 $usercompcourse = \core_competency\user_competency_course::get_record($filterparams);
2523 // There should be a user_competency_course object when adding a grade.
2524 $this->assertNotEmpty($usercompcourse);
2525 $grade = $evidence->get('grade');
2526 $this->assertEquals($grade, $usercompcourse->get('grade'));
2527 $this->assertEquals(3, $usercompcourse->get('grade'));
2528 $proficiency = $comp->get_proficiency_of_grade($grade);
2529 $this->assertEquals($proficiency, $usercompcourse->get('proficiency'));
2531 // Confirm that the user competency's grade/proficiency has not been affected by the grade.
2532 $usercompetencyparams = [
2533 'userid' => $student->id,
2534 'competencyid' => $comp->get('id'),
2536 $usercompetency = \core_competency\user_competency::get_record($usercompetencyparams);
2537 $this->assertNotEmpty($usercompetency);
2538 $this->assertNotEquals($usercompcourse->get('grade'), $usercompetency->get('grade'));
2539 $this->assertNotEquals($usercompcourse->get('proficiency'), $usercompetency->get('proficiency'));
2542 public function test_observe_course_completed(): void {
2543 $this->resetAfterTest(true);
2544 $dg = $this->getDataGenerator();
2545 $lpg = $dg->get_plugin_generator('core_competency');
2547 // Set-up users, framework, competencies and course competencies.
2548 $course = $dg->create_course();
2549 $coursectx = \context_course::instance($course->id);
2550 $u1 = $dg->create_user();
2551 $f1 = $lpg->create_framework();
2552 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2553 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2554 $c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2555 $c4 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2556 $cc1 = $lpg->create_course_competency(array('competencyid' => $c1->get('id'), 'courseid' => $course->id,
2557 'ruleoutcome' => \core_competency\course_competency::OUTCOME_NONE));
2558 $cc2 = $lpg->create_course_competency(array('competencyid' => $c2->get('id'), 'courseid' => $course->id,
2559 'ruleoutcome' => \core_competency\course_competency::OUTCOME_EVIDENCE));
2560 $cc3 = $lpg->create_course_competency(array('competencyid' => $c3->get('id'), 'courseid' => $course->id,
2561 'ruleoutcome' => \core_competency\course_competency::OUTCOME_RECOMMEND));
2562 $cc4 = $lpg->create_course_competency(array('competencyid' => $c4->get('id'), 'courseid' => $course->id,
2563 'ruleoutcome' => \core_competency\course_competency::OUTCOME_COMPLETE));
2565 $event = \core\event\course_completed::create(array(
2566 'objectid' => 1,
2567 'relateduserid' => $u1->id,
2568 'context' => $coursectx,
2569 'courseid' => $course->id,
2570 'other' => array('relateduserid' => $u1->id)
2572 $this->assertEquals(0, \core_competency\user_competency::count_records());
2573 $this->assertEquals(0, \core_competency\evidence::count_records());
2575 // Let's go!
2576 api::observe_course_completed($event);
2577 $this->assertEquals(3, \core_competency\user_competency::count_records());
2578 $this->assertEquals(3, \core_competency\evidence::count_records());
2580 // Outcome NONE did nothing.
2581 $this->assertFalse(\core_competency\user_competency::record_exists_select('userid = :uid AND competencyid = :cid', array(
2582 'uid' => $u1->id, 'cid' => $c1->get('id')
2583 )));
2585 // Outcome evidence.
2586 $uc2 = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c2->get('id')));
2587 $ev2 = \core_competency\evidence::get_record(array('usercompetencyid' => $uc2->get('id')));
2589 $this->assertEquals(null, $uc2->get('grade'));
2590 $this->assertEquals(null, $uc2->get('proficiency'));
2591 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc2->get('status'));
2593 $this->assertEquals('evidence_coursecompleted', $ev2->get('descidentifier'));
2594 $this->assertEquals('core_competency', $ev2->get('desccomponent'));
2595 $this->assertEquals($course->shortname, $ev2->get('desca'));
2596 $this->assertStringEndsWith('/report/completion/index.php?course=' . $course->id, $ev2->get('url'));
2597 $this->assertEquals(null, $ev2->get('grade'));
2598 $this->assertEquals($coursectx->id, $ev2->get('contextid'));
2599 $this->assertEquals(\core_competency\evidence::ACTION_LOG, $ev2->get('action'));
2600 $this->assertEquals(null, $ev2->get('actionuserid'));
2602 // Outcome recommend.
2603 $uc3 = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c3->get('id')));
2604 $ev3 = \core_competency\evidence::get_record(array('usercompetencyid' => $uc3->get('id')));
2606 $this->assertEquals(null, $uc3->get('grade'));
2607 $this->assertEquals(null, $uc3->get('proficiency'));
2608 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc3->get('status'));
2610 $this->assertEquals('evidence_coursecompleted', $ev3->get('descidentifier'));
2611 $this->assertEquals('core_competency', $ev3->get('desccomponent'));
2612 $this->assertEquals($course->shortname, $ev3->get('desca'));
2613 $this->assertStringEndsWith('/report/completion/index.php?course=' . $course->id, $ev3->get('url'));
2614 $this->assertEquals(null, $ev3->get('grade'));
2615 $this->assertEquals($coursectx->id, $ev3->get('contextid'));
2616 $this->assertEquals(\core_competency\evidence::ACTION_LOG, $ev3->get('action'));
2617 $this->assertEquals(null, $ev3->get('actionuserid'));
2619 // Outcome complete.
2620 $uc4 = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c4->get('id')));
2621 $ev4 = \core_competency\evidence::get_record(array('usercompetencyid' => $uc4->get('id')));
2623 $this->assertEquals(3, $uc4->get('grade'));
2624 $this->assertEquals(1, $uc4->get('proficiency'));
2625 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc4->get('status'));
2627 $this->assertEquals('evidence_coursecompleted', $ev4->get('descidentifier'));
2628 $this->assertEquals('core_competency', $ev4->get('desccomponent'));
2629 $this->assertEquals($course->shortname, $ev4->get('desca'));
2630 $this->assertStringEndsWith('/report/completion/index.php?course=' . $course->id, $ev4->get('url'));
2631 $this->assertEquals(3, $ev4->get('grade'));
2632 $this->assertEquals($coursectx->id, $ev4->get('contextid'));
2633 $this->assertEquals(\core_competency\evidence::ACTION_COMPLETE, $ev4->get('action'));
2634 $this->assertEquals(null, $ev4->get('actionuserid'));
2637 public function test_list_evidence_in_course(): void {
2638 global $SITE;
2640 $this->resetAfterTest(true);
2641 $dg = $this->getDataGenerator();
2642 $lpg = $dg->get_plugin_generator('core_competency');
2643 $u1 = $dg->create_user();
2644 $course = $dg->create_course();
2645 $coursecontext = \context_course::instance($course->id);
2647 $this->setAdminUser();
2648 $f = $lpg->create_framework();
2649 $c = $lpg->create_competency(array('competencyframeworkid' => $f->get('id')));
2650 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f->get('id')));
2651 $cc = api::add_competency_to_course($course->id, $c->get('id'));
2652 $cc2 = api::add_competency_to_course($course->id, $c2->get('id'));
2654 $pagegenerator = $this->getDataGenerator()->get_plugin_generator('mod_page');
2655 $page = $pagegenerator->create_instance(array('course' => $course->id));
2657 $cm = get_coursemodule_from_instance('page', $page->id);
2658 $cmcontext = \context_module::instance($cm->id);
2659 // Add the competency to the course module.
2660 $ccm = api::add_competency_to_course_module($cm, $c->get('id'));
2662 // Now add the evidence to the course.
2663 $evidence1 = api::add_evidence($u1->id, $c->get('id'), $coursecontext->id, \core_competency\evidence::ACTION_LOG,
2664 'invaliddata', 'error');
2666 $result = api::list_evidence_in_course($u1->id, $course->id, $c->get('id'));
2667 $this->assertEquals($result[0]->get('id'), $evidence1->get('id'));
2669 // Now add the evidence to the course module.
2670 $evidence2 = api::add_evidence($u1->id, $c->get('id'), $cmcontext->id, \core_competency\evidence::ACTION_LOG,
2671 'invaliddata', 'error');
2673 $result = api::list_evidence_in_course($u1->id, $course->id, $c->get('id'), 'timecreated', 'ASC');
2674 $this->assertEquals($evidence1->get('id'), $result[0]->get('id'));
2675 $this->assertEquals($evidence2->get('id'), $result[1]->get('id'));
2678 public function test_list_course_modules_using_competency(): void {
2679 global $SITE;
2681 $this->resetAfterTest(true);
2682 $dg = $this->getDataGenerator();
2683 $lpg = $dg->get_plugin_generator('core_competency');
2684 $u1 = $dg->create_user();
2685 $u2 = $dg->create_user();
2686 $course = $dg->create_course();
2687 $course2 = $dg->create_course();
2689 $this->setAdminUser();
2690 $f = $lpg->create_framework();
2691 $c = $lpg->create_competency(array('competencyframeworkid' => $f->get('id')));
2692 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f->get('id')));
2693 $cc = api::add_competency_to_course($course->id, $c->get('id'));
2694 $cc2 = api::add_competency_to_course($course->id, $c2->get('id'));
2696 // First check we get an empty list when there are no links.
2697 $expected = array();
2698 $result = api::list_course_modules_using_competency($c->get('id'), $course->id);
2699 $this->assertEquals($expected, $result);
2701 $pagegenerator = $this->getDataGenerator()->get_plugin_generator('mod_page');
2702 $page = $pagegenerator->create_instance(array('course' => $course->id));
2704 $cm = get_coursemodule_from_instance('page', $page->id);
2705 // Add a link and list again.
2706 $ccm = api::add_competency_to_course_module($cm, $c->get('id'));
2707 $expected = array($cm->id);
2708 $result = api::list_course_modules_using_competency($c->get('id'), $course->id);
2709 $this->assertEquals($expected, $result);
2711 // Check a different course.
2712 $expected = array();
2713 $result = api::list_course_modules_using_competency($c->get('id'), $course2->id);
2714 $this->assertEquals($expected, $result);
2716 // Remove the link and check again.
2717 $result = api::remove_competency_from_course_module($cm, $c->get('id'));
2718 $expected = true;
2719 $this->assertEquals($expected, $result);
2720 $expected = array();
2721 $result = api::list_course_modules_using_competency($c->get('id'), $course->id);
2722 $this->assertEquals($expected, $result);
2724 // Now add 2 links.
2725 api::add_competency_to_course_module($cm, $c->get('id'));
2726 api::add_competency_to_course_module($cm, $c2->get('id'));
2727 $result = api::list_course_module_competencies_in_course_module($cm->id);
2728 $this->assertEquals($result[0]->get('competencyid'), $c->get('id'));
2729 $this->assertEquals($result[1]->get('competencyid'), $c2->get('id'));
2731 // Now re-order.
2732 api::reorder_course_module_competency($cm, $c->get('id'), $c2->get('id'));
2733 $result = api::list_course_module_competencies_in_course_module($cm->id);
2734 $this->assertEquals($result[0]->get('competencyid'), $c2->get('id'));
2735 $this->assertEquals($result[1]->get('competencyid'), $c->get('id'));
2737 // And re-order again.
2738 api::reorder_course_module_competency($cm, $c->get('id'), $c2->get('id'));
2739 $result = api::list_course_module_competencies_in_course_module($cm->id);
2740 $this->assertEquals($result[0]->get('competencyid'), $c->get('id'));
2741 $this->assertEquals($result[1]->get('competencyid'), $c2->get('id'));
2743 // Now get the course competency and coursemodule competency together.
2744 $result = api::list_course_module_competencies($cm->id);
2745 // Now we should have an array and each element of the array should have a competency and
2746 // a coursemodulecompetency.
2747 foreach ($result as $instance) {
2748 $cmc = $instance['coursemodulecompetency'];
2749 $c = $instance['competency'];
2750 $this->assertEquals($cmc->get('competencyid'), $c->get('id'));
2755 * Test update ruleoutcome for course_competency.
2757 public function test_set_ruleoutcome_course_competency(): void {
2758 $this->resetAfterTest(true);
2759 $dg = $this->getDataGenerator();
2760 $lpg = $dg->get_plugin_generator('core_competency');
2761 $u1 = $dg->create_user();
2762 $u2 = $dg->create_user();
2763 $course = $dg->create_course();
2765 $this->setAdminUser();
2766 $f = $lpg->create_framework();
2767 $c = $lpg->create_competency(array('competencyframeworkid' => $f->get('id')));
2768 $cc = api::add_competency_to_course($course->id, $c->get('id'));
2770 // Check record was created with default rule value Evidence.
2771 $this->assertEquals(1, \core_competency\course_competency::count_records());
2772 $recordscc = api::list_course_competencies($course->id);
2773 $this->assertEquals(\core_competency\course_competency::OUTCOME_EVIDENCE,
2774 $recordscc[0]['coursecompetency']->get('ruleoutcome'));
2776 // Check ruleoutcome value is updated to None.
2777 $this->assertTrue(api::set_course_competency_ruleoutcome($recordscc[0]['coursecompetency']->get('id'),
2778 \core_competency\course_competency::OUTCOME_NONE));
2779 $recordscc = api::list_course_competencies($course->id);
2780 $this->assertEquals(\core_competency\course_competency::OUTCOME_NONE,
2781 $recordscc[0]['coursecompetency']->get('ruleoutcome'));
2785 * Test validation on grade on user_competency.
2787 public function test_validate_grade_in_user_competency(): void {
2788 global $DB;
2790 $this->resetAfterTest(true);
2791 $this->setAdminUser();
2792 $dg = $this->getDataGenerator();
2793 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
2794 $user = $dg->create_user();
2796 $s1 = $dg->create_scale(array("scale" => "value1, value2"));
2797 $s2 = $dg->create_scale(array("scale" => "value3, value4, value5, value6"));
2799 $scaleconfiguration1 = '[{"scaleid":"'.$s1->id.'"},{"name":"value1","id":1,"scaledefault":1,"proficient":0},' .
2800 '{"name":"value2","id":2,"scaledefault":0,"proficient":1}]';
2801 $scaleconfiguration2 = '[{"scaleid":"'.$s2->id.'"},{"name":"value3","id":1,"scaledefault":1,"proficient":0},'
2802 . '{"name":"value4","id":2,"scaledefault":0,"proficient":1}]';
2804 // Create a framework with scale configuration1.
2805 $frm = array(
2806 'scaleid' => $s1->id,
2807 'scaleconfiguration' => $scaleconfiguration1
2809 $framework = $lpg->create_framework($frm);
2810 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2812 // Create competency with its own scale configuration.
2813 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'),
2814 'scaleid' => $s2->id,
2815 'scaleconfiguration' => $scaleconfiguration2
2818 // Detecte invalid grade in competency using its framework competency scale.
2819 try {
2820 $usercompetency = new user_competency(0, (object) array('userid' => $user->id, 'competencyid' => $c1->get('id'),
2821 'proficiency' => true, 'grade' => 3 ));
2822 $usercompetency->create();
2823 $this->fail('Invalid grade not detected in framework scale');
2824 } catch (\core\invalid_persistent_exception $e) {
2825 $this->assertTrue(true);
2828 // Detecte invalid grade in competency using its own scale.
2829 try {
2830 $usercompetency = new user_competency(0, (object) array('userid' => $user->id, 'competencyid' => $c2->get('id'),
2831 'proficiency' => true, 'grade' => 5 ));
2832 $usercompetency->create();
2833 $this->fail('Invalid grade not detected in competency scale');
2834 } catch (\core\invalid_persistent_exception $e) {
2835 $this->assertTrue(true);
2838 // Accept valid grade in competency using its framework competency scale.
2839 try {
2840 $usercompetency = new user_competency(0, (object) array('userid' => $user->id, 'competencyid' => $c1->get('id'),
2841 'proficiency' => true, 'grade' => 1 ));
2842 $usercompetency->create();
2843 $this->assertTrue(true);
2844 } catch (\core\invalid_persistent_exception $e) {
2845 $this->fail('Valide grade rejected in framework scale');
2848 // Accept valid grade in competency using its framework competency scale.
2849 try {
2850 $usercompetency = new user_competency(0, (object) array('userid' => $user->id, 'competencyid' => $c2->get('id'),
2851 'proficiency' => true, 'grade' => 4 ));
2852 $usercompetency->create();
2853 $this->assertTrue(true);
2854 } catch (\core\invalid_persistent_exception $e) {
2855 $this->fail('Valide grade rejected in competency scale');
2860 * Test when adding competency that belong to hidden framework to plan/template/course.
2862 public function test_hidden_framework(): void {
2863 $this->resetAfterTest(true);
2864 $this->setAdminUser();
2865 $dg = $this->getDataGenerator();
2866 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
2867 $user = $dg->create_user();
2869 // Create a course.
2870 $cat1 = $dg->create_category();
2871 $course = $dg->create_course(array('category' => $cat1->id));
2872 // Create a template.
2873 $template = $lpg->create_template();
2874 // Create a plan.
2875 $plan = $lpg->create_plan(array('userid' => $user->id));
2877 // Create a hidden framework.
2878 $frm = array(
2879 'visible' => false
2881 $framework = $lpg->create_framework($frm);
2882 $competency = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2884 // Linking competency that belong to hidden framework to course.
2885 try {
2886 api::add_competency_to_course($course->id, $competency->get('id'));
2887 $this->fail('A competency belonging to hidden framework can not be linked to course');
2888 } catch (\coding_exception $e) {
2889 $this->assertTrue(true);
2892 // Adding competency that belong to hidden framework to template.
2893 try {
2894 api::add_competency_to_template($template->get('id'), $competency->get('id'));
2895 $this->fail('A competency belonging to hidden framework can not be added to template');
2896 } catch (\coding_exception $e) {
2897 $this->assertTrue(true);
2900 // Adding competency that belong to hidden framework to plan.
2901 try {
2902 api::add_competency_to_plan($plan->get('id'), $competency->get('id'));
2903 $this->fail('A competency belonging to hidden framework can not be added to plan');
2904 } catch (\coding_exception $e) {
2905 $this->assertTrue(true);
2910 * Test when using hidden template in plan/cohort.
2912 public function test_hidden_template(): void {
2913 $this->resetAfterTest(true);
2914 $this->setAdminUser();
2915 $dg = $this->getDataGenerator();
2916 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
2917 $user = $dg->create_user();
2919 // Create a cohort.
2920 $cohort = $dg->create_cohort();
2921 // Create a hidden template.
2922 $template = $lpg->create_template(array('visible' => false));
2924 // Can not link hidden template to plan.
2925 try {
2926 api::create_plan_from_template($template->get('id'), $user->id);
2927 $this->fail('Can not link a hidden template to plan');
2928 } catch (\coding_exception $e) {
2929 $this->assertTrue(true);
2932 // Can associate hidden template to cohort.
2933 $templatecohort = api::create_template_cohort($template->get('id'), $cohort->id);
2934 $this->assertInstanceOf('\core_competency\template_cohort', $templatecohort);
2938 * Test that completed plan created form a template does not change when template is modified.
2940 public function test_completed_plan_doesnot_change(): void {
2941 global $DB;
2943 $this->resetAfterTest(true);
2944 $this->setAdminUser();
2945 $dg = $this->getDataGenerator();
2946 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
2947 $user = $dg->create_user();
2949 // Create a framework and assign competencies.
2950 $framework = $lpg->create_framework();
2951 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2952 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2953 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2954 $c4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
2956 // Create template and assign competencies.
2957 $tp = $lpg->create_template();
2958 $tpc1 = $lpg->create_template_competency(array('templateid' => $tp->get('id'), 'competencyid' => $c1->get('id')));
2959 $tpc2 = $lpg->create_template_competency(array('templateid' => $tp->get('id'), 'competencyid' => $c2->get('id')));
2960 $tpc3 = $lpg->create_template_competency(array('templateid' => $tp->get('id'), 'competencyid' => $c3->get('id')));
2962 // Create a plan form template and change it status to complete.
2963 $plan = $lpg->create_plan(array('userid' => $user->id, 'templateid' => $tp->get('id')));
2964 api::complete_plan($plan);
2966 // Check user competency plan created correctly.
2967 $this->assertEquals(3, \core_competency\user_competency_plan::count_records());
2968 $ucp = \core_competency\user_competency_plan::get_records();
2969 $this->assertEquals($ucp[0]->get('competencyid'), $c1->get('id'));
2970 $this->assertEquals($ucp[1]->get('competencyid'), $c2->get('id'));
2971 $this->assertEquals($ucp[2]->get('competencyid'), $c3->get('id'));
2973 // Add and remove a competency from the template.
2974 api::add_competency_to_template($tp->get('id'), $c4->get('id'));
2975 api::remove_competency_from_template($tp->get('id'), $c1->get('id'));
2977 // Check that user competency plan did not change.
2978 $competencies = $plan->get_competencies();
2979 $this->assertEquals(3, count($competencies));
2980 $ucp1 = array($c1->get('id'), $c2->get('id'), $c3->get('id'));
2981 $ucp2 = array();
2982 foreach ($competencies as $id => $cmp) {
2983 $ucp2[] = $id;
2985 $this->assertEquals(0, count(array_diff($ucp1, $ucp2)));
2988 protected function setup_framework_for_reset_rules_tests() {
2989 $this->resetAfterTest(true);
2990 $dg = $this->getDataGenerator();
2991 $lpg = $dg->get_plugin_generator('core_competency');
2993 $this->setAdminUser();
2994 $f1 = $lpg->create_framework();
2995 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
2996 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
2997 $c1a1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
2998 $c1a1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a1->get('id')));
2999 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3000 $c1b1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3001 $c1b1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b1->get('id')));
3002 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3003 $c2a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3005 $c1->set('ruleoutcome', competency::OUTCOME_EVIDENCE);
3006 $c1->set('ruletype', 'core_competency\\competency_rule_all');
3007 $c1->update();
3008 $c1a->set('ruleoutcome', competency::OUTCOME_EVIDENCE);
3009 $c1a->set('ruletype', 'core_competency\\competency_rule_all');
3010 $c1a->update();
3011 $c1a1->set('ruleoutcome', competency::OUTCOME_EVIDENCE);
3012 $c1a1->set('ruletype', 'core_competency\\competency_rule_all');
3013 $c1a1->update();
3014 $c1b->set('ruleoutcome', competency::OUTCOME_EVIDENCE);
3015 $c1b->set('ruletype', 'core_competency\\competency_rule_all');
3016 $c1b->update();
3017 $c2->set('ruleoutcome', competency::OUTCOME_EVIDENCE);
3018 $c2->set('ruletype', 'core_competency\\competency_rule_all');
3019 $c2->update();
3021 return array(
3022 'f1' => $f1,
3023 'c1' => $c1,
3024 'c1a' => $c1a,
3025 'c1a1' => $c1a1,
3026 'c1a1a' => $c1a1a,
3027 'c1b' => $c1b,
3028 'c1b1' => $c1b1,
3029 'c1b1a' => $c1b1a,
3030 'c2' => $c2,
3031 'c2a' => $c2a,
3035 public function test_moving_competency_reset_rules_updown(): void {
3036 $data = $this->setup_framework_for_reset_rules_tests();
3037 $f1 = $data['f1'];
3038 $c1 = $data['c1'];
3039 $c1a = $data['c1a'];
3040 $c1a1 = $data['c1a1'];
3041 $c1a1a = $data['c1a1a'];
3042 $c1b = $data['c1b'];
3043 $c1b1 = $data['c1b1'];
3044 $c1b1a = $data['c1b1a'];
3045 $c2 = $data['c2'];
3046 $c2a = $data['c2a'];
3048 // Moving up and down doesn't change anything.
3049 api::move_down_competency($c1a->get('id'));
3050 $c1->read();
3051 $c1a->read();
3052 $c1a1->read();
3053 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1->get('ruleoutcome'));
3054 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a->get('ruleoutcome'));
3055 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a1->get('ruleoutcome'));
3056 api::move_up_competency($c1a->get('id'));
3057 $c1->read();
3058 $c1a->read();
3059 $c1a1->read();
3060 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1->get('ruleoutcome'));
3061 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a->get('ruleoutcome'));
3062 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a1->get('ruleoutcome'));
3065 public function test_moving_competency_reset_rules_parent(): void {
3066 $data = $this->setup_framework_for_reset_rules_tests();
3067 $f1 = $data['f1'];
3068 $c1 = $data['c1'];
3069 $c1a = $data['c1a'];
3070 $c1a1 = $data['c1a1'];
3071 $c1a1a = $data['c1a1a'];
3072 $c1b = $data['c1b'];
3073 $c1b1 = $data['c1b1'];
3074 $c1b1a = $data['c1b1a'];
3075 $c2 = $data['c2'];
3076 $c2a = $data['c2a'];
3078 // Moving out of parent will reset the parent, and the destination.
3079 api::set_parent_competency($c1a->get('id'), $c1b->get('id'));
3080 $c1->read();
3081 $c1a->read();
3082 $c1a1->read();
3083 $c1b->read();
3084 $c2->read();
3085 $this->assertEquals(competency::OUTCOME_NONE, $c1->get('ruleoutcome'));
3086 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a->get('ruleoutcome'));
3087 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a1->get('ruleoutcome'));
3088 $this->assertEquals(competency::OUTCOME_NONE, $c1b->get('ruleoutcome'));
3089 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c2->get('ruleoutcome'));
3092 public function test_moving_competency_reset_rules_totoplevel(): void {
3093 $data = $this->setup_framework_for_reset_rules_tests();
3094 $f1 = $data['f1'];
3095 $c1 = $data['c1'];
3096 $c1a = $data['c1a'];
3097 $c1a1 = $data['c1a1'];
3098 $c1a1a = $data['c1a1a'];
3099 $c1b = $data['c1b'];
3100 $c1b1 = $data['c1b1'];
3101 $c1b1a = $data['c1b1a'];
3102 $c2 = $data['c2'];
3103 $c2a = $data['c2a'];
3105 // Moving to top level only affects the initial parent.
3106 api::set_parent_competency($c1a1->get('id'), 0);
3107 $c1->read();
3108 $c1a->read();
3109 $c1a1->read();
3110 $c1b->read();
3111 $c2->read();
3112 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1->get('ruleoutcome'));
3113 $this->assertEquals(competency::OUTCOME_NONE, $c1a->get('ruleoutcome'));
3114 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a1->get('ruleoutcome'));
3115 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1b->get('ruleoutcome'));
3116 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c2->get('ruleoutcome'));
3119 public function test_moving_competency_reset_rules_fromtoplevel(): void {
3120 $data = $this->setup_framework_for_reset_rules_tests();
3121 $f1 = $data['f1'];
3122 $c1 = $data['c1'];
3123 $c1a = $data['c1a'];
3124 $c1a1 = $data['c1a1'];
3125 $c1a1a = $data['c1a1a'];
3126 $c1b = $data['c1b'];
3127 $c1b1 = $data['c1b1'];
3128 $c1b1a = $data['c1b1a'];
3129 $c2 = $data['c2'];
3130 $c2a = $data['c2a'];
3132 // Moving from top level only affects the destination parent.
3133 api::set_parent_competency($c2->get('id'), $c1a1->get('id'));
3134 $c1->read();
3135 $c1a->read();
3136 $c1a1->read();
3137 $c1b->read();
3138 $c2->read();
3139 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1->get('ruleoutcome'));
3140 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a->get('ruleoutcome'));
3141 $this->assertEquals(competency::OUTCOME_NONE, $c1a1->get('ruleoutcome'));
3142 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1b->get('ruleoutcome'));
3143 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c2->get('ruleoutcome'));
3146 public function test_moving_competency_reset_rules_child(): void {
3147 $data = $this->setup_framework_for_reset_rules_tests();
3148 $f1 = $data['f1'];
3149 $c1 = $data['c1'];
3150 $c1a = $data['c1a'];
3151 $c1a1 = $data['c1a1'];
3152 $c1a1a = $data['c1a1a'];
3153 $c1b = $data['c1b'];
3154 $c1b1 = $data['c1b1'];
3155 $c1b1a = $data['c1b1a'];
3156 $c2 = $data['c2'];
3157 $c2a = $data['c2a'];
3159 // Moving to a child of self resets self, parent and destination.
3160 api::set_parent_competency($c1a->get('id'), $c1a1->get('id'));
3161 $c1->read();
3162 $c1a->read();
3163 $c1a1->read();
3164 $c1b->read();
3165 $c2->read();
3166 $this->assertEquals(competency::OUTCOME_NONE, $c1->get('ruleoutcome'));
3167 $this->assertEquals(competency::OUTCOME_NONE, $c1a->get('ruleoutcome'));
3168 $this->assertEquals(competency::OUTCOME_NONE, $c1a1->get('ruleoutcome'));
3169 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1b->get('ruleoutcome'));
3170 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c2->get('ruleoutcome'));
3173 public function test_create_competency_reset_rules(): void {
3174 $data = $this->setup_framework_for_reset_rules_tests();
3175 $f1 = $data['f1'];
3176 $c1 = $data['c1'];
3177 $c1a = $data['c1a'];
3178 $c1a1 = $data['c1a1'];
3179 $c1a1a = $data['c1a1a'];
3180 $c1b = $data['c1b'];
3181 $c1b1 = $data['c1b1'];
3182 $c1b1a = $data['c1b1a'];
3183 $c2 = $data['c2'];
3184 $c2a = $data['c2a'];
3186 // Adding a new competency resets the rule of its parent.
3187 api::create_competency((object) array('shortname' => 'A', 'parentid' => $c1->get('id'), 'idnumber' => 'A',
3188 'competencyframeworkid' => $f1->get('id')));
3189 $c1->read();
3190 $c1a->read();
3191 $c1a1->read();
3192 $c1b->read();
3193 $c2->read();
3194 $this->assertEquals(competency::OUTCOME_NONE, $c1->get('ruleoutcome'));
3195 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a->get('ruleoutcome'));
3196 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1a1->get('ruleoutcome'));
3197 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1b->get('ruleoutcome'));
3198 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c2->get('ruleoutcome'));
3201 public function test_delete_competency_reset_rules(): void {
3202 $data = $this->setup_framework_for_reset_rules_tests();
3203 $f1 = $data['f1'];
3204 $c1 = $data['c1'];
3205 $c1a = $data['c1a'];
3206 $c1a1 = $data['c1a1'];
3207 $c1a1a = $data['c1a1a'];
3208 $c1b = $data['c1b'];
3209 $c1b1 = $data['c1b1'];
3210 $c1b1a = $data['c1b1a'];
3211 $c2 = $data['c2'];
3212 $c2a = $data['c2a'];
3214 // Deleting a competency resets the rule of its parent.
3215 api::delete_competency($c1a->get('id'));
3216 $c1->read();
3217 $c1b->read();
3218 $c2->read();
3219 $this->assertEquals(competency::OUTCOME_NONE, $c1->get('ruleoutcome'));
3220 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c1b->get('ruleoutcome'));
3221 $this->assertEquals(competency::OUTCOME_EVIDENCE, $c2->get('ruleoutcome'));
3224 public function test_template_has_related_data(): void {
3225 $this->resetAfterTest(true);
3226 $this->setAdminUser();
3228 $dg = $this->getDataGenerator();
3229 $user = $dg->create_user();
3230 $lpg = $dg->get_plugin_generator('core_competency');
3231 $tpl1 = $lpg->create_template();
3232 $tpl2 = $lpg->create_template();
3234 // Create plans for first template.
3235 $time = time();
3236 $plan1 = $lpg->create_plan(array('templateid' => $tpl1->get('id'), 'userid' => $user->id,
3237 'name' => 'Not good name', 'duedate' => $time + 3600, 'description' => 'Ahah', 'descriptionformat' => FORMAT_PLAIN));
3239 $this->assertTrue(api::template_has_related_data($tpl1->get('id')));
3240 $this->assertFalse(api::template_has_related_data($tpl2->get('id')));
3244 public function test_delete_template_delete_plans(): void {
3245 $this->resetAfterTest(true);
3246 $this->setAdminUser();
3248 $dg = $this->getDataGenerator();
3249 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
3251 $u1 = $dg->create_user();
3252 $f = $lpg->create_framework();
3253 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f->get('id')));
3254 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f->get('id')));
3256 $tpl = $lpg->create_template();
3258 $tplc1 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $c1->get('id'),
3259 'sortorder' => 1));
3260 $tplc2 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $c2->get('id'),
3261 'sortorder' => 2));
3263 $p1 = $lpg->create_plan(array('templateid' => $tpl->get('id'), 'userid' => $u1->id));
3265 // Check pre-test.
3266 $this->assertTrue(\core_competency\template::record_exists($tpl->get('id')));
3267 $this->assertEquals(2, \core_competency\template_competency::count_competencies($tpl->get('id')));
3268 $this->assertEquals(1, count(\core_competency\plan::get_records(array('templateid' => $tpl->get('id')))));
3270 $result = api::delete_template($tpl->get('id'), true);
3271 $this->assertTrue($result);
3273 // Check that the template does not exist anymore.
3274 $this->assertFalse(\core_competency\template::record_exists($tpl->get('id')));
3276 // Check that associated competencies are also deleted.
3277 $this->assertEquals(0, \core_competency\template_competency::count_competencies($tpl->get('id')));
3279 // Check that associated plan are also deleted.
3280 $this->assertEquals(0, count(\core_competency\plan::get_records(array('templateid' => $tpl->get('id')))));
3283 public function test_delete_template_unlink_plans(): void {
3284 $this->resetAfterTest(true);
3285 $this->setAdminUser();
3287 $dg = $this->getDataGenerator();
3288 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
3290 $u1 = $dg->create_user();
3291 $f = $lpg->create_framework();
3292 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f->get('id')));
3293 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f->get('id')));
3295 $tpl = $lpg->create_template();
3297 $tplc1 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $c1->get('id'),
3298 'sortorder' => 1));
3299 $tplc2 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $c2->get('id'),
3300 'sortorder' => 2));
3302 $p1 = $lpg->create_plan(array('templateid' => $tpl->get('id'), 'userid' => $u1->id));
3304 // Check pre-test.
3305 $this->assertTrue(\core_competency\template::record_exists($tpl->get('id')));
3306 $this->assertEquals(2, \core_competency\template_competency::count_competencies($tpl->get('id')));
3307 $this->assertEquals(1, count(\core_competency\plan::get_records(array('templateid' => $tpl->get('id')))));
3309 $result = api::delete_template($tpl->get('id'), false);
3310 $this->assertTrue($result);
3312 // Check that the template does not exist anymore.
3313 $this->assertFalse(\core_competency\template::record_exists($tpl->get('id')));
3315 // Check that associated competencies are also deleted.
3316 $this->assertEquals(0, \core_competency\template_competency::count_competencies($tpl->get('id')));
3318 // Check that associated plan still exist but unlink from template.
3319 $plans = \core_competency\plan::get_records(array('id' => $p1->get('id')));
3320 $this->assertEquals(1, count($plans));
3321 $this->assertEquals($plans[0]->get('origtemplateid'), $tpl->get('id'));
3322 $this->assertNull($plans[0]->get('templateid'));
3325 public function test_delete_competency(): void {
3326 $this->resetAfterTest(true);
3327 $dg = $this->getDataGenerator();
3328 $lpg = $dg->get_plugin_generator('core_competency');
3329 $this->setAdminUser();
3331 $u1 = $dg->create_user();
3333 $f1 = $lpg->create_framework();
3334 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3335 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3336 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3337 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3338 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3339 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3341 // Set rules on parent competency.
3342 $c1->set('ruleoutcome', competency::OUTCOME_EVIDENCE);
3343 $c1->set('ruletype', 'core_competency\\competency_rule_all');
3344 $c1->update();
3346 // If we delete competeny, the related competencies relations and evidences should be deleted.
3347 // Create related competencies using one of c1a competency descendants.
3348 $rc = $lpg->create_related_competency(array(
3349 'competencyid' => $c2->get('id'),
3350 'relatedcompetencyid' => $c11b->get('id')
3352 $this->assertEquals($c11b->get('id'), $rc->get('relatedcompetencyid'));
3354 // Creating a standard evidence with minimal information.
3355 $uc2 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c11b->get('id')));
3356 $evidence = $lpg->create_evidence(array('usercompetencyid' => $uc2->get('id')));
3357 $this->assertEquals($uc2->get('id'), $evidence->get('usercompetencyid'));
3358 $uc2->delete();
3360 $this->assertTrue(api::delete_competency($c1a->get('id')));
3361 $this->assertFalse(competency::record_exists($c1a->get('id')));
3363 // Check that on delete, we reset the rule on parent competency.
3364 $c1->read();
3365 $this->assertNull($c1->get('ruletype'));
3366 $this->assertNull($c1->get('ruletype'));
3367 $this->assertEquals(competency::OUTCOME_NONE, $c1->get('ruleoutcome'));
3369 // Check that descendants were also deleted.
3370 $this->assertFalse(competency::record_exists($c1b->get('id')));
3371 $this->assertFalse(competency::record_exists($c11b->get('id')));
3372 $this->assertFalse(competency::record_exists($c12b->get('id')));
3374 // Check if evidence are also deleted.
3375 $this->assertEquals(0, \core_competency\user_evidence_competency::count_records(array('competencyid' => $c11b->get('id'))));
3377 // Check if related conpetency relation is deleted.
3378 $this->assertEquals(0, count(api::list_related_competencies($c2->get('id'))));
3380 // Delete a simple competency.
3381 $this->assertTrue(api::delete_competency($c2->get('id')));
3382 $this->assertFalse(competency::record_exists($c2->get('id')));
3385 public function test_delete_competency_used_in_plan(): void {
3386 $this->resetAfterTest(true);
3387 $dg = $this->getDataGenerator();
3388 $lpg = $dg->get_plugin_generator('core_competency');
3389 $this->setAdminUser();
3391 $u1 = $dg->create_user();
3393 $plan = $lpg->create_plan((object) array('userid' => $u1->id));
3395 $f1 = $lpg->create_framework();
3396 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3397 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3398 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3399 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3400 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3401 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3403 // Add competency to plan.
3404 $pc = $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c11b->get('id')));
3405 // We can not delete a competency , if competency or competency children is associated to plan.
3406 $this->assertFalse(api::delete_competency($c1a->get('id')));
3408 // We can delete the competency if we remove the competency from the plan.
3409 $pc->delete();
3411 $this->assertTrue(api::delete_competency($c1a->get('id')));
3412 $this->assertFalse(competency::record_exists($c1a->get('id')));
3413 $this->assertFalse(competency::record_exists($c1b->get('id')));
3414 $this->assertFalse(competency::record_exists($c11b->get('id')));
3415 $this->assertFalse(competency::record_exists($c12b->get('id')));
3418 public function test_delete_competency_used_in_usercompetency(): void {
3419 $this->resetAfterTest(true);
3420 $dg = $this->getDataGenerator();
3421 $lpg = $dg->get_plugin_generator('core_competency');
3422 $this->setAdminUser();
3424 $u1 = $dg->create_user();
3426 $f1 = $lpg->create_framework();
3427 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3428 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3429 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3430 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3431 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3432 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3434 // Create user competency.
3435 $uc1 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c11b->get('id')));
3437 // We can not delete a competency , if competency or competency children exist in user competency.
3438 $this->assertFalse(api::delete_competency($c1a->get('id')));
3440 // We can delete the competency if we remove the competency from user competency.
3441 $uc1->delete();
3443 $this->assertTrue(api::delete_competency($c1a->get('id')));
3444 $this->assertFalse(competency::record_exists($c1a->get('id')));
3445 $this->assertFalse(competency::record_exists($c1b->get('id')));
3446 $this->assertFalse(competency::record_exists($c11b->get('id')));
3447 $this->assertFalse(competency::record_exists($c12b->get('id')));
3450 public function test_delete_competency_used_in_usercompetencyplan(): void {
3451 $this->resetAfterTest(true);
3452 $dg = $this->getDataGenerator();
3453 $lpg = $dg->get_plugin_generator('core_competency');
3454 $this->setAdminUser();
3456 $u1 = $dg->create_user();
3458 $plan = $lpg->create_plan((object) array('userid' => $u1->id));
3460 $f1 = $lpg->create_framework();
3461 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3462 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3463 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3464 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3465 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3466 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3468 // Create user competency plan.
3469 $uc2 = $lpg->create_user_competency_plan(array(
3470 'userid' => $u1->id,
3471 'competencyid' => $c11b->get('id'),
3472 'planid' => $plan->get('id')
3475 // We can not delete a competency , if competency or competency children exist in user competency plan.
3476 $this->assertFalse(api::delete_competency($c1a->get('id')));
3478 // We can delete the competency if we remove the competency from user competency plan.
3479 $uc2->delete();
3481 $this->assertTrue(api::delete_competency($c1a->get('id')));
3482 $this->assertFalse(competency::record_exists($c1a->get('id')));
3483 $this->assertFalse(competency::record_exists($c1b->get('id')));
3484 $this->assertFalse(competency::record_exists($c11b->get('id')));
3485 $this->assertFalse(competency::record_exists($c12b->get('id')));
3488 public function test_delete_competency_used_in_template(): void {
3489 $this->resetAfterTest(true);
3490 $dg = $this->getDataGenerator();
3491 $lpg = $dg->get_plugin_generator('core_competency');
3492 $this->setAdminUser();
3494 $template = $lpg->create_template();
3496 $f1 = $lpg->create_framework();
3497 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3498 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3499 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3500 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3501 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3502 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3504 // Add competency to a template.
3505 $tc = $lpg->create_template_competency(array(
3506 'templateid' => $template->get('id'),
3507 'competencyid' => $c11b->get('id')
3509 // We can not delete a competency , if competency or competency children is linked to template.
3510 $this->assertFalse(api::delete_competency($c1a->get('id')));
3512 // We can delete the competency if we remove the competency from template.
3513 $tc->delete();
3515 $this->assertTrue(api::delete_competency($c1a->get('id')));
3516 $this->assertFalse(competency::record_exists($c1a->get('id')));
3517 $this->assertFalse(competency::record_exists($c1b->get('id')));
3518 $this->assertFalse(competency::record_exists($c11b->get('id')));
3519 $this->assertFalse(competency::record_exists($c12b->get('id')));
3522 public function test_delete_competency_used_in_course(): void {
3523 $this->resetAfterTest(true);
3524 $dg = $this->getDataGenerator();
3525 $lpg = $dg->get_plugin_generator('core_competency');
3526 $this->setAdminUser();
3528 $cat1 = $dg->create_category();
3530 $course = $dg->create_course(array('category' => $cat1->id));
3532 $f1 = $lpg->create_framework();
3533 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3534 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3535 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3536 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3537 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3538 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3540 // Add competency to course.
3541 $cc = $lpg->create_course_competency(array(
3542 'courseid' => $course->id,
3543 'competencyid' => $c11b->get('id')
3546 // We can not delete a competency if the competency or competencies children is linked to a course.
3547 $this->assertFalse(api::delete_competency($c1a->get('id')));
3549 // We can delete the competency if we remove the competency from course.
3550 $cc->delete();
3552 $this->assertTrue(api::delete_competency($c1a->get('id')));
3553 $this->assertFalse(competency::record_exists($c1a->get('id')));
3554 $this->assertFalse(competency::record_exists($c1b->get('id')));
3555 $this->assertFalse(competency::record_exists($c11b->get('id')));
3556 $this->assertFalse(competency::record_exists($c12b->get('id')));
3559 public function test_delete_framework(): void {
3560 $this->resetAfterTest(true);
3561 $dg = $this->getDataGenerator();
3562 $lpg = $dg->get_plugin_generator('core_competency');
3563 $this->setAdminUser();
3565 $u1 = $dg->create_user();
3567 $f1 = $lpg->create_framework();
3568 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3569 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3570 $c2id = $c2->get('id');
3571 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3572 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3573 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3574 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3576 // If we delete framework, the related competencies relations and evidences should be deleted.
3577 // Create related competencies using one of c1a competency descendants.
3578 $rc = $lpg->create_related_competency(array(
3579 'competencyid' => $c2->get('id'),
3580 'relatedcompetencyid' => $c11b->get('id')
3582 $this->assertEquals($c11b->get('id'), $rc->get('relatedcompetencyid'));
3584 // Creating a standard evidence with minimal information.
3585 $uc2 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c11b->get('id')));
3586 $evidence = $lpg->create_evidence(array('usercompetencyid' => $uc2->get('id')));
3587 $this->assertEquals($uc2->get('id'), $evidence->get('usercompetencyid'));
3588 $uc2->delete();
3590 $this->assertTrue(api::delete_framework($f1->get('id')));
3591 $this->assertFalse(competency_framework::record_exists($f1->get('id')));
3593 // Check that all competencies were also deleted.
3594 $this->assertFalse(competency::record_exists($c1->get('id')));
3595 $this->assertFalse(competency::record_exists($c2->get('id')));
3596 $this->assertFalse(competency::record_exists($c1a->get('id')));
3597 $this->assertFalse(competency::record_exists($c1b->get('id')));
3598 $this->assertFalse(competency::record_exists($c11b->get('id')));
3599 $this->assertFalse(competency::record_exists($c12b->get('id')));
3601 // Check if evidence are also deleted.
3602 $this->assertEquals(0, \core_competency\user_evidence_competency::count_records(array('competencyid' => $c11b->get('id'))));
3604 // Check if related conpetency relation is deleted.
3605 $this->assertEquals(0, count(\core_competency\related_competency::get_multiple_relations(array($c2id))));
3607 // Delete a simple framework.
3608 $f2 = $lpg->create_framework();
3609 $this->assertTrue(api::delete_framework($f2->get('id')));
3610 $this->assertFalse(competency_framework::record_exists($f2->get('id')));
3613 public function test_delete_framework_competency_used_in_plan(): void {
3614 $this->resetAfterTest(true);
3615 $dg = $this->getDataGenerator();
3616 $lpg = $dg->get_plugin_generator('core_competency');
3617 $this->setAdminUser();
3619 $u1 = $dg->create_user();
3621 $plan = $lpg->create_plan((object) array('userid' => $u1->id));
3623 $f1 = $lpg->create_framework();
3624 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3625 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3626 $c2id = $c2->get('id');
3627 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3628 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3629 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3630 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3632 // Create related competencies.
3633 $rc = $lpg->create_related_competency(array(
3634 'competencyid' => $c2->get('id'),
3635 'relatedcompetencyid' => $c11b->get('id')
3637 $this->assertEquals($c11b->get('id'), $rc->get('relatedcompetencyid'));
3639 // Creating a standard evidence with minimal information.
3640 $uc2 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c11b->get('id')));
3641 $usercompetencyid = $uc2->get('id');
3642 $evidence = $lpg->create_evidence(array('usercompetencyid' => $usercompetencyid));
3643 $this->assertEquals($uc2->get('id'), $evidence->get('usercompetencyid'));
3644 $uc2->delete();
3646 // Add competency to plan.
3647 $pc = $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c11b->get('id')));
3648 // We can not delete a framework , if competency or competency children is associated to plan.
3649 $this->assertFalse(api::delete_framework($f1->get('id')));
3650 // Check that none of associated data are deleted.
3651 $this->assertEquals($usercompetencyid, $evidence->read()->get('usercompetencyid'));
3652 $this->assertEquals($c2->get('id'), $rc->read()->get('competencyid'));
3654 // We can delete the competency if we remove the competency from the plan.
3655 $pc->delete();
3657 $this->assertTrue(api::delete_framework($f1->get('id')));
3658 $this->assertFalse(competency::record_exists($c1->get('id')));
3659 $this->assertFalse(competency::record_exists($c2->get('id')));
3660 $this->assertFalse(competency::record_exists($c1a->get('id')));
3661 $this->assertFalse(competency::record_exists($c1b->get('id')));
3662 $this->assertFalse(competency::record_exists($c11b->get('id')));
3663 $this->assertFalse(competency::record_exists($c12b->get('id')));
3664 // Check if evidence are also deleted.
3665 $this->assertEquals(0, \core_competency\user_evidence_competency::count_records(array('competencyid' => $c11b->get('id'))));
3667 // Check if related conpetency relation is deleted.
3668 $this->assertEquals(0, count(\core_competency\related_competency::get_multiple_relations(array($c2id))));
3671 public function test_delete_framework_competency_used_in_usercompetency(): void {
3672 $this->resetAfterTest(true);
3673 $dg = $this->getDataGenerator();
3674 $lpg = $dg->get_plugin_generator('core_competency');
3675 $this->setAdminUser();
3677 $u1 = $dg->create_user();
3679 $f1 = $lpg->create_framework();
3680 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3681 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3682 $c2id = $c2->get('id');
3683 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3684 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3685 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3686 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3688 // Create related competencies.
3689 $rc = $lpg->create_related_competency(array(
3690 'competencyid' => $c2->get('id'),
3691 'relatedcompetencyid' => $c11b->get('id')
3693 $this->assertEquals($c11b->get('id'), $rc->get('relatedcompetencyid'));
3695 // Creating a standard evidence with minimal information.
3696 $uc1 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c11b->get('id')));
3697 $usercompetencyid = $uc1->get('id');
3698 $evidence = $lpg->create_evidence(array('usercompetencyid' => $usercompetencyid));
3699 $this->assertEquals($uc1->get('id'), $evidence->get('usercompetencyid'));
3700 $uc1->delete();
3702 // Create user competency.
3703 $uc2 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c11b->get('id')));
3705 // We can not delete a framework , if competency or competency children exist in user competency.
3706 $this->assertFalse(api::delete_framework($f1->get('id')));
3707 // Check that none of associated data are deleted.
3708 $this->assertEquals($usercompetencyid, $evidence->read()->get('usercompetencyid'));
3709 $this->assertEquals($c2->get('id'), $rc->read()->get('competencyid'));
3711 // We can delete the framework if we remove the competency from user competency.
3712 $uc2->delete();
3714 $this->assertTrue(api::delete_framework($f1->get('id')));
3715 $this->assertFalse(competency::record_exists($c1->get('id')));
3716 $this->assertFalse(competency::record_exists($c2->get('id')));
3717 $this->assertFalse(competency::record_exists($c1a->get('id')));
3718 $this->assertFalse(competency::record_exists($c1b->get('id')));
3719 $this->assertFalse(competency::record_exists($c11b->get('id')));
3720 $this->assertFalse(competency::record_exists($c12b->get('id')));
3721 // Check if evidence are also deleted.
3722 $this->assertEquals(0, \core_competency\user_evidence_competency::count_records(array('competencyid' => $c11b->get('id'))));
3724 // Check if related conpetency relation is deleted.
3725 $this->assertEquals(0, count(\core_competency\related_competency::get_multiple_relations(array($c2id))));
3728 public function test_delete_framework_competency_used_in_usercompetencyplan(): void {
3729 $this->resetAfterTest(true);
3730 $dg = $this->getDataGenerator();
3731 $lpg = $dg->get_plugin_generator('core_competency');
3732 $this->setAdminUser();
3734 $u1 = $dg->create_user();
3736 $plan = $lpg->create_plan((object) array('userid' => $u1->id));
3738 $f1 = $lpg->create_framework();
3739 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3740 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3741 $c2id = $c2->get('id');
3742 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3743 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3744 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3745 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3747 // Create related competencies.
3748 $rc = $lpg->create_related_competency(array(
3749 'competencyid' => $c2->get('id'),
3750 'relatedcompetencyid' => $c11b->get('id')
3752 $this->assertEquals($c11b->get('id'), $rc->get('relatedcompetencyid'));
3754 // Creating a standard evidence with minimal information.
3755 $uc1 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c11b->get('id')));
3756 $usercompetencyid = $uc1->get('id');
3757 $evidence = $lpg->create_evidence(array('usercompetencyid' => $usercompetencyid));
3758 $this->assertEquals($uc1->get('id'), $evidence->get('usercompetencyid'));
3759 $uc1->delete();
3761 // Create user competency plan.
3762 $uc2 = $lpg->create_user_competency_plan(array(
3763 'userid' => $u1->id,
3764 'competencyid' => $c11b->get('id'),
3765 'planid' => $plan->get('id')
3768 // We can not delete a framework , if competency or competency children exist in user competency plan.
3769 $this->assertFalse(api::delete_framework($f1->get('id')));
3770 // Check that none of associated data are deleted.
3771 $this->assertEquals($usercompetencyid, $evidence->read()->get('usercompetencyid'));
3772 $this->assertEquals($c2->get('id'), $rc->read()->get('competencyid'));
3774 // We can delete the framework if we remove the competency from user competency plan.
3775 $uc2->delete();
3777 $this->assertTrue(api::delete_framework($f1->get('id')));
3778 $this->assertFalse(competency::record_exists($c1->get('id')));
3779 $this->assertFalse(competency::record_exists($c2->get('id')));
3780 $this->assertFalse(competency::record_exists($c1a->get('id')));
3781 $this->assertFalse(competency::record_exists($c1b->get('id')));
3782 $this->assertFalse(competency::record_exists($c11b->get('id')));
3783 $this->assertFalse(competency::record_exists($c12b->get('id')));
3784 // Check if evidence are also deleted.
3785 $this->assertEquals(0, \core_competency\user_evidence_competency::count_records(array('competencyid' => $c11b->get('id'))));
3787 // Check if related conpetency relation is deleted.
3788 $this->assertEquals(0, count(\core_competency\related_competency::get_multiple_relations(array($c2id))));
3791 public function test_delete_framework_competency_used_in_template(): void {
3792 $this->resetAfterTest(true);
3793 $dg = $this->getDataGenerator();
3794 $lpg = $dg->get_plugin_generator('core_competency');
3795 $this->setAdminUser();
3797 $u1 = $dg->create_user();
3798 $template = $lpg->create_template();
3800 $f1 = $lpg->create_framework();
3801 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3802 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3803 $c2id = $c2->get('id');
3804 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3805 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3806 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3807 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3809 // Create related competencies.
3810 $rc = $lpg->create_related_competency(array(
3811 'competencyid' => $c2->get('id'),
3812 'relatedcompetencyid' => $c11b->get('id')
3814 $this->assertEquals($c11b->get('id'), $rc->get('relatedcompetencyid'));
3816 // Creating a standard evidence with minimal information.
3817 $uc1 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c11b->get('id')));
3818 $usercompetencyid = $uc1->get('id');
3819 $evidence = $lpg->create_evidence(array('usercompetencyid' => $usercompetencyid));
3820 $this->assertEquals($uc1->get('id'), $evidence->get('usercompetencyid'));
3821 $uc1->delete();
3823 // Add competency to a template.
3824 $tc = $lpg->create_template_competency(array(
3825 'templateid' => $template->get('id'),
3826 'competencyid' => $c11b->get('id')
3828 // We can not delete a framework , if competency or competency children is linked to template.
3829 $this->assertFalse(api::delete_framework($f1->get('id')));
3830 // Check that none of associated data are deleted.
3831 $this->assertEquals($usercompetencyid, $evidence->read()->get('usercompetencyid'));
3832 $this->assertEquals($c2->get('id'), $rc->read()->get('competencyid'));
3834 // We can delete the framework if we remove the competency from template.
3835 $tc->delete();
3837 $this->assertTrue(api::delete_framework($f1->get('id')));
3838 $this->assertFalse(competency::record_exists($c1->get('id')));
3839 $this->assertFalse(competency::record_exists($c2->get('id')));
3840 $this->assertFalse(competency::record_exists($c1a->get('id')));
3841 $this->assertFalse(competency::record_exists($c1b->get('id')));
3842 $this->assertFalse(competency::record_exists($c11b->get('id')));
3843 $this->assertFalse(competency::record_exists($c12b->get('id')));
3844 // Check if evidence are also deleted.
3845 $this->assertEquals(0, \core_competency\user_evidence_competency::count_records(array('competencyid' => $c11b->get('id'))));
3847 // Check if related conpetency relation is deleted.
3848 $this->assertEquals(0, count(\core_competency\related_competency::get_multiple_relations(array($c2id))));
3851 public function test_delete_framework_competency_used_in_course(): void {
3852 $this->resetAfterTest(true);
3853 $dg = $this->getDataGenerator();
3854 $lpg = $dg->get_plugin_generator('core_competency');
3855 $this->setAdminUser();
3857 $cat1 = $dg->create_category();
3858 $u1 = $dg->create_user();
3859 $course = $dg->create_course(array('category' => $cat1->id));
3861 $f1 = $lpg->create_framework();
3862 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3863 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id')));
3864 $c2id = $c2->get('id');
3865 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id')));
3866 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1a->get('id')));
3867 $c11b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3868 $c12b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1b->get('id')));
3870 // Create related competencies.
3871 $rc = $lpg->create_related_competency(array(
3872 'competencyid' => $c2->get('id'),
3873 'relatedcompetencyid' => $c11b->get('id')
3875 $this->assertEquals($c11b->get('id'), $rc->get('relatedcompetencyid'));
3877 // Creating a standard evidence with minimal information.
3878 $uc1 = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c11b->get('id')));
3879 $usercompetencyid = $uc1->get('id');
3880 $evidence = $lpg->create_evidence(array('usercompetencyid' => $usercompetencyid));
3881 $this->assertEquals($uc1->get('id'), $evidence->get('usercompetencyid'));
3882 $uc1->delete();
3884 // Add competency to course.
3885 $cc = $lpg->create_course_competency(array(
3886 'courseid' => $course->id,
3887 'competencyid' => $c11b->get('id')
3890 // We can not delete a framework if the competency or competencies children is linked to a course.
3891 $this->assertFalse(api::delete_framework($f1->get('id')));
3892 // Check that none of associated data are deleted.
3893 $this->assertEquals($usercompetencyid, $evidence->read()->get('usercompetencyid'));
3894 $this->assertEquals($c2->get('id'), $rc->read()->get('competencyid'));
3896 // We can delete the framework if we remove the competency from course.
3897 $cc->delete();
3899 $this->assertTrue(api::delete_framework($f1->get('id')));
3900 $this->assertFalse(competency::record_exists($c1->get('id')));
3901 $this->assertFalse(competency::record_exists($c2->get('id')));
3902 $this->assertFalse(competency::record_exists($c1a->get('id')));
3903 $this->assertFalse(competency::record_exists($c1b->get('id')));
3904 $this->assertFalse(competency::record_exists($c11b->get('id')));
3905 $this->assertFalse(competency::record_exists($c12b->get('id')));
3906 // Check if evidence are also deleted.
3907 $this->assertEquals(0, \core_competency\user_evidence_competency::count_records(array('competencyid' => $c11b->get('id'))));
3909 // Check if related conpetency relation is deleted.
3910 $this->assertEquals(0, count(\core_competency\related_competency::get_multiple_relations(array($c2id))));
3913 public function test_grade_competency_in_course_permissions(): void {
3914 $this->resetAfterTest();
3915 $dg = $this->getDataGenerator();
3917 $c1 = $dg->create_course();
3918 $c2 = $dg->create_course();
3919 $sysctx = \context_system::instance();
3920 $c1ctx = \context_course::instance($c1->id);
3921 $c2ctx = \context_course::instance($c2->id);
3923 $teacher1 = $dg->create_user();
3924 $noneditingteacher = $dg->create_user();
3925 $student1 = $dg->create_user();
3926 $student2 = $dg->create_user();
3927 $notstudent1 = $dg->create_user();
3929 $lpg = $dg->get_plugin_generator('core_competency');
3930 $framework = $lpg->create_framework();
3931 $comp1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
3932 $comp2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
3933 $lpg->create_course_competency(array('courseid' => $c1->id, 'competencyid' => $comp1->get('id')));
3935 $studentarch = get_archetype_roles('student');
3936 $studentrole = array_shift($studentarch);
3938 $gradablerole = $dg->create_role();
3939 assign_capability('moodle/competency:coursecompetencygradable', CAP_ALLOW, $gradablerole, $sysctx->id);
3941 $notgradablerole = $dg->create_role();
3942 assign_capability('moodle/competency:coursecompetencygradable', CAP_PROHIBIT, $notgradablerole, $sysctx->id);
3944 $canviewucrole = $dg->create_role();
3945 assign_capability('moodle/competency:usercompetencyview', CAP_ALLOW, $canviewucrole, $sysctx->id);
3947 $cannotviewcomp = $dg->create_role();
3948 assign_capability('moodle/competency:competencyview', CAP_PROHIBIT, $cannotviewcomp, $sysctx->id);
3950 $canmanagecomp = $dg->create_role();
3951 assign_capability('moodle/competency:competencymanage', CAP_ALLOW, $canmanagecomp, $sysctx->id);
3953 $cangraderole = $dg->create_role();
3954 assign_capability('moodle/competency:competencygrade', CAP_ALLOW, $cangraderole, $sysctx->id);
3956 // Enrol s1 and s2 as students in course 1.
3957 $dg->enrol_user($student1->id, $c1->id, $studentrole->id);
3958 $dg->enrol_user($student2->id, $c1->id, $studentrole->id);
3960 // Mark the s2 as not being 'gradable'.
3961 $dg->role_assign($notgradablerole, $student2->id, $c1ctx->id);
3963 // Mark the 'non a student' as 'gradable' throughout the site.
3964 $dg->role_assign($gradablerole, $notstudent1->id, $sysctx->id);
3966 // From now we'll iterate over each permission.
3967 accesslib_clear_all_caches_for_unit_testing();
3968 $this->setUser($teacher1);
3970 $this->assertExceptionWithGradeCompetencyInCourse('required_capability_exception', 'View a user competency',
3971 $c1->id, $student1->id, $comp1->get('id'));
3973 // Give permission to view competencies.
3974 $dg->role_assign($canviewucrole, $teacher1->id, $c1ctx->id);
3975 accesslib_clear_all_caches_for_unit_testing();
3976 $this->assertExceptionWithGradeCompetencyInCourse('required_capability_exception', 'Set competency rating',
3977 $c1->id, $student1->id, $comp1->get('id'));
3979 // Give permission to rate.
3980 $dg->role_assign($cangraderole, $teacher1->id, $c1ctx->id);
3981 accesslib_clear_all_caches_for_unit_testing();
3982 $this->assertSuccessWithGradeCompetencyInCourse($c1->id, $student1->id, $comp1->get('id'));
3984 // Remove permssion to read competencies, this leads to error.
3985 $dg->role_assign($cannotviewcomp, $teacher1->id, $sysctx->id);
3986 accesslib_clear_all_caches_for_unit_testing();
3987 $this->assertExceptionWithGradeCompetencyInCourse('required_capability_exception', 'View competency frameworks',
3988 $c1->id, $student1->id, $comp1->get('id'));
3990 // Give permssion to manage course competencies, this leads to success.
3991 $dg->role_assign($canmanagecomp, $teacher1->id, $sysctx->id);
3992 accesslib_clear_all_caches_for_unit_testing();
3993 $this->assertSuccessWithGradeCompetencyInCourse($c1->id, $student1->id, $comp1->get('id'));
3995 // Try to grade a user that is not gradable, lead to errors.
3996 $this->assertExceptionWithGradeCompetencyInCourse('coding_exception', 'The competency may not be rated at this time.',
3997 $c1->id, $student2->id, $comp1->get('id'));
3999 // Try to grade a competency not in the course.
4000 $this->assertExceptionWithGradeCompetencyInCourse('coding_exception', 'The competency does not belong to this course',
4001 $c1->id, $student1->id, $comp2->get('id'));
4003 // Try to grade a user that is not enrolled, even though they are 'gradable'.
4004 $this->assertExceptionWithGradeCompetencyInCourse('coding_exception', 'The competency may not be rated at this time.',
4005 $c1->id, $notstudent1->id, $comp1->get('id'));
4007 // Give permission for non-editing teacher to grade.
4008 $dg->role_assign($canviewucrole, $noneditingteacher->id, $c1ctx->id);
4009 $dg->role_assign($cangraderole, $noneditingteacher->id, $c1ctx->id);
4010 $this->setUser($noneditingteacher);
4012 accesslib_clear_all_caches_for_unit_testing();
4013 $this->assertSuccessWithGradeCompetencyInCourse($c1->id, $student1->id, $comp1->get('id'));
4017 * Assert that a competency was graded in a course.
4019 * @param int $courseid The course ID.
4020 * @param int $userid The user ID.
4021 * @param int $compid The competency ID.
4022 * @param int $grade The grade.
4024 protected function assertSuccessWithGradeCompetencyInCourse($courseid, $userid, $compid, $grade = 1) {
4025 $beforecount = evidence::count_records();
4026 api::grade_competency_in_course($courseid, $userid, $compid, $grade);
4027 $this->assertEquals($beforecount + 1, evidence::count_records());
4028 $uc = user_competency::get_record(array('userid' => $userid, 'competencyid' => $compid));
4029 $records = evidence::get_records(array(), 'id', 'DESC', 0, 1);
4030 $evidence = array_pop($records);
4031 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid'));
4035 * Assert that grading a competency in course throws an exception.
4037 * @param string $exceptiontype The exception type.
4038 * @param string $exceptiontest The exceptiont text.
4039 * @param int $courseid The course ID.
4040 * @param int $userid The user ID.
4041 * @param int $compid The competency ID.
4042 * @param int $grade The grade.
4044 protected function assertExceptionWithGradeCompetencyInCourse($exceptiontype, $exceptiontext, $courseid, $userid, $compid,
4045 $grade = 1) {
4047 $raised = false;
4048 try {
4049 api::grade_competency_in_course($courseid, $userid, $compid, $grade);
4050 } catch (\moodle_exception $e) {
4051 $raised = true;
4052 $this->assertInstanceOf($exceptiontype, $e);
4053 $this->assertMatchesRegularExpression('@' . $exceptiontext . '@', $e->getMessage());
4056 if (!$raised) {
4057 $this->fail('Grading should not be allowed.');
4062 * Test list of evidences for plan completed and not completed.
4064 public function test_list_evidence(): void {
4065 global $DB;
4067 $this->resetAfterTest(true);
4068 $dg = $this->getDataGenerator();
4069 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
4071 $syscontext = \context_system::instance();
4073 // Create users.
4074 $user = $dg->create_user();
4075 $this->setUser($user);
4077 // Create a framework and assign competencies.
4078 $framework = $lpg->create_framework();
4079 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4080 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4082 // Create 2 user plans and add competency to each plan.
4083 $p1 = $lpg->create_plan(array('userid' => $user->id));
4084 $p2 = $lpg->create_plan(array('userid' => $user->id));
4085 $pc1 = $lpg->create_plan_competency(array('planid' => $p1->get('id'), 'competencyid' => $c1->get('id')));
4086 $pc2 = $lpg->create_plan_competency(array('planid' => $p2->get('id'), 'competencyid' => $c1->get('id')));
4088 // Create user competency and add an evidence.
4089 $uc = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id')));
4090 $e1 = $lpg->create_evidence(array('usercompetencyid' => $uc->get('id')));
4092 // Check both plans as one evidence.
4093 $this->assertEquals(1, count(api::list_evidence($user->id, $c1->get('id'), $p1->get('id'))));
4094 $this->assertEquals(1, count(api::list_evidence($user->id, $c1->get('id'), $p2->get('id'))));
4096 // Complete second plan.
4097 $p2->set('status', plan::STATUS_COMPLETE);
4098 $p2->update();
4100 // Add another evidence for the same competency, but in the future (time + 1).
4101 $e2 = $lpg->create_evidence(array('usercompetencyid' => $uc->get('id')));
4102 $evidencesql = "UPDATE {" . evidence::TABLE . "} SET timecreated = :currenttime WHERE id = :evidenceid";
4103 $DB->execute($evidencesql, array('currenttime' => time() + 1, 'evidenceid' => $e2->get('id')));
4105 // Check that the first plan, which is not completed, has all the evidence.
4106 $this->assertEquals(2, count(api::list_evidence($user->id, $c1->get('id'), $p1->get('id'))));
4108 // Check that the second plan, completed before the new evidence, only has the first piece of evidence.
4109 $listevidences = api::list_evidence($user->id, $c1->get('id'), $p2->get('id'));
4110 $this->assertEquals(1, count($listevidences));
4111 $this->assertEquals($e1->get('id'), $listevidences[$e1->get('id')]->get('id'));
4115 * Get a user competency in a course.
4117 public function test_get_user_competency_in_course(): void {
4118 $this->resetAfterTest(true);
4119 $dg = $this->getDataGenerator();
4120 $lpg = $dg->get_plugin_generator('core_competency');
4121 $this->setAdminUser();
4123 $user = $dg->create_user();
4124 $c1 = $dg->create_course();
4126 // Enrol the user so they can be rated in the course.
4127 $studentarch = get_archetype_roles('student');
4128 $studentrole = array_shift($studentarch);
4129 $coursecontext = \context_course::instance($c1->id);
4130 $dg->role_assign($studentrole->id, $user->id, $coursecontext->id);
4131 $dg->enrol_user($user->id, $c1->id, $studentrole->id);
4133 $framework = $lpg->create_framework();
4134 $comp1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4135 $comp2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4136 $lpg->create_course_competency(array('competencyid' => $comp1->get('id'), 'courseid' => $c1->id));
4137 $lpg->create_course_competency(array('competencyid' => $comp2->get('id'), 'courseid' => $c1->id));
4139 // Create a user competency for comp1.
4140 api::grade_competency_in_course($c1, $user->id, $comp1->get('id'), 3, 'Unit test');
4142 // Test for competency already exist in user_competency.
4143 $uc = api::get_user_competency_in_course($c1->id, $user->id, $comp1->get('id'));
4144 $this->assertEquals($comp1->get('id'), $uc->get('competencyid'));
4145 $this->assertEquals($user->id, $uc->get('userid'));
4146 $this->assertEquals(3, $uc->get('grade'));
4147 $this->assertEquals(true, $uc->get('proficiency'));
4149 // Test for competency does not exist in user_competency.
4150 $uc2 = api::get_user_competency_in_course($c1->id, $user->id, $comp2->get('id'));
4151 $this->assertEquals($comp2->get('id'), $uc2->get('competencyid'));
4152 $this->assertEquals($user->id, $uc2->get('userid'));
4153 $this->assertEquals(null, $uc2->get('grade'));
4154 $this->assertEquals(null, $uc2->get('proficiency'));
4158 * Test course statistics api functions.
4160 public function test_course_statistics(): void {
4161 $this->resetAfterTest(true);
4162 $dg = $this->getDataGenerator();
4163 $lpg = $dg->get_plugin_generator('core_competency');
4164 $this->setAdminUser();
4166 $u1 = $dg->create_user();
4167 $u2 = $dg->create_user();
4168 $u3 = $dg->create_user();
4169 $u4 = $dg->create_user();
4170 $c1 = $dg->create_course();
4171 $framework = $lpg->create_framework();
4172 // Enrol students in the course.
4173 $studentarch = get_archetype_roles('student');
4174 $studentrole = array_shift($studentarch);
4175 $coursecontext = \context_course::instance($c1->id);
4176 $dg->role_assign($studentrole->id, $u1->id, $coursecontext->id);
4177 $dg->enrol_user($u1->id, $c1->id, $studentrole->id);
4178 $dg->role_assign($studentrole->id, $u2->id, $coursecontext->id);
4179 $dg->enrol_user($u2->id, $c1->id, $studentrole->id);
4180 $dg->role_assign($studentrole->id, $u3->id, $coursecontext->id);
4181 $dg->enrol_user($u3->id, $c1->id, $studentrole->id);
4182 $dg->role_assign($studentrole->id, $u4->id, $coursecontext->id);
4183 $dg->enrol_user($u4->id, $c1->id, $studentrole->id);
4185 // Create 6 competencies.
4186 $comp1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4187 $comp2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4188 $comp3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4189 $comp4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4190 $comp5 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4191 $comp6 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4193 // Link 6 out of 6 to a course.
4194 $lpg->create_course_competency(array('competencyid' => $comp1->get('id'), 'courseid' => $c1->id));
4195 $lpg->create_course_competency(array('competencyid' => $comp2->get('id'), 'courseid' => $c1->id));
4196 $lpg->create_course_competency(array('competencyid' => $comp3->get('id'), 'courseid' => $c1->id));
4197 $lpg->create_course_competency(array('competencyid' => $comp4->get('id'), 'courseid' => $c1->id));
4198 $lpg->create_course_competency(array('competencyid' => $comp5->get('id'), 'courseid' => $c1->id));
4199 $lpg->create_course_competency(array('competencyid' => $comp6->get('id'), 'courseid' => $c1->id));
4201 // Rate some competencies.
4202 // User 1.
4203 api::grade_competency_in_course($c1, $u1->id, $comp1->get('id'), 4, 'Unit test');
4204 api::grade_competency_in_course($c1, $u1->id, $comp2->get('id'), 4, 'Unit test');
4205 api::grade_competency_in_course($c1, $u1->id, $comp3->get('id'), 4, 'Unit test');
4206 api::grade_competency_in_course($c1, $u1->id, $comp4->get('id'), 4, 'Unit test');
4207 // User 2.
4208 api::grade_competency_in_course($c1, $u2->id, $comp1->get('id'), 1, 'Unit test');
4209 api::grade_competency_in_course($c1, $u2->id, $comp2->get('id'), 1, 'Unit test');
4210 api::grade_competency_in_course($c1, $u2->id, $comp3->get('id'), 1, 'Unit test');
4211 api::grade_competency_in_course($c1, $u2->id, $comp4->get('id'), 1, 'Unit test');
4212 // User 3.
4213 api::grade_competency_in_course($c1, $u3->id, $comp1->get('id'), 3, 'Unit test');
4214 api::grade_competency_in_course($c1, $u3->id, $comp2->get('id'), 3, 'Unit test');
4215 // User 4.
4216 api::grade_competency_in_course($c1, $u4->id, $comp1->get('id'), 2, 'Unit test');
4217 api::grade_competency_in_course($c1, $u4->id, $comp2->get('id'), 2, 'Unit test');
4219 // OK we have enough data - lets call some API functions and check for expected results.
4221 $result = api::count_proficient_competencies_in_course_for_user($c1->id, $u1->id);
4222 $this->assertEquals(4, $result);
4223 $result = api::count_proficient_competencies_in_course_for_user($c1->id, $u2->id);
4224 $this->assertEquals(0, $result);
4225 $result = api::count_proficient_competencies_in_course_for_user($c1->id, $u3->id);
4226 $this->assertEquals(2, $result);
4227 $result = api::count_proficient_competencies_in_course_for_user($c1->id, $u4->id);
4228 $this->assertEquals(0, $result);
4230 $result = api::get_least_proficient_competencies_for_course($c1->id, 0, 2);
4231 // We should get 5 and 6 in repeatable order.
4232 $valid = false;
4233 if (($comp5->get('id') == $result[0]->get('id')) || ($comp6->get('id') == $result[0]->get('id'))) {
4234 $valid = true;
4236 $this->assertTrue($valid);
4237 $valid = false;
4238 if (($comp5->get('id') == $result[1]->get('id')) || ($comp6->get('id') == $result[1]->get('id'))) {
4239 $valid = true;
4241 $this->assertTrue($valid);
4242 $expected = $result[1]->get('id');
4243 $result = api::get_least_proficient_competencies_for_course($c1->id, 1, 1);
4244 $this->assertEquals($result[0]->get('id'), $expected);
4248 * Test template statistics api functions.
4250 public function test_template_statistics(): void {
4251 $this->resetAfterTest(true);
4252 $dg = $this->getDataGenerator();
4253 $lpg = $dg->get_plugin_generator('core_competency');
4254 $this->setAdminUser();
4256 $u1 = $dg->create_user();
4257 $u2 = $dg->create_user();
4258 $u3 = $dg->create_user();
4259 $u4 = $dg->create_user();
4260 $c1 = $dg->create_course();
4261 $framework = $lpg->create_framework();
4263 // Create 6 competencies.
4264 $comp1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4265 $comp2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4266 $comp3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4267 $comp4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4268 $comp5 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4269 $comp6 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4271 // Link 5 out of 6 to a course.
4272 $lpg->create_course_competency(array('competencyid' => $comp1->get('id'), 'courseid' => $c1->id));
4273 $lpg->create_course_competency(array('competencyid' => $comp2->get('id'), 'courseid' => $c1->id));
4274 $lpg->create_course_competency(array('competencyid' => $comp3->get('id'), 'courseid' => $c1->id));
4275 $lpg->create_course_competency(array('competencyid' => $comp4->get('id'), 'courseid' => $c1->id));
4276 $lpg->create_course_competency(array('competencyid' => $comp5->get('id'), 'courseid' => $c1->id));
4278 // Put all 6 in a template.
4279 $tpl = $this->getDataGenerator()->get_plugin_generator('core_competency')->create_template();
4280 $tplc1 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $comp1->get('id')));
4281 $tplc2 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $comp2->get('id')));
4282 $tplc3 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $comp3->get('id')));
4283 $tplc4 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $comp4->get('id')));
4284 $tplc5 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $comp5->get('id')));
4285 $tplc6 = $lpg->create_template_competency(array('templateid' => $tpl->get('id'), 'competencyid' => $comp6->get('id')));
4287 // Create some plans from the template.
4288 $p1 = $lpg->create_plan(array('templateid' => $tpl->get('id'), 'userid' => $u1->id));
4289 $p2 = $lpg->create_plan(array('templateid' => $tpl->get('id'), 'userid' => $u2->id));
4290 $p3 = $lpg->create_plan(array('templateid' => $tpl->get('id'), 'userid' => $u3->id));
4291 $p4 = $lpg->create_plan(array('templateid' => $tpl->get('id'), 'userid' => $u4->id));
4293 // Rate some competencies.
4294 // User 1.
4295 $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $comp1->get('id'),
4296 'proficiency' => true, 'grade' => 1 ));
4297 $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $comp2->get('id'),
4298 'proficiency' => true, 'grade' => 1 ));
4299 $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $comp3->get('id'),
4300 'proficiency' => true, 'grade' => 1 ));
4301 // User 2.
4302 $lpg->create_user_competency(array('userid' => $u2->id, 'competencyid' => $comp1->get('id'),
4303 'proficiency' => false, 'grade' => 1 ));
4304 $lpg->create_user_competency(array('userid' => $u2->id, 'competencyid' => $comp2->get('id'),
4305 'proficiency' => false, 'grade' => 1 ));
4306 $lpg->create_user_competency(array('userid' => $u2->id, 'competencyid' => $comp3->get('id'),
4307 'proficiency' => false, 'grade' => 1 ));
4308 // User 3.
4309 $lpg->create_user_competency(array('userid' => $u3->id, 'competencyid' => $comp2->get('id'),
4310 'proficiency' => false, 'grade' => 1 ));
4311 $lpg->create_user_competency(array('userid' => $u3->id, 'competencyid' => $comp3->get('id'),
4312 'proficiency' => true, 'grade' => 1 ));
4313 $lpg->create_user_competency(array('userid' => $u3->id, 'competencyid' => $comp4->get('id'),
4314 'proficiency' => false, 'grade' => 1 ));
4315 $lpg->create_user_competency(array('userid' => $u3->id, 'competencyid' => $comp5->get('id'),
4316 'proficiency' => true, 'grade' => 1 ));
4317 // User 4.
4318 $lpg->create_user_competency(array('userid' => $u4->id, 'competencyid' => $comp3->get('id'),
4319 'proficiency' => true, 'grade' => 1 ));
4320 $lpg->create_user_competency(array('userid' => $u4->id, 'competencyid' => $comp5->get('id'),
4321 'proficiency' => true, 'grade' => 1 ));
4323 // Complete 3 out of 4 plans.
4324 api::complete_plan($p1->get('id'));
4325 api::complete_plan($p2->get('id'));
4326 api::complete_plan($p3->get('id'));
4328 // OK we have enough data - lets call some API functions and check for expected results.
4330 $result = api::count_competencies_in_template_with_no_courses($tpl->get('id'));
4331 $this->assertEquals(1, $result);
4333 $result = api::count_plans_for_template($tpl->get('id'));
4334 $this->assertEquals(4, $result);
4336 $result = api::count_plans_for_template($tpl->get('id'), plan::STATUS_COMPLETE);
4337 $this->assertEquals(3, $result);
4339 // This counts the records of competencies in completed plans for all users with a plan from this template.
4340 $result = api::count_user_competency_plans_for_template($tpl->get('id'));
4341 // There should be 3 plans * 6 competencies.
4342 $this->assertEquals(18, $result);
4344 // This counts the records of proficient competencies in completed plans for all users with a plan from this template.
4345 $result = api::count_user_competency_plans_for_template($tpl->get('id'), true);
4346 // There should be 5.
4347 $this->assertEquals(5, $result);
4349 // This counts the records of not proficient competencies in completed plans for all users with a plan from this template.
4350 $result = api::count_user_competency_plans_for_template($tpl->get('id'), false);
4351 // There should be 13.
4352 $this->assertEquals(13, $result);
4354 // This lists the plans based on this template, optionally filtered by status.
4355 $result = api::list_plans_for_template($tpl->get('id'));
4357 $this->assertEquals(4, count($result));
4358 foreach ($result as $one) {
4359 $this->assertInstanceOf('\core_competency\plan', $one);
4361 // This lists the plans based on this template, optionally filtered by status.
4362 $result = api::list_plans_for_template($tpl->get('id'), plan::STATUS_COMPLETE);
4364 $this->assertEquals(3, count($result));
4365 foreach ($result as $one) {
4366 $this->assertInstanceOf('\core_competency\plan', $one);
4367 $this->assertEquals(plan::STATUS_COMPLETE, $one->get('status'));
4370 $result = api::get_least_proficient_competencies_for_template($tpl->get('id'), 0, 2);
4372 // Our times completed counts should look like this:
4373 // - comp1 - 1
4374 // - comp2 - 1
4375 // - comp3 - 2
4376 // - comp4 - 0
4377 // - comp5 - 1
4378 // - comp6 - 0
4380 // And this is a fullstop to make CiBoT happy.
4381 $this->assertEquals(2, count($result));
4382 $leastarray = array($comp4->get('id'), $comp6->get('id'));
4383 foreach ($result as $one) {
4384 $this->assertInstanceOf('\core_competency\competency', $one);
4385 $this->assertContainsEquals($one->get('id'), $leastarray);
4389 public function test_is_scale_used_anywhere(): void {
4390 $this->resetAfterTest();
4391 $dg = $this->getDataGenerator();
4392 $lpg = $dg->get_plugin_generator('core_competency');
4394 $scale1 = $dg->create_scale();
4395 $scale2 = $dg->create_scale();
4396 $scale3 = $dg->create_scale();
4397 $scale4 = $dg->create_scale();
4399 $this->assertFalse(api::is_scale_used_anywhere($scale1->id));
4400 $this->assertFalse(api::is_scale_used_anywhere($scale2->id));
4401 $this->assertFalse(api::is_scale_used_anywhere($scale3->id));
4402 $this->assertFalse(api::is_scale_used_anywhere($scale4->id));
4404 // Using scale 1 in a framework.
4405 $f1 = $lpg->create_framework([
4406 'scaleid' => $scale1->id,
4407 'scaleconfiguration' => json_encode([
4408 ['scaleid' => $scale1->id],
4409 ['id' => 1, 'scaledefault' => 1, 'proficient' => 1]
4412 $this->assertTrue(api::is_scale_used_anywhere($scale1->id));
4413 $this->assertFalse(api::is_scale_used_anywhere($scale2->id));
4414 $this->assertFalse(api::is_scale_used_anywhere($scale3->id));
4415 $this->assertFalse(api::is_scale_used_anywhere($scale4->id));
4417 // Using scale 2 in a competency.
4418 $f2 = $lpg->create_framework();
4419 $c2 = $lpg->create_competency([
4420 'competencyframeworkid' => $f2->get('id'),
4421 'scaleid' => $scale2->id,
4422 'scaleconfiguration' => json_encode([
4423 ['scaleid' => $scale2->id],
4424 ['id' => 1, 'scaledefault' => 1, 'proficient' => 1]
4428 $this->assertTrue(api::is_scale_used_anywhere($scale1->id));
4429 $this->assertTrue(api::is_scale_used_anywhere($scale2->id));
4430 $this->assertFalse(api::is_scale_used_anywhere($scale3->id));
4431 $this->assertFalse(api::is_scale_used_anywhere($scale4->id));
4433 // Using scale 3 in a framework, and scale 4 in a competency of that framework.
4434 $f3 = $lpg->create_framework([
4435 'scaleid' => $scale3->id,
4436 'scaleconfiguration' => json_encode([
4437 ['scaleid' => $scale3->id],
4438 ['id' => 1, 'scaledefault' => 1, 'proficient' => 1]
4441 $c3 = $lpg->create_competency([
4442 'competencyframeworkid' => $f3->get('id'),
4443 'scaleid' => $scale4->id,
4444 'scaleconfiguration' => json_encode([
4445 ['scaleid' => $scale4->id],
4446 ['id' => 1, 'scaledefault' => 1, 'proficient' => 1]
4450 $this->assertTrue(api::is_scale_used_anywhere($scale1->id));
4451 $this->assertTrue(api::is_scale_used_anywhere($scale2->id));
4452 $this->assertTrue(api::is_scale_used_anywhere($scale3->id));
4453 $this->assertTrue(api::is_scale_used_anywhere($scale4->id));
4455 // Multiple occurrences of the same scale (3, and 4).
4456 $f4 = $lpg->create_framework([
4457 'scaleid' => $scale3->id,
4458 'scaleconfiguration' => json_encode([
4459 ['scaleid' => $scale3->id],
4460 ['id' => 1, 'scaledefault' => 1, 'proficient' => 1]
4463 $c4 = $lpg->create_competency([
4464 'competencyframeworkid' => $f3->get('id'),
4465 'scaleid' => $scale4->id,
4466 'scaleconfiguration' => json_encode([
4467 ['scaleid' => $scale4->id],
4468 ['id' => 1, 'scaledefault' => 1, 'proficient' => 1]
4471 $this->assertTrue(api::is_scale_used_anywhere($scale1->id));
4472 $this->assertTrue(api::is_scale_used_anywhere($scale2->id));
4473 $this->assertTrue(api::is_scale_used_anywhere($scale3->id));
4474 $this->assertTrue(api::is_scale_used_anywhere($scale4->id));
4477 public function test_delete_evidence(): void {
4478 $this->resetAfterTest();
4479 $dg = $this->getDataGenerator();
4480 $ccg = $dg->get_plugin_generator('core_competency');
4482 $u1 = $dg->create_user();
4483 $f1 = $ccg->create_framework();
4484 $comp1 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
4485 $uc1 = $ccg->create_user_competency(['userid' => $u1->id, 'competencyid' => $comp1->get('id')]);
4487 $ev1 = $ccg->create_evidence(['usercompetencyid' => $uc1->get('id')]);
4488 $ev2 = $ccg->create_evidence(['usercompetencyid' => $uc1->get('id')]);
4490 $this->setAdminUser($u1);
4492 $this->assertEquals(2, evidence::count_records());
4493 api::delete_evidence($ev1);
4494 $this->assertEquals(1, evidence::count_records());
4495 $this->assertFalse(evidence::record_exists($ev1->get('id')));
4496 $this->assertTrue(evidence::record_exists($ev2->get('id')));
4499 public function test_delete_evidence_without_permissions(): void {
4500 $this->resetAfterTest();
4501 $dg = $this->getDataGenerator();
4502 $ccg = $dg->get_plugin_generator('core_competency');
4504 $u1 = $dg->create_user();
4505 $f1 = $ccg->create_framework();
4506 $comp1 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
4507 $uc1 = $ccg->create_user_competency(['userid' => $u1->id, 'competencyid' => $comp1->get('id')]);
4508 $ev1 = $ccg->create_evidence(['usercompetencyid' => $uc1->get('id')]);
4510 $this->setUser($u1);
4512 $this->expectException(\required_capability_exception::class);
4513 api::delete_evidence($ev1);
4516 public function test_list_plans_to_review(): void {
4517 $dg = $this->getDataGenerator();
4518 $this->resetAfterTest();
4519 $ccg = $dg->get_plugin_generator('core_competency');
4520 $sysctx = \context_system::instance();
4521 $this->setAdminUser();
4523 $reviewer = $dg->create_user();
4524 $roleallow = $dg->create_role();
4525 $roleprohibit = $dg->create_role();
4526 assign_capability('moodle/competency:planreview', CAP_ALLOW, $roleallow, $sysctx->id);
4527 assign_capability('moodle/competency:planreview', CAP_PROHIBIT, $roleprohibit, $sysctx->id);
4528 role_assign($roleallow, $reviewer->id, $sysctx->id);
4529 accesslib_clear_all_caches_for_unit_testing();
4531 $u1 = $dg->create_user();
4532 $u2 = $dg->create_user();
4533 $f1 = $ccg->create_framework();
4534 $comp1 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
4535 $p1a = $ccg->create_plan(['userid' => $u1->id, 'status' => plan::STATUS_WAITING_FOR_REVIEW]);
4536 $p1b = $ccg->create_plan(['userid' => $u1->id, 'status' => plan::STATUS_IN_REVIEW, 'reviewerid' => $reviewer->id]);
4537 $p1c = $ccg->create_plan(['userid' => $u1->id, 'status' => plan::STATUS_DRAFT]);
4538 $p2a = $ccg->create_plan(['userid' => $u2->id, 'status' => plan::STATUS_WAITING_FOR_REVIEW]);
4539 $p2b = $ccg->create_plan(['userid' => $u2->id, 'status' => plan::STATUS_IN_REVIEW]);
4540 $p2c = $ccg->create_plan(['userid' => $u2->id, 'status' => plan::STATUS_ACTIVE]);
4541 $p2d = $ccg->create_plan(['userid' => $u2->id, 'status' => plan::STATUS_ACTIVE]);
4542 api::complete_plan($p2d);
4544 // The reviewer can review all plans waiting for review, or in review where they are the reviewer.
4545 $this->setUser($reviewer);
4546 $result = api::list_plans_to_review();
4547 $this->assertEquals(3, $result['count']);
4548 $this->assertEquals($p1a->get('id'), $result['plans'][0]->plan->get('id'));
4549 $this->assertEquals($p1b->get('id'), $result['plans'][1]->plan->get('id'));
4550 $this->assertEquals($p2a->get('id'), $result['plans'][2]->plan->get('id'));
4552 // The reviewer cannot view the plans when they do not have the permission in the user's context.
4553 role_assign($roleprohibit, $reviewer->id, \context_user::instance($u2->id)->id);
4554 accesslib_clear_all_caches_for_unit_testing();
4555 $result = api::list_plans_to_review();
4556 $this->assertEquals(2, $result['count']);
4557 $this->assertEquals($p1a->get('id'), $result['plans'][0]->plan->get('id'));
4558 $this->assertEquals($p1b->get('id'), $result['plans'][1]->plan->get('id'));
4561 public function test_list_user_competencies_to_review(): void {
4562 global $CFG;
4563 require_once($CFG->dirroot . '/user/lib.php');
4565 $dg = $this->getDataGenerator();
4566 $this->resetAfterTest();
4567 $ccg = $dg->get_plugin_generator('core_competency');
4568 $sysctx = \context_system::instance();
4569 $this->setAdminUser();
4571 $reviewer = $dg->create_user();
4572 $roleallow = $dg->create_role();
4573 $roleprohibit = $dg->create_role();
4574 assign_capability('moodle/competency:usercompetencyreview', CAP_ALLOW, $roleallow, $sysctx->id);
4575 assign_capability('moodle/competency:usercompetencyreview', CAP_PROHIBIT, $roleprohibit, $sysctx->id);
4576 role_assign($roleallow, $reviewer->id, $sysctx->id);
4577 accesslib_clear_all_caches_for_unit_testing();
4579 $u1 = $dg->create_user();
4580 $u2 = $dg->create_user();
4581 $u3 = $dg->create_user();
4582 $f1 = $ccg->create_framework();
4583 $c1 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
4584 $c2 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
4585 $c3 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
4586 $c4 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
4587 $uc1a = $ccg->create_user_competency(['userid' => $u1->id, 'competencyid' => $c1->get('id'),
4588 'status' => user_competency::STATUS_IDLE]);
4589 $uc1b = $ccg->create_user_competency(['userid' => $u1->id, 'competencyid' => $c2->get('id'),
4590 'status' => user_competency::STATUS_WAITING_FOR_REVIEW]);
4591 $uc1c = $ccg->create_user_competency(['userid' => $u1->id, 'competencyid' => $c3->get('id'),
4592 'status' => user_competency::STATUS_IN_REVIEW, 'reviewerid' => $reviewer->id]);
4593 $uc2a = $ccg->create_user_competency(['userid' => $u2->id, 'competencyid' => $c1->get('id'),
4594 'status' => user_competency::STATUS_WAITING_FOR_REVIEW]);
4595 $uc2b = $ccg->create_user_competency(['userid' => $u2->id, 'competencyid' => $c2->get('id'),
4596 'status' => user_competency::STATUS_IDLE]);
4597 $uc2c = $ccg->create_user_competency(['userid' => $u2->id, 'competencyid' => $c3->get('id'),
4598 'status' => user_competency::STATUS_IN_REVIEW]);
4599 $uc3a = $ccg->create_user_competency(['userid' => $u3->id, 'competencyid' => $c4->get('id'),
4600 'status' => user_competency::STATUS_WAITING_FOR_REVIEW]);
4602 // The reviewer can review all plans waiting for review, or in review where they are the reviewer.
4603 $this->setUser($reviewer);
4604 $result = api::list_user_competencies_to_review();
4605 $this->assertEquals(4, $result['count']);
4606 $this->assertEquals($uc2a->get('id'), $result['competencies'][0]->usercompetency->get('id'));
4607 $this->assertEquals($uc1b->get('id'), $result['competencies'][1]->usercompetency->get('id'));
4608 $this->assertEquals($uc1c->get('id'), $result['competencies'][2]->usercompetency->get('id'));
4609 $this->assertEquals($uc3a->get('id'), $result['competencies'][3]->usercompetency->get('id'));
4611 // Now, let's delete user 3.
4612 // It should not be listed on user competencies to review any more.
4613 user_delete_user($u3);
4614 $result = api::list_user_competencies_to_review();
4615 $this->assertEquals(3, $result['count']);
4617 // The reviewer cannot view the plans when they do not have the permission in the user's context.
4618 role_assign($roleprohibit, $reviewer->id, \context_user::instance($u2->id)->id);
4619 accesslib_clear_all_caches_for_unit_testing();
4620 $result = api::list_user_competencies_to_review();
4621 $this->assertEquals(2, $result['count']);
4622 $this->assertEquals($uc1b->get('id'), $result['competencies'][0]->usercompetency->get('id'));
4623 $this->assertEquals($uc1c->get('id'), $result['competencies'][1]->usercompetency->get('id'));
4627 * Test we can get all of a users plans with a competency.
4629 public function test_list_plans_with_competency(): void {
4630 $this->resetAfterTest(true);
4631 $this->setAdminUser();
4632 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency');
4634 $u1 = $this->getDataGenerator()->create_user();
4635 $tpl = $this->getDataGenerator()->get_plugin_generator('core_competency')->create_template();
4637 // Create a framework and assign competencies.
4638 $framework = $lpg->create_framework();
4639 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id')));
4641 // Create two plans and assign the competency to each.
4642 $plan1 = $lpg->create_plan(array('userid' => $u1->id));
4643 $plan2 = $lpg->create_plan(array('userid' => $u1->id));
4645 $lpg->create_plan_competency(array('planid' => $plan1->get('id'), 'competencyid' => $c1->get('id')));
4646 $lpg->create_plan_competency(array('planid' => $plan2->get('id'), 'competencyid' => $c1->get('id')));
4648 // Create one more plan without the competency.
4649 $plan3 = $lpg->create_plan(array('userid' => $u1->id));
4651 $plans = api::list_plans_with_competency($u1->id, $c1);
4653 $this->assertEquals(2, count($plans));
4655 $this->assertEquals(reset($plans)->get('id'), $plan1->get('id'));
4656 $this->assertEquals(end($plans)->get('id'), $plan2->get('id'));