Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | 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
declare(strict_types=1);
18
 
19
namespace core_course\reportbuilder\datasource;
20
 
21
use completion_completion;
22
use completion_criteria_self;
23
use core_reportbuilder\local\filters\boolean_select;
24
use core_reportbuilder\local\filters\date;
25
use core_reportbuilder\local\filters\duration;
26
use core_reportbuilder\local\filters\select;
27
use core_reportbuilder\local\filters\text;
28
use core_reportbuilder_generator;
29
use core_reportbuilder_testcase;
30
use core_user;
31
use grade_item;
32
 
33
defined('MOODLE_INTERNAL') || die();
34
 
35
global $CFG;
36
require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
37
 
38
/**
39
 * Course participants datasource tests
40
 *
41
 * @package     core_course
42
 * @covers      \core_course\reportbuilder\datasource\participants
43
 * @copyright   2022 David Matamoros <davidmc@moodle.com>
44
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
45
 */
46
class participants_test extends core_reportbuilder_testcase {
47
 
48
    /**
49
     * Load required test libraries
50
     */
51
    public static function setUpBeforeClass(): void {
52
        global $CFG;
53
 
54
        require_once("{$CFG->libdir}/gradelib.php");
55
        require_once("{$CFG->dirroot}/completion/criteria/completion_criteria_self.php");
56
    }
57
 
58
    /**
59
     * Test default datasource
60
     */
61
    public function test_datasource_default(): void {
62
        global $DB;
63
 
64
        $this->resetAfterTest();
65
 
66
        // Course one, two manually enrolled users.
67
        $courseone = $this->getDataGenerator()->create_course(['fullname' => 'Zebras']);
68
        $userone = $this->getDataGenerator()->create_and_enrol($courseone, 'student', ['firstname' => 'Zoe']);
69
        $usertwo = $this->getDataGenerator()->create_and_enrol($courseone, 'student', ['firstname' => 'Amy']);
70
 
71
        // Course two, two self enrolled users (one inactive).
72
        $coursetwo = $this->getDataGenerator()->create_course(['fullname' => 'Aardvarks']);
73
 
74
        $enrol = $DB->get_record('enrol', ['courseid' => $coursetwo->id, 'enrol' => 'self']);
75
        enrol_get_plugin($enrol->enrol)->update_status($enrol, ENROL_INSTANCE_ENABLED);
76
 
77
        $this->getDataGenerator()->enrol_user($userone->id, $coursetwo->id, null, 'self');
78
        $this->getDataGenerator()->enrol_user($usertwo->id, $coursetwo->id, null, 'self', 0, 0, ENROL_USER_SUSPENDED);
79
 
80
        /** @var core_reportbuilder_generator $generator */
81
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
82
        $report = $generator->create_report(['name' => 'Participants', 'source' => participants::class, 'default' => 1]);
83
 
84
        $content = $this->get_custom_report_content($report->get('id'));
85
 
86
        // Default columns are course, user, method. Sorted by each.
87
        $courseoneurl = course_get_url($courseone);
88
        $coursetwourl = course_get_url($coursetwo);
89
 
90
        $useroneurl = core_user::get_profile_url($userone);
91
        $usertwourl = core_user::get_profile_url($usertwo);
92
 
93
        $this->assertEquals([
94
            ["<a href=\"{$coursetwourl}\">{$coursetwo->fullname}</a>",
95
                "<a href=\"{$useroneurl}\">" . fullname($userone) . "</a>", 'Self enrolment (Student)'],
96
            ["<a href=\"{$courseoneurl}\">{$courseone->fullname}</a>",
97
                "<a href=\"{$usertwourl}\">" . fullname($usertwo) . "</a>", 'Manual enrolments'],
98
            ["<a href=\"{$courseoneurl}\">{$courseone->fullname}</a>",
99
                "<a href=\"{$useroneurl}\">" . fullname($userone) . "</a>", 'Manual enrolments'],
100
        ], array_map('array_values', $content));
101
    }
102
 
103
    /**
104
     * Test datasource columns that aren't added by default
105
     */
106
    public function test_datasource_non_default_columns(): void {
107
        global $DB;
108
        $this->resetAfterTest();
109
 
110
        $timestart = time() - DAYSECS;
111
        $timeend = $timestart + 3 * DAYSECS;
112
        $timecompleted = $timestart + 2 * DAYSECS;
113
        $timelastaccess = time() + 4 * DAYSECS;
114
 
115
        $category = $this->getDataGenerator()->create_category(['name' => 'Music']);
116
        $course = $this->getDataGenerator()->create_course([
117
            'category' => $category->id,
118
            'fullname' => 'All about Lionel at the work place',
119
            'enablecompletion' => true,
120
            'startdate' => $timestart,
121
            'enddate' => $timeend,
122
        ]);
123
 
124
        $user1 = self::getDataGenerator()->create_user();
125
        $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student',
126
            'manual', $timestart, $timeend, ENROL_USER_ACTIVE);
127
 
128
        // Add them to a group.
129
        $group = self::getDataGenerator()->create_group(['courseid' => $course->id]);
130
        self::getDataGenerator()->create_group_member(['groupid' => $group->id, 'userid' => $user1->id]);
131
 
132
        // Create self completion, mark as complete for the user.
133
        $criteriaconfig = (object) ['id' => $course->id, 'criteria_self' => true];
134
        (new completion_criteria_self())->update_config($criteriaconfig);
135
 
136
        $ccompletion = new completion_completion(['course' => $course->id, 'userid' => $user1->id]);
137
        $ccompletion->mark_enrolled($timestart);
138
        $ccompletion->mark_complete($timecompleted);
139
 
140
        // Update final grade for the user.
141
        $courseitem = grade_item::fetch_course_item($course->id);
142
        $courseitem->update_final_grade($user1->id, 42.5);
143
 
144
        // Set some last access value for the user in the course.
145
        $DB->insert_record('user_lastaccess',
146
            ['userid' => $user1->id, 'courseid' => $course->id, 'timeaccess' => $timelastaccess]);
147
 
148
        /** @var core_reportbuilder_generator $generator */
149
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
150
        $report = $generator->create_report(['name' => 'Participants', 'source' => participants::class, 'default' => false]);
151
 
152
        $generator->create_column(['reportid' => $report->get('id'),
153
            'uniqueidentifier' => 'course:fullname']);
154
        $generator->create_column(['reportid' => $report->get('id'),
155
            'uniqueidentifier' => 'course_category:name']);
156
        $generator->create_column(['reportid' => $report->get('id'),
157
            'uniqueidentifier' => 'user:fullname']);
158
 
159
        // Enrol entity (report ordering by enrolment name).
160
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:name', 'sortenabled' => 1]);
161
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:plugin']);
162
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:enabled']);
163
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:period']);
164
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:startdate']);
165
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:enddate']);
166
 
167
        // Role entity.
168
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:name']);
169
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:shortname']);
170
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:description']);
171
 
172
        $generator->create_column(['reportid' => $report->get('id'),
173
            'uniqueidentifier' => 'group:name']);
174
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'completion:criteria']);
175
        $generator->create_column(['reportid' => $report->get('id'),
176
            'uniqueidentifier' => 'completion:completed']);
177
        $generator->create_column(['reportid' => $report->get('id'),
178
            'uniqueidentifier' => 'access:timeaccess']);
179
        $generator->create_column(['reportid' => $report->get('id'),
180
            'uniqueidentifier' => 'completion:progresspercent']);
181
        $generator->create_column(['reportid' => $report->get('id'),
182
            'uniqueidentifier' => 'completion:timeenrolled']);
183
        $generator->create_column(['reportid' => $report->get('id'),
184
            'uniqueidentifier' => 'completion:timestarted']);
185
        $generator->create_column(['reportid' => $report->get('id'),
186
            'uniqueidentifier' => 'completion:timecompleted']);
187
        $generator->create_column(['reportid' => $report->get('id'),
188
            'uniqueidentifier' => 'completion:reaggregate']);
189
        $generator->create_column(['reportid' => $report->get('id'),
190
            'uniqueidentifier' => 'completion:dayscourse']);
191
        $generator->create_column(['reportid' => $report->get('id'),
192
            'uniqueidentifier' => 'completion:daysuntilcompletion']);
193
        $generator->create_column(['reportid' => $report->get('id'),
194
            'uniqueidentifier' => 'completion:grade']);
195
 
196
        // Add filter to the report.
197
        $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:plugin']);
198
 
199
        $content = $this->get_custom_report_content($report->get('id'));
200
 
201
        // It should get 3 records (manual enrolment, self and guest).
202
        $this->assertCount(3, $content);
203
 
204
        // Filter by Manual enrolment method.
205
        $content = $this->get_custom_report_content($report->get('id'), 30, [
206
            'enrol:plugin_operator' => select::EQUAL_TO,
207
            'enrol:plugin_value' => 'manual',
208
        ]);
209
 
210
        $this->assertCount(1, $content);
211
 
212
        $this->assertEquals([
213
            'All about Lionel at the work place', // Course name.
214
            'Music', // Course category name.
215
            fullname($user1), // User fullname.
216
            'Manual enrolments', // Enrolment method.
217
            'Manual enrolments', // Enrolment plugin.
218
            'Yes', // Enrolment enabled.
219
            '', // Enrolment period.
220
            '', // Enrolment start date.
221
            '', // Enrolment end date.
222
            'Student', // Role name.
223
            'student', // Role shortname.
224
            'Students generally have fewer privileges within a course.', // Role description.
225
            $group->name, // Group name.
226
            "All criteria below are required<ul>\n<li>Self completion: Self completion</li>\n</ul>", // Completion criteria.
227
            'Yes', // Course completed.
228
            userdate($timelastaccess), // Time last access.
229
            '100.0%', // Progress percentage.
230
            userdate($timestart), // Time enrolled.
231
            '', // Time started.
232
            userdate($timecompleted), // Time completed.
233
            '', // Reagreggate.
234
            2, // Days taking course.
235
            2, // Days until completion.
236
            '42.50', // Grade.
237
        ], array_values($content[0]));
238
    }
239
 
240
    /**
241
     * Data provider for {@see test_datasource_filters}
242
     *
243
     * @return array
244
     */
245
    public function datasource_filters_provider(): array {
246
        global $DB;
247
 
248
        return [
249
            [
250
                'enrolment:status',
251
                [
252
                    'enrolment:status_operator' => select::EQUAL_TO,
253
                    'enrolment:status_value' => 1,
254
                ],
255
                ['Luna'],
256
            ],
257
            [
258
                'enrolment:timecreated',
259
                [
260
                    'enrolment:timecreated_operator' => date::DATE_CURRENT,
261
                    'enrolment:timecreated_unit' => date::DATE_UNIT_DAY,
262
                ],
263
                ['Kira'],
264
            ],
265
            [
266
                'enrolment:timestarted',
267
                [
268
                    'enrolment:timestarted_operator' => date::DATE_CURRENT,
269
                    'enrolment:timecreated_unit' => date::DATE_UNIT_DAY,
270
                ],
271
                ['Luna'],
272
            ],
273
            [
274
                'enrolment:timeended',
275
                [
276
                    'enrolment:timeended_operator' => date::DATE_CURRENT,
277
                    'enrolment:timeended_unit' => date::DATE_UNIT_DAY,
278
                ],
279
                ['Luna'],
280
            ],
281
            [
282
                'enrol:enabled',
283
                [
284
                    'completion:enabled_operator' => boolean_select::CHECKED,
285
                ],
286
                ['Lionel', 'Kira', 'Luna'],
287
            ],
288
            [
289
                'enrol:period',
290
                [
291
                    'enrol:period_operator' => duration::DURATION_MAXIMUM,
292
                    'enrol:period_unit' => MINSECS,
293
                    'enrol:period_value' => 2,
294
                ],
295
                ['Lionel', 'Kira', 'Luna'],
296
            ],
297
            [
298
                'enrol:startdate',
299
                [
300
                    'enrol:startdate_operator' => date::DATE_EMPTY,
301
                ],
302
                ['Lionel', 'Kira', 'Luna'],
303
            ],
304
            [
305
                'enrol:enddate',
306
                [
307
                    'enrol:enddate_operator' => date::DATE_EMPTY,
308
                ],
309
                ['Lionel', 'Kira', 'Luna'],
310
            ],
311
            [
312
                'enrol:customname',
313
                [
314
                    'enrol:customname_operator' => text::IS_EMPTY,
315
                ],
316
                ['Luna', 'Kira', 'Lionel'],
317
            ],
318
            [
319
                'enrol:customname',
320
                [
321
                    'enrol:customname_operator' => text::IS_EQUAL_TO,
322
                    'enrol:customname_value' => 'All night long'
323
                ],
324
                [],
325
            ],
326
            [
327
                'role:name',
328
                [
329
                    'role:name_operator' => select::EQUAL_TO,
330
                    'role:name_value' => $DB->get_field('role', 'id', ['shortname' => 'editingteacher']),
331
                ],
332
                ['Luna'],
333
            ],
334
            [
335
                'completion:completed',
336
                [
337
                    'completion:completed_operator' => boolean_select::CHECKED,
338
                ],
339
                ['Lionel'],
340
            ],
341
            [
342
                'completion:timecompleted',
343
                [
344
                    'completion:timecompleted_operator' => date::DATE_NOT_EMPTY,
345
                ],
346
                ['Lionel'],
347
            ],
348
            [
349
                'completion:timeenrolled',
350
                [
351
                    'completion:timeenrolled_operator' => date::DATE_NOT_EMPTY,
352
                ],
353
                ['Lionel'],
354
            ],
355
            [
356
                'completion:timestarted',
357
                [
358
                    'completion:timestarted_operator' => date::DATE_NOT_EMPTY,
359
                ],
360
                ['Lionel'],
361
            ],
362
            [
363
                'completion:reaggregate',
364
                [
365
                    'completion:reaggregate_operator' => date::DATE_NOT_EMPTY,
366
                ],
367
                ['Lionel'],
368
            ],
369
        ];
370
    }
371
 
372
    /**
373
     * Test getting filter SQL
374
     *
375
     * @param string $filter
376
     * @param array $filtervalues
377
     * @param string[] $expected
378
     *
379
     * @dataProvider datasource_filters_provider
380
     */
381
    public function test_datasource_filters(string $filter, array $filtervalues, array $expected): void {
382
        global $DB;
383
        $this->resetAfterTest();
384
 
385
        $timestart = time() - DAYSECS;
386
        $timeend = $timestart + 3 * DAYSECS;
387
        $timecompleted = $timestart + 2 * DAYSECS;
388
        $timelastaccess = time() + 4 * DAYSECS;
389
 
390
        $category = $this->getDataGenerator()->create_category(['name' => 'Music']);
391
        $course = $this->getDataGenerator()->create_course([
392
            'category' => $category->id,
393
            'fullname' => 'All about Lionel at the work place',
394
            'enablecompletion' => true,
395
            'startdate' => $timestart,
396
            'enddate' => $timeend,
397
        ]);
398
 
399
        $user1 = self::getDataGenerator()->create_user(['firstname' => 'Lionel']);
400
        $user2 = self::getDataGenerator()->create_user(['firstname' => 'Kira']);
401
        $user3 = self::getDataGenerator()->create_user(['firstname' => 'Luna']);
402
 
403
        $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student',
404
            'manual', $timestart - 8 * DAYSECS, $timeend, ENROL_USER_ACTIVE);
405
        $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student',
406
            'manual', $timestart, $timeend, ENROL_USER_ACTIVE);
407
        $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'editingteacher',
408
            'manual', time(), time(), ENROL_USER_SUSPENDED);
409
 
410
        // Mark course as completed for the user.
411
        $ccompletion = new completion_completion(array('course' => $course->id, 'userid' => $user1->id));
412
        $ccompletion->mark_enrolled($timestart);
413
        $ccompletion->mark_inprogress($timestart);
414
        $ccompletion->mark_complete($timecompleted);
415
 
416
        // Set some last access value for the user in the course.
417
        $DB->insert_record('user_lastaccess',
418
            ['userid' => $user1->id, 'courseid' => $course->id, 'timeaccess' => $timelastaccess]);
419
 
420
        /** @var core_reportbuilder_generator $generator */
421
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
422
        $report = $generator->create_report(['name' => 'Participants', 'source' => participants::class, 'default' => false]);
423
 
424
        // Add user firstname column to the report.
425
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:firstname']);
426
 
427
        $DB->set_field('user_enrolments', 'timecreated', 0, ['userid' => $user1->id]);
428
        $DB->set_field('user_enrolments', 'timecreated', 0, ['userid' => $user3->id]);
429
 
430
        // Add filters to the report.
431
        $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => 'enrol:plugin']);
432
        $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filter]);
433
 
434
        // Apply filters.
435
        $filtermanual = ['enrol:plugin_operator' => select::EQUAL_TO, 'enrol:plugin_value' => 'manual'];
436
        $content = $this->get_custom_report_content($report->get('id'), 30, $filtermanual + $filtervalues);
437
 
438
        $this->assertEqualsCanonicalizing($expected, array_column($content, 'c0_firstname'));
439
    }
440
 
441
    /**
442
     * Stress test datasource
443
     *
444
     * In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
445
     */
446
    public function test_stress_datasource(): void {
447
        if (!PHPUNIT_LONGTEST) {
448
            $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
449
        }
450
 
451
        $this->resetAfterTest();
452
 
453
        $course = $this->getDataGenerator()->create_course();
454
        $this->getDataGenerator()->create_and_enrol($course);
455
 
456
        $this->datasource_stress_test_columns(participants::class);
457
        $this->datasource_stress_test_columns_aggregation(participants::class);
458
        $this->datasource_stress_test_conditions(participants::class, 'course:idnumber');
459
    }
460
}