Differences Between: [Versions 39 and 310]
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 the grade API at /lib/classes/grades_external.php 19 * 20 * @package core_grades 21 * @category external 22 * @copyright 2012 Andrew Davis 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 * @since Moodle 2.7 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 global $CFG; 30 31 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 32 33 /** 34 * Grades functions unit tests 35 * 36 * @package core_grades 37 * @category external 38 * @copyright 2012 Andrew Davis 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class core_grades_external_testcase extends externallib_advanced_testcase { 42 43 /** 44 * Load initial test information 45 * 46 * @param string $assignmentname Assignment name 47 * @param int $student1rawgrade Student 1 grade 48 * @param int $student2rawgrade Student 2 grade 49 * @return array Array of vars with test information 50 */ 51 protected function load_test_data($assignmentname, $student1rawgrade, $student2rawgrade) { 52 global $DB; 53 54 // Adds a course, a teacher, 2 students, an assignment and grades for the students. 55 $course = $this->getDataGenerator()->create_course(); 56 $coursecontext = context_course::instance($course->id); 57 58 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 59 60 $student1 = $this->getDataGenerator()->create_user(); 61 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id); 62 63 $student2 = $this->getDataGenerator()->create_user(); 64 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id); 65 66 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 67 $teacher = $this->getDataGenerator()->create_user(); 68 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id); 69 70 $parent = $this->getDataGenerator()->create_user(); 71 $this->setUser($parent); 72 $student1context = context_user::instance($student1->id); 73 // Creates a new role, gives it the capability and gives $USER that role. 74 $parentroleid = $this->assignUserCapability('moodle/grade:viewall', $student1context->id); 75 // Enrol the user in the course using the new role. 76 $this->getDataGenerator()->enrol_user($parent->id, $course->id, $parentroleid); 77 78 $assignment = $this->getDataGenerator()->create_module('assign', array('name' => $assignmentname, 'course' => $course->id)); 79 $modcontext = get_coursemodule_from_instance('assign', $assignment->id, $course->id); 80 $assignment->cmidnumber = $modcontext->id; 81 82 $student1grade = array('userid' => $student1->id, 'rawgrade' => $student1rawgrade); 83 $student2grade = array('userid' => $student2->id, 'rawgrade' => $student2rawgrade); 84 $studentgrades = array($student1->id => $student1grade, $student2->id => $student2grade); 85 assign_grade_item_update($assignment, $studentgrades); 86 87 // Insert a custom grade scale to be used by an outcome. 88 $gradescale = new grade_scale(); 89 $gradescale->name = 'unittestscale3'; 90 $gradescale->courseid = $course->id; 91 $gradescale->userid = 0; 92 $gradescale->scale = 'Distinction, Very Good, Good, Pass, Fail'; 93 $gradescale->description = 'This scale is used to mark standard assignments.'; 94 $gradescale->insert(); 95 96 // Insert an outcome. 97 $data = new stdClass(); 98 $data->courseid = $course->id; 99 $data->fullname = 'Team work'; 100 $data->shortname = 'Team work'; 101 $data->scaleid = $gradescale->id; 102 $outcome = new grade_outcome($data, false); 103 $outcome->insert(); 104 105 $outcomegradeitem = new grade_item(); 106 $outcomegradeitem->itemname = $outcome->shortname; 107 $outcomegradeitem->itemtype = 'mod'; 108 $outcomegradeitem->itemmodule = 'assign'; 109 $outcomegradeitem->iteminstance = $assignment->id; 110 $outcomegradeitem->outcomeid = $outcome->id; 111 $outcomegradeitem->cmid = 0; 112 $outcomegradeitem->courseid = $course->id; 113 $outcomegradeitem->aggregationcoef = 0; 114 $outcomegradeitem->itemnumber = 1; // The activity's original grade item will be 0. 115 $outcomegradeitem->gradetype = GRADE_TYPE_SCALE; 116 $outcomegradeitem->scaleid = $outcome->scaleid; 117 // This next two values for testing that returns parameters are correcly formatted. 118 $outcomegradeitem->set_locked(true); 119 $outcomegradeitem->hidden = ''; 120 $outcomegradeitem->insert(); 121 122 $assignmentgradeitem = grade_item::fetch( 123 array( 124 'itemtype' => 'mod', 125 'itemmodule' => 'assign', 126 'iteminstance' => $assignment->id, 127 'itemnumber' => 0, 128 'courseid' => $course->id 129 ) 130 ); 131 $outcomegradeitem->set_parent($assignmentgradeitem->categoryid); 132 $outcomegradeitem->move_after_sortorder($assignmentgradeitem->sortorder); 133 134 return array($course, $assignment, $student1, $student2, $teacher, $parent); 135 } 136 137 /** 138 * Test get_grades() 139 */ 140 public function test_get_grades() { 141 global $CFG; 142 143 $this->resetAfterTest(true); 144 $CFG->enableoutcomes = 1; 145 146 $assignmentname = 'The assignment'; 147 $student1rawgrade = 10; 148 $student2rawgrade = 20; 149 list($course, $assignment, $student1, $student2, $teacher, $parent) = 150 $this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade); 151 $assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid, 0, false, MUST_EXIST); 152 153 // Teacher requesting a student grade for the assignment. 154 $this->setUser($teacher); 155 $grades = core_grades_external::get_grades( 156 $course->id, 157 'mod_assign', 158 $assigmentcm->id, 159 array($student1->id) 160 ); 161 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades); 162 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id)); 163 164 // Teacher requesting all the grades of student1 in a course. 165 $grades = core_grades_external::get_grades( 166 $course->id, 167 null, 168 null, 169 array($student1->id) 170 ); 171 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades); 172 $this->assertTrue(count($grades['items']) == 2); 173 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id)); 174 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, 'course', $student1->id)); 175 176 $outcome = $this->get_outcome($grades, $assigmentcm->id); 177 $this->assertEquals($outcome['name'], 'Team work'); 178 $this->assertEquals(0, $this->get_outcome_student_grade($grades, $assigmentcm->id, $student1->id)); 179 180 // Teacher requesting all the grades of all the students in a course. 181 $grades = core_grades_external::get_grades( 182 $course->id, 183 null, 184 null, 185 array($student1->id, $student2->id) 186 ); 187 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades); 188 $this->assertTrue(count($grades['items']) == 2); 189 $this->assertTrue(count($grades['items'][0]['grades']) == 2); 190 $this->assertTrue(count($grades['items'][1]['grades']) == 2); 191 192 // Student requesting another student's grade for the assignment (should fail). 193 $this->setUser($student1); 194 try { 195 $grades = core_grades_external::get_grades( 196 $course->id, 197 'mod_assign', 198 $assigmentcm->id, 199 array($student2->id) 200 ); 201 $this->fail('moodle_exception expected'); 202 } catch (moodle_exception $ex) { 203 $this->assertTrue(true); 204 } 205 206 // Parent requesting their child's grade for the assignment (should fail). 207 $this->setUser($parent); 208 try { 209 $grades = core_grades_external::get_grades( 210 $course->id, 211 'mod_assign', 212 $assigmentcm->id, 213 array($student1->id) 214 ); 215 $this->fail('moodle_exception expected'); 216 } catch (moodle_exception $ex) { 217 $this->assertTrue(true); 218 } 219 220 // Parent requesting another student's grade for the assignment(should fail). 221 try { 222 $grades = core_grades_external::get_grades( 223 $course->id, 224 'mod_assign', 225 $assigmentcm->id, 226 array($student2->id) 227 ); 228 $this->fail('moodle_exception expected'); 229 } catch (moodle_exception $ex) { 230 $this->assertTrue(true); 231 } 232 233 // Student requesting all other student grades for the assignment (should fail). 234 try { 235 $grades = core_grades_external::get_grades( 236 $course->id, 237 'mod_assign', 238 $assigmentcm->id, 239 array($student1->id, $student2->id) 240 ); 241 $this->fail('moodle_exception expected'); 242 } catch (moodle_exception $ex) { 243 $this->assertTrue(true); 244 } 245 246 // Student requesting only grade item information (should fail). 247 try { 248 $grades = core_grades_external::get_grades( 249 $course->id, 250 'mod_assign', 251 $assigmentcm->id, 252 array() 253 ); 254 $this->fail('moodle_exception expected'); 255 } catch (moodle_exception $ex) { 256 $this->assertTrue(true); 257 } 258 259 // Teacher requesting student grades for a course. 260 $this->setUser($teacher); 261 $grades = core_grades_external::get_grades( 262 $course->id, 263 'mod_assign', 264 $assigmentcm->id, 265 array($student1->id, $student2->id) 266 ); 267 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades); 268 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id)); 269 $this->assertEquals($student2rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student2->id)); 270 271 // Teacher requesting grade item information. 272 $grades = core_grades_external::get_grades( 273 $course->id, 274 'mod_assign', 275 $assigmentcm->id 276 ); 277 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades); 278 $activity = $this->get_activity($grades, $assigmentcm->id); 279 $this->assertEquals($activity['name'], $assignmentname); 280 $this->assertEquals(count($activity['grades']), 0); 281 282 // Teacher requesting all grade items in a course. 283 $grades = core_grades_external::get_grades( 284 $course->id 285 ); 286 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades); 287 $this->assertTrue(count($grades['items']) == 2); 288 289 $activity = $this->get_activity($grades, $assigmentcm->id); 290 $this->assertEquals($activity['name'], $assignmentname); 291 $this->assertEquals(count($activity['grades']), 0); 292 293 $outcome = $this->get_outcome($grades, $assigmentcm->id); 294 $this->assertEquals($outcome['name'], 'Team work'); 295 296 // Hide a grade item then have student request it. 297 $result = core_grades_external::update_grades( 298 'test', 299 $course->id, 300 'mod_assign', 301 $assigmentcm->id, 302 0, 303 array(), 304 array('hidden' => 1) 305 ); 306 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result); 307 $this->assertTrue($result == GRADE_UPDATE_OK); 308 309 // Check it's definitely hidden. 310 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id); 311 $this->assertEquals($grades->items[0]->hidden, 1); 312 313 // Teacher should still be able to see the hidden grades. 314 $this->setUser($teacher); 315 $grades = core_grades_external::get_grades( 316 $course->id, 317 'mod_assign', 318 $assigmentcm->id, 319 array($student1->id) 320 ); 321 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades); 322 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id)); 323 } 324 325 /** 326 * Get an activity 327 * 328 * @param array $grades Array of grades 329 * @param int $cmid Activity course module id 330 * @return strdClass Activity object 331 */ 332 private function get_activity($grades, $cmid) { 333 foreach ($grades['items'] as $item) { 334 if ($item['activityid'] == $cmid) { 335 return $item; 336 } 337 } 338 return null; 339 } 340 341 /** 342 * Get a grade for an activity 343 * 344 * @param array $grades Array of grades 345 * @param int $cmid Activity course module id 346 * @param int $studentid Student it 347 * @return stdClass Activity Object 348 */ 349 private function get_activity_student_grade($grades, $cmid, $studentid) { 350 $item = $this->get_activity($grades, $cmid); 351 foreach ($item['grades'] as $grade) { 352 if ($grade['userid'] == $studentid) { 353 return $grade['grade']; 354 } 355 } 356 return null; 357 } 358 359 /** 360 * Get an ouctome 361 * 362 * @param array $grades Array of grades 363 * @param int $cmid Activity course module id 364 * @return stdClass Outcome object 365 */ 366 private function get_outcome($grades, $cmid) { 367 foreach ($grades['outcomes'] as $outcome) { 368 if ($outcome['activityid'] == $cmid) { 369 return $outcome; 370 } 371 } 372 return null; 373 } 374 375 /** 376 * Get a grade from an outcome 377 * 378 * @param array $grades Array of grades 379 * @param int $cmid Activity course module id 380 * @param int $studentid Student id 381 * @return stdClass Outcome object 382 */ 383 private function get_outcome_student_grade($grades, $cmid, $studentid) { 384 $outcome = $this->get_outcome($grades, $cmid); 385 foreach ($outcome['grades'] as $grade) { 386 if ($grade['userid'] == $studentid) { 387 return $grade['grade']; 388 } 389 } 390 return null; 391 } 392 393 /** 394 * Test update_grades() 395 */ 396 public function test_update_grades() { 397 global $DB; 398 399 $this->resetAfterTest(true); 400 401 $assignmentname = 'The assignment'; 402 $student1rawgrade = 10; 403 $student2rawgrade = 20; 404 list($course, $assignment, $student1, $student2, $teacher, $parent) = 405 $this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade); 406 $assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid, 0, false, MUST_EXIST); 407 408 $this->setUser($teacher); 409 410 // Teacher updating grade item information. 411 $changedmax = 93; 412 $result = core_grades_external::update_grades( 413 'test', 414 $course->id, 415 'mod_assign', 416 $assigmentcm->id, 417 0, 418 array(), 419 array('grademax' => $changedmax) 420 ); 421 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result); 422 $this->assertTrue($result == GRADE_UPDATE_OK); 423 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id); 424 $this->assertTrue($grades->items[0]->grademax == $changedmax); 425 426 // Teacher updating 1 student grade. 427 $student1grade = 23; 428 $result = core_grades_external::update_grades( 429 'test', 430 $course->id, 431 'mod_assign', 432 $assigmentcm->id, 433 0, 434 array(array('studentid' => $student1->id, 'grade' => $student1grade)) 435 ); 436 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result); 437 $this->assertTrue($result == GRADE_UPDATE_OK); 438 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id, array($student1->id)); 439 $this->assertTrue($grades->items[0]->grades[$student1->id]->grade == $student1grade); 440 441 // Teacher updating multiple student grades. 442 $student1grade = 11; 443 $student2grade = 13; 444 $result = core_grades_external::update_grades( 445 'test', 446 $course->id, 447 'mod_assign', 448 $assigmentcm->id, 449 0, 450 array( 451 array('studentid' => $student1->id, 'grade' => $student1grade), 452 array('studentid' => $student2->id, 'grade' => $student2grade) 453 ) 454 ); 455 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result); 456 $this->assertTrue($result == GRADE_UPDATE_OK); 457 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id, array($student1->id, $student2->id)); 458 $this->assertTrue($grades->items[0]->grades[$student1->id]->grade == $student1grade); 459 $this->assertTrue($grades->items[0]->grades[$student2->id]->grade == $student2grade); 460 461 // Student attempting to update their own grade (should fail). 462 $this->setUser($student1); 463 try { 464 $student1grade = 17; 465 $result = core_grades_external::update_grades( 466 'test', 467 $course->id, 468 'mod_assign', 469 $assigmentcm->id, 470 0, 471 array( array('studentid' => $student1->id, 'grade' => $student1grade)) 472 ); 473 $this->fail('moodle_exception expected'); 474 } catch (moodle_exception $ex) { 475 $this->assertTrue(true); 476 } 477 478 // Parent attempting to update their child's grade (should fail). 479 $this->setUser($parent); 480 try { 481 $student1grade = 13; 482 $result = core_grades_external::update_grades( 483 'test', 484 $course->id, 485 'mod_assign', 486 $assigmentcm->id, 487 0, 488 array( array('studentid' => $student1->id, 'grade' => $student1grade)) 489 ); 490 $this->fail('moodle_exception expected'); 491 } catch (moodle_exception $ex) { 492 $this->assertTrue(true); 493 } 494 495 // Student trying to hide a grade item (should fail). 496 $this->setUser($student1); 497 try { 498 $result = core_grades_external::update_grades( 499 'test', 500 $course->id, 501 'mod_assign', 502 $assigmentcm->id, 503 0, 504 array(), 505 array('hidden' => 1) 506 ); 507 $this->fail('moodle_exception expected'); 508 } catch (moodle_exception $ex) { 509 $this->assertTrue(true); 510 } 511 512 // Give the student role 'moodle/grade:hide' and they should now be able to hide the grade item. 513 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 514 $coursecontext = context_course::instance($course->id); 515 assign_capability('moodle/grade:hide', CAP_ALLOW, $studentrole->id, $coursecontext->id); 516 accesslib_clear_all_caches_for_unit_testing(); 517 518 // Check the activity isn't already hidden. 519 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id); 520 $this->assertTrue($grades->items[0]->hidden == 0); 521 522 $result = core_grades_external::update_grades( 523 'test', 524 $course->id, 525 'mod_assign', 526 $assigmentcm->id, 527 0, 528 array(), 529 array('hidden' => 1) 530 ); 531 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result); 532 $this->assertTrue($result == GRADE_UPDATE_OK); 533 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id); 534 $this->assertTrue($grades->items[0]->hidden == 1); 535 } 536 537 /** 538 * Test create_gradecategory. 539 * 540 * @return void 541 */ 542 public function test_create_gradecategory() { 543 global $DB; 544 $this->resetAfterTest(true); 545 $course = $this->getDataGenerator()->create_course(); 546 $this->setAdminUser(); 547 548 // Test the most basic gradecategory creation. 549 $status1 = core_grades_external::create_gradecategory($course->id, 'Test Category 1', []); 550 551 $courseparentcat = new grade_category(['courseid' => $course->id, 'depth' => 1], true); 552 $record1 = $DB->get_record('grade_categories', ['id' => $status1['categoryid']]); 553 $this->assertEquals('Test Category 1', $record1->fullname); 554 // Confirm that the parent category for this category is the top level category for the course. 555 $this->assertEquals($courseparentcat->id, $record1->parent); 556 $this->assertEquals(2, $record1->depth); 557 558 // Now create a category as a child of the newly created category. 559 $status2 = core_grades_external::create_gradecategory($course->id, 'Test Category 2', ['parentcategoryid' => $record1->id]); 560 $record2 = $DB->get_record('grade_categories', ['id' => $status2['categoryid']]); 561 $this->assertEquals($record1->id, $record2->parent); 562 $this->assertEquals(3, $record2->depth); 563 // Check the path is correct. 564 $this->assertEquals('/' . implode('/', [$courseparentcat->id, $record1->id, $record2->id]) . '/', $record2->path); 565 566 /* MDL-72377 commenting broken test. 567 // Now create a category with some customised data and check the returns. This customises every value. 568 $customopts = [ 569 'aggregation' => GRADE_AGGREGATE_MEAN, 570 'aggregateonlygraded' => 0, 571 'aggregateoutcomes' => 1, 572 'droplow' => 1, 573 'itemname' => 'item', 574 'iteminfo' => 'info', 575 'idnumber' => 'idnumber', 576 'gradetype' => GRADE_TYPE_TEXT, 577 'grademax' => 5, 578 'grademin' => 2, 579 'gradepass' => 3, 580 'display' => GRADE_DISPLAY_TYPE_LETTER, 581 // Hack. This must be -2 to use the default setting. 582 'decimals' => 3, 583 'hiddenuntil' => time(), 584 'locktime' => time(), 585 'weightoverride' => 1, 586 'aggregationcoef2' => 20, 587 'parentcategoryid' => $record2->id 588 ]; 589 590 $status3 = core_grades_external::create_gradecategory($course->id, 'Test Category 3', $customopts); 591 $cat3 = new grade_category(['courseid' => $course->id, 'id' => $status3['categoryid']], true); 592 $cat3->load_grade_item(); 593 594 // Lets check all of the data is in the right shape. 595 $this->assertEquals(GRADE_AGGREGATE_MEAN, $cat3->aggregation); 596 $this->assertEquals(0, $cat3->aggregateonlygraded); 597 $this->assertEquals(1, $cat3->aggregateoutcomes); 598 $this->assertEquals(1, $cat3->droplow); 599 $this->assertEquals('item', $cat3->grade_item->itemname); 600 $this->assertEquals('info', $cat3->grade_item->iteminfo); 601 $this->assertEquals('idnumber', $cat3->grade_item->idnumber); 602 $this->assertEquals(GRADE_TYPE_TEXT, $cat3->grade_item->gradetype); 603 $this->assertEquals(5, $cat3->grade_item->grademax); 604 $this->assertEquals(2, $cat3->grade_item->grademin); 605 $this->assertEquals(3, $cat3->grade_item->gradepass); 606 $this->assertEquals(GRADE_DISPLAY_TYPE_LETTER, $cat3->grade_item->display); 607 $this->assertEquals(3, $cat3->grade_item->decimals); 608 $this->assertGreaterThanOrEqual($cat3->grade_item->hidden, time()); 609 $this->assertGreaterThanOrEqual($cat3->grade_item->locktime, time()); 610 $this->assertEquals(1, $cat3->grade_item->weightoverride); 611 // Coefficient is converted to percentage. 612 $this->assertEquals(0.2, $cat3->grade_item->aggregationcoef2); 613 $this->assertEquals($record2->id, $cat3->parent);*/ 614 } 615 616 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body