Differences Between: [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body