function test_grade_grade_fetch() { $grade_grade = new grade_grade(); $this->assertTrue(method_exists($grade_grade, 'fetch')); $grades = grade_grade::fetch(array('id' => $this->grade_grades[0]->id)); $this->assertEqual($this->grade_grades[0]->id, $grades->id); $this->assertEqual($this->grade_grades[0]->rawgrade, $grades->rawgrade); }
/** * Tests grade_report_grader::process_data() * * process_data() processes submitted grade and feedback data */ public function test_process_data() { global $DB, $CFG; $this->resetAfterTest(true); $course = $this->getDataGenerator()->create_course(); $coursecontext = context_course::instance($course->id); // Create and enrol a student. $student = $this->getDataGenerator()->create_user(array('username' => 'Student Sam')); $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST); $this->getDataGenerator()->enrol_user($student->id, $course->id, $role->id); // Test with limited grades. $CFG->unlimitedgrades = 0; $forummax = 80; $forum1 = $this->getDataGenerator()->create_module('forum', array('assessed' => 1, 'scale' => $forummax, 'course' => $course->id)); // Switch the stdClass instance for a grade item instance. $forum1 = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'forum', 'iteminstance' => $forum1->id, 'courseid' => $course->id)); $report = $this->create_report($course, $coursecontext); $testgrade = 60.0; $data = new stdClass(); $data->id = $course->id; $data->report = 'grader'; $data->grade = array(); $data->grade[$student->id] = array(); $data->grade[$student->id][$forum1->id] = $testgrade; $warnings = $report->process_data($data); $this->assertEquals(count($warnings), 0); $studentgrade = grade_grade::fetch(array('itemid' => $forum1->id, '' => $student->id)); $this->assertEquals($studentgrade->finalgrade, $testgrade); // Grade above max. Should be pulled down to max. $toobig = 200.0; $data->grade[$student->id][$forum1->id] = $toobig; $warnings = $report->process_data($data); $this->assertEquals(count($warnings), 1); $studentgrade = grade_grade::fetch(array('itemid' => $forum1->id, '' => $student->id)); $this->assertEquals($studentgrade->finalgrade, $forummax); // Grade below min. Should be pulled up to min. $toosmall = -10.0; $data->grade[$student->id][$forum1->id] = $toosmall; $warnings = $report->process_data($data); $this->assertEquals(count($warnings), 1); $studentgrade = grade_grade::fetch(array('itemid' => $forum1->id, '' => $student->id)); $this->assertEquals($studentgrade->finalgrade, 0); // Test unlimited grades so we can give a student a grade about max. $CFG->unlimitedgrades = 1; $data->grade[$student->id][$forum1->id] = $toobig; $warnings = $report->process_data($data); $this->assertEquals(count($warnings), 0); $studentgrade = grade_grade::fetch(array('itemid' => $forum1->id, '' => $student->id)); $this->assertEquals($studentgrade->finalgrade, $toobig); }
public function test_task_timefilter() { $task = new \local_gradelock\task\lock_grades(); $grade_grade = new grade_grade(); $grade_grade->itemid = $this->grade_items[0]->id; $grade_grade->userid = 10; $grade_grade->rawgrade = 88; $grade_grade->rawgrademax = 110; $grade_grade->rawgrademin = 18; $grade_grade->load_grade_item(); $grade_grade->insert(); $grade_grade->grade_item->update_final_grade($this->user[0]->id, 100, 'gradebook', '', FORMAT_MOODLE); $grade_grade->update(); $task->execute(); $grade_grade = grade_grade::fetch(array('userid' => $this->user[0]->id, 'itemid' => $this->grade_items[0]->id)); $this->assertFalse($grade_grade->is_locked()); }
/** * Returns latest grades * * @param string $user Username * @param int $cid Course identifier */ function get_last_user_grades($username, $limit) { global $CFG, $DB; $username = utf8_decode($username); $username = strtolower($username); $user = get_complete_user_data('username', $username); $uid = $user->id; if (!$limit) { $limit = 1000; } $SQL = "SELECT distinct(g.itemid), g.finalgrade,gi.courseid,gi.itemname,gi.id, g.timemodified as tm\n FROM {$CFG->prefix}grade_items gi\n JOIN {$CFG->prefix}grade_grades g ON g.itemid = gi.id\n JOIN {$CFG->prefix}user u ON u.id = g.userid\n JOIN {$CFG->prefix}role_assignments ra ON ra.userid = u.id\n WHERE g.finalgrade IS NOT NULL \n\t\t \tand gi.itemname IS NOT NULL\n\t\t\t AND u.id = ?\n \tORDER BY tm\n\t\t\tLIMIT {$limit}"; $sum_array = array(); $params = array($uid); if ($sums = $DB->get_records_sql($SQL, $params)) { $i = 0; foreach ($sums as $sum) { if (!($grade_grade = grade_grade::fetch(array('itemid' => $sum->id, 'userid' => $uid)))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $grade_object->id; } $grade_item = $grade_grade->load_grade_item(); $scale = $grade_item->load_scale(); $formatted_grade = grade_format_gradevalue($sum->finalgrade, $grade_item, true, GRADE_DISPLAY_TYPE_REAL); $t['itemname'] = $sum->itemname; $t['finalgrade'] = $formatted_grade; $t['average'] = $this->get_average_grade($grade_grade->itemid); $tareas[] = $t; $i++; } return $tareas; } return array(); }
/** * Retrieve grade items and, optionally, student grades * * @param int $courseid Course id * @param string $component Component name * @param int $activityid Activity id * @param array $userids Array of user ids * @return array Array of grades * @since Moodle 2.7 */ public static function get_grades($courseid, $component = null, $activityid = null, $userids = array()) { global $CFG, $USER, $DB; $params = self::validate_parameters(self::get_grades_parameters(), array('courseid' => $courseid, 'component' => $component, 'activityid' => $activityid, 'userids' => $userids)); $coursecontext = context_course::instance($params['courseid']); try { self::validate_context($coursecontext); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $params['courseid']; throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam); } $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST); $access = false; if (has_capability('moodle/grade:viewall', $coursecontext)) { // Can view all user's grades in this course. $access = true; } else { if ($course->showgrades && count($params['userids']) == 1) { // Course showgrades == students/parents can access grades. if ($params['userids'][0] == $USER->id and has_capability('moodle/grade:view', $coursecontext)) { // Student can view their own grades in this course. $access = true; } else { if (has_capability('moodle/grade:viewall', context_user::instance($params['userids'][0]))) { // User can view the grades of this user. Parent most probably. $access = true; } } } } if (!$access) { throw new moodle_exception('nopermissiontoviewgrades', 'error'); } $itemtype = null; $itemmodule = null; if (!empty($params['component'])) { list($itemtype, $itemmodule) = normalize_component($params['component']); } $cm = null; if (!empty($itemmodule) && !empty($activityid)) { if (!($cm = get_coursemodule_from_id($itemmodule, $activityid))) { throw new moodle_exception('invalidcoursemodule'); } } $cminstanceid = null; if (!empty($cm)) { $cminstanceid = $cm->instance; } $grades = grade_get_grades($params['courseid'], $itemtype, $itemmodule, $cminstanceid, $params['userids']); $acitivityinstances = null; if (empty($cm)) { // If we're dealing with multiple activites load all the module info. $modinfo = get_fast_modinfo($params['courseid']); $acitivityinstances = $modinfo->get_instances(); } foreach ($grades->items as $gradeitem) { if (!empty($cm)) { // If they only requested one activity we will already have the cm. $modulecm = $cm; } else { if (!empty($gradeitem->itemmodule)) { $modulecm = $acitivityinstances[$gradeitem->itemmodule][$gradeitem->iteminstance]; } else { // Course grade item. continue; } } // Make student feedback ready for output. foreach ($gradeitem->grades as $studentgrade) { if (!empty($studentgrade->feedback)) { list($studentgrade->feedback, $categoryinfo->feedbackformat) = external_format_text($studentgrade->feedback, $studentgrade->feedbackformat, $modulecm->id, $params['component'], 'feedback', null); } } } // Convert from objects to arrays so all web service clients are supported. // While we're doing that we also remove grades the current user can't see due to hiding. $gradesarray = array(); $canviewhidden = has_capability('moodle/grade:viewhidden', context_course::instance($params['courseid'])); $gradesarray['items'] = array(); foreach ($grades->items as $gradeitem) { // Switch the stdClass instance for a grade item instance so we can call is_hidden() and use the ID. $gradeiteminstance = self::get_grade_item($course->id, $gradeitem->itemtype, $gradeitem->itemmodule, $gradeitem->iteminstance, 0); if (!$canviewhidden && $gradeiteminstance->is_hidden()) { continue; } // Format mixed bool/integer parameters. $gradeitem->hidden = empty($gradeitem->hidden) ? 0 : $gradeitem->hidden; $gradeitem->locked = empty($gradeitem->locked) ? 0 : $gradeitem->locked; $gradeitemarray = (array) $gradeitem; $gradeitemarray['grades'] = array(); if (!empty($gradeitem->grades)) { foreach ($gradeitem->grades as $studentid => $studentgrade) { if (!$canviewhidden) { // Need to load the grade_grade object to check visibility. $gradegradeinstance = grade_grade::fetch(array('userid' => $studentid, 'itemid' => $gradeiteminstance->id)); // The grade grade may be legitimately missing if the student has no grade. if (!empty($gradegradeinstance) && $gradegradeinstance->is_hidden()) { continue; } } // Format mixed bool/integer parameters. $studentgrade->hidden = empty($studentgrade->hidden) ? 0 : $studentgrade->hidden; $studentgrade->locked = empty($studentgrade->locked) ? 0 : $studentgrade->locked; $studentgrade->overridden = empty($studentgrade->overridden) ? 0 : $studentgrade->overridden; $gradeitemarray['grades'][$studentid] = (array) $studentgrade; // Add the student ID as some WS clients can't access the array key. $gradeitemarray['grades'][$studentid]['userid'] = $studentid; } } // If they requested grades for multiple activities load the cm object now. $modulecm = $cm; if (empty($modulecm) && !empty($gradeiteminstance->itemmodule)) { $modulecm = $acitivityinstances[$gradeiteminstance->itemmodule][$gradeiteminstance->iteminstance]; } if ($gradeiteminstance->itemtype == 'course') { $gradesarray['items']['course'] = $gradeitemarray; $gradesarray['items']['course']['activityid'] = 'course'; } else { $gradesarray['items'][$modulecm->id] = $gradeitemarray; // Add the activity ID as some WS clients can't access the array key. $gradesarray['items'][$modulecm->id]['activityid'] = $modulecm->id; } } $gradesarray['outcomes'] = array(); foreach ($grades->outcomes as $outcome) { $modulecm = $cm; if (empty($modulecm)) { $modulecm = $acitivityinstances[$outcome->itemmodule][$outcome->iteminstance]; } // Format mixed bool/integer parameters. $outcome->hidden = empty($outcome->hidden) ? 0 : $outcome->hidden; $outcome->locked = empty($outcome->locked) ? 0 : $outcome->locked; $gradesarray['outcomes'][$modulecm->id] = (array) $outcome; $gradesarray['outcomes'][$modulecm->id]['activityid'] = $modulecm->id; $gradesarray['outcomes'][$modulecm->id]['grades'] = array(); if (!empty($outcome->grades)) { foreach ($outcome->grades as $studentid => $studentgrade) { if (!$canviewhidden) { // Need to load the grade_grade object to check visibility. $gradeiteminstance = self::get_grade_item($course->id, $outcome->itemtype, $outcome->itemmodule, $outcome->iteminstance, $outcome->itemnumber); $gradegradeinstance = grade_grade::fetch(array('userid' => $studentid, 'itemid' => $gradeiteminstance->id)); // The grade grade may be legitimately missing if the student has no grade. if (!empty($gradegradeinstance) && $gradegradeinstance->is_hidden()) { continue; } } // Format mixed bool/integer parameters. $studentgrade->hidden = empty($studentgrade->hidden) ? 0 : $studentgrade->hidden; $studentgrade->locked = empty($studentgrade->locked) ? 0 : $studentgrade->locked; $gradesarray['outcomes'][$modulecm->id]['grades'][$studentid] = (array) $studentgrade; // Add the student ID into the grade structure as some WS clients can't access the key. $gradesarray['outcomes'][$modulecm->id]['grades'][$studentid]['userid'] = $studentid; } } } return $gradesarray; }
/** * Returns the locked state of this grade_item (if the grade_item is locked OR no specific * $userid is given) or the locked state of a specific grade within this item if a specific * $userid is given and the grade_item is unlocked. * * @param int $userid The user's ID * @return bool Locked state */ public function is_locked($userid = NULL) { global $CFG; // Override for any grade items belonging to activities which are in the process of being deleted. require_once $CFG->dirroot . '/course/lib.php'; if (course_module_instance_pending_deletion($this->courseid, $this->itemmodule, $this->iteminstance)) { return true; } if (!empty($this->locked)) { return true; } if (!empty($userid)) { if ($grade = grade_grade::fetch(array('itemid' => $this->id, 'userid' => $userid))) { $grade->grade_item =& $this; // prevent db fetching of cached grade_item return $grade->is_locked(); } } return false; }
private function fill_table_recursive(&$element) { global $DB, $CFG; $type = $element['type']; $depth = $element['depth']; $grade_object = $element['object']; $eid = $grade_object->id; $element['userid'] = $this->user->id; $fullname = $this->gtree->get_element_header($element, true, true, true); $data = array(); $hidden = ''; $excluded = ''; $class = ''; $classfeedback = ''; // If this is a hidden grade category, hide it completely from the user if ($type == 'category' && $grade_object->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_object->is_hiddenuntil())) { return false; } if ($type == 'category') { $this->evenodd[$depth] = ($this->evenodd[$depth] + 1) % 2; } $alter = $this->evenodd[$depth] == 0 ? 'even' : 'odd'; /// Process those items that have scores associated if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') { $header_row = "row_{$eid}_{$this->user->id}"; $header_cat = "cat_{$grade_object->categoryid}_{$this->user->id}"; if (!($grade_grade = grade_grade::fetch(array('itemid' => $grade_object->id, 'userid' => $this->user->id)))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $grade_object->id; } $grade_grade->load_grade_item(); /// Hidden Items if ($grade_grade->grade_item->is_hidden()) { $hidden = ' dimmed_text'; } $hide = false; // If this is a hidden grade item, hide it completely from the user. if ($grade_grade->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_grade->is_hiddenuntil())) { $hide = true; } else { if (!empty($grade_object->itemmodule) && !empty($grade_object->iteminstance)) { // The grade object can be marked visible but still be hidden if // the student cannot see the activity due to conditional access // and it's set to be hidden entirely. $instances = $this->modinfo->get_instances_of($grade_object->itemmodule); if (!empty($instances[$grade_object->iteminstance])) { $cm = $instances[$grade_object->iteminstance]; if (!$cm->uservisible) { // If there is 'availableinfo' text then it is only greyed // out and not entirely hidden. if (!$cm->availableinfo) { $hide = true; } } } } } if (!$hide) { /// Excluded Item if ($grade_grade->is_excluded()) { $fullname .= ' [' . get_string('excluded', 'grades') . ']'; $excluded = ' excluded'; } /// Other class information $class = "{$hidden} {$excluded}"; if ($this->switch) { // alter style based on whether aggregation is first or last $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggt b2b" : " item b1b"; } else { $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggb" : " item b1b"; } if ($type == 'categoryitem' or $type == 'courseitem') { $header_cat = "cat_{$grade_object->iteminstance}_{$this->user->id}"; } /// Name $data['itemname']['content'] = $fullname; $data['itemname']['class'] = $class; $data['itemname']['colspan'] = $this->maxdepth - $depth; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = $header_row; /// Actual Grade $gradeval = $grade_grade->finalgrade; if (!$this->canviewhidden) { /// Virtual Grade (may be calculated excluding hidden items etc). $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($this->courseid, $grade_grade->grade_item, $gradeval); $gradeval = $adjustedgrade['grade']; // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $grade_grade->grade_item->grademax = $adjustedgrade['grademax']; $grade_grade->grade_item->grademin = $adjustedgrade['grademin']; } if ($this->showfeedback) { // Copy $class before appending itemcenter as feedback should not be centered $classfeedback = $class; } $class .= " itemcenter "; if ($this->showweight) { $data['weight']['class'] = $class; $data['weight']['content'] = '-'; $data['weight']['headers'] = "{$header_cat} {$header_row} weight"; // has a weight assigned, might be extra credit if ($grade_object->aggregationcoef > 0 && $type != 'courseitem') { $data['weight']['content'] = number_format($grade_object->aggregationcoef, 2); } } if ($this->showgrade) { if ($grade_grade->grade_item->needsupdate) { $data['grade']['class'] = $class . ' gradingerror'; $data['grade']['content'] = get_string('error'); } else { if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden() and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) { // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records $class .= ' datesubmitted'; $data['grade']['class'] = $class; $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))); } else { if ($grade_grade->is_hidden()) { $data['grade']['class'] = $class . ' dimmed_text'; $data['grade']['content'] = '-'; if ($this->canviewhidden) { $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } } else { $data['grade']['class'] = $class; $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } } } $data['grade']['headers'] = "{$header_cat} {$header_row} grade"; } // Range if ($this->showrange) { $data['range']['class'] = $class; $data['range']['content'] = $grade_grade->grade_item->get_formatted_range(GRADE_DISPLAY_TYPE_REAL, $this->rangedecimals); $data['range']['headers'] = "{$header_cat} {$header_row} range"; } // Percentage if ($this->showpercentage) { if ($grade_grade->grade_item->needsupdate) { $data['percentage']['class'] = $class . ' gradingerror'; $data['percentage']['content'] = get_string('error'); } else { if ($grade_grade->is_hidden()) { $data['percentage']['class'] = $class . ' dimmed_text'; $data['percentage']['content'] = '-'; if ($this->canviewhidden) { $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } else { $data['percentage']['class'] = $class; $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } $data['percentage']['headers'] = "{$header_cat} {$header_row} percentage"; } // Lettergrade if ($this->showlettergrade) { if ($grade_grade->grade_item->needsupdate) { $data['lettergrade']['class'] = $class . ' gradingerror'; $data['lettergrade']['content'] = get_string('error'); } else { if ($grade_grade->is_hidden()) { $data['lettergrade']['class'] = $class . ' dimmed_text'; if (!$this->canviewhidden) { $data['lettergrade']['content'] = '-'; } else { $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } else { $data['lettergrade']['class'] = $class; $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } $data['lettergrade']['headers'] = "{$header_cat} {$header_row} lettergrade"; } // Rank if ($this->showrank) { if ($grade_grade->grade_item->needsupdate) { $data['rank']['class'] = $class . ' gradingerror'; $data['rank']['content'] = get_string('error'); } elseif ($grade_grade->is_hidden()) { $data['rank']['class'] = $class . ' dimmed_text'; $data['rank']['content'] = '-'; } else { if (is_null($gradeval)) { // no grade, no rank $data['rank']['class'] = $class; $data['rank']['content'] = '-'; } else { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade > ?\n AND itemid = ?\n AND hidden = 0"; $rank = $DB->count_records_sql($sql, array($grade_grade->finalgrade, $grade_grade->grade_item->id)) + 1; $data['rank']['class'] = $class; $data['rank']['content'] = "{$rank}/" . $this->get_numusers(false); // total course users } } $data['rank']['headers'] = "{$header_cat} {$header_row} rank"; } // Average if ($this->showaverage) { $data['average']['class'] = $class; if (!empty($this->gtree->items[$eid]->avg)) { $data['average']['content'] = $this->gtree->items[$eid]->avg; } else { $data['average']['content'] = '-'; } $data['average']['headers'] = "{$header_cat} {$header_row} average"; } // Feedback if ($this->showfeedback) { if ($grade_grade->overridden > 0 and ($type == 'categoryitem' or $type == 'courseitem')) { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = get_string('overridden', 'grades') . ': ' . format_text($grade_grade->feedback, $grade_grade->feedbackformat); } else { if (empty($grade_grade->feedback) or !$this->canviewhidden and $grade_grade->is_hidden()) { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = ' '; } else { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat); } } $data['feedback']['headers'] = "{$header_cat} {$header_row} feedback"; } } } /// Category if ($type == 'category') { $data['leader']['class'] = $class . ' ' . $alter . "d{$depth} b1t b2b b1l"; $data['leader']['rowspan'] = $element['rowspan']; if ($this->switch) { // alter style based on whether aggregation is first or last $data['itemname']['class'] = $class . ' ' . $alter . "d{$depth} b1b b1t"; } else { $data['itemname']['class'] = $class . ' ' . $alter . "d{$depth} b2t"; } $data['itemname']['colspan'] = $this->maxdepth - $depth + count($this->tablecolumns) - 1; $data['itemname']['content'] = $fullname; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = "cat_{$grade_object->id}_{$this->user->id}"; } /// Add this row to the overall system $this->tabledata[] = $data; /// Recursively iterate through all child elements if (isset($element['children'])) { foreach ($element['children'] as $key => $child) { $this->fill_table_recursive($element['children'][$key]); } } }
protected function sub_test_update_final_grade() { // MDL-31713 Check that min and max are set on the grade_grade instance // if the grade is overridden before the activity has supplied a grade. $min = 2; $max = 8; // Create a brand new grade item. $grade_item = new grade_item(); $this->assertTrue(method_exists($grade_item, 'insert')); $grade_item->courseid = $this->courseid; $grade_item->categoryid = $this->grade_categories[1]->id; $grade_item->itemname = 'brand new unit test grade item'; $grade_item->itemtype = 'mod'; $grade_item->itemmodule = 'quiz'; $grade_item->iteminfo = 'Grade item used for unit testing'; $grade_item->grademin = $min; $grade_item->grademax = $max; $grade_item->insert(); // Override the student grade. $grade_item->update_final_grade($this->user[1]->id, 7, 'gradebook', '', FORMAT_MOODLE); // Check the student's grade has the correct min and max grade. $grade_grade = grade_grade::fetch(array('userid' => $this->user[1]->id, 'itemid' => $grade_item->id)); $this->assertEquals($min, $grade_grade->rawgrademin); $this->assertEquals($max, $grade_grade->rawgrademax); }
function test_grade_item_compute() { $grade_item = new grade_item($this->grade_items[1]); $this->assertTrue(method_exists($grade_item, 'compute')); $grade_grade = grade_grade::fetch(array('id' => $this->grade_grades[3]->id)); $grade_grade->delete(); $grade_grade = grade_grade::fetch(array('id' => $this->grade_grades[4]->id)); $grade_grade->delete(); $grade_grade = grade_grade::fetch(array('id' => $this->grade_grades[5]->id)); $grade_grade->delete(); $grade_item->compute(); $grade_grade = grade_grade::fetch(array('userid' => $this->grade_grades[3]->userid, 'itemid' => $this->grade_grades[3]->itemid)); $this->assertEqual($this->grade_grades[3]->finalgrade, $grade_grade->finalgrade); $grade_grade = grade_grade::fetch(array('userid' => $this->grade_grades[4]->userid, 'itemid' => $this->grade_grades[4]->itemid)); $this->assertEqual($this->grade_grades[4]->finalgrade, $grade_grade->finalgrade); $grade_grade = grade_grade::fetch(array('userid' => $this->grade_grades[5]->userid, 'itemid' => $this->grade_grades[5]->itemid)); $this->assertEqual($this->grade_grades[5]->finalgrade, $grade_grade->finalgrade); }
/** * Tests for importing grades from an external source. */ public function test_grade_import_commit() { global $USER, $DB, $CFG; $this->resetAfterTest(); $importcode = get_new_importcode(); $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id)); $itemname = $assign->name; $modulecontext = context_module::instance($assign->cmid); // The generator returns a dummy object, lets get the real assign object. $assign = new assign($modulecontext, false, false); $cm = $assign->get_course_module(); // Enrol users in the course. $this->getDataGenerator()->enrol_user($user1->id, $course->id); $this->getDataGenerator()->enrol_user($user2->id, $course->id); // Enter a new grade into an existing grade item. $gradeitem = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod')); // Keep this value around for a test further down. $originalgrade = 55; $this->import_grades(array('importcode' => $importcode, 'itemid' => $gradeitem->id, 'userid' => $user1->id, 'finalgrade' => $originalgrade)); $status = grade_import_commit($course->id, $importcode, false, false); $this->assertTrue($status); // Get imported grade_grade. $gradegrade = grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user1->id)); $this->assertEquals($originalgrade, $gradegrade->finalgrade); // Overriden field will be a timestamp and will evaluate out to true. $this->assertTrue($gradegrade->is_overridden()); // Create a new grade item and import into that. $importcode = get_new_importcode(); $record = new stdClass(); $record->itemname = 'New grade item'; $record->importcode = $importcode; $record->importer = $USER->id; $insertid = $DB->insert_record('grade_import_newitem', $record); $finalgrade = 75; $this->import_grades(array('importcode' => $importcode, 'userid' => $user1->id, 'finalgrade' => $finalgrade, 'newgradeitem' => $insertid)); $status = grade_import_commit($course->id, $importcode, false, false); $this->assertTrue($status); // Check that we have a new grade_item. $gradeitem = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'manual')); $this->assertEquals($record->itemname, $gradeitem->itemname); // Grades were imported. $gradegrade = grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user1->id)); $this->assertEquals($finalgrade, $gradegrade->finalgrade); // As this is a new item the grade has not been overridden. $this->assertFalse($gradegrade->is_overridden()); // Import feedback only. $importcode = get_new_importcode(); $gradeitem = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod')); $originalfeedback = 'feedback can be useful'; $this->import_grades(array('importcode' => $importcode, 'userid' => $user1->id, 'itemid' => $gradeitem->id, 'feedback' => $originalfeedback, 'importonlyfeedback' => true)); $status = grade_import_commit($course->id, $importcode, true, false); $this->assertTrue($status); $gradegrade = grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user1->id)); // The final grade should be the same as the first record further up. We are only altering the feedback. $this->assertEquals($originalgrade, $gradegrade->finalgrade); $this->assertTrue($gradegrade->is_overridden()); // Import grades only. $importcode = get_new_importcode(); $gradeitem = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod')); $finalgrade = 60; $this->import_grades(array('importcode' => $importcode, 'userid' => $user1->id, 'itemid' => $gradeitem->id, 'finalgrade' => $finalgrade, 'feedback' => 'feedback can still be useful')); $status = grade_import_commit($course->id, $importcode, false, false); $this->assertTrue($status); $gradegrade = grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user1->id)); $this->assertEquals($finalgrade, $gradegrade->finalgrade); // The final feedback should not have changed. $this->assertEquals($originalfeedback, $gradegrade->feedback); $this->assertTrue($gradegrade->is_overridden()); // Check that printing of import status is correct. $importcode = get_new_importcode(); $gradeitem = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod')); $this->import_grades(array('importcode' => $importcode, 'userid' => $user1->id, 'itemid' => $gradeitem->id)); $url = $CFG->wwwroot . '/grade/index.php'; $expectedresponse = "++ Grade import success ++\n<div class=\"continuebutton\"><form method=\"get\" action=\"{$url}\"><div><input type=\"submit\" value=\"Continue\" /><input type=\"hidden\" name=\"id\" value=\"{$course->id}\" /></div></form></div>"; ob_start(); $status = grade_import_commit($course->id, $importcode); $output = ob_get_contents(); ob_end_clean(); $this->assertTrue($status); $this->assertEquals($expectedresponse, $output); }
function fill_table() { global $CFG; $numusers = $this->get_numusers(false); // total course users $items =& $this->gseq->items; $grades = array(); $canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->courseid)); // fetch or create all grades foreach ($items as $key => $unused) { if (!($grade_grade = grade_grade::fetch(array('itemid' => $items[$key]->id, 'userid' => $this->user->id)))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $items[$key]->id; } $grades[$key] = $grade_grade; $grades[$key]->grade_item =& $items[$key]; } if ($canviewhidden) { $altered = array(); $unknown = array(); } else { $hiding_affected = grade_grade::get_hiding_affected($grades, $items); $altered = $hiding_affected['altered']; $unknown = $hiding_affected['unknown']; unset($hiding_affected); } foreach ($items as $itemid => $unused) { $grade_item =& $items[$itemid]; $grade_grade =& $grades[$itemid]; if (!$canviewhidden and $grade_item->is_hidden()) { if ($this->showhiddenitems == 0) { // no hidden items at all continue; } else { if ($this->showhiddenitems == 1 and !$grade_item->is_hiddenuntil()) { // hidden until that are still hidden are visible continue; } } } $class = 'gradeitem'; if ($grade_item->is_course_item()) { $class = 'courseitem'; } else { if ($grade_item->is_category_item()) { $class = 'categoryitem'; } } if (in_array($itemid, $unknown)) { $gradeval = null; } else { if (array_key_exists($itemid, $altered)) { $gradeval = $altered[$itemid]; } else { $gradeval = $grade_grade->finalgrade; } } $data = array(); // all users should know which items are still hidden $hidden = ''; if ($grade_item->is_hidden()) { $hidden = ' hidden '; } $element = $this->gseq->locate_element($this->gseq->get_item_eid($grade_item)); $header = $this->gseq->get_element_header($element, true, true, true); /// prints grade item name $data[] = '<span class="' . $hidden . $class . '">' . $header . '</span>'; /// prints category $cat = $grade_item->get_parent_category(); $data[] = '<span class="' . $hidden . $class . '">' . $cat->get_name() . '</span>'; $hidden = ''; if ($grade_item->is_hidden()) { // can not see grades in hidden items $hidden = ' hidden '; } else { if ($canviewhidden and $grade_grade->is_hidden()) { // if user can see hidden grades, indicate which ones are hidden $hidden = ' hidden '; } } /// prints the grade if ($grade_grade->is_excluded()) { $excluded = get_string('excluded', 'grades') . ' '; } else { $excluded = ''; } if ($grade_item->needsupdate) { $data[] = '<span class="' . $hidden . $class . ' gradingerror">' . get_string('error') . '</span>'; } else { if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$canviewhidden and $grade_grade->is_hidden() and !$grade_item->is_category_item() and !$grade_item->is_course_item()) { // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records $data[] = '<span class="' . $hidden . $class . ' datesubmitted">' . $excluded . get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))) . '</span>'; } else { $data[] = '<span class="' . $hidden . $class . '">' . $excluded . grade_format_gradevalue($gradeval, $grade_item, true) . '</span>'; } } /// prints percentage if ($grade_item->needsupdate) { $data[] = '<span class="' . $hidden . $class . 'gradingerror">' . get_string('error') . '</span>'; } else { $data[] = '<span class="' . $hidden . $class . '">' . grade_format_gradevalue($gradeval, $grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE) . '</span>'; } /// prints rank if ($this->showrank) { // TODO: this is broken if hidden grades present!! if ($grade_item->needsupdate) { $data[] = '<span class="' . $hidden . $class . 'gradingerror">' . get_string('error') . '</span>'; } else { if (is_null($gradeval)) { // no grade, no rank $data[] = '<span class="' . $hidden . $class . '">-</span>'; } else { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {$CFG->prefix}grade_grades\n WHERE finalgrade > {$grade_grade->finalgrade}\n AND itemid = {$grade_item->id}"; $rank = count_records_sql($sql) + 1; $data[] = '<span class="' . $hidden . $class . '">' . "{$rank}/{$numusers}" . '</span>'; } } } /// prints feedback if (empty($grade_grade->feedback) or !$canviewhidden and $grade_grade->is_hidden()) { $data[] = '<div class="' . $hidden . 'feedbacktext"> </div>'; } else { $data[] = '<div class="' . $hidden . 'feedbacktext">' . format_text($grade_grade->feedback, $grade_grade->feedbackformat) . '</div>'; } $this->table->add_data($data); } return true; }
if ($alis = grade_grade::fetch(array('itemid' => $itemids['min'], 'userid' => $student->id))) { $alis->rawgrade = $mtg['grade']; $alis->finalgrade = $mtg['grade']; $alis->timemodified = time(); $alis->update('report_targetgrades'); } else { $alis = new grade_grade(); $alis->itemid = $itemids['min']; $alis->userid = $student->id; $alis->rawgrade = $mtg['grade']; $alis->finalgrade = $mtg['grade']; $alis->timecreated = time(); $alis->timemodified = time(); $alis->insert('report_targetgrades'); } if ($alis_num = grade_grade::fetch(array('itemid' => $itemids['alisnum'], 'userid' => $student->id))) { $alis_num->rawgrade = $mtg['number']; $alis_num->finalgrade = $mtg['number']; $alis_num->timemodified = time(); $alis_num->update('report_targetgrades'); } else { $alis_num = new grade_grade(); $alis_num->itemid = $itemids['alisnum']; $alis_num->userid = $student->id; $alis_num->rawgrade = $mtg['number']; $alis_num->finalgrade = $mtg['number']; $alis_num->timecreated = time(); $alis_num->timemodified = time(); $alis_num->insert('report_targetgrades'); } } catch (tg\no_data_for_student_exception $e) {
function test_gradebook() { global $DB; $this->resetAfterTest(true); // reset all changes automatically after this test $location_str = 'manual'; // try to get category $grade_category = grade_category::fetch(array('courseid' => $this->courseid, 'fullname' => $this->cat_name)); // NOTE: grade category will not be null but it will be empty $this->assertFalse($grade_category); // create a category $params = new stdClass(); $params->courseid = $this->courseid; $params->fullname = $this->cat_name; $grade_category = new grade_category($params, false); $this->assertTrue(method_exists($grade_category, 'insert')); $grade_category->insert($location_str); // now we will really get the category that we just made $grade_category_fetched = grade_category::fetch(array('courseid' => $this->courseid, 'fullname' => $this->cat_name)); $this->assertTrue($grade_category_fetched !== false); $this->assertEquals($grade_category->id, $grade_category_fetched->id); $this->assertEquals($grade_category->courseid, $grade_category_fetched->courseid); $this->assertEquals($grade_category->path, $grade_category_fetched->path); $this->assertEquals($grade_category->fullname, $grade_category_fetched->fullname); $this->assertEquals($grade_category->parent, $grade_category_fetched->parent); // try to get grade item $grade_item = grade_item::fetch(array('courseid' => $this->courseid, 'categoryid' => $grade_category->id, 'itemname' => $this->item_name)); // NOTE: grade category will not be null but it will be empty $this->assertFalse($grade_item); // create a grade item $grade_item = new grade_item(); $this->assertTrue(method_exists($grade_item, 'insert')); $grade_item->courseid = $this->courseid; $grade_item->categoryid = $grade_category->id; $grade_item->idnumber = $this->item_name; // lookup $grade_item->itemname = $this->item_name; // display $grade_item->itemtype = 'blocks'; $grade_item->itemmodule = 'iclicker'; $grade_item->iteminfo = 'blocks/iclicker for unit testing'; // grademax=100, grademin=0 $grade_item->grademax = 100.0; $grade_item->insert($location_str); // now we will really get the new item $grade_item_fetched = grade_item::fetch(array('courseid' => $this->courseid, 'categoryid' => $grade_category->id, 'itemname' => $this->item_name)); $this->assertTrue($grade_item_fetched !== false); $this->assertEquals($grade_item->id, $grade_item_fetched->id); $this->assertEquals($grade_item->courseid, $grade_item_fetched->courseid); $this->assertEquals($grade_item->categoryid, $grade_item_fetched->categoryid); $this->assertEquals($grade_item->itemname, $grade_item_fetched->itemname); // get empty grades list $all_grades = grade_grade::fetch_all(array('itemid' => $grade_item->id)); $this->assertFalse($all_grades); // add grade $grade_grade = new grade_grade(); $this->assertTrue(method_exists($grade_grade, 'insert')); $grade_grade->itemid = $grade_item->id; $grade_grade->userid = $this->studentid1; $grade_grade->rawgrade = $this->grade_score; $grade_grade->insert($location_str); // get new grade $grade_grade_fetched = grade_grade::fetch(array('itemid' => $grade_item->id, 'userid' => $this->studentid1)); $this->assertTrue($grade_grade_fetched !== false); $this->assertEquals($grade_grade->id, $grade_grade_fetched->id); $this->assertEquals($grade_grade->itemid, $grade_grade_fetched->itemid); $this->assertEquals($grade_grade->userid, $grade_grade_fetched->userid); $this->assertEquals($grade_grade->rawgrade, $grade_grade_fetched->rawgrade); // update the grade $grade_grade->rawgrade = 50; $result = $grade_grade->update($location_str); $this->assertTrue($result); $grade_grade_fetched = grade_grade::fetch(array('id' => $grade_grade->id)); $this->assertTrue($grade_grade_fetched !== false); $this->assertEquals($grade_grade->id, $grade_grade_fetched->id); $this->assertEquals($grade_grade->rawgrade, $grade_grade_fetched->rawgrade); $this->assertEquals(50, $grade_grade_fetched->rawgrade); // get grades $all_grades = grade_grade::fetch_all(array('itemid' => $grade_item->id)); $this->assertTrue($all_grades !== false); $this->assertEquals(1, sizeof($all_grades)); // add more grades $grade_grade2 = new grade_grade(); $grade_grade2->itemid = $grade_item->id; $grade_grade2->userid = $this->studentid2; $grade_grade2->rawgrade = $this->grade_score; $grade_grade2->insert($location_str); // get grades $all_grades = grade_grade::fetch_all(array('itemid' => $grade_item->id)); $this->assertTrue($all_grades !== false); $this->assertEquals(2, sizeof($all_grades)); // make sure this can run $result = $grade_item->regrade_final_grades(); $this->assertTrue($result); // remove grades $this->assertTrue(method_exists($grade_grade, 'delete')); $result = $grade_grade->delete($location_str); $this->assertTrue($result); $result = $grade_grade2->delete($location_str); $this->assertTrue($result); // check no grades left $all_grades = grade_grade::fetch_all(array('itemid' => $grade_item->id)); $this->assertFalse($all_grades); // remove grade item $this->assertTrue(method_exists($grade_item, 'delete')); $result = $grade_item->delete($location_str); $this->assertTrue($result); // remove grade category $this->assertTrue(method_exists($grade_category, 'delete')); $result = $grade_category->delete($location_str); $this->assertTrue($result); $this->resetAfterTest(); }
/** * Fill the table with data. * * @param $element - An array containing the table data for the current row. */ private function fill_table_recursive(&$element) { global $DB, $CFG; $type = $element['type']; $depth = $element['depth']; $grade_object = $element['object']; $eid = $grade_object->id; $element['userid'] = $this->user->id; $fullname = $this->gtree->get_element_header($element, true, true, true, true, true); $data = array(); $hidden = ''; $excluded = ''; $itemlevel = ($type == 'categoryitem' || $type == 'category' || $type == 'courseitem') ? $depth : ($depth + 1); $class = 'level' . $itemlevel . ' level' . ($itemlevel % 2 ? 'odd' : 'even'); $classfeedback = ''; // If this is a hidden grade category, hide it completely from the user if ($type == 'category' && $grade_object->is_hidden() && !$this->canviewhidden && ( $this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_object->is_hiddenuntil()))) { return false; } if ($type == 'category') { $this->evenodd[$depth] = (($this->evenodd[$depth] + 1) % 2); } $alter = ($this->evenodd[$depth] == 0) ? 'even' : 'odd'; /// Process those items that have scores associated if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') { $header_row = "row_{$eid}_{$this->user->id}"; $header_cat = "cat_{$grade_object->categoryid}_{$this->user->id}"; if (! $grade_grade = grade_grade::fetch(array('itemid'=>$grade_object->id,'userid'=>$this->user->id))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $grade_object->id; } $grade_grade->load_grade_item(); /// Hidden Items if ($grade_grade->grade_item->is_hidden()) { $hidden = ' dimmed_text'; } $hide = false; // If this is a hidden grade item, hide it completely from the user. if ($grade_grade->is_hidden() && !$this->canviewhidden && ( $this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_grade->is_hiddenuntil()))) { $hide = true; } else if (!empty($grade_object->itemmodule) && !empty($grade_object->iteminstance)) { // The grade object can be marked visible but still be hidden if // the student cannot see the activity due to conditional access // and it's set to be hidden entirely. $instances = $this->modinfo->get_instances_of($grade_object->itemmodule); if (!empty($instances[$grade_object->iteminstance])) { $cm = $instances[$grade_object->iteminstance]; if (!$cm->uservisible) { // If there is 'availableinfo' text then it is only greyed // out and not entirely hidden. if (!$cm->availableinfo) { $hide = true; } } } } // Actual Grade - We need to calculate this whether the row is hidden or not. $gradeval = $grade_grade->finalgrade; $hint = $grade_grade->get_aggregation_hint(); if (!$this->canviewhidden) { /// Virtual Grade (may be calculated excluding hidden items etc). $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($this->courseid, $grade_grade->grade_item, $gradeval); $gradeval = $adjustedgrade['grade']; // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $grade_grade->grade_item->grademax = $adjustedgrade['grademax']; $grade_grade->grade_item->grademin = $adjustedgrade['grademin']; $hint['status'] = $adjustedgrade['aggregationstatus']; $hint['weight'] = $adjustedgrade['aggregationweight']; } else { // The max and min for an aggregation may be different to the grade_item. if (!is_null($gradeval)) { $grade_grade->grade_item->grademax = $grade_grade->rawgrademax; $grade_grade->grade_item->grademin = $grade_grade->rawgrademin; } } if (!$hide) { /// Excluded Item /** if ($grade_grade->is_excluded()) { $fullname .= ' ['.get_string('excluded', 'grades').']'; $excluded = ' excluded'; } **/ /// Other class information $class .= $hidden . $excluded; if ($this->switch) { // alter style based on whether aggregation is first or last $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggt b2b" : " item b1b"; } else { $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggb" : " item b1b"; } if ($type == 'categoryitem' or $type == 'courseitem') { $header_cat = "cat_{$grade_object->iteminstance}_{$this->user->id}"; } /// Name $data['itemname']['content'] = $fullname; $data['itemname']['class'] = $class; $data['itemname']['colspan'] = ($this->maxdepth - $depth); $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = $header_row; if ($this->showfeedback) { // Copy $class before appending itemcenter as feedback should not be centered $classfeedback = $class; } $class .= " itemcenter "; if ($this->showweight) { $data['weight']['class'] = $class; $data['weight']['content'] = '-'; $data['weight']['headers'] = "$header_cat $header_row weight"; // has a weight assigned, might be extra credit // This obliterates the weight because it provides a more informative description. if (is_numeric($hint['weight'])) { $data['weight']['content'] = format_float($hint['weight'] * 100.0, 2) . ' %'; } if ($hint['status'] != 'used' && $hint['status'] != 'unknown') { $data['weight']['content'] .= '<br>' . get_string('aggregationhint' . $hint['status'], 'grades'); } } if ($this->showgrade) { if ($grade_grade->grade_item->needsupdate) { $data['grade']['class'] = $class.' gradingerror'; $data['grade']['content'] = get_string('error'); } else if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden() and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) { // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records $class .= ' datesubmitted'; $data['grade']['class'] = $class; $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))); } else if ($grade_grade->is_hidden()) { $data['grade']['class'] = $class.' dimmed_text'; $data['grade']['content'] = '-'; if ($this->canviewhidden) { $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } } else { $data['grade']['class'] = $class; $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } $data['grade']['headers'] = "$header_cat $header_row grade"; } // Range if ($this->showrange) { $data['range']['class'] = $class; $data['range']['content'] = $grade_grade->grade_item->get_formatted_range(GRADE_DISPLAY_TYPE_REAL, $this->rangedecimals); $data['range']['headers'] = "$header_cat $header_row range"; } // Percentage if ($this->showpercentage) { if ($grade_grade->grade_item->needsupdate) { $data['percentage']['class'] = $class.' gradingerror'; $data['percentage']['content'] = get_string('error'); } else if ($grade_grade->is_hidden()) { $data['percentage']['class'] = $class.' dimmed_text'; $data['percentage']['content'] = '-'; if ($this->canviewhidden) { $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } else { $data['percentage']['class'] = $class; $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } $data['percentage']['headers'] = "$header_cat $header_row percentage"; } // Lettergrade if ($this->showlettergrade) { if ($grade_grade->grade_item->needsupdate) { $data['lettergrade']['class'] = $class.' gradingerror'; $data['lettergrade']['content'] = get_string('error'); } else if ($grade_grade->is_hidden()) { $data['lettergrade']['class'] = $class.' dimmed_text'; if (!$this->canviewhidden) { $data['lettergrade']['content'] = '-'; } else { $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } else { $data['lettergrade']['class'] = $class; $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } $data['lettergrade']['headers'] = "$header_cat $header_row lettergrade"; } // Rank if ($this->showrank) { if ($grade_grade->grade_item->needsupdate) { $data['rank']['class'] = $class.' gradingerror'; $data['rank']['content'] = get_string('error'); } elseif ($grade_grade->is_hidden()) { $data['rank']['class'] = $class.' dimmed_text'; $data['rank']['content'] = '-'; } else if (is_null($gradeval)) { // no grade, no rank $data['rank']['class'] = $class; $data['rank']['content'] = '-'; } else { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid)) FROM {grade_grades} WHERE finalgrade > ? AND itemid = ? AND hidden = 0"; $rank = $DB->count_records_sql($sql, array($grade_grade->finalgrade, $grade_grade->grade_item->id)) + 1; $data['rank']['class'] = $class; $data['rank']['content'] = "$rank/".$this->get_numusers(false); // total course users } $data['rank']['headers'] = "$header_cat $header_row rank"; } // Average if ($this->showaverage) { $data['average']['class'] = $class; if (!empty($this->gtree->items[$eid]->avg)) { $data['average']['content'] = $this->gtree->items[$eid]->avg; } else { $data['average']['content'] = '-'; } $data['average']['headers'] = "$header_cat $header_row average"; } // Feedback if ($this->showfeedback) { if ($grade_grade->overridden > 0 AND ($type == 'categoryitem' OR $type == 'courseitem')) { $data['feedback']['class'] = $classfeedback.' feedbacktext'; $data['feedback']['content'] = get_string('overridden', 'grades').': ' . format_text($grade_grade->feedback, $grade_grade->feedbackformat); } else if (empty($grade_grade->feedback) or (!$this->canviewhidden and $grade_grade->is_hidden())) { $data['feedback']['class'] = $classfeedback.' feedbacktext'; $data['feedback']['content'] = ' '; } else { $data['feedback']['class'] = $classfeedback.' feedbacktext'; $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat); } $data['feedback']['headers'] = "$header_cat $header_row feedback"; } // Contribution to the course total column. if ($this->showcontributiontocoursetotal) { $data['contributiontocoursetotal']['class'] = $class; $data['contributiontocoursetotal']['content'] = '-'; $data['contributiontocoursetotal']['headers'] = "$header_cat $header_row contributiontocoursetotal"; } } // We collect the aggregation hints whether they are hidden or not. if ($this->showcontributiontocoursetotal) { $hint['grademax'] = $grade_grade->grade_item->grademax; $hint['grademin'] = $grade_grade->grade_item->grademin; $hint['grade'] = $gradeval; $parent = $grade_object->load_parent_category(); if ($grade_object->is_category_item()) { $parent = $parent->load_parent_category(); } $hint['parent'] = $parent->load_grade_item()->id; $this->aggregationhints[$grade_grade->itemid] = $hint; } } /// Category if ($type == 'category') { $data['leader']['class'] = $class.' '.$alter."d$depth b1t b2b b1l"; $data['leader']['rowspan'] = $element['rowspan']; if ($this->switch) { // alter style based on whether aggregation is first or last $data['itemname']['class'] = $class.' '.$alter."d$depth b1b b1t"; } else { $data['itemname']['class'] = $class.' '.$alter."d$depth b2t"; } $data['itemname']['colspan'] = ($this->maxdepth - $depth + count($this->tablecolumns) - 1); $data['itemname']['content'] = $fullname; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = "cat_{$grade_object->id}_{$this->user->id}"; } /// Add this row to the overall system foreach ($data as $key => $celldata) { $data[$key]['class'] .= ' column-' . $key; } $this->tabledata[] = $data; /// Recursively iterate through all child elements if (isset($element['children'])) { foreach ($element['children'] as $key=>$child) { $this->fill_table_recursive($element['children'][$key]); } } // Check we are showing this column, and we are looking at the root of the table. // This should be the very last thing this fill_table_recursive function does. if ($this->showcontributiontocoursetotal && ($type == 'category' && $depth == 1)) { // We should have collected all the hints by now - walk the tree again and build the contributions column. $this->fill_contributions_column($element); } }
function test_upgrade_calculated_grade_items_regrade() { global $DB, $CFG; $this->resetAfterTest(); // Create a user. $user = $this->getDataGenerator()->create_user(); // Create a course. $course = $this->getDataGenerator()->create_course(); // Enrol the user in the course. $studentrole = $DB->get_record('role', array('shortname' => 'student')); $maninstance1 = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST); $manual = enrol_get_plugin('manual'); $manual->enrol_user($maninstance1, $user->id, $studentrole->id); set_config('upgrade_calculatedgradeitemsonlyregrade', 1); // Creating a category for a grade item. $gradecategory = new grade_category(); $gradecategory->fullname = 'calculated grade category'; $gradecategory->courseid = $course->id; $gradecategory->insert(); $gradecategoryid = $gradecategory->id; // This is a manual grade item. $gradeitem = new grade_item(); $gradeitem->itemname = 'grade item one'; $gradeitem->itemtype = 'manual'; $gradeitem->categoryid = $gradecategoryid; $gradeitem->courseid = $course->id; $gradeitem->idnumber = 'gi1'; $gradeitem->insert(); // Changing the category into a calculated grade category. $gradecategoryitem = grade_item::fetch(array('iteminstance' => $gradecategory->id)); $gradecategoryitem->calculation = '=##gi' . $gradeitem->id . '##/2'; $gradecategoryitem->grademax = 50; $gradecategoryitem->grademin = 15; $gradecategoryitem->update(); // Setting a grade for the student. $grade = $gradeitem->get_grade($user->id, true); $grade->finalgrade = 50; $grade->update(); grade_regrade_final_grades($course->id); $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id)); $grade->rawgrademax = 100; $grade->rawgrademin = 0; $grade->update(); $this->assertNotEquals($gradecategoryitem->grademax, $grade->rawgrademax); $this->assertNotEquals($gradecategoryitem->grademin, $grade->rawgrademin); // This is the function that we are testing. If we comment out this line, then the test fails because the grade items // are not flagged for regrading. upgrade_calculated_grade_items(); grade_regrade_final_grades($course->id); $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id)); $this->assertEquals($gradecategoryitem->grademax, $grade->rawgrademax); $this->assertEquals($gradecategoryitem->grademin, $grade->rawgrademin); }
public function test_grade_grade_min_max_with_category_item() { global $CFG, $DB; $initialminmaxtouse = $CFG->grade_minmaxtouse; $this->setAdminUser(); $course = $this->getDataGenerator()->create_course(); $user = $this->getDataGenerator()->create_user(); $coursegi = grade_item::fetch_course_item($course->id); // Create a category item. $gc = new grade_category(array('courseid' => $course->id, 'fullname' => 'test'), false); $gc->insert(); $gi = $gc->get_grade_item(); $gi->grademax = 100; $gi->grademin = 0; $gi->update(); // Fetch the category item. $giparams = array('itemtype' => 'category', 'iteminstance' => $gc->id); $gi = grade_item::fetch($giparams); $this->assertEquals(0, $gi->grademin); $this->assertEquals(100, $gi->grademax); // Give a grade to the student. $gi->update_final_grade($user->id, 10); // Check the grade min/max stored in gradebook. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id)); $this->assertEquals(0, $gg->get_grade_min()); $this->assertEquals(100, $gg->get_grade_max()); // Change the min/max grade of the item. $gi->grademin = 2; $gi->grademax = 50; $gi->update(); // Fetch the updated item. $gi = grade_item::fetch($giparams); // Now check the grade grade min/max with system setting. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM; grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id)); $this->assertEquals(0, $gg->get_grade_min()); $this->assertEquals(100, $gg->get_grade_max()); // Now with other system setting. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE; grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting, and reset static cache. $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id)); $this->assertEquals(0, $gg->get_grade_min()); $this->assertEquals(100, $gg->get_grade_max()); // Now with overriden setting in course. $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM; grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_GRADE); $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id)); $this->assertEquals(0, $gg->get_grade_min()); $this->assertEquals(100, $gg->get_grade_max()); $CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE; grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_ITEM); $gg = grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id)); $this->assertEquals(0, $gg->get_grade_min()); $this->assertEquals(100, $gg->get_grade_max()); $CFG->grade_minmaxtouse = $initialminmaxtouse; }
/** * Optionally blank out course/category totals if they contain any hidden items * @param string $courseid the course id * @param string $course_item an instance of grade_item * @param string $finalgrade the grade for the course_item * @return array[] containing values for 'grade', 'grademax', 'grademin', 'aggregationstatus' and 'aggregationweight' */ protected function blank_hidden_total_and_adjust_bounds($courseid, $course_item, $finalgrade) { global $CFG, $DB; static $hiding_affected = null; //array of items in this course affected by hiding // If we're dealing with multiple users we need to know when we've moved on to a new user. static $previous_userid = null; // If we're dealing with multiple courses we need to know when we've moved on to a new course. static $previous_courseid = null; $coursegradegrade = grade_grade::fetch(array('userid' => $this->user->id, 'itemid' => $course_item->id)); $grademin = $course_item->grademin; $grademax = $course_item->grademax; if ($coursegradegrade) { $grademin = $coursegradegrade->rawgrademin; $grademax = $coursegradegrade->rawgrademax; } else { $coursegradegrade = new grade_grade(array('userid' => $this->user->id, 'itemid' => $course_item->id), false); } $hint = $coursegradegrade->get_aggregation_hint(); $aggregationstatus = $hint['status']; $aggregationweight = $hint['weight']; if (!is_array($this->showtotalsifcontainhidden)) { debugging('showtotalsifcontainhidden should be an array', DEBUG_DEVELOPER); $this->showtotalsifcontainhidden = array($courseid => $this->showtotalsifcontainhidden); } if ($this->showtotalsifcontainhidden[$courseid] == GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN) { return array('grade' => $finalgrade, 'grademin' => $grademin, 'grademax' => $grademax, 'aggregationstatus' => $aggregationstatus, 'aggregationweight' => $aggregationweight); } // If we've moved on to another course or user, reload the grades. if ($previous_userid != $this->user->id || $previous_courseid != $courseid) { $hiding_affected = null; $previous_userid = $this->user->id; $previous_courseid = $courseid; } if (!$hiding_affected) { $items = grade_item::fetch_all(array('courseid' => $courseid)); $grades = array(); $sql = "SELECT g.*\n FROM {grade_grades} g\n JOIN {grade_items} gi ON gi.id = g.itemid\n WHERE g.userid = {$this->user->id} AND gi.courseid = {$courseid}"; if ($gradesrecords = $DB->get_records_sql($sql)) { foreach ($gradesrecords as $grade) { $grades[$grade->itemid] = new grade_grade($grade, false); } unset($gradesrecords); } foreach ($items as $itemid => $unused) { if (!isset($grades[$itemid])) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $items[$itemid]->id; $grades[$itemid] = $grade_grade; } $grades[$itemid]->grade_item =& $items[$itemid]; } $hiding_affected = grade_grade::get_hiding_affected($grades, $items); } //if the item definitely depends on a hidden item if (array_key_exists($course_item->id, $hiding_affected['altered']) || array_key_exists($course_item->id, $hiding_affected['alteredgrademin']) || array_key_exists($course_item->id, $hiding_affected['alteredgrademax']) || array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus']) || array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) { if (!$this->showtotalsifcontainhidden[$courseid] && array_key_exists($course_item->id, $hiding_affected['altered'])) { // Hide the grade, but only when it has changed. $finalgrade = null; } else { //use reprocessed marks that exclude hidden items if (array_key_exists($course_item->id, $hiding_affected['altered'])) { $finalgrade = $hiding_affected['altered'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredgrademin'])) { $grademin = $hiding_affected['alteredgrademin'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredgrademax'])) { $grademax = $hiding_affected['alteredgrademax'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus'])) { $aggregationstatus = $hiding_affected['alteredaggregationstatus'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) { $aggregationweight = $hiding_affected['alteredaggregationweight'][$course_item->id]; } if (!$this->showtotalsifcontainhidden[$courseid]) { // If the course total is hidden we must hide the weight otherwise // it can be used to compute the course total. $aggregationstatus = 'unknown'; $aggregationweight = null; } } } else { if (!empty($hiding_affected['unknown'][$course_item->id])) { //not sure whether or not this item depends on a hidden item if (!$this->showtotalsifcontainhidden[$courseid]) { //hide the grade $finalgrade = null; } else { //use reprocessed marks that exclude hidden items $finalgrade = $hiding_affected['unknown'][$course_item->id]; if (array_key_exists($course_item->id, $hiding_affected['alteredgrademin'])) { $grademin = $hiding_affected['alteredgrademin'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredgrademax'])) { $grademax = $hiding_affected['alteredgrademax'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus'])) { $aggregationstatus = $hiding_affected['alteredaggregationstatus'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) { $aggregationweight = $hiding_affected['alteredaggregationweight'][$course_item->id]; } } } } return array('grade' => $finalgrade, 'grademin' => $grademin, 'grademax' => $grademax, 'aggregationstatus' => $aggregationstatus, 'aggregationweight' => $aggregationweight); }
private function fill_table_recursive(&$element) { global $DB, $CFG; $type = $element['type']; $depth = $element['depth']; $grade_object = $element['object']; $eid = $grade_object->id; $element['userid'] = $this->user->id; $fullname = $this->gtree->get_element_header($element, true, false, true); $data = array(); $hidden = ''; $excluded = ''; $class = ''; $classfeedback = ''; $row_class = ''; $activity_start_date = ''; // If this is a hidden grade category, hide it completely from the user if ($type == 'category' && $grade_object->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_MARKSHEET_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_MARKSHEET_HIDE_UNTIL && !$grade_object->is_hiddenuntil())) { return false; } if ($type == 'category') { $this->evenodd[$depth] = ($this->evenodd[$depth] + 1) % 2; } $alter = $this->evenodd[$depth] == 0 ? 'even' : 'odd'; if ($type == 'item') { $cat_id = $grade_object->categoryid; } else { $cat_id = ' '; } /// Process those items that have scores associated if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') { //&& ($depth == 2)) { $header_row = "row_{$eid}_{$this->user->id}"; $header_cat = "cat_{$grade_object->categoryid}_{$this->user->id}"; if (!($grade_grade = grade_grade::fetch(array('itemid' => $grade_object->id, 'userid' => $this->user->id)))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $grade_object->id; } $grade_grade->load_grade_item(); $hide = false; // If this is a hidden grade item, hide it completely from the user. if ($grade_grade->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_MARKSHEET_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_MARKSHEET_HIDE_UNTIL && !$grade_grade->is_hiddenuntil())) { $hide = true; } else { if (!empty($grade_object->itemmodule) && !empty($grade_object->iteminstance)) { // The grade object can be marked visible but still be hidden if... // 1) "enablegroupmembersonly" is on and the activity is assigned to a grouping the user is not in. // 2) the student cannot see the activity due to conditional access and its set to be hidden entirely. $instances = $this->gtree->modinfo->get_instances_of($grade_object->itemmodule); if (!empty($instances[$grade_object->iteminstance])) { $cm = $instances[$grade_object->iteminstance]; if (!$cm->uservisible) { // Further checks are required to determine whether the activity is entirely hidden or just greyed out. if ($cm->is_user_access_restricted_by_group() || $cm->is_user_access_restricted_by_conditional_access() || $cm->is_user_access_restricted_by_capability()) { $hide = true; } } } } } if ($grade_grade->grade_item->is_hidden() && !$this->showhiddenactivity) { $hide = true; } //set start dates by category $categoryid = $grade_grade->grade_item->categoryid; if ($type == 'item' && isset($cm)) { $this->grade_category_modids[$categoryid][] = $cm->id; } /// Hidden Items if ($grade_grade->grade_item->is_hidden()) { $hidden = ' hidden'; } if (!$hide) { /// Excluded Item if ($grade_grade->is_excluded()) { $fullname .= ' [' . get_string('excluded', 'grades') . ']'; $excluded = ' excluded'; } if (isset($grade_grade->grade_item->itemmodule)) { $modname = $grade_grade->grade_item->itemmodule; } if (isset($cm)) { $modid = $cm->id; } if ($type == "categoryitem") { // print_r($grade_grade); // $timemodified = $grade_grade->grade_item->timemodified; // $activity_start_date = new DateTime('@'.$timemodified); // print_r($activity_start_date); echo "<br>"; } /// Other class information if (isset($modname) && isset($modid)) { $modaction = "view"; $course = $this->course; $user = $this->user->id; $activity_start_date = get_activity_start_date($course, $user, $modname, $modaction, $modid); } if ($type == 'item') { $categoryid = $grade_grade->grade_item->categoryid; if (!isset($this->grade_category_start_dates[$categoryid])) { $this->grade_category_start_dates[$categoryid] = new DateTime("now"); } if ($activity_start_date < $this->grade_category_start_dates[$categoryid]) { $this->grade_category_start_dates[$categoryid] = $activity_start_date; } } if ($type == 'categoryitem') { $iteminstance = $grade_grade->grade_item->iteminstance; if (isset($iteminstance) && isset($this->grade_category_start_dates)) { $activity_start_date = $this->grade_category_start_dates[$iteminstance]; } } $class = "{$hidden} {$excluded}"; if ($this->switch) { // alter style based on whether aggregation is first or last $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggt b2b" : " item b1b"; } else { $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggb" : " item b1b"; $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " categoryitem " : ""; } if ($type == 'categoryitem' or $type == 'courseitem') { $header_cat = "cat_{$grade_object->iteminstance}_{$this->user->id}"; } /// Name $data['itemname']['content'] = $fullname; $data['itemname']['class'] = $class; $data['itemname']['colspan'] = $this->maxdepth - $depth + 2; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = $header_row; /// Actual Grade $gradeval = $grade_grade->finalgrade; if ($this->showfeedback) { // Copy $class before appending itemcenter as feedback should not be centered $classfeedback = $class; } $class .= " itemcenter "; if ($this->showweight) { $data['weight']['class'] = $class; $data['weight']['content'] = '-'; $data['weight']['headers'] = "{$header_cat} {$header_row} weight"; // has a weight assigned, might be extra credit if ($grade_object->aggregationcoef > 0 && $type != 'courseitem') { $data['weight']['content'] = number_format($grade_object->aggregationcoef, 2); } } if ($this->showgrade) { if ($grade_grade->grade_item->needsupdate) { $data['grade']['class'] = $class . ' gradingerror'; $data['grade']['content'] = get_string('error'); } else { if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden() and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) { // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records $class .= ' datesubmitted'; $data['grade']['class'] = $class; $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))); } elseif ($grade_grade->is_hidden()) { $data['grade']['class'] = $class . ' hidden'; $data['grade']['content'] = '-'; } else { $data['grade']['class'] = $class; $gradeval = $this->blank_hidden_total($this->courseid, $grade_grade->grade_item, $gradeval); $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } } $data['grade']['headers'] = "{$header_cat} {$header_row} grade"; } // Range if ($this->showrange) { $data['range']['class'] = $class; $data['range']['content'] = $grade_grade->grade_item->get_formatted_range(GRADE_DISPLAY_TYPE_REAL, $this->rangedecimals); $data['range']['headers'] = "{$header_cat} {$header_row} range"; } // Percentage if ($this->showpercentage) { if ($grade_grade->grade_item->needsupdate) { $data['percentage']['class'] = $class . ' gradingerror'; $data['percentage']['content'] = get_string('error'); } else { if ($grade_grade->is_hidden()) { $data['percentage']['class'] = $class . ' hidden'; $data['percentage']['content'] = '-'; } else { $data['percentage']['class'] = $class; $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } $data['percentage']['headers'] = "{$header_cat} {$header_row} percentage"; } // Lettergrade if ($this->showlettergrade) { if ($grade_grade->grade_item->needsupdate) { $data['lettergrade']['class'] = $class . ' gradingerror'; $data['lettergrade']['content'] = get_string('error'); } else { if ($grade_grade->is_hidden()) { $data['lettergrade']['class'] = $class . ' hidden'; if (!$this->canviewhidden) { $data['lettergrade']['content'] = '-'; } else { $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } else { $data['lettergrade']['class'] = $class; $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } $data['lettergrade']['headers'] = "{$header_cat} {$header_row} lettergrade"; } // Rank if ($this->showrank) { if ($grade_grade->grade_item->needsupdate) { $data['rank']['class'] = $class . ' gradingerror'; $data['rank']['content'] = get_string('error'); } elseif ($grade_grade->is_hidden()) { $data['rank']['class'] = $class . ' hidden'; $data['rank']['content'] = '-'; } else { if (is_null($gradeval)) { // no grade, no rank $data['rank']['class'] = $class; $data['rank']['content'] = '-'; } else { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade > ?\n AND itemid = ?\n AND hidden = 0"; $rank = $DB->count_records_sql($sql, array($grade_grade->finalgrade, $grade_grade->grade_item->id)) + 1; $data['rank']['class'] = $class; $data['rank']['content'] = "{$rank}/" . $this->get_numusers(false); // total course users } } $data['rank']['headers'] = "{$header_cat} {$header_row} rank"; } // Average if ($this->showaverage) { $data['average']['class'] = $class; if (!empty($this->gtree->items[$eid]->avg)) { $data['average']['content'] = $this->gtree->items[$eid]->avg; } else { $data['average']['content'] = '-'; } $data['average']['headers'] = "{$header_cat} {$header_row} average"; } // Start Date if ($this->showtimeupdate) { $data['startdate']['class'] = $class; if (!empty($activity_start_date)) { $data['startdate']['content'] = $activity_start_date->format('d-m-Y'); } else { $data['startdate']['content'] = '-'; } $data['startdate']['headers'] = "{$header_cat} {$header_row} startdate"; } // Time Update if ($this->showtimeupdate) { $data['timeupdate']['class'] = $class; if (!empty($grade_grade->timemodified)) { $data['timeupdate']['content'] = date("d/m/Y", $grade_grade->timemodified); } else { // if ((!empty($grade_grade->grade_item->timemodified)) && ($type == "categoryitem")){ // $data['timeupdate']['content'] = date("d/m/Y", $grade_grade->grade_item->timemodified); // }else { // } $data['timeupdate']['content'] = '-'; } $data['timeupdate']['headers'] = "{$header_cat} {$header_row} timeupdate"; } // Feedback if ($this->showfeedback) { if ($grade_grade->overridden > 0 and ($type == 'categoryitem' or $type == 'courseitem')) { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = get_string('overridden', 'grades') . ': ' . format_text($grade_grade->feedback, $grade_grade->feedbackformat); } else { if (empty($grade_grade->feedback) or !$this->canviewhidden and $grade_grade->is_hidden()) { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = ' '; } else { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat); } } $data['feedback']['headers'] = "{$header_cat} {$header_row} feedback"; } } } /// Category if ($type == 'category') { //&& ($depth == 2)){ // $data['leader']['class'] = $class.' '.$alter."d$depth b1t b2b b1l"; // $data['leader']['rowspan'] = $element['rowspan']; // if ($this->switch) { // alter style based on whether aggregation is first or last // $data['itemname']['class'] = $class.' '.$alter."d$depth b1b b1t"; // } else { // $data['itemname']['class'] = $class.' '.$alter."d$depth b2t"; $data['itemname']['class'] = $class . ' category'; // } $data['itemname']['colspan'] = $this->maxdepth - $depth + count($this->tablecolumns) - 0; $data['itemname']['content'] = $fullname; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = "cat_{$grade_object->id}_{$this->user->id}"; } /// Add this row to the overall system $this->tabledata[] = $data; /// Recursively iterate through all child elements if (isset($element['children'])) { foreach ($element['children'] as $key => $child) { $this->fill_table_recursive($element['children'][$key]); } } }
/** * Returns the locked state of this grade_item (if the grade_item is locked OR no specific * $userid is given) or the locked state of a specific grade within this item if a specific * $userid is given and the grade_item is unlocked. * * @param int $userid * @return boolean Locked state */ function is_locked($userid = NULL) { if (!empty($this->locked)) { return true; } if (!empty($userid)) { if ($grade = grade_grade::fetch(array('itemid' => $this->id, 'userid' => $userid))) { $grade->grade_item =& $this; // prevent db fetching of cached grade_item return $grade->is_locked(); } } return false; }
public function sub_test_grade_item_compute() { $grade_item = grade_item::fetch(array('id' => $this->grade_items[1]->id)); $this->assertTrue(method_exists($grade_item, 'compute')); //check the grade_grades in the array match those in the DB then delete $this->grade_items[1]'s grade_grades $this->grade_grades[3] = grade_grade::fetch(array('id' => $this->grade_grades[3]->id)); $grade_grade = grade_grade::fetch(array('id' => $this->grade_grades[3]->id)); $grade_grade->delete(); $this->grade_grades[4] = grade_grade::fetch(array('id' => $this->grade_grades[4]->id)); $grade_grade = grade_grade::fetch(array('id' => $this->grade_grades[4]->id)); $grade_grade->delete(); $this->grade_grades[5] = grade_grade::fetch(array('id' => $this->grade_grades[5]->id)); $grade_grade = grade_grade::fetch(array('id' => $this->grade_grades[5]->id)); $grade_grade->delete(); //recalculate the grades (its a calculation so pulls values from other grade_items) and reinsert them $grade_item->compute(); $grade_grade = grade_grade::fetch(array('userid' => $this->grade_grades[3]->userid, 'itemid' => $this->grade_grades[3]->itemid)); $this->assertEquals($this->grade_grades[3]->finalgrade, $grade_grade->finalgrade); $grade_grade = grade_grade::fetch(array('userid' => $this->grade_grades[4]->userid, 'itemid' => $this->grade_grades[4]->itemid)); $this->assertEquals($this->grade_grades[4]->finalgrade, $grade_grade->finalgrade); $grade_grade = grade_grade::fetch(array('userid' => $this->grade_grades[5]->userid, 'itemid' => $this->grade_grades[5]->itemid)); $this->assertEquals($this->grade_grades[5]->finalgrade, $grade_grade->finalgrade); }
/** * If the requirements are met - reopen the submission for another attempt. * Only call this function when grading the latest attempt. * * @param int $userid The userid. * @param stdClass $submission The submission (may be a group submission). * @param bool $addattempt - True if the "allow another attempt" checkbox was checked. * @return bool - true if another attempt was added. */ protected function reopen_submission_if_required($userid, $submission, $addattempt) { $instance = $this->get_instance(); $maxattemptsreached = !empty($submission) && $submission->attemptnumber >= $instance->maxattempts - 1 && $instance->maxattempts != ASSIGN_UNLIMITED_ATTEMPTS; $shouldreopen = false; if ($instance->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS) { // Check the gradetopass from the gradebook. $gradeitem = $this->get_grade_item(); if ($gradeitem) { $gradegrade = grade_grade::fetch(array('userid' => $userid, 'itemid' => $gradeitem->id)); // Do not reopen if is_passed returns null, e.g. if there is no pass criterion set. if ($gradegrade && $gradegrade->is_passed() === false) { $shouldreopen = true; } } } if ($instance->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL && !empty($addattempt)) { $shouldreopen = true; } if ($shouldreopen && !$maxattemptsreached) { $this->add_attempt($userid); return true; } return false; }
private function fill_table_recursive(&$element) { global $CFG, $DB; $type = $element['type']; $depth = $element['depth']; $grade_object = $element['object']; $eid = $grade_object->id; $fullname = $this->gtree->get_element_header($element, true, true, true); $data = array(); $hidden = ''; $excluded = ''; $class = ''; // If this is a hidden grade category, hide it completely from the user if ($type == 'category' && $grade_object->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_object->is_hiddenuntil())) { return false; } if ($type == 'category') { $this->evenodd[$depth] = ($this->evenodd[$depth] + 1) % 2; } $alter = $this->evenodd[$depth] == 0 ? 'even' : 'odd'; /// Process those items that have scores associated if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') { if (!($grade_grade = grade_grade::fetch(array('itemid' => $grade_object->id, 'userid' => $this->user->id)))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $grade_object->id; } $grade_grade->load_grade_item(); /// Hidden Items if ($grade_grade->grade_item->is_hidden()) { $hidden = ' hidden'; } // If this is a hidden grade item, hide it completely from the user. if ($grade_grade->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_grade->is_hiddenuntil())) { // return false; } else { /// Excluded Item if ($grade_grade->is_excluded()) { $fullname .= ' [' . get_string('excluded', 'grades') . ']'; $excluded = ' excluded'; } /// Other class information $class = "{$hidden} {$excluded}"; if ($this->switch) { // alter style based on whether aggregation is first or last $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggt b2b" : " item b1b"; } else { $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggb" : " item b1b"; } /// Name $data['itemname']['content'] = $fullname; $data['itemname']['class'] = $class; $data['itemname']['colspan'] = $this->maxdepth - $depth; /// Actual Grade $gradeval = $grade_grade->finalgrade; if ($grade_grade->grade_item->needsupdate) { $data['grade']['class'] = $class . ' gradingerror'; $data['grade']['content'] = get_string('error'); } else { if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden() and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) { // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records $class .= ' datesubmitted'; $data['grade']['class'] = $class; $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))); } elseif ($grade_grade->is_hidden()) { $data['grade']['class'] = $class . ' hidden'; $data['grade']['content'] = '-'; } else { $data['grade']['class'] = $class; $gradeval = $this->blank_hidden_total($this->courseid, $grade_grade->grade_item, $gradeval); $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } } /// Percentage if ($this->showpercentage) { if ($grade_grade->grade_item->needsupdate) { $data['percentage']['class'] = $class . ' gradingerror'; $data['percentage']['content'] = get_string('error'); } elseif ($grade_grade->is_hidden()) { $data['percentage']['class'] = $class . ' hidden'; $data['percentage']['content'] = '-'; } else { $data['percentage']['class'] = $class; $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } /// Rank if ($this->showrank) { // TODO: this is broken if hidden grades present!! if ($grade_grade->grade_item->needsupdate) { $data['rank']['class'] = $class . ' gradingerror'; $data['rank']['content'] = get_string('error'); } elseif ($grade_grade->is_hidden()) { $data['rank']['class'] = $class . ' hidden'; $data['rank']['content'] = '-'; } else { if (is_null($gradeval)) { // no grade, no rank $data['rank']['class'] = $class; $data['rank']['content'] = '-'; } else { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade > ?\n AND itemid = ?"; $rank = $DB->count_records_sql($sql, array($grade_grade->finalgrade, $grade_grade->grade_item->id)) + 1; $data['rank']['class'] = $class; $data['rank']['content'] = "{$rank}/" . $this->get_numusers(false); // total course users } } } /// Feedback if (empty($grade_grade->feedback) or !$this->canviewhidden and $grade_grade->is_hidden()) { $data['feedback']['class'] = $class . ' feedbacktext'; $data['feedback']['content'] = ' '; } else { $data['feedback']['class'] = $class . ' feedbacktext'; $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat, array('overflowdiv' => true)); } /// Range if ($this->showrange) { $data['range']['class'] = $class; $data['range']['content'] = $grade_grade->grade_item->get_formatted_range(); } } } /// Category if ($type == 'category') { $data['leader']['class'] = $class . ' ' . $alter . "d{$depth} b1t b2b b1l"; $data['leader']['rowspan'] = $element['rowspan']; if ($this->switch) { // alter style based on whether aggregation is first or last $data['itemname']['class'] = $class . ' ' . $alter . "d{$depth} b1b b1t"; } else { $data['itemname']['class'] = $class . ' ' . $alter . "d{$depth} b2t"; } $data['itemname']['colspan'] = $this->maxdepth - $depth + count($this->tablecolumns) - 1; $data['itemname']['content'] = $fullname; } /// Add this row to the overall system $this->tabledata[] = $data; /// Recursively iterate through all child elements if (isset($element['children'])) { foreach ($element['children'] as $key => $child) { $this->fill_table_recursive($element['children'][$key]); } } }
/** * Parses the array in search of a given eid and returns a element object with * information about the element it has found. * @param int $eid Gradetree Element ID * @return object element */ public function locate_element($eid) { // it is a grade - construct a new object if (strpos($eid, 'n') === 0) { if (!preg_match('/n(\\d+)u(\\d+)/', $eid, $matches)) { return null; } $itemid = $matches[1]; $userid = $matches[2]; //extra security check - the grade item must be in this tree if (!($item_el = $this->locate_element('i' . $itemid))) { return null; } // $gradea->id may be null - means does not exist yet $grade = new grade_grade(array('itemid' => $itemid, 'userid' => $userid)); $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! return array('eid' => 'n' . $itemid . 'u' . $userid, 'object' => $grade, 'type' => 'grade'); } else { if (strpos($eid, 'g') === 0) { $id = (int) substr($eid, 1); if (!($grade = grade_grade::fetch(array('id' => $id)))) { return null; } //extra security check - the grade item must be in this tree if (!($item_el = $this->locate_element('i' . $grade->itemid))) { return null; } $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! return array('eid' => 'g' . $id, 'object' => $grade, 'type' => 'grade'); } } // it is a category or item foreach ($this->levels as $row) { foreach ($row as $element) { if ($element['type'] == 'filler') { continue; } if ($element['eid'] == $eid) { return $element; } } } return null; }
/** * Tests that the event is fired in the correct locations in core. */ public function test_event_is_triggered() { global $DB; // Create the items we need to test with. $course = $this->getDataGenerator()->create_course(); $user = $this->getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($user->id, $course->id); $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id)); $quizitemparams = array('itemtype' => 'mod', 'itemmodule' => 'quiz', 'iteminstance' => $quiz->id, 'courseid' => $course->id); $gradeitem = grade_item::fetch($quizitemparams); $courseitem = grade_item::fetch_course_item($course->id); // Now mark the quiz using grade_update as this is the function that modules use. $grade = array(); $grade['userid'] = $user->id; $grade['rawgrade'] = 60; $sink = $this->redirectEvents(); grade_update('mod/quiz', $course->id, 'mod', 'quiz', $quiz->id, 0, $grade); $events = $sink->get_events(); $sink->close(); // Ensure we have two user_graded events, one for the item, one for the course. $this->assertEquals(2, count($events)); $this->assertInstanceOf('\\core\\event\\user_graded', $events[0]); $this->assertEquals($gradeitem->id, $events[0]->other['itemid']); $this->assertInstanceOf('\\core\\event\\user_graded', $events[1]); $this->assertEquals($courseitem->id, $events[1]->other['itemid']); // Remove the grades, force the regrading and re-fetch the item. This is needed because the item // will be set as needing an update when the grades are deleted. $gradeitem->delete_all_grades(); grade_regrade_final_grades($course->id); $gradeitem = grade_item::fetch($quizitemparams); // Now, create a grade using grade_item::update_final_grade(). $sink = $this->redirectEvents(); $gradeitem->update_raw_grade($user->id, 10); $events = $sink->get_events(); $sink->close(); // Ensure we have two user_graded events, one for the item, one for the course. $this->assertEquals(2, count($events)); $this->assertInstanceOf('\\core\\event\\user_graded', $events[0]); $this->assertEquals($gradeitem->id, $events[0]->other['itemid']); $this->assertInstanceOf('\\core\\event\\user_graded', $events[1]); $this->assertEquals($courseitem->id, $events[1]->other['itemid']); // Now, update this grade using grade_item::update_raw_grade(). $sink = $this->redirectEvents(); $gradeitem->update_raw_grade($user->id, 20); $events = $sink->get_events(); $sink->close(); // Ensure we have two user_graded events, one for the item, one for the course. $this->assertEquals(2, count($events)); $this->assertInstanceOf('\\core\\event\\user_graded', $events[0]); $this->assertEquals($gradeitem->id, $events[0]->other['itemid']); $this->assertInstanceOf('\\core\\event\\user_graded', $events[1]); $this->assertEquals($courseitem->id, $events[1]->other['itemid']); // Remove the grades, force the regrading and re-fetch the item. This is needed because the item // will be set as needing an update when the grades are deleted. $gradeitem->delete_all_grades(); grade_regrade_final_grades($course->id); $gradeitem = grade_item::fetch($quizitemparams); // Now, create a grade using grade_item::update_final_grade(). $sink = $this->redirectEvents(); $gradeitem->update_final_grade($user->id, 30); $events = $sink->get_events(); $sink->close(); // Ensure we have two user_graded events, one for the item, one for the course. $this->assertEquals(2, count($events)); $this->assertInstanceOf('\\core\\event\\user_graded', $events[0]); $this->assertEquals($gradeitem->id, $events[0]->other['itemid']); $this->assertInstanceOf('\\core\\event\\user_graded', $events[1]); $this->assertEquals($courseitem->id, $events[1]->other['itemid']); // Now, update this grade using grade_item::update_final_grade(). $sink = $this->redirectEvents(); $gradeitem->update_final_grade($user->id, 40); $events = $sink->get_events(); $sink->close(); // Ensure we have two user_graded events, one for the item, one for the course. $this->assertEquals(2, count($events)); $this->assertInstanceOf('\\core\\event\\user_graded', $events[0]); $this->assertEquals($gradeitem->id, $events[0]->other['itemid']); $this->assertInstanceOf('\\core\\event\\user_graded', $events[1]); $this->assertEquals($courseitem->id, $events[1]->other['itemid']); // Remove the overridden flag from the grade, this was set by grade_item::update_final_grade(). $gradegrade = grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user->id)); $gradegrade->set_overridden(false, false); // Let's change the calculation to anything that won't cause an error. $calculation = calc_formula::unlocalize("=3"); $gradeitem->set_calculation($calculation); // Now force the computation of the grade. $sink = $this->redirectEvents(); grade_regrade_final_grades($course->id); $events = $sink->get_events(); $sink->close(); // Ensure we have two user_graded events, one for the item, one for the course. $this->assertEquals(2, count($events)); $this->assertInstanceOf('\\core\\event\\user_graded', $events[0]); $this->assertEquals($gradeitem->id, $events[0]->other['itemid']); $this->assertInstanceOf('\\core\\event\\user_graded', $events[1]); $this->assertEquals($courseitem->id, $events[1]->other['itemid']); // Now, let's trick the gradebook, we manually update a grade, and flag the grade item as // needing a regrading, so we can trigger the event in grade_item::regrade_final_grades(). $gradeitem = grade_item::fetch($quizitemparams); $gradeitem->set_calculation(''); $gradegrade = grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user->id)); $gradegrade->rawgrade = 50; $gradegrade->update(); $sink = $this->redirectEvents(); grade_regrade_final_grades($course->id); $events = $sink->get_events(); $sink->close(); // Ensure we have two user_graded events, one for the item, one for the course. $this->assertEquals(2, count($events)); $this->assertInstanceOf('\\core\\event\\user_graded', $events[0]); $this->assertEquals($gradeitem->id, $events[0]->other['itemid']); $this->assertInstanceOf('\\core\\event\\user_graded', $events[1]); $this->assertEquals($courseitem->id, $events[1]->other['itemid']); }