Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 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 /** 18 * Unit tests for core targets. 19 * 20 * @package core_course 21 * @copyright 2019 Victor Deniz <victor@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 global $CFG; 27 28 require_once($CFG->dirroot . '/completion/criteria/completion_criteria.php'); 29 require_once($CFG->dirroot . '/completion/criteria/completion_criteria_activity.php'); 30 require_once($CFG->dirroot . '/lib/grade/grade_item.php'); 31 require_once($CFG->dirroot . '/lib/grade/grade_grade.php'); 32 require_once($CFG->dirroot . '/lib/grade/grade_category.php'); 33 require_once($CFG->dirroot . '/lib/grade/constants.php'); 34 35 /** 36 * Unit tests for core targets. 37 * 38 * @package core_course 39 * @copyright 2019 Victor Deniz <victor@moodle.com> 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 */ 42 class core_analytics_targets_testcase extends advanced_testcase { 43 44 /** 45 * Provides course params for the {@link self::test_core_target_course_completion_analysable()} method. 46 * 47 * @return array 48 */ 49 public function analysable_provider() { 50 51 $now = new DateTime("now", core_date::get_server_timezone_object()); 52 $year = $now->format('Y'); 53 $month = $now->format('m'); 54 55 return [ 56 'coursenotyetstarted' => [ 57 'params' => [ 58 'enablecompletion' => 1, 59 'startdate' => mktime(0, 0, 0, 10, 24, $year + 1) 60 ], 61 'isvalid' => get_string('coursenotyetstarted', 'course') 62 ], 63 'coursenostudents' => [ 64 'params' => [ 65 'enablecompletion' => 1, 66 'startdate' => mktime(0, 0, 0, 10, 24, $year - 2), 67 'enddate' => mktime(0, 0, 0, 10, 24, $year - 1) 68 ], 69 'isvalid' => get_string('nocoursestudents', 'course') 70 ], 71 'coursenosections' => [ 72 'params' => [ 73 'enablecompletion' => 1, 74 'format' => 'social', 75 'students' => true 76 ], 77 'isvalid' => get_string('nocoursesections', 'course') 78 ], 79 'coursenoendtime' => [ 80 'params' => [ 81 'enablecompletion' => 1, 82 'format' => 'topics', 83 'enddate' => 0, 84 'students' => true 85 ], 86 'isvalid' => get_string('nocourseendtime', 'course') 87 ], 88 'courseendbeforestart' => [ 89 'params' => [ 90 'enablecompletion' => 1, 91 'enddate' => mktime(0, 0, 0, 10, 23, $year - 2), 92 'students' => true 93 ], 94 'isvalid' => get_string('errorendbeforestart', 'course') 95 ], 96 'coursetoolong' => [ 97 'params' => [ 98 'enablecompletion' => 1, 99 'startdate' => mktime(0, 0, 0, 10, 24, $year - 2), 100 'enddate' => mktime(0, 0, 0, 10, 23, $year), 101 'students' => true 102 ], 103 'isvalid' => get_string('coursetoolong', 'course') 104 ], 105 'coursealreadyfinished' => [ 106 'params' => [ 107 'enablecompletion' => 1, 108 'startdate' => mktime(0, 0, 0, 10, 24, $year - 2), 109 'enddate' => mktime(0, 0, 0, 10, 23, $year - 1), 110 'students' => true 111 ], 112 'isvalid' => get_string('coursealreadyfinished', 'course'), 113 'fortraining' => false 114 ], 115 'coursenotyetfinished' => [ 116 'params' => [ 117 'enablecompletion' => 1, 118 'startdate' => mktime(0, 0, 0, $month - 1, 24, $year), 119 'enddate' => mktime(0, 0, 0, $month + 2, 23, $year), 120 'students' => true 121 ], 122 'isvalid' => get_string('coursenotyetfinished', 'course') 123 ], 124 'coursenocompletion' => [ 125 'params' => [ 126 'enablecompletion' => 0, 127 'startdate' => mktime(0, 0, 0, $month - 2, 24, $year), 128 'enddate' => mktime(0, 0, 0, $month - 1, 23, $year), 129 'students' => true 130 ], 131 'isvalid' => get_string('completionnotenabledforcourse', 'completion') 132 ], 133 'coursehiddentraining' => [ 134 'params' => [ 135 'enablecompletion' => 1, 136 'startdate' => mktime(0, 0, 0, $month - 1, 24, $year - 1), 137 'enddate' => mktime(0, 0, 0, $month - 1, 23, $year), 138 'students' => true, 139 'visible' => '0', 140 ], 141 'isvalid' => true, 142 ], 143 'coursehiddenprediction' => [ 144 'params' => [ 145 'enablecompletion' => 1, 146 'startdate' => mktime(0, 0, 0, $month - 1, 24, $year), 147 'enddate' => mktime(0, 0, 0, $month - 1, 23, $year + 1), 148 'students' => true, 149 'visible' => '0', 150 ], 151 'isvalid' => get_string('hiddenfromstudents'), 152 'fortraining' => false 153 ], 154 ]; 155 } 156 157 /** 158 * Provides enrolment params for the {@link self::test_core_target_course_completion_samples()} method. 159 * 160 * @return array 161 */ 162 public function sample_provider() { 163 $now = time(); 164 return [ 165 'enrolmentendbeforecourse' => [ 166 'coursestart' => $now, 167 'courseend' => $now + (WEEKSECS * 8), 168 'timestart' => $now, 169 'timeend' => $now - DAYSECS, 170 'isvalidfortraining' => false, 171 'isvalidforprediction' => false 172 ], 173 'enrolmenttoolong' => [ 174 'coursestart' => $now, 175 'courseend' => $now + (WEEKSECS * 8), 176 'timestart' => $now - (YEARSECS + (WEEKSECS * 8)), 177 'timeend' => $now + (WEEKSECS * 8), 178 'isvalidfortraining' => false, 179 'isvalidforprediction' => false 180 ], 181 'enrolmentstartaftercourse' => [ 182 'coursestart' => $now, 183 'courseend' => $now + (WEEKSECS * 8), 184 'timestart' => $now + (WEEKSECS * 9), 185 'timeend' => $now + (WEEKSECS * 10), 186 'isvalidfortraining' => false, 187 'isvalidforprediction' => false 188 ], 189 'enrolmentstartsafternow' => [ 190 'coursestart' => $now, 191 'courseend' => $now + (WEEKSECS * 8), 192 'timestart' => $now + (WEEKSECS * 2), 193 'timeend' => $now + (WEEKSECS * 7), 194 'isvalidfortraining' => false, 195 'isvalidforprediction' => false 196 ], 197 'enrolmentfinishedbeforenow' => [ 198 'coursestart' => $now - (WEEKSECS * 4), 199 'courseend' => $now - (WEEKSECS * 1), 200 'timestart' => $now - (WEEKSECS * 3), 201 'timeend' => $now - (WEEKSECS * 2), 202 'isvalidfortraining' => true, 203 'isvalidforprediction' => false 204 ], 205 ]; 206 } 207 208 /** 209 * Provides enrolment params for the {@link self::test_core_target_course_completion_samples()} method. 210 * 211 * @return array 212 */ 213 public function active_during_analysis_time_provider() { 214 $now = time(); 215 216 return [ 217 'enrol-after-end' => [ 218 'starttime' => $now, 219 'endtime' => $now + WEEKSECS, 220 'timestart' => $now + (WEEKSECS * 2), 221 'timeend' => $now + (WEEKSECS * 3), 222 'nullcalculation' => true, 223 ], 224 'enrol-before-start' => [ 225 'starttime' => $now + (WEEKSECS * 2), 226 'endtime' => $now + (WEEKSECS * 3), 227 'timestart' => $now, 228 'timeend' => $now + WEEKSECS, 229 'nullcalculation' => true, 230 ], 231 'enrol-active-exact-match' => [ 232 'starttime' => $now, 233 'endtime' => $now + (WEEKSECS * 1), 234 'timestart' => $now, 235 'timeend' => $now + (WEEKSECS * 1), 236 'nullcalculation' => false, 237 ], 238 'enrol-active' => [ 239 'starttime' => $now + WEEKSECS, 240 'endtime' => $now + (WEEKSECS * 2), 241 'timestart' => $now, 242 'timeend' => $now + (WEEKSECS * 3), 243 'nullcalculation' => false, 244 ], 245 'enrol-during-analysis-active-just-for-a-while' => [ 246 'starttime' => $now, 247 'endtime' => $now + (WEEKSECS * 10), 248 'timestart' => $now + WEEKSECS, 249 'timeend' => $now + (WEEKSECS * 2), 250 'nullcalculation' => true, 251 ], 252 'enrol-during-analysis-mostly-active' => [ 253 'starttime' => $now, 254 'endtime' => $now + (WEEKSECS * 20), 255 'timestart' => $now + WEEKSECS, 256 'timeend' => $now + (WEEKSECS * 19), 257 'nullcalculation' => false, 258 ], 259 'enrol-partly-active-starts-before' => [ 260 'starttime' => $now + WEEKSECS, 261 'endtime' => $now + (WEEKSECS * 10), 262 'timestart' => $now, 263 'timeend' => $now + (WEEKSECS * 2), 264 'nullcalculation' => true, 265 ], 266 'enrol-mostly-active-starts-before' => [ 267 'starttime' => $now + WEEKSECS, 268 'endtime' => $now + (WEEKSECS * 10), 269 'timestart' => $now, 270 'timeend' => $now + (WEEKSECS * 9), 271 'nullcalculation' => false, 272 ], 273 'enrol-partly-active-ends-afterwards' => [ 274 'starttime' => $now, 275 'endtime' => $now + (WEEKSECS * 10), 276 'timestart' => $now + (WEEKSECS * 9), 277 'timeend' => $now + (WEEKSECS * 11), 278 'nullcalculation' => true, 279 ], 280 'enrol-mostly-active-ends-afterwards' => [ 281 'starttime' => $now, 282 'endtime' => $now + (WEEKSECS * 10), 283 'timestart' => $now + WEEKSECS, 284 'timeend' => $now + (WEEKSECS * 11), 285 'nullcalculation' => false, 286 ], 287 'enrol-partly-active-no-enrolment-end' => [ 288 'starttime' => $now, 289 'endtime' => $now + (WEEKSECS * 10), 290 'timestart' => $now + (WEEKSECS * 9), 291 'timeend' => false, 292 'nullcalculation' => true, 293 ], 294 'enrol-mostly-active-no-enrolment-end-false' => [ 295 'starttime' => $now, 296 'endtime' => $now + (WEEKSECS * 10), 297 'timestart' => $now + WEEKSECS, 298 'timeend' => false, 299 'nullcalculation' => false, 300 ], 301 'enrol-mostly-active-no-enrolment-end-zero' => [ 302 'starttime' => $now, 303 'endtime' => $now + (WEEKSECS * 10), 304 'timestart' => $now + WEEKSECS, 305 'timeend' => 0, 306 'nullcalculation' => false, 307 ], 308 'enrol-no-enrolment-start-false' => [ 309 'starttime' => $now, 310 'endtime' => $now + (WEEKSECS * 10), 311 'timestart' => false, 312 'timeend' => $now + (WEEKSECS * 9), 313 'nullcalculation' => false, 314 ], 315 'enrol-no-enrolment-start-zero' => [ 316 'starttime' => $now, 317 'endtime' => $now + (WEEKSECS * 10), 318 'timestart' => 0, 319 'timeend' => $now + (WEEKSECS * 9), 320 'nullcalculation' => false, 321 ], 322 'no-start' => [ 323 'starttime' => 0, 324 'endtime' => $now + (WEEKSECS * 2), 325 'timestart' => $now + WEEKSECS, 326 'timeend' => $now + (WEEKSECS * 3), 327 'nullcalculation' => false, 328 ], 329 'no-end' => [ 330 'starttime' => $now, 331 'endtime' => 0, 332 'timestart' => $now + (WEEKSECS * 2), 333 'timeend' => $now + (WEEKSECS * 3), 334 'nullcalculation' => false, 335 ] 336 ]; 337 } 338 339 /** 340 * Test the conditions of a valid analysable, both common and specific to this target (course_completion). 341 * 342 * @dataProvider analysable_provider 343 * @param mixed $courseparams Course data 344 * @param true|string $isvalid True when analysable is valid, string when it is not 345 * @param boolean $fortraining True if the course is for training the model 346 */ 347 public function test_core_target_course_completion_analysable($courseparams, $isvalid, $fortraining = true) { 348 global $DB; 349 350 $this->resetAfterTest(true); 351 352 try { 353 $course = $this->getDataGenerator()->create_course($courseparams); 354 } catch (moodle_exception $e) { 355 $course = $this->getDataGenerator()->create_course(); 356 $courserecord = $courseparams; 357 $courserecord['id'] = $course->id; 358 unset($courserecord['students']); 359 360 $DB->update_record_raw('course', $courserecord); 361 $course = get_course($course->id); 362 } 363 $user = $this->getDataGenerator()->create_user(); 364 365 if (!empty($courseparams['enablecompletion'])) { 366 $assign = $this->getDataGenerator()->create_module('assign', ['course' => $course->id, 'completion' => 1]); 367 $cm = get_coursemodule_from_id('assign', $assign->cmid); 368 369 $criteriadata = (object) [ 370 'id' => $course->id, 371 'criteria_activity' => [ 372 $cm->id => 1 373 ] 374 ]; 375 $criterion = new completion_criteria_activity(); 376 $criterion->update_config($criteriadata); 377 } 378 379 $target = new \core_course\analytics\target\course_completion(); 380 381 // Test valid analysables. 382 383 if (!empty($courseparams['students'])) { 384 // Enroll user in course. 385 $this->getDataGenerator()->enrol_user($user->id, $course->id); 386 } 387 388 $analysable = new \core_analytics\course($course); 389 $this->assertEquals($isvalid, $target->is_valid_analysable($analysable, $fortraining)); 390 } 391 392 /** 393 * Test the conditions of a valid sample, both common and specific to this target (course_completion). 394 * 395 * @dataProvider sample_provider 396 * @param int $coursestart Course start date 397 * @param int $courseend Course end date 398 * @param int $timestart Enrol start date 399 * @param int $timeend Enrol end date 400 * @param boolean $isvalidfortraining True when sample is valid for training, false when it is not 401 * @param boolean $isvalidforprediction True when sample is valid for prediction, false when it is not 402 */ 403 public function test_core_target_course_completion_samples($coursestart, $courseend, $timestart, $timeend, 404 $isvalidfortraining, $isvalidforprediction) { 405 406 $this->resetAfterTest(true); 407 408 $courserecord = new stdClass(); 409 $courserecord->startdate = $coursestart; 410 $courserecord->enddate = $courseend; 411 412 $user = $this->getDataGenerator()->create_user(); 413 $course = $this->getDataGenerator()->create_course($courserecord); 414 $this->getDataGenerator()->enrol_user($user->id, $course->id, null, 'manual', $timestart, $timeend); 415 416 $target = new \core_course\analytics\target\course_completion(); 417 $analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []); 418 $analysable = new \core_analytics\course($course); 419 420 $class = new ReflectionClass('\core\analytics\analyser\student_enrolments'); 421 $method = $class->getMethod('get_all_samples'); 422 $method->setAccessible(true); 423 424 list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable); 425 $target->add_sample_data($samplesdata); 426 $sampleid = reset($sampleids); 427 428 $this->assertEquals($isvalidfortraining, $target->is_valid_sample($sampleid, $analysable, true)); 429 $this->assertEquals($isvalidforprediction, $target->is_valid_sample($sampleid, $analysable, false)); 430 } 431 432 /** 433 * Test the conditions of a valid calculation (course_completion). 434 * 435 * @dataProvider active_during_analysis_time_provider 436 * @param int $starttime Analysis start time 437 * @param int $endtime Analysis end time 438 * @param int $timestart Enrol start date 439 * @param int $timeend Enrol end date 440 * @param boolean $nullcalculation Whether the calculation should be null or not 441 */ 442 public function test_core_target_course_completion_active_during_analysis_time($starttime, $endtime, $timestart, $timeend, 443 $nullcalculation) { 444 445 $this->resetAfterTest(true); 446 447 $user = $this->getDataGenerator()->create_user(); 448 $course = $this->getDataGenerator()->create_course(); 449 $this->getDataGenerator()->enrol_user($user->id, $course->id, null, 'manual', $timestart, $timeend); 450 451 $target = new \core_course\analytics\target\course_completion(); 452 $analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []); 453 $analysable = new \core_analytics\course($course); 454 455 $class = new ReflectionClass('\core\analytics\analyser\student_enrolments'); 456 $method = $class->getMethod('get_all_samples'); 457 $method->setAccessible(true); 458 459 list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable); 460 $target->add_sample_data($samplesdata); 461 $sampleid = reset($sampleids); 462 463 $reftarget = new ReflectionObject($target); 464 $refmethod = $reftarget->getMethod('calculate_sample'); 465 $refmethod->setAccessible(true); 466 467 if ($nullcalculation) { 468 $this->assertNull($refmethod->invoke($target, $sampleid, $analysable, $starttime, $endtime)); 469 } else { 470 $this->assertNotNull($refmethod->invoke($target, $sampleid, $analysable, $starttime, $endtime)); 471 } 472 } 473 474 /** 475 * Setup user, framework, competencies and course competencies. 476 */ 477 protected function setup_competencies_environment() { 478 $this->resetAfterTest(true); 479 $now = time(); 480 $this->setAdminUser(); 481 $dg = $this->getDataGenerator(); 482 $lpg = $dg->get_plugin_generator('core_competency'); 483 484 $course = $dg->create_course(array('startdate' => $now - WEEKSECS, 'enddate' => $now - DAYSECS)); 485 $coursenocompetencies = $dg->create_course(array('startdate' => $now - WEEKSECS, 'enddate' => $now - DAYSECS)); 486 487 $u1 = $dg->create_user(); 488 $this->getDataGenerator()->enrol_user($u1->id, $course->id); 489 $this->getDataGenerator()->enrol_user($u1->id, $coursenocompetencies->id); 490 $f1 = $lpg->create_framework(); 491 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 492 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 493 $c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 494 $c4 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 495 $cc1 = $lpg->create_course_competency(array('competencyid' => $c1->get('id'), 'courseid' => $course->id, 496 'ruleoutcome' => \core_competency\course_competency::OUTCOME_NONE)); 497 $cc2 = $lpg->create_course_competency(array('competencyid' => $c2->get('id'), 'courseid' => $course->id, 498 'ruleoutcome' => \core_competency\course_competency::OUTCOME_EVIDENCE)); 499 $cc3 = $lpg->create_course_competency(array('competencyid' => $c3->get('id'), 'courseid' => $course->id, 500 'ruleoutcome' => \core_competency\course_competency::OUTCOME_RECOMMEND)); 501 $cc4 = $lpg->create_course_competency(array('competencyid' => $c4->get('id'), 'courseid' => $course->id, 502 'ruleoutcome' => \core_competency\course_competency::OUTCOME_COMPLETE)); 503 504 return array( 505 'course' => $course, 506 'coursenocompetencies' => $coursenocompetencies, 507 'user' => $u1, 508 'course_competencies' => array($cc1, $cc2, $cc3, $cc4) 509 ); 510 } 511 512 /** 513 * Test the specific conditions of a valid analysable for the course_competencies target. 514 */ 515 public function test_core_target_course_competencies_analysable() { 516 517 $data = $this->setup_competencies_environment(); 518 519 $analysable = new \core_analytics\course($data['course']); 520 $target = new \core_course\analytics\target\course_competencies(); 521 522 $this->assertTrue($target->is_valid_analysable($analysable)); 523 524 $analysable = new \core_analytics\course($data['coursenocompetencies']); 525 $this->assertEquals(get_string('nocompetenciesincourse', 'tool_lp'), $target->is_valid_analysable($analysable)); 526 } 527 528 /** 529 * Test the target value calculation. 530 */ 531 public function test_core_target_course_competencies_calculate() { 532 533 $data = $this->setup_competencies_environment(); 534 535 $target = new \core_course\analytics\target\course_competencies(); 536 $analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []); 537 $analysable = new \core_analytics\course($data['course']); 538 539 $class = new ReflectionClass('\core\analytics\analyser\student_enrolments'); 540 $method = $class->getMethod('get_all_samples'); 541 $method->setAccessible(true); 542 543 list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable); 544 $target->add_sample_data($samplesdata); 545 $sampleid = reset($sampleids); 546 547 $class = new ReflectionClass('\core_course\analytics\target\course_competencies'); 548 $method = $class->getMethod('calculate_sample'); 549 $method->setAccessible(true); 550 551 // Method calculate_sample() returns 1 when the user has not achieved all the competencies assigned to the course. 552 $this->assertEquals(1, $method->invoke($target, $sampleid, $analysable)); 553 554 // Grading of all the competences assigned to the course, in such way that the user achieves them all. 555 foreach ($data['course_competencies'] as $competency) { 556 \core_competency\api::grade_competency_in_course($data['course']->id, $data['user']->id, 557 $competency->get('competencyid'), 3, 'Unit test'); 558 } 559 // Method calculate_sample() returns 0 when the user has achieved all the competencies assigned to the course. 560 $this->assertEquals(0, $method->invoke($target, $sampleid, $analysable)); 561 } 562 563 /** 564 * Test the specific conditions of a valid analysable for the course_gradetopass target. 565 */ 566 public function test_core_target_course_gradetopass_analysable() { 567 global $DB; 568 569 $this->resetAfterTest(true); 570 $now = time(); 571 572 $dg = $this->getDataGenerator(); 573 574 // Course without grade to pass set. 575 $course1 = $dg->create_course(array('startdate' => $now - WEEKSECS, 'enddate' => $now - DAYSECS)); 576 $student1 = $dg->create_user(); 577 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 578 $dg->enrol_user($student1->id, $course1->id, $studentrole->id); 579 580 $analysable = new \core_analytics\course($course1); 581 $target = new \core_course\analytics\target\course_gradetopass(); 582 $this->assertEquals(get_string('gradetopassnotset', 'course'), $target->is_valid_analysable($analysable)); 583 584 // Set grade to pass. 585 $courseitem = grade_item::fetch_course_item($course1->id); 586 $courseitem->gradepass = 50; 587 $DB->update_record('grade_items', $courseitem); 588 // Since the grade to pass value is cached in the target, a new one it is instanciated. 589 $target = new \core_course\analytics\target\course_gradetopass(); 590 $this->assertTrue($target->is_valid_analysable($analysable)); 591 592 } 593 594 /** 595 * Test the target value calculation of the course_gradetopass target. 596 */ 597 public function test_core_target_course_gradetopass_calculate() { 598 global $DB; 599 600 $this->resetAfterTest(true); 601 602 $dg = $this->getDataGenerator(); 603 $course1 = $dg->create_course(); 604 // Set grade to pass. 605 $student1 = $dg->create_user(); 606 $student2 = $dg->create_user(); 607 $student3 = $dg->create_user(); 608 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 609 $dg->enrol_user($student1->id, $course1->id, $studentrole->id); 610 $dg->enrol_user($student2->id, $course1->id, $studentrole->id); 611 $dg->enrol_user($student3->id, $course1->id, $studentrole->id); 612 613 // get_all_samples() does not guarantee any order, so let's 614 // explicitly define the expectations here for later comparing. 615 // Expectations format being array($userid => expectation, ...) 616 $expectations = []; 617 618 $courseitem = grade_item::fetch_course_item($course1->id); 619 // Student1 (< gradepass) fails, so it's non achieved sample. 620 $courseitem->update_final_grade($student1->id, 30); 621 $expectations[$student1->id] = 1; 622 623 // Student2 (> gradepass) passes, so it's achieved sample. 624 $courseitem->update_final_grade($student2->id, 60); 625 $expectations[$student2->id] = 0; 626 627 // Student 3 (has no grade) fails, so it's non achieved sample. 628 $expectations[$student3->id] = 1; 629 630 $courseitem->gradepass = 50; 631 $DB->update_record('grade_items', $courseitem); 632 633 $target = new \core_course\analytics\target\course_gradetopass(); 634 $analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []); 635 $analysable = new \core_analytics\course($course1); 636 637 $class = new ReflectionClass('\core\analytics\analyser\student_enrolments'); 638 $method = $class->getMethod('get_all_samples'); 639 $method->setAccessible(true); 640 641 list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable); 642 $target->add_sample_data($samplesdata); 643 644 $class = new ReflectionClass('\core_course\analytics\target\course_gradetopass'); 645 $method = $class->getMethod('calculate_sample'); 646 $method->setAccessible(true); 647 648 // Verify all the expectations are fulfilled. 649 foreach ($sampleids as $sampleid => $key) { 650 $this->assertEquals($expectations[$samplesdata[$key]['user']->id], $method->invoke($target, $sampleid, $analysable)); 651 } 652 } 653 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body