Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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