/** * Initialise the iterator * @return boolean success */ function init() { global $CFG; $this->close(); grade_regrade_final_grades($this->course->id); $course_item = grade_item::fetch_course_item($this->course->id); if ($course_item->needsupdate) { // can not calculate all final grades - sorry return false; } if (strpos($CFG->gradebookroles, ',') !== false) { $gradebookroles = " = {$CFG->gradebookroles}"; } else { $gradebookroles = " IN ({$CFG->gradebookroles})"; } $relatedcontexts = get_related_contexts_string(get_context_instance(CONTEXT_COURSE, $this->course->id)); if ($this->groupid) { $groupsql = "INNER JOIN {$CFG->prefix}groups_members gm ON gm.userid = u.id"; $groupwheresql = "AND gm.groupid = {$this->groupid}"; } else { $groupsql = ""; $groupwheresql = ""; } $users_sql = "SELECT u.*\n FROM {$CFG->prefix}user u\n INNER JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid\n {$groupsql}\n WHERE ra.roleid {$gradebookroles}\n AND ra.contextid {$relatedcontexts}\n {$groupwheresql}\n ORDER BY u.id ASC"; $this->users_rs = get_recordset_sql($users_sql); if (!empty($this->grade_items)) { $itemids = array_keys($this->grade_items); $itemids = implode(',', $itemids); $grades_sql = "SELECT g.*\n FROM {$CFG->prefix}grade_grades g\n INNER JOIN {$CFG->prefix}user u ON g.userid = u.id\n INNER JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid\n {$groupsql}\n WHERE ra.roleid {$gradebookroles}\n AND ra.contextid {$relatedcontexts}\n AND g.itemid IN ({$itemids})\n {$groupwheresql}\n ORDER BY g.userid ASC, g.itemid ASC"; $this->grades_rs = get_recordset_sql($grades_sql); } return true; }
/** * Initialise the iterator * @return boolean success */ public function init() { global $CFG, $DB; $this->close(); grade_regrade_final_grades($this->course->id); $course_item = grade_item::fetch_course_item($this->course->id); if ($course_item->needsupdate) { // can not calculate all final grades - sorry return false; } $coursecontext = get_context_instance(CONTEXT_COURSE, $this->course->id); $relatedcontexts = get_related_contexts_string($coursecontext); list($gradebookroles_sql, $params) = $DB->get_in_or_equal(explode(',', $CFG->gradebookroles), SQL_PARAMS_NAMED, 'grbr'); //limit to users with an active enrolment list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext); $params = array_merge($params, $enrolledparams); if ($this->groupid) { $groupsql = "INNER JOIN {groups_members} gm ON gm.userid = u.id"; $groupwheresql = "AND gm.groupid = :groupid"; // $params contents: gradebookroles $params['groupid'] = $this->groupid; } else { $groupsql = ""; $groupwheresql = ""; } if (empty($this->sortfield1)) { // we must do some sorting even if not specified $ofields = ", u.id AS usrt"; $order = "usrt ASC"; } else { $ofields = ", u.{$this->sortfield1} AS usrt1"; $order = "usrt1 {$this->sortorder1}"; if (!empty($this->sortfield2)) { $ofields .= ", u.{$this->sortfield2} AS usrt2"; $order .= ", usrt2 {$this->sortorder2}"; } if ($this->sortfield1 != 'id' and $this->sortfield2 != 'id') { // user order MUST be the same in both queries, // must include the only unique user->id if not already present $ofields .= ", u.id AS usrt"; $order .= ", usrt ASC"; } } // $params contents: gradebookroles and groupid (for $groupwheresql) $users_sql = "SELECT u.* {$ofields}\n FROM {user} u\n JOIN ({$enrolledsql}) je ON je.id = u.id\n {$groupsql}\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookroles_sql}\n AND ra.contextid {$relatedcontexts}\n ) rainner ON rainner.userid = u.id\n WHERE u.deleted = 0\n {$groupwheresql}\n ORDER BY {$order}"; $this->users_rs = $DB->get_recordset_sql($users_sql, $params); if (!empty($this->grade_items)) { $itemids = array_keys($this->grade_items); list($itemidsql, $grades_params) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED, 'items'); $params = array_merge($params, $grades_params); // $params contents: gradebookroles, enrolledparams, groupid (for $groupwheresql) and itemids $grades_sql = "SELECT g.* {$ofields}\n FROM {grade_grades} g\n JOIN {user} u ON g.userid = u.id\n JOIN ({$enrolledsql}) je ON je.id = u.id\n {$groupsql}\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookroles_sql}\n AND ra.contextid {$relatedcontexts}\n ) rainner ON rainner.userid = u.id\n WHERE u.deleted = 0\n AND g.itemid {$itemidsql}\n {$groupwheresql}\n ORDER BY {$order}, g.itemid ASC"; $this->grades_rs = $DB->get_recordset_sql($grades_sql, $params); } else { $this->grades_rs = false; } return true; }
/** * Initialise the iterator * @return boolean success */ function init() { global $CFG; $this->close(); grade_regrade_final_grades($this->course->id); $course_item = grade_item::fetch_course_item($this->course->id); if ($course_item->needsupdate) { // can not calculate all final grades - sorry return false; } if (strpos($CFG->gradebookroles, ',') === false) { $gradebookroles = " = {$CFG->gradebookroles}"; } else { $gradebookroles = " IN ({$CFG->gradebookroles})"; } $relatedcontexts = get_related_contexts_string(get_context_instance(CONTEXT_COURSE, $this->course->id)); if ($this->groupid) { $groupsql = "INNER JOIN {$CFG->prefix}groups_members gm ON gm.userid = u.id"; $groupwheresql = "AND gm.groupid = {$this->groupid}"; } else { $groupsql = ""; $groupwheresql = ""; } if (empty($this->sortfield1)) { // we must do some sorting even if not specified $ofields = ", u.id AS usrt"; $order = "usrt ASC"; } else { $ofields = ", u.{$this->sortfield1} AS usrt1"; $order = "usrt1 {$this->sortorder1}"; if (!empty($this->sortfield2)) { $ofields .= ", u.{$this->sortfield2} AS usrt2"; $order .= ", usrt2 {$this->sortorder2}"; } if ($this->sortfield1 != 'id' and $this->sortfield2 != 'id') { // user order MUST be the same in both queries, must include the only unique user->id if not already present $ofields .= ", u.id AS usrt"; $order .= ", usrt ASC"; } } $users_sql = "SELECT u.* {$ofields}\n FROM {$CFG->prefix}user u\n INNER JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid\n {$groupsql}\n WHERE ra.roleid {$gradebookroles}\n AND ra.contextid {$relatedcontexts}\n {$groupwheresql}\n ORDER BY {$order}"; $this->users_rs = get_recordset_sql($users_sql); if (!empty($this->grade_items)) { $itemids = array_keys($this->grade_items); $itemids = implode(',', $itemids); $grades_sql = "SELECT g.* {$ofields}\n FROM {$CFG->prefix}grade_grades g\n INNER JOIN {$CFG->prefix}user u ON g.userid = u.id\n INNER JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid\n {$groupsql}\n WHERE ra.roleid {$gradebookroles}\n AND ra.contextid {$relatedcontexts}\n AND g.itemid IN ({$itemids})\n {$groupwheresql}\n ORDER BY {$order}, g.itemid ASC"; $this->grades_rs = get_recordset_sql($grades_sql); } else { $this->grades_rs = false; } return true; }
/** * Prepare to print the course grade. * * @deprecated since iomadcertificate version 2012052501 * @param stdClass $course * @return mixed */ function iomadcertificate_print_course_grade($course) { debugging('iomadcertificate_print_course_grade is deprecated, please use iomadcertificate_get_grade instead. Ideally you should be using iomadcertificate_get_grade in your iomadcertificate type which will either get the course or module grade depending on your iomadcertificate settings.', DEBUG_DEVELOPER); global $USER, $DB; if ($course_item = grade_item::fetch_course_item($course->id)) { $grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $USER->id)); $course_item->gradetype = GRADE_TYPE_VALUE; $coursegrade = new stdClass(); $coursegrade->points = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_REAL, $decimals = 2); $coursegrade->percentage = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE, $decimals = 2); $coursegrade->letter = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_LETTER, $decimals = 0); return $coursegrade; } return false; }
/** * Does this course have any visible feedback for current user?. * * @param $course * @return stdClass | null */ public static function course_feedback($course) { global $USER; // Get course context. $coursecontext = \context_course::instance($course->id); // Security check - should they be allowed to see course grade? $onlyactive = true; if (!is_enrolled($coursecontext, $USER, 'moodle/grade:view', $onlyactive)) { return self::skipgradewarning('User not enrolled on course with capability moodle/grade:view'); } // Security check - are they allowed to see the grade report for the course? if (!has_capability('gradereport/user:view', $coursecontext)) { return self::skipgradewarning('User does not have required course capability gradereport/user:view'); } // See if user can view hidden grades for this course. $canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext); // Do not show grade if grade book disabled for students. // Note - moodle/grade:viewall is a capability held by teachers and thus used to exclude them from not getting // the grade. if (empty($course->showgrades) && !has_capability('moodle/grade:viewall', $coursecontext)) { return self::skipgradewarning('Course set up to not show gradebook to students'); } // Get course grade_item. $courseitem = \grade_item::fetch_course_item($course->id); // Get the stored grade. $coursegrade = new \grade_grade(array('itemid' => $courseitem->id, 'userid' => $USER->id)); $coursegrade->grade_item =& $courseitem; // Return null if can't view. if ($coursegrade->is_hidden() && !$canviewhidden) { return self::skipgradewarning('Course grade is hidden from students'); } // Use user grade report to get course total - this is to take hidden grade settings into account. $gpr = new \grade_plugin_return(array('type' => 'report', 'plugin' => 'user', 'courseid' => $course->id, 'userid' => $USER->id)); $report = new \grade_report_user($course->id, $gpr, $coursecontext, $USER->id); $report->fill_table(); $visiblegradefound = false; foreach ($report->tabledata as $item) { if (self::item_has_grade_or_feedback($item)) { $visiblegradefound = true; break; } } $feedbackhtml = ''; if ($visiblegradefound) { // Just output - feedback available. $url = new \moodle_url('/grade/report/user/index.php', array('id' => $course->id)); $feedbackhtml = \html_writer::link($url, get_string('feedbackavailable', 'theme_snap'), array('class' => 'coursegrade')); } return (object) array('feedbackhtml' => $feedbackhtml); }
/** * Prepare to print the course grade. * * @param stdClass $course * @return mixed */ function certificate_print_course_grade($course) { global $USER, $DB; if ($course_item = grade_item::fetch_course_item($course->id)) { $grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $USER->id)); $course_item->gradetype = GRADE_TYPE_VALUE; $coursegrade = new stdClass(); $coursegrade->points = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_REAL, $decimals = 2); $coursegrade->percentage = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE, $decimals = 2); $coursegrade->letter = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_LETTER, $decimals = 0); return $coursegrade; } return false; }
/** * Validate that the method respects the locked status when run for a * specific user */ public function test_methodonlyupdatesunlockedenrolmentsforspecificuserid() { global $DB; $this->load_csv_data(); // Set up enrolments. $this->make_course_enrollable(); enrol_try_internal_enrol(2, 100, 1); enrol_try_internal_enrol(2, 101, 1); // Set required PM course grade. $pmcourse = new \course(array('id' => 100, 'completion_grade' => 50)); $pmcourse->save(); // Set up course grade item. $coursegradeitem = \grade_item::fetch_course_item(2); $coursegradeitem->grademax = 100; $coursegradeitem->needsupdate = false; $coursegradeitem->locked = true; $coursegradeitem->update(); // Assign student grades. $coursegradegrade = new \grade_grade(array('itemid' => 1, 'userid' => 100, 'finalgrade' => 100)); $coursegradegrade->insert(); $coursegradegrade = new \grade_grade(array('itemid' => 1, 'userid' => 101, 'finalgrade' => 100)); $coursegradegrade->insert(); // Enrol the student. $student = new \student(); $student->userid = 103; $student->classid = 100; $student->grade = 0; $student->completestatusid = STUSTATUS_NOTCOMPLETE; $student->locked = 1; $student->save(); // Call and validate that locked record is not changed. $sync = new \local_elisprogram\moodle\synchronize(); $sync->synchronize_moodle_class_grades(100); $this->assert_student_exists(100, 103, 0, STUSTATUS_NOTCOMPLETE, null, null, 1); $DB->execute("UPDATE {" . \student::TABLE . "} SET locked = 0"); // Call and validate that unlocked record is changed. $sync = new \local_elisprogram\moodle\synchronize(); $sync->synchronize_moodle_class_grades(100); // Validate count. $count = $DB->count_records(\student::TABLE, array('completestatusid' => STUSTATUS_PASSED)); $this->assertEquals(1, $count); // NOTE: this method does not lock enrolments. $this->assert_student_exists(100, 103, 100, STUSTATUS_PASSED, null, null, 0); }
protected function sub_test_grade_item_fetch_course_item() { $grade_item = grade_item::fetch_course_item($this->courseid); $this->assertTrue(method_exists($grade_item, 'fetch_course_item')); $this->assertEquals($grade_item->itemtype, 'course'); }
/** * Tests grade_report::blank_hidden_total_and_adjust_bounds() */ public function test_blank_hidden_total_and_adjust_bounds() { global $DB; $this->resetAfterTest(true); $student = $this->getDataGenerator()->create_user(); $this->setUser($student); // Create a course and two activities. // One activity will be hidden. $course = $this->getDataGenerator()->create_course(); $coursegradeitem = grade_item::fetch_course_item($course->id); $coursecontext = context_course::instance($course->id); $data = $this->getDataGenerator()->create_module('data', array('assessed' => 1, 'scale' => 100, 'course' => $course->id)); $datacm = get_coursemodule_from_id('data', $data->cmid); $forum = $this->getDataGenerator()->create_module('forum', array('assessed' => 1, 'scale' => 100, 'course' => $course->id)); $forumcm = get_coursemodule_from_id('forum', $forum->cmid); // Insert student grades for the two activities. $gi = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'data', 'iteminstance' => $data->id, 'courseid' => $course->id)); $datagrade = 50; $grade_grade = new grade_grade(); $grade_grade->itemid = $gi->id; $grade_grade->userid = $student->id; $grade_grade->rawgrade = $datagrade; $grade_grade->finalgrade = $datagrade; $grade_grade->rawgrademax = 100; $grade_grade->rawgrademin = 0; $grade_grade->timecreated = time(); $grade_grade->timemodified = time(); $grade_grade->insert(); $gi = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'forum', 'iteminstance' => $forum->id, 'courseid' => $course->id)); $forumgrade = 70; $grade_grade = new grade_grade(); $grade_grade->itemid = $gi->id; $grade_grade->userid = $student->id; $grade_grade->rawgrade = $forumgrade; $grade_grade->finalgrade = $forumgrade; $grade_grade->rawgrademax = 100; $grade_grade->rawgrademin = 0; $grade_grade->timecreated = time(); $grade_grade->timemodified = time(); $grade_grade->insert(); // Hide the database activity. set_coursemodule_visible($datacm->id, 0); $gpr = new grade_plugin_return(array('type' => 'report', 'courseid' => $course->id)); $report = new grade_report_test($course->id, $gpr, $coursecontext, $student); // Should return the supplied student total grade regardless of hiding. $report->showtotalsifcontainhidden = array($course->id => GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN); $result = $report->blank_hidden_total_and_adjust_bounds($course->id, $coursegradeitem, $datagrade + $forumgrade); $this->assertEquals(array('grade' => $datagrade + $forumgrade, 'grademax' => $coursegradeitem->grademax, 'grademin' => $coursegradeitem->grademin, 'aggregationstatus' => 'unknown', 'aggregationweight' => null), $result); // Should blank the student total as course grade depends on a hidden item. $report->showtotalsifcontainhidden = array($course->id => GRADE_REPORT_HIDE_TOTAL_IF_CONTAINS_HIDDEN); $result = $report->blank_hidden_total_and_adjust_bounds($course->id, $coursegradeitem, $datagrade + $forumgrade); $this->assertEquals(array('grade' => null, 'grademax' => $coursegradeitem->grademax, 'grademin' => $coursegradeitem->grademin, 'aggregationstatus' => 'unknown', 'aggregationweight' => null), $result); // Should return the course total minus the hidden database activity grade. $report->showtotalsifcontainhidden = array($course->id => GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN); $result = $report->blank_hidden_total_and_adjust_bounds($course->id, $coursegradeitem, $datagrade + $forumgrade); $this->assertEquals(array('grade' => floatval($forumgrade), 'grademax' => $coursegradeitem->grademax, 'grademin' => $coursegradeitem->grademin, 'aggregationstatus' => 'unknown', 'aggregationweight' => null), $result); // Note: we cannot simply hide modules and call $report->blank_hidden_total() again. // It stores grades in a static variable so $report->blank_hidden_total() will return incorrect totals // In practice this isn't a problem. Grade visibility isn't altered mid-request outside of the unit tests. // Add a second course to test: // 1) How a course with no visible activities behaves. // 2) That $report->blank_hidden_total() correctly moves on to the new course. $course = $this->getDataGenerator()->create_course(); $coursegradeitem = grade_item::fetch_course_item($course->id); $coursecontext = context_course::instance($course->id); $data = $this->getDataGenerator()->create_module('data', array('assessed' => 1, 'scale' => 100, 'course' => $course->id)); $datacm = get_coursemodule_from_id('data', $data->cmid); $forum = $this->getDataGenerator()->create_module('forum', array('assessed' => 1, 'scale' => 100, 'course' => $course->id)); $forumcm = get_coursemodule_from_id('forum', $forum->cmid); $gi = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'data', 'iteminstance' => $data->id, 'courseid' => $course->id)); $datagrade = 50; $grade_grade = new grade_grade(); $grade_grade->itemid = $gi->id; $grade_grade->userid = $student->id; $grade_grade->rawgrade = $datagrade; $grade_grade->finalgrade = $datagrade; $grade_grade->rawgrademax = 100; $grade_grade->rawgrademin = 0; $grade_grade->timecreated = time(); $grade_grade->timemodified = time(); $grade_grade->insert(); $gi = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'forum', 'iteminstance' => $forum->id, 'courseid' => $course->id)); $forumgrade = 70; $grade_grade = new grade_grade(); $grade_grade->itemid = $gi->id; $grade_grade->userid = $student->id; $grade_grade->rawgrade = $forumgrade; $grade_grade->finalgrade = $forumgrade; $grade_grade->rawgrademax = 100; $grade_grade->rawgrademin = 0; $grade_grade->timecreated = time(); $grade_grade->timemodified = time(); $grade_grade->insert(); // Hide both activities. set_coursemodule_visible($datacm->id, 0); set_coursemodule_visible($forumcm->id, 0); $gpr = new grade_plugin_return(array('type' => 'report', 'courseid' => $course->id)); $report = new grade_report_test($course->id, $gpr, $coursecontext, $student); // Should return the supplied student total grade regardless of hiding. $report->showtotalsifcontainhidden = array($course->id => GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN); $result = $report->blank_hidden_total_and_adjust_bounds($course->id, $coursegradeitem, $datagrade + $forumgrade); $this->assertEquals(array('grade' => $datagrade + $forumgrade, 'grademax' => $coursegradeitem->grademax, 'grademin' => $coursegradeitem->grademin, 'aggregationstatus' => 'unknown', 'aggregationweight' => null), $result); // Should blank the student total as course grade depends on a hidden item. $report->showtotalsifcontainhidden = array($course->id => GRADE_REPORT_HIDE_TOTAL_IF_CONTAINS_HIDDEN); $result = $report->blank_hidden_total_and_adjust_bounds($course->id, $coursegradeitem, $datagrade + $forumgrade); $this->assertEquals(array('grade' => null, 'grademax' => $coursegradeitem->grademax, 'grademin' => $coursegradeitem->grademin, 'aggregationstatus' => 'unknown', 'aggregationweight' => null), $result); // Should return the course total minus the hidden activity grades. // They are both hidden so should return null. $report->showtotalsifcontainhidden = array($course->id => GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN); $result = $report->blank_hidden_total_and_adjust_bounds($course->id, $coursegradeitem, $datagrade + $forumgrade); $this->assertEquals(array('grade' => null, 'grademax' => $coursegradeitem->grademax, 'grademin' => $coursegradeitem->grademin, 'aggregationstatus' => 'unknown', 'aggregationweight' => null), $result); }
/** * Updates raw grade value for given user, this is a only way to update raw * grades from external source (modules, etc.), * because it logs the change in history table and deals with final grade recalculation. * * @param int $userid the graded user * @param mixed $rawgrade float value of raw grade - false means do not change * @param string $howmodified modification source * @param string $note optional note * @param mixed $feedback teachers feedback as string - false means do not change * @param int $feedbackformat * @param int $usermodified - user which did the grading * @param int $dategraded * @param int $datesubmitted * @param object $grade object - usefull for bulk upgrades * @return boolean success */ function update_raw_grade($userid, $rawgrade = false, $source = NULL, $feedback = false, $feedbackformat = FORMAT_MOODLE, $usermodified = null, $dategraded = null, $datesubmitted = null, $grade = null) { global $USER; $result = true; // calculated grades can not be updated; course and category can not be updated because they are aggregated if (!$this->is_raw_used() or $this->gradetype == GRADE_TYPE_NONE or $this->is_locked()) { return false; } if (is_null($grade)) { //fetch from db $grade = new grade_grade(array('itemid' => $this->id, 'userid' => $userid)); } $grade->grade_item =& $this; // prevent db fetching of this grade_item if (empty($usermodified)) { $grade->usermodified = $USER->id; } else { $grade->usermodified = $usermodified; } if ($grade->is_locked()) { // do not update locked grades at all return false; } $locktime = $grade->get_locktime(); if ($locktime and $locktime < time()) { // do not update grades that should be already locked and force regrade $this->force_regrading(); return false; } $oldgrade = new object(); $oldgrade->finalgrade = $grade->finalgrade; $oldgrade->rawgrade = $grade->rawgrade; $oldgrade->rawgrademin = $grade->rawgrademin; $oldgrade->rawgrademax = $grade->rawgrademax; $oldgrade->rawscaleid = $grade->rawscaleid; $oldgrade->feedback = $grade->feedback; $oldgrade->feedbackformat = $grade->feedbackformat; // use new min and max $grade->rawgrade = $grade->rawgrade; $grade->rawgrademin = $this->grademin; $grade->rawgrademax = $this->grademax; $grade->rawscaleid = $this->scaleid; // change raw grade? if ($rawgrade !== false) { $grade->rawgrade = $rawgrade; } // empty feedback means no feedback at all if ($feedback === '') { $feedback = null; } // do we have comment from teacher? if ($feedback !== false and !$grade->is_overridden()) { $grade->feedback = $feedback; $grade->feedbackformat = $feedbackformat; } // update final grade if possible if (!$grade->is_locked() and !$grade->is_overridden()) { $grade->finalgrade = $this->adjust_raw_grade($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax); } // TODO: hack alert - create new fields for these in 2.0 $oldgrade->timecreated = $grade->timecreated; $oldgrade->timemodified = $grade->timemodified; $grade->timecreated = $datesubmitted; if ($grade->is_overridden()) { // keep original graded date - update_final_grade() sets this for overridden grades } else { if (is_null($grade->rawgrade) and is_null($grade->feedback)) { // no grade and feedback means no grading yet $grade->timemodified = null; } else { if (!empty($dategraded)) { // fine - module sends info when graded (yay!) $grade->timemodified = $dategraded; } else { if (grade_floats_different($grade->finalgrade, $oldgrade->finalgrade) or $grade->feedback !== $oldgrade->feedback) { // guess - if either grade or feedback changed set new graded date $grade->timemodified = time(); } else { //keep original graded date } } } } // end of hack alert if (empty($grade->id)) { $result = (bool) $grade->insert($source); } else { if (grade_floats_different($grade->finalgrade, $oldgrade->finalgrade) or grade_floats_different($grade->rawgrade, $oldgrade->rawgrade) or grade_floats_different($grade->rawgrademin, $oldgrade->rawgrademin) or grade_floats_different($grade->rawgrademax, $oldgrade->rawgrademax) or $grade->rawscaleid != $oldgrade->rawscaleid or $grade->feedback !== $oldgrade->feedback or $grade->feedbackformat != $oldgrade->feedbackformat or $grade->timecreated != $oldgrade->timecreated or $grade->timemodified != $oldgrade->timemodified) { $result = $grade->update($source); } else { return $result; } } if (!$result) { // something went wrong - better force final grade recalculation $this->force_regrading(); } else { if (!$this->needsupdate) { $course_item = grade_item::fetch_course_item($this->courseid); if (!$course_item->needsupdate) { if (grade_regrade_final_grades($this->courseid, $userid, $this) !== true) { $this->force_regrading(); } } } } return $result; }
/** * Test a specific type of module. * * @param string $modulename - the module name to test */ private function update_specific_module_test($modulename) { global $DB, $CFG; $this->resetAfterTest(true); $this->setAdminUser(); // Warnings: you'll need to change this line if ever you come to test a module not following Moodle standard. require_once $CFG->dirroot . '/mod/' . $modulename . '/lib.php'; // Enable avaibility. // If not enabled all conditional fields will be ignored. set_config('enableavailability', 1); // Enable course completion. // If not enabled all completion settings will be ignored. set_config('enablecompletion', COMPLETION_ENABLED); // Enable forum RSS feeds. set_config('enablerssfeeds', 1); set_config('forum_enablerssfeeds', 1); $course = $this->getDataGenerator()->create_course(array('numsections' => 1, 'enablecompletion' => COMPLETION_ENABLED), array('createsections' => true)); $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id)); // Create assign module instance for testing gradeitem. $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); $params['course'] = $course->id; $instance = $generator->create_instance($params); $assigncm = get_coursemodule_from_instance('assign', $instance->id); // Create the test forum to update. $initvalues = new stdClass(); $initvalues->introformat = FORMAT_HTML; $initvalues->course = $course->id; $forum = self::getDataGenerator()->create_module('forum', $initvalues); // Retrieve course module. $cm = get_coursemodule_from_instance('forum', $forum->id); // Module test values. $moduleinfo = new stdClass(); // Always mandatory generic values to any module. $moduleinfo->coursemodule = $cm->id; $moduleinfo->modulename = $modulename; $moduleinfo->course = $course->id; $moduleinfo->groupingid = $grouping->id; $moduleinfo->visible = true; // Sometimes optional generic values for some modules. $moduleinfo->name = 'My test module'; $moduleinfo->showdescription = 1; // standard boolean require_once $CFG->libdir . '/gradelib.php'; $gradecats = grade_get_categories_menu($moduleinfo->course, false); $gradecatid = current(array_keys($gradecats)); // Retrieve the first key of $gradecats $moduleinfo->gradecat = $gradecatid; $moduleinfo->groupmode = VISIBLEGROUPS; $moduleinfo->cmidnumber = 'idnumber_XXX'; // Completion common to all module. $moduleinfo->completion = COMPLETION_TRACKING_AUTOMATIC; $moduleinfo->completionview = COMPLETION_VIEW_REQUIRED; $moduleinfo->completiongradeitemnumber = 1; $moduleinfo->completionexpected = time() + 7 * 24 * 3600; $moduleinfo->completionunlocked = 1; // Conditional activity. $coursegradeitem = grade_item::fetch_course_item($moduleinfo->course); //the activity will become available only when the user reach some grade into the course itself. $moduleinfo->availability = json_encode(\core_availability\tree::get_root_json(array(\availability_date\condition::get_json('>=', time()), \availability_date\condition::get_json('<', time() + 7 * 24 * 3600), \availability_grade\condition::get_json($coursegradeitem->id, 10, 80), \availability_profile\condition::get_json(false, 'email', 'contains', '@'), \availability_completion\condition::get_json($assigncm->id, COMPLETION_COMPLETE)), '&')); // Grading and Advanced grading. require_once $CFG->dirroot . '/rating/lib.php'; $moduleinfo->assessed = RATING_AGGREGATE_AVERAGE; $moduleinfo->scale = 10; // Note: it could be minus (for specific course scale). It is a signed number. $moduleinfo->assesstimestart = time(); $moduleinfo->assesstimefinish = time() + 7 * 24 * 3600; // RSS. $moduleinfo->rsstype = 2; $moduleinfo->rssarticles = 10; // Optional intro editor (depends of module). $draftid_editor = 0; file_prepare_draft_area($draftid_editor, null, null, null, null); $moduleinfo->introeditor = array('text' => 'This is a module', 'format' => FORMAT_HTML, 'itemid' => $draftid_editor); // Following is the advanced grading method area called 'submissions' for the 'assign' module. if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) { $moduleinfo->grade = 100; } // Plagiarism form values. // No plagiarism plugin installed by default. Use this space to make your own test. // Values specific to the module. $modulesetvalues = $modulename . '_update_set_values'; $this->{$modulesetvalues}($moduleinfo); // Create the module. $result = update_module($moduleinfo); // Retrieve the module info. $dbmodinstance = $DB->get_record($moduleinfo->modulename, array('id' => $result->instance)); $dbcm = get_coursemodule_from_instance($moduleinfo->modulename, $result->instance); // Retrieve the grade item. $gradeitem = $DB->get_record('grade_items', array('courseid' => $moduleinfo->course, 'iteminstance' => $dbmodinstance->id, 'itemmodule' => $moduleinfo->modulename)); // Compare the values common to all module instances. $this->assertEquals($moduleinfo->modulename, $dbcm->modname); $this->assertEquals($moduleinfo->course, $dbcm->course); $this->assertEquals($moduleinfo->groupingid, $dbcm->groupingid); $this->assertEquals($moduleinfo->visible, $dbcm->visible); $this->assertEquals($moduleinfo->completion, $dbcm->completion); $this->assertEquals($moduleinfo->completionview, $dbcm->completionview); $this->assertEquals($moduleinfo->completiongradeitemnumber, $dbcm->completiongradeitemnumber); $this->assertEquals($moduleinfo->completionexpected, $dbcm->completionexpected); $this->assertEquals($moduleinfo->availability, $dbcm->availability); $this->assertEquals($moduleinfo->showdescription, $dbcm->showdescription); $this->assertEquals($moduleinfo->groupmode, $dbcm->groupmode); $this->assertEquals($moduleinfo->cmidnumber, $dbcm->idnumber); $this->assertEquals($moduleinfo->gradecat, $gradeitem->categoryid); // Optional grade testing. if (plugin_supports('mod', $modulename, FEATURE_GRADE_HAS_GRADE, false) && !plugin_supports('mod', $modulename, FEATURE_RATE, false)) { $this->assertEquals($moduleinfo->grade, $dbmodinstance->grade); } // Some optional (but quite common) to some module. $this->assertEquals($moduleinfo->name, $dbmodinstance->name); $this->assertEquals($moduleinfo->intro, $dbmodinstance->intro); $this->assertEquals($moduleinfo->introformat, $dbmodinstance->introformat); // Test specific to the module. $modulerunasserts = $modulename . '_update_run_asserts'; $this->{$modulerunasserts}($moduleinfo, $dbmodinstance); return $moduleinfo; }
function fill_table() { global $CFG; // MDL-11679, only show 'mycourses' instead of all courses if ($courses = get_my_courses($this->user->id, 'c.sortorder ASC', 'id, shortname')) { $numusers = $this->get_numusers(false); foreach ($courses as $course) { $courselink = '<a href="' . $CFG->wwwroot . '/grade/report/user/index.php?id=' . $course->id . '">' . $course->shortname . '</a>'; // Get course grade_item $grade_item = grade_item::fetch_course_item($course->id); // Get the grade $grade = new grade_grade(array('itemid' => $grade_item->id, 'userid' => $this->user->id)); $grade->grade_item =& $grade_item; $finalgrade = $grade->finalgrade; // TODO: this DOES NOT work properly if there are any hidden grades, // rank might be wrong & totals might be different from user report!!! if ($grade->is_hidden() and !has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $course->id))) { $finalgrade = null; } $data = array($courselink, grade_format_gradevalue($finalgrade, $grade_item, true)); if (!$this->showrank) { //nothing to do } else { if (!is_null($finalgrade)) { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {$CFG->prefix}grade_grades\n WHERE finalgrade IS NOT NULL AND finalgrade > {$finalgrade}\n AND itemid = {$grade_item->id}"; $rank = count_records_sql($sql) + 1; $data[] = "{$rank}/{$numusers}"; } else { // no grade, no rank $data[] = '-'; } } $this->table->add_data($data); } return true; } else { notify(get_string('nocourses', 'grades')); return false; } }
/** * Returns the grade to display for the certificate. * * @param stdClass $certificate * @param stdClass $course * @param int $userid * @param bool $valueonly if true return only the points, %age, or letter with no prefix * @return string the grade result */ function certificate_get_grade($certificate, $course, $userid = null, $valueonly = false) { global $USER; if (empty($userid)) { $userid = $USER->id; } if ($certificate->printgrade > 0) { if ($certificate->printgrade == 1) { if ($course_item = grade_item::fetch_course_item($course->id)) { // Check we want to add a prefix to the grade. $strprefix = ''; if (!$valueonly) { $strprefix = get_string('coursegrade', 'certificate') . ': '; } $grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $userid)); $course_item->gradetype = GRADE_TYPE_VALUE; $coursegrade = new stdClass(); $coursegrade->points = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_REAL, $decimals = 2); $coursegrade->percentage = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE, $decimals = 2); $coursegrade->letter = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_LETTER, $decimals = 0); if ($certificate->gradefmt == 1) { $grade = $strprefix . $coursegrade->percentage; } else { if ($certificate->gradefmt == 2) { $grade = $strprefix . $coursegrade->points; } else { if ($certificate->gradefmt == 3) { $grade = $strprefix . $coursegrade->letter; } } } return $grade; } } else { // Print the mod grade if ($modinfo = certificate_get_mod_grade($course, $certificate->printgrade, $userid)) { // Check we want to add a prefix to the grade. $strprefix = ''; if (!$valueonly) { $strprefix = $modinfo->name . ' ' . get_string('grade', 'certificate') . ': '; } if ($certificate->gradefmt == 1) { $grade = $strprefix . $modinfo->percentage; } else { if ($certificate->gradefmt == 2) { $grade = $strprefix . $modinfo->points; } else { if ($certificate->gradefmt == 3) { $grade = $strprefix . $modinfo->letter; } } } return $grade; } } } else { if ($certificate->printgrade < 0) { // Must be a category id. if ($category_item = grade_item::fetch(array('itemtype' => 'category', 'iteminstance' => -$certificate->printgrade))) { $category_item->gradetype = GRADE_TYPE_VALUE; $grade = new grade_grade(array('itemid' => $category_item->id, 'userid' => $userid)); $category_grade = new stdClass(); $category_grade->points = grade_format_gradevalue($grade->finalgrade, $category_item, true, GRADE_DISPLAY_TYPE_REAL, $decimals = 2); $category_grade->percentage = grade_format_gradevalue($grade->finalgrade, $category_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE, $decimals = 2); $category_grade->letter = grade_format_gradevalue($grade->finalgrade, $category_item, true, GRADE_DISPLAY_TYPE_LETTER, $decimals = 0); if ($certificate->gradefmt == 1) { $formattedgrade = $category_grade->percentage; } else { if ($certificate->gradefmt == 2) { $formattedgrade = $category_grade->points; } else { if ($certificate->gradefmt == 3) { $formattedgrade = $category_grade->letter; } } } return $formattedgrade; } } } return ''; }
// } $row[] = date('d M, Y', $course->timemodified); //$modules = $mycourse->progress_modules_in_use($course->id); //$events = $mycourse->progress_event_information($modules, $course->id); //$attempts = $mycourse->progress_attempts($modules, $course->id, $events, $userid); //$percent = $mycourse->progress_percentage($events, $attempts); //$completedcount = $mycourse->progress_percentage($events, $attempts, true); //$bar = '<div class="dynamic_bar" style="width:' . $percent . '%"></div>'; //$title = $completedcount . ' out of ' . sizeof($events) . ' graded activities are completed.'; //$html = '<div class="main_list_bar" title="' . $title . '"> // <div class="task_bar">' . $bar . '</div> // <div class="percent">' . $percent . '%' . '</div> // </div>'; //$row[] = $html; $course_item = grade_item::fetch_course_item($course->id); $course_grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $userid)); $final_grade = round($course_grade->finalgrade, 2); $row[] = html_writer::tag('a', $final_grade, array('href' => $CFG->wwwroot . '/grade/report/user/index.php?id=' . $course->id)); } else if ($is_teacher) { $row[] = html_writer::tag('a', 'Submit', array('href' => $CFG->wwwroot . '/grade/report/grader/index.php?id=' . $course->id)); } $row[] = html_writer::tag('a', 'Launch', array('href' => $CFG->wwwroot . '/course/view.php?id=' . $course->id)); $data[] = $row; } $table = new html_table(); //$table->head = array('Course', 'Enrolled on', 'Progress', 'Launch Course'); $table->align = array('left', 'center', 'center', 'center'); if ($is_student) $table->size = array('25%', '23%', '13%', '12%'); if ($is_teacher)
public function fill_table() { global $CFG, $DB, $OUTPUT; // MDL-11679, only show user's courses instead of all courses if ($courses = enrol_get_users_courses($this->user->id, false, 'id, shortname, showgrades')) { $numusers = $this->get_numusers(false); foreach ($courses as $course) { if (!$course->showgrades) { continue; } $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { // The course is hidden and the user isn't allowed to see it continue; } $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext)); $courselink = html_writer::link(new moodle_url('/grade/report/user/index.php', array('id' => $course->id, 'userid' => $this->user->id)), $courseshortname); $canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext); // Get course grade_item $course_item = grade_item::fetch_course_item($course->id); // Get the stored grade $course_grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $this->user->id)); $course_grade->grade_item =& $course_item; $finalgrade = $course_grade->finalgrade; if (!$canviewhidden and !is_null($finalgrade)) { if ($course_grade->is_hidden()) { $finalgrade = null; } else { $finalgrade = $this->blank_hidden_total($course->id, $course_item, $finalgrade); } } $data = array($courselink, grade_format_gradevalue($finalgrade, $course_item, true)); if (!$this->showrank) { //nothing to do } else { if (!is_null($finalgrade)) { /// find the number of users with a higher grade /// please note this can not work if hidden grades involved :-( to be fixed in 2.0 $params = array($finalgrade, $course_item->id); $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade IS NOT NULL AND finalgrade > ?\n AND itemid = ?"; $rank = $DB->count_records_sql($sql, $params) + 1; $data[] = "{$rank}/{$numusers}"; } else { // no grade, no rank $data[] = '-'; } } $this->table->add_data($data); } return true; } else { echo $OUTPUT->notification(get_string('nocourses', 'grades')); return false; } }
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; }
/** * Fill the table for displaying. * * @param bool $activitylink If this report link to the activity report or the user report. * @param bool $studentcoursesonly Only show courses that the user is a student of. */ public function fill_table($activitylink = false, $studentcoursesonly = false) { global $CFG, $DB, $OUTPUT, $USER; if ($studentcoursesonly && count($this->studentcourseids) == 0) { return false; } // Only show user's courses instead of all courses. if ($this->courses) { $numusers = $this->get_numusers(false); foreach ($this->courses as $course) { if (!$course->showgrades) { continue; } // If we are only showing student courses and this course isn't part of the group, then move on. if ($studentcoursesonly && !isset($this->studentcourseids[$course->id])) { continue; } $coursecontext = context_course::instance($course->id); if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { // The course is hidden and the user isn't allowed to see it continue; } if (!has_capability('moodle/user:viewuseractivitiesreport', context_user::instance($this->user->id)) && ((!has_capability('moodle/grade:view', $coursecontext) || $this->user->id != $USER->id) && !has_capability('moodle/grade:viewall', $coursecontext))) { continue; } $coursename = format_string(get_course_display_name_for_list($course), true, array('context' => $coursecontext)); // Link to the activity report version of the user grade report. if ($activitylink) { $courselink = html_writer::link(new moodle_url('/course/user.php', array('mode' => 'grade', 'id' => $course->id, 'user' => $this->user->id)), $coursename); } else { $courselink = html_writer::link(new moodle_url('/grade/report/user/index.php', array('id' => $course->id, 'userid' => $this->user->id)), $coursename); } $canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext); // Get course grade_item $course_item = grade_item::fetch_course_item($course->id); // Get the stored grade $course_grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $this->user->id)); $course_grade->grade_item =& $course_item; $finalgrade = $course_grade->finalgrade; if (!$canviewhidden and !is_null($finalgrade)) { if ($course_grade->is_hidden()) { $finalgrade = null; } else { $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($course->id, $course_item, $finalgrade); // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $finalgrade = $adjustedgrade['grade']; $course_item->grademax = $adjustedgrade['grademax']; $course_item->grademin = $adjustedgrade['grademin']; } } else { // We must use the rawgrademin / rawgrademax because it can be different for // each grade_grade when items are excluded from sum of grades. if (!is_null($finalgrade)) { $course_item->grademin = $course_grade->rawgrademin; $course_item->grademax = $course_grade->rawgrademax; } } $data = array($courselink, grade_format_gradevalue($finalgrade, $course_item, true)); if (!$this->showrank['any']) { //nothing to do } else { if ($this->showrank[$course->id] && !is_null($finalgrade)) { /// find the number of users with a higher grade /// please note this can not work if hidden grades involved :-( to be fixed in 2.0 $params = array($finalgrade, $course_item->id); $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade IS NOT NULL AND finalgrade > ?\n AND itemid = ?"; $rank = $DB->count_records_sql($sql, $params) + 1; $data[] = "{$rank}/{$numusers}"; } else { // No grade, no rank. // Or this course wants rank hidden. $data[] = '-'; } } $this->table->add_data($data); } return true; } else { echo $OUTPUT->notification(get_string('notenrolled', 'grades'), 'notifymessage'); return false; } }
function get_content() { if ($this->content !== NULL) { return $this->content; } global $CFG, $COURSE, $USER; $this->content = new stdClass(); $this->content->text = ''; $this->content->footer = ''; // Does the user have the right capability? // Line 4354 of lib/weblib.php suggests that a teacher // is defined by one being able to view details of other users $context = get_context_instance(CONTEXT_COURSE, $COURSE->id); if (!has_capability('moodle/user:viewdetails', $context)) { return $this->content; } $user = CoursePrefsUser::findByUnique($USER->username); // No Atheletes in the course means this block is hidden if (get_athelete_count($COURSE) == 0) { // If in Edit mode: display the no athletes text $edit = optional_param('edit', 0, PARAM_INT); if ($edit) { $this->content->text = '<span>' . get_string('no_atheletes', 'block_athelete_reporting') . '</span>'; } return $this->content; } // Check course's grade to pass $this->courseitem = grade_item::fetch_course_item($COURSE->id); if (is_siteadmin($USER->id)) { $sections = cps_sections($COURSE); } else { if ($user) { $sections = $user->getSectionsForMoodleCourse($COURSE); } else { return $this->content; } } $out = '<div class="block_athelete_reporting_body"> <form method="POST" action="' . $CFG->wwwroot . '/blocks/athelete_reporting/report.php">'; foreach ($sections as $section) { $atheletes = get_atheletes($section); if (!$atheletes) { continue; } $id = 'block_athelete_reporting_section_' . $section->id; $out .= '<div class="block_athelete_reporting_header"> <input type="image" src="' . $CFG->pixpath . '/t/switch_plus.gif" onclick="elementToggleHide(this, true, function(el) { return document.getElementById(\'' . $id . '\');}, \'Show Section\', \'Hide Section\'); return false;"> ' . get_string('format_section', 'block_courseprefs', $section) . ' </div> <div id="' . $id . '" class="block_athelete_reporting_section_body hidden"> ' . array_reduce($atheletes, array($this, 'reduce_athelete')) . ' </div>'; } if (!empty($sections)) { $out .= ' <input type="hidden" name="id" value="' . $COURSE->id . '"> <input type="submit" value="' . get_string('report', 'block_athelete_reporting') . '"> </form>'; } $out .= '<a href="' . $CFG->wwwroot . '/blocks/student_gradeviewer/analysis.php?id=' . $COURSE->id . '">' . get_string('analysis', 'block_student_gradeviewer') . '</a>'; $user = CoursePrefsUser::findByUnique($USER->username); if (!empty($CFG->cas_email) and $user and $user->getSectionsInfoAsTeacher(false, null, true)) { $out .= '<br/><span class="athlete_small_text"> <a href="' . $CFG->wwwroot . '/blocks/student_gradeviewer/options.php?id=' . $COURSE->id . '">' . get_string('options', 'block_student_gradeviewer') . '</a> </span>'; } $out .= '</div>'; $this->content->text = $out; return $this->content; }
/** * The availability data format was changed in Moodle 2.7. This test * ensures that a Moodle 2.6 backup with this data can still be correctly * restored. */ public function test_restore_legacy_availability() { global $DB, $USER, $CFG; require_once $CFG->dirroot . '/grade/querylib.php'; require_once $CFG->libdir . '/completionlib.php'; $this->resetAfterTest(true); $this->setAdminUser(); $CFG->enableavailability = true; $CFG->enablecompletion = true; // Extract backup file. $backupid = 'abc'; $backuppath = $CFG->tempdir . '/backup/' . $backupid; check_dir_exists($backuppath); get_file_packer('application/vnd.moodle.backup')->extract_to_pathname(__DIR__ . '/fixtures/availability_26_format.mbz', $backuppath); // Do restore to new course with default settings. $generator = $this->getDataGenerator(); $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); $newcourseid = restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid); $rc = new restore_controller($backupid, $newcourseid, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, backup::TARGET_NEW_COURSE); $thrown = null; try { $this->assertTrue($rc->execute_precheck()); $rc->execute_plan(); $rc->destroy(); } catch (Exception $e) { $thrown = $e; // Because of the PHPUnit exception behaviour in this situation, we // will not see this message unless it is explicitly echoed (just // using it in a fail() call or similar will not work). echo "\n\nEXCEPTION: " . $thrown->getMessage() . '[' . $thrown->getFile() . ':' . $thrown->getLine() . "]\n\n"; } // Must set restore_controller variable to null so that php // garbage-collects it; otherwise the file will be left open and // attempts to delete it will cause a permission error on Windows // systems, breaking unit tests. $rc = null; $this->assertNull($thrown); // Get information about the resulting course and check that it is set // up correctly. $modinfo = get_fast_modinfo($newcourseid); $pages = array_values($modinfo->get_instances_of('page')); $forums = array_values($modinfo->get_instances_of('forum')); $quizzes = array_values($modinfo->get_instances_of('quiz')); $grouping = $DB->get_record('groupings', array('courseid' => $newcourseid)); // FROM date. $this->assertEquals('{"op":"&","showc":[true],"c":[{"type":"date","d":">=","t":1893456000}]}', $pages[1]->availability); // UNTIL date. $this->assertEquals('{"op":"&","showc":[false],"c":[{"type":"date","d":"<","t":1393977600}]}', $pages[2]->availability); // FROM and UNTIL. $this->assertEquals('{"op":"&","showc":[true,false],"c":[' . '{"type":"date","d":">=","t":1449705600},' . '{"type":"date","d":"<","t":1893456000}' . ']}', $pages[3]->availability); // Grade >= 75%. $grades = array_values(grade_get_grade_items_for_activity($quizzes[0], true)); $gradeid = $grades[0]->id; $coursegrade = grade_item::fetch_course_item($newcourseid); $this->assertEquals('{"op":"&","showc":[true],"c":[{"type":"grade","id":' . $gradeid . ',"min":75}]}', $pages[4]->availability); // Grade < 25%. $this->assertEquals('{"op":"&","showc":[true],"c":[{"type":"grade","id":' . $gradeid . ',"max":25}]}', $pages[5]->availability); // Grade 90-100%. $this->assertEquals('{"op":"&","showc":[true],"c":[{"type":"grade","id":' . $gradeid . ',"min":90,"max":100}]}', $pages[6]->availability); // Email contains frog. $this->assertEquals('{"op":"&","showc":[true],"c":[{"type":"profile","op":"contains","sf":"email","v":"frog"}]}', $pages[7]->availability); // Page marked complete.. $this->assertEquals('{"op":"&","showc":[true],"c":[{"type":"completion","cm":' . $pages[0]->id . ',"e":' . COMPLETION_COMPLETE . '}]}', $pages[8]->availability); // Quiz complete but failed. $this->assertEquals('{"op":"&","showc":[true],"c":[{"type":"completion","cm":' . $quizzes[0]->id . ',"e":' . COMPLETION_COMPLETE_FAIL . '}]}', $pages[9]->availability); // Quiz complete and succeeded. $this->assertEquals('{"op":"&","showc":[true],"c":[{"type":"completion","cm":' . $quizzes[0]->id . ',"e":' . COMPLETION_COMPLETE_PASS . '}]}', $pages[10]->availability); // Quiz not complete. $this->assertEquals('{"op":"&","showc":[true],"c":[{"type":"completion","cm":' . $quizzes[0]->id . ',"e":' . COMPLETION_INCOMPLETE . '}]}', $pages[11]->availability); // Grouping. $this->assertEquals('{"op":"&","showc":[false],"c":[{"type":"grouping","id":' . $grouping->id . '}]}', $pages[12]->availability); // All the options. $this->assertEquals('{"op":"&",' . '"showc":[false,true,false,true,true,true,true,true,true],' . '"c":[' . '{"type":"grouping","id":' . $grouping->id . '},' . '{"type":"date","d":">=","t":1488585600},' . '{"type":"date","d":"<","t":1709510400},' . '{"type":"profile","op":"contains","sf":"email","v":"@"},' . '{"type":"profile","op":"contains","sf":"city","v":"Frogtown"},' . '{"type":"grade","id":' . $gradeid . ',"min":30,"max":35},' . '{"type":"grade","id":' . $coursegrade->id . ',"min":5,"max":10},' . '{"type":"completion","cm":' . $pages[0]->id . ',"e":' . COMPLETION_COMPLETE . '},' . '{"type":"completion","cm":' . $quizzes[0]->id . ',"e":' . COMPLETION_INCOMPLETE . '}' . ']}', $pages[13]->availability); // Group members only forum. $this->assertEquals('{"op":"&","showc":[false],"c":[{"type":"group"}]}', $forums[0]->availability); // Section with lots of conditions. $this->assertEquals('{"op":"&","showc":[false,false,false,false],"c":[' . '{"type":"date","d":">=","t":1417737600},' . '{"type":"profile","op":"contains","sf":"email","v":"@"},' . '{"type":"grade","id":' . $gradeid . ',"min":20},' . '{"type":"completion","cm":' . $pages[0]->id . ',"e":' . COMPLETION_COMPLETE . '}]}', $modinfo->get_section_info(3)->availability); // Section with grouping. $this->assertEquals('{"op":"&","showc":[false],"c":[{"type":"grouping","id":' . $grouping->id . '}]}', $modinfo->get_section_info(4)->availability); }
/** * Updates all final grades in course. * * @param int $courseid The course ID * @param int $userid If specified try to do a quick regrading of the grades of this user only * @param object $updated_item Optional grade item to be marked for regrading * @param \core\progress\base $progress If provided, will be used to update progress on this long operation. * @return bool true if ok, array of errors if problems found. Grade item id => error message */ function grade_regrade_final_grades($courseid, $userid = null, $updated_item = null, $progress = null) { // This may take a very long time. \core_php_time_limit::raise(); $course_item = grade_item::fetch_course_item($courseid); if ($progress == null) { $progress = new \core\progress\none(); } if ($userid) { // one raw grade updated for one user if (empty($updated_item)) { print_error("cannotbenull", 'debug', '', "updated_item"); } if ($course_item->needsupdate) { $updated_item->force_regrading(); return array($course_item->id => 'Can not do fast regrading after updating of raw grades'); } } else { if (!$course_item->needsupdate) { // nothing to do :-) return true; } } // Categories might have to run some processing before we fetch the grade items. // This gives them a final opportunity to update and mark their children to be updated. // We need to work on the children categories up to the parent ones, so that, for instance, // if a category total is updated it will be reflected in the parent category. $cats = grade_category::fetch_all(array('courseid' => $courseid)); $flatcattree = array(); foreach ($cats as $cat) { if (!isset($flatcattree[$cat->depth])) { $flatcattree[$cat->depth] = array(); } $flatcattree[$cat->depth][] = $cat; } krsort($flatcattree); foreach ($flatcattree as $depth => $cats) { foreach ($cats as $cat) { $cat->pre_regrade_final_grades(); } } $progresstotal = 0; $progresscurrent = 0; $grade_items = grade_item::fetch_all(array('courseid' => $courseid)); $depends_on = array(); foreach ($grade_items as $gid => $gitem) { if ((!empty($updated_item) and $updated_item->id == $gid) || $gitem->is_course_item() || $gitem->is_category_item() || $gitem->is_calculated()) { $grade_items[$gid]->needsupdate = 1; } // We load all dependencies of these items later we can discard some grade_items based on this. if ($grade_items[$gid]->needsupdate) { $depends_on[$gid] = $grade_items[$gid]->depends_on(); $progresstotal++; } } $progress->start_progress('regrade_course', $progresstotal); $errors = array(); $finalids = array(); $updatedids = array(); $gids = array_keys($grade_items); $failed = 0; while (count($finalids) < count($gids)) { // work until all grades are final or error found $count = 0; foreach ($gids as $gid) { if (in_array($gid, $finalids)) { continue; // already final } if (!$grade_items[$gid]->needsupdate) { $finalids[] = $gid; // we can make it final - does not need update continue; } $thisprogress = $progresstotal; foreach ($grade_items as $item) { if ($item->needsupdate) { $thisprogress--; } } // Clip between $progresscurrent and $progresstotal. $thisprogress = max(min($thisprogress, $progresstotal), $progresscurrent); $progress->progress($thisprogress); $progresscurrent = $thisprogress; foreach ($depends_on[$gid] as $did) { if (!in_array($did, $finalids)) { // This item depends on something that is not yet in finals array. continue 2; } } // If this grade item has no dependancy with any updated item at all, then remove it from being recalculated. // When we get here, all of this grade item's decendents are marked as final so they would be marked as updated too // if they would have been regraded. We don't need to regrade items which dependants (not only the direct ones // but any dependant in the cascade) have not been updated. // If $updated_item was specified we discard the grade items that do not depend on it or on any grade item that // depend on $updated_item. // Here we check to see if the direct decendants are marked as updated. if (!empty($updated_item) && $gid != $updated_item->id && !in_array($updated_item->id, $depends_on[$gid])) { // We need to ensure that none of this item's dependencies have been updated. // If we find that one of the direct decendants of this grade item is marked as updated then this // grade item needs to be recalculated and marked as updated. // Being marked as updated is done further down in the code. $updateddependencies = false; foreach ($depends_on[$gid] as $dependency) { if (in_array($dependency, $updatedids)) { $updateddependencies = true; break; } } if ($updateddependencies === false) { // If no direct descendants are marked as updated, then we don't need to update this grade item. We then mark it // as final. $finalids[] = $gid; continue; } } // Let's update, calculate or aggregate. $result = $grade_items[$gid]->regrade_final_grades($userid); if ($result === true) { // We should only update the database if we regraded all users. if (empty($userid)) { $grade_items[$gid]->regrading_finished(); // Do the locktime item locking. $grade_items[$gid]->check_locktime(); } else { $grade_items[$gid]->needsupdate = 0; } $count++; $finalids[] = $gid; $updatedids[] = $gid; } else { $grade_items[$gid]->force_regrading(); $errors[$gid] = $result; } } if ($count == 0) { $failed++; } else { $failed = 0; } if ($failed > 1) { foreach ($gids as $gid) { if (in_array($gid, $finalids)) { continue; // this one is ok } $grade_items[$gid]->force_regrading(); $errors[$grade_items[$gid]->id] = get_string('errorcalculationbroken', 'grades'); } break; // Found error. } } $progress->end_progress(); if (count($errors) == 0) { if (empty($userid)) { // do the locktime locking of grades, but only when doing full regrading grade_grade::check_locktime_all($gids); } return true; } else { return $errors; } }
/** * Returns the aggregated or calculated course grade for the given user(s). * @public * @param int $userid * @param int $courseid optional id of course or array of ids, empty means all uses courses (returns array if not present) * @return mixed grade info or grades array including item info, false if error */ function grade_get_course_grade($userid, $courseid_or_ids = null) { if (!is_array($courseid_or_ids)) { if (empty($courseid_or_ids)) { if (!($courses = enrol_get_users_courses($userid))) { return false; } $courseids = array_keys($courses); return grade_get_course_grade($userid, $courseids); } if (!is_numeric($courseid_or_ids)) { return false; } if (!($grades = grade_get_course_grade($userid, array($courseid_or_ids)))) { return false; } else { // only one grade - not array $grade = reset($grades); return $grade; } } foreach ($courseid_or_ids as $courseid) { $grade_item = grade_item::fetch_course_item($courseid); $course_items[$grade_item->courseid] = $grade_item; } $grades = array(); foreach ($course_items as $grade_item) { if ($grade_item->needsupdate) { grade_regrade_final_grades($courseid); } $item = new stdClass(); $item->scaleid = $grade_item->scaleid; $item->name = $grade_item->get_name(); $item->grademin = $grade_item->grademin; $item->grademax = $grade_item->grademax; $item->gradepass = $grade_item->gradepass; $item->locked = $grade_item->is_locked(); $item->hidden = $grade_item->is_hidden(); switch ($grade_item->gradetype) { case GRADE_TYPE_NONE: continue; case GRADE_TYPE_VALUE: $item->scaleid = 0; break; case GRADE_TYPE_TEXT: $item->scaleid = 0; $item->grademin = 0; $item->grademax = 0; $item->gradepass = 0; break; } $grade_grade = new grade_grade(array('userid' => $userid, 'itemid' => $grade_item->id)); $grade_grade->grade_item =& $grade_item; $grade = new stdClass(); $grade->grade = $grade_grade->finalgrade; $grade->locked = $grade_grade->is_locked(); $grade->hidden = $grade_grade->is_hidden(); $grade->overridden = $grade_grade->overridden; $grade->feedback = $grade_grade->feedback; $grade->feedbackformat = $grade_grade->feedbackformat; $grade->usermodified = $grade_grade->usermodified; $grade->dategraded = $grade_grade->get_dategraded(); $grade->item = $item; // create text representation of grade if ($grade_item->needsupdate) { $grade->grade = false; $grade->str_grade = get_string('error'); $grade->str_long_grade = $grade->str_grade; } else { if (is_null($grade->grade)) { $grade->str_grade = '-'; $grade->str_long_grade = $grade->str_grade; } else { $grade->str_grade = grade_format_gradevalue($grade->grade, $grade_item); if ($grade_item->gradetype == GRADE_TYPE_SCALE or $grade_item->get_displaytype() != GRADE_DISPLAY_TYPE_REAL) { $grade->str_long_grade = $grade->str_grade; } else { $a = new stdClass(); $a->grade = $grade->str_grade; $a->max = grade_format_gradevalue($grade_item->grademax, $grade_item); $grade->str_long_grade = get_string('gradelong', 'grades', $a); } } } // create html representation of feedback if (is_null($grade->feedback)) { $grade->str_feedback = ''; } else { $grade->str_feedback = format_text($grade->feedback, $grade->feedbackformat); } $grades[$grade_item->courseid] = $grade; } return $grades; }
public function fill_table() { global $CFG, $DB, $OUTPUT; // Only show user's courses instead of all courses. if ($this->courses) { $numusers = $this->get_numusers(false); foreach ($this->courses as $course) { if (!$course->showgrades) { continue; } $coursecontext = context_course::instance($course->id); if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { // The course is hidden and the user isn't allowed to see it continue; } $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext)); $courselink = html_writer::link(new moodle_url('/grade/report/user/index.php', array('id' => $course->id, 'userid' => $this->user->id)), $courseshortname); $canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext); // Get course grade_item $course_item = grade_item::fetch_course_item($course->id); // Get the stored grade $course_grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $this->user->id)); $course_grade->grade_item =& $course_item; $finalgrade = $course_grade->finalgrade; if (!$canviewhidden and !is_null($finalgrade)) { if ($course_grade->is_hidden()) { $finalgrade = null; } else { $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($course->id, $course_item, $finalgrade); // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $finalgrade = $adjustedgrade['grade']; $course_item->grademax = $adjustedgrade['grademax']; $course_item->grademin = $adjustedgrade['grademin']; } } $data = array($courselink, grade_format_gradevalue($finalgrade, $course_item, true)); if (!$this->showrank['any']) { //nothing to do } else { if ($this->showrank[$course->id] && !is_null($finalgrade)) { /// find the number of users with a higher grade /// please note this can not work if hidden grades involved :-( to be fixed in 2.0 $params = array($finalgrade, $course_item->id); $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade IS NOT NULL AND finalgrade > ?\n AND itemid = ?"; $rank = $DB->count_records_sql($sql, $params) + 1; $data[] = "{$rank}/{$numusers}"; } else { // No grade, no rank. // Or this course wants rank hidden. $data[] = '-'; } } $this->table->add_data($data); } return true; } else { echo $OUTPUT->notification(get_string('nocourses', 'grades')); return false; } }
function backup_gradebook_category_info($bf, $preferences) { global $CFG; $status = true; // get grade categories in proper order - specified in category grade items $course_item = grade_item::fetch_course_item($preferences->backup_course); $grade_categories = get_records_sql("SELECT gc.*, gi.sortorder\n FROM {$CFG->prefix}grade_categories gc\n JOIN {$CFG->prefix}grade_items gi\n ON (gi.iteminstance = gc.id)\n WHERE gc.courseid = {$preferences->backup_course}\n AND (gi.itemtype='course' OR gi.itemtype='category')\n ORDER BY gi.sortorder ASC"); if ($grade_categories) { //Begin grade_categories tag fwrite($bf, start_tag("GRADE_CATEGORIES", 3, true)); //Iterate for each category foreach ($grade_categories as $grade_category) { //Begin grade_category fwrite($bf, start_tag("GRADE_CATEGORY", 4, true)); //Output individual fields fwrite($bf, full_tag("ID", 5, false, $grade_category->id)); // not keeping path and depth because they can be derived fwrite($bf, full_tag("PARENT", 5, false, $grade_category->parent)); fwrite($bf, full_tag("FULLNAME", 5, false, $grade_category->fullname)); fwrite($bf, full_tag("AGGREGATION", 5, false, $grade_category->aggregation)); fwrite($bf, full_tag("KEEPHIGH", 5, false, $grade_category->keephigh)); fwrite($bf, full_tag("DROPLOW", 5, false, $grade_category->droplow)); fwrite($bf, full_tag("AGGREGATEONLYGRADED", 5, false, $grade_category->aggregateonlygraded)); fwrite($bf, full_tag("AGGREGATEOUTCOMES", 5, false, $grade_category->aggregateoutcomes)); fwrite($bf, full_tag("AGGREGATESUBCATS", 5, false, $grade_category->aggregatesubcats)); fwrite($bf, full_tag("TIMECREATED", 5, false, $grade_category->timecreated)); fwrite($bf, full_tag("TIMEMODIFIED", 5, false, $grade_category->timemodified)); //End grade_category fwrite($bf, end_tag("GRADE_CATEGORY", 4, true)); } //End grade_categories tag $status = fwrite($bf, end_tag("GRADE_CATEGORIES", 3, true)); } return $status; }
/** * Updates raw grade value for given user, this is a only way to update raw * grades from external source (modules, etc.), * because it logs the change in history table and deals with final grade recalculation. * * @param int $userid the graded user * @param mixed $rawgrade float value of raw grade - false means do not change * @param string $howmodified modification source * @param string $note optional note * @param mixed $feedback teachers feedback as string - false means do not change * @param int $feedbackformat * @return boolean success */ function update_raw_grade($userid, $rawgrade = false, $source = NULL, $note = NULL, $feedback = false, $feedbackformat = FORMAT_MOODLE, $usermodified = null) { global $USER; if (empty($usermodified)) { $usermodified = $USER->id; } $result = true; // calculated grades can not be updated; course and category can not be updated because they are aggregated if ($this->is_calculated() or $this->is_outcome_item() or !$this->is_normal_item() or $this->gradetype == GRADE_TYPE_NONE or $this->is_locked()) { return false; } $grade = new grade_grade(array('itemid' => $this->id, 'userid' => $userid)); $grade->grade_item =& $this; // prevent db fetching of this grade_item $grade->usermodified = $usermodified; if ($grade->is_locked()) { // do not update locked grades at all return false; } $locktime = $grade->get_locktime(); if ($locktime and $locktime < time()) { // do not update grades that should be already locked and force regrade $this->force_regrading(); return false; } $oldgrade = new object(); $oldgrade->finalgrade = $grade->finalgrade; $oldgrade->rawgrade = $grade->rawgrade; $oldgrade->rawgrademin = $grade->rawgrademin; $oldgrade->rawgrademax = $grade->rawgrademax; $oldgrade->rawscaleid = $grade->rawscaleid; $oldgrade->feedback = $grade->feedback; $oldgrade->feedbackformat = $grade->feedbackformat; // fist copy current grademin/max and scale $grade->rawgrademin = $this->grademin; $grade->rawgrademax = $this->grademax; $grade->rawscaleid = $this->scaleid; // change raw grade? if ($rawgrade !== false) { $grade->rawgrade = $rawgrade; } // do we have comment from teacher? if ($feedback !== false) { $grade->feedback = $feedback; $grade->feedbackformat = $feedbackformat; } if (empty($grade->id)) { $result = (bool) $grade->insert($source); } else { if ($grade->finalgrade !== $oldgrade->finalgrade or $grade->rawgrade !== $oldgrade->rawgrade or $grade->rawgrademin !== $oldgrade->rawgrademin or $grade->rawgrademax !== $oldgrade->rawgrademax or $grade->rawscaleid !== $oldgrade->rawscaleid or $grade->feedback !== $oldgrade->feedback or $grade->feedbackformat !== $oldgrade->feedbackformat) { $result = $grade->update($source); } } if (!$result) { // something went wrong - better force final grade recalculation $this->force_regrading(); } else { if (!$this->needsupdate) { $course_item = grade_item::fetch_course_item($this->courseid); if (!$course_item->needsupdate) { if (!grade_regrade_final_grades($this->courseid, $userid, $this)) { $this->force_regrading(); } } else { $this->force_regrading(); } } } return $result; }
/** * Updates all final grades in course. * * @param int $courseid * @param int $userid if specified, try to do a quick regrading of grades of this user only * @param object $updated_item the item in which * @return boolean true if ok, array of errors if problems found (item id is used as key) */ function grade_regrade_final_grades($courseid, $userid = null, $updated_item = null) { $course_item = grade_item::fetch_course_item($courseid); if ($userid) { // one raw grade updated for one user if (empty($updated_item)) { error("updated_item_id can not be null!"); } if ($course_item->needsupdate) { $updated_item->force_regrading(); return 'Can not do fast regrading after updating of raw grades'; } } else { if (!$course_item->needsupdate) { // nothing to do :-) return true; } } $grade_items = grade_item::fetch_all(array('courseid' => $courseid)); $depends_on = array(); // first mark all category and calculated items as needing regrading // this is slower, but 100% accurate foreach ($grade_items as $gid => $gitem) { if (!empty($updated_item) and $updated_item->id == $gid) { $grade_items[$gid]->needsupdate = 1; } else { if ($gitem->is_course_item() or $gitem->is_category_item() or $gitem->is_calculated()) { $grade_items[$gid]->needsupdate = 1; } } // construct depends_on lookup array $depends_on[$gid] = $grade_items[$gid]->depends_on(); } $errors = array(); $finalids = array(); $gids = array_keys($grade_items); $failed = 0; while (count($finalids) < count($gids)) { // work until all grades are final or error found $count = 0; foreach ($gids as $gid) { if (in_array($gid, $finalids)) { continue; // already final } if (!$grade_items[$gid]->needsupdate) { $finalids[] = $gid; // we can make it final - does not need update continue; } $doupdate = true; foreach ($depends_on[$gid] as $did) { if (!in_array($did, $finalids)) { $doupdate = false; continue; // this item depends on something that is not yet in finals array } } //oki - let's update, calculate or aggregate :-) if ($doupdate) { $result = $grade_items[$gid]->regrade_final_grades($userid); if ($result === true) { $grade_items[$gid]->regrading_finished(); $grade_items[$gid]->check_locktime(); // do the locktime item locking $count++; $finalids[] = $gid; } else { $grade_items[$gid]->force_regrading(); $errors[$gid] = $result; } } } if ($count == 0) { $failed++; } else { $failed = 0; } if ($failed > 1) { foreach ($gids as $gid) { if (in_array($gid, $finalids)) { continue; // this one is ok } $grade_items[$gid]->force_regrading(); $errors[$grade_items[$gid]->id] = 'Probably circular reference or broken calculation formula'; // TODO: localize } break; // oki, found error } } if (count($errors) == 0) { if (empty($userid)) { // do the locktime locking of grades, but only when doing full regrading grade_grade::check_locktime_all($gids); } return true; } else { return $errors; } }
/** * Returns the grade to display for the certificate. * * @param int $userid * @return string the grade result */ protected function get_grade($userid = null) { global $USER, $DB; if (empty($userid)) { $userid = $USER->id; } //If certgrade = 0 return nothing if (empty($this->get_instance()->certgrade)) { //No grade return ''; } switch ($this->get_instance()->certgrade) { case self::COURSE_GRADE: //Course grade if ($course_item = grade_item::fetch_course_item($this->get_course()->id)) { $grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $userid)); $course_item->gradetype = GRADE_TYPE_VALUE; $coursegrade = new stdClass(); // String used $coursegrade->points = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_REAL, $decimals = 2); $coursegrade->percentage = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE, $decimals = 2); $coursegrade->letter = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_LETTER, $decimals = 0); } break; default: // Module grade //Get grade from a specific module, stored at certgrade if ($modinfo = $this->get_mod_grade($this->get_instance()->certgrade, $userid)) { // String used $coursegrade = new stdClass(); $coursegrade->points = $modinfo->points; $coursegrade->percentage = $modinfo->percentage; $coursegrade->letter = $modinfo->letter; break; } } return $this->get_formated_grade($coursegrade); }
public function fill_table() { global $CFG, $DB; // MDL-11679, only show 'mycourses' instead of all courses if ($courses = get_my_courses($this->user->id, 'c.sortorder ASC', 'id, shortname, showgrades')) { $numusers = $this->get_numusers(false); foreach ($courses as $course) { if (!$course->showgrades) { continue; } $courselink = '<a href="' . $CFG->wwwroot . '/grade/report/user/index.php?id=' . $course->id . '">' . $course->shortname . '</a>'; $canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $course->id)); // Get course grade_item $course_item = grade_item::fetch_course_item($course->id); // Get the stored grade $course_grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $this->user->id)); $course_grade->grade_item =& $course_item; $finalgrade = $course_grade->finalgrade; if (!$canviewhidden and !is_null($finalgrade)) { if ($course_grade->is_hidden()) { $finalgrade = null; } else { // This is a really ugly hack, it will be fixed in 2.0 $items = grade_item::fetch_all(array('courseid' => $course->id)); $grades = array(); $sql = "SELECT g.*\n FROM {grade_grades} g\n JOIN {grade_items} gi ON gi.id = g.itemid\n WHERE g.userid = ? AND gi.courseid = ?"; if ($gradesrecords = $DB->get_records_sql($sql, array($this->user->id, $course->id))) { 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 (array_key_exists($course_item->id, $hiding_affected['altered'])) { $finalgrade = $hiding_affected['altered'][$course_item->id]; } else { if (!empty($hiding_affected['unknown'][$course_item->id])) { $finalgrade = null; } } unset($hiding_affected); unset($grades); unset($items); } } $data = array($courselink, grade_format_gradevalue($finalgrade, $course_item, true)); if (!$this->showrank) { //nothing to do } else { if (!is_null($finalgrade)) { /// find the number of users with a higher grade /// please note this can not work if hidden grades involved :-( to be fixed in 2.0 $params = array($finalgrade, $course_item->id); $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade IS NOT NULL AND finalgrade > ?\n AND itemid = ?"; $rank = $DB->count_records_sql($sql, $params) + 1; $data[] = "{$rank}/{$numusers}"; } else { // no grade, no rank $data[] = '-'; } } $this->table->add_data($data); } return true; } else { notify(get_string('nocourses', 'grades')); return false; } }
/** * Fetches grade_item info and grades from the referenced course * * Returned structure is * object( * ->grades = array[userid] of object(->userid ->rawgrade ->feedback ->feedbackformat) * ->grademax * ->grademin * ->itemname * ... * ) * * @access public * @param int $subcourseid ID of subcourse instance * @param int $refcourseid ID of referenced course * @param bool|\boolan $gradeitemonly If true, fetch only grade item info without grades * @return object Object containing grades array and gradeitem info */ function subcourse_fetch_refgrades($subcourseid, $refcourseid, $gradeitemonly = false) { global $CFG; $fetchedfields = subcourse_get_fetched_item_fields(); if (!function_exists('grade_update')) { //workaround for buggy PHP versions require_once $CFG->libdir . '/gradelib.php'; } $return = new stdClass(); $return->grades = array(); if (empty($refcourseid)) { return false; } $refgradeitem = grade_item::fetch_course_item($refcourseid); // get grade_item info foreach ($fetchedfields as $property) { if (!empty($refgradeitem->{$property})) { $return->{$property} = $refgradeitem->{$property}; } else { $return->{$property} = null; } } // if the remote grade_item is non-global scale, do not fetch grades - they can't be used if ($refgradeitem->gradetype == GRADE_TYPE_SCALE && !subcourse_is_global_scale($refgradeitem->scaleid)) { $gradeitemonly = true; $return->localremotescale = true; } if (!$gradeitemonly) { // get grades $cm = get_coursemodule_from_instance("subcourse", $subcourseid); $context = get_context_instance(CONTEXT_MODULE, $cm->id); $users = get_users_by_capability($context, 'mod/subcourse:begraded', 'u.id,u.lastname', 'u.lastname', '', '', '', '', false, true); foreach ($users as $user) { $grade = new grade_grade(array('itemid' => $refgradeitem->id, 'userid' => $user->id)); $grade->grade_item =& $refgradeitem; $return->grades[$user->id]->userid = $user->id; $return->grades[$user->id]->rawgrade = $grade->finalgrade; $return->grades[$user->id]->feedback = $grade->feedback; $return->grades[$user->id]->feedbackformat = $grade->feedbackformat; } } return $return; }
/** * Initialise the iterator * * @return boolean success */ public function init() { global $CFG, $DB; $this->close(); export_verify_grades($this->course->id); $course_item = grade_item::fetch_course_item($this->course->id); if ($course_item->needsupdate) { // Can not calculate all final grades - sorry. return false; } $coursecontext = context_course::instance($this->course->id); list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($coursecontext->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx'); list($gradebookroles_sql, $params) = $DB->get_in_or_equal(explode(',', $CFG->gradebookroles), SQL_PARAMS_NAMED, 'grbr'); list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, '', 0, $this->onlyactive); $params = array_merge($params, $enrolledparams, $relatedctxparams); if ($this->groupid) { $groupsql = "INNER JOIN {groups_members} gm ON gm.userid = u.id"; $groupwheresql = "AND gm.groupid = :groupid"; // $params contents: gradebookroles $params['groupid'] = $this->groupid; } else { $groupsql = ""; $groupwheresql = ""; } if (empty($this->sortfield1)) { // We must do some sorting even if not specified. $ofields = ", u.id AS usrt"; $order = "usrt ASC"; } else { $ofields = ", u.{$this->sortfield1} AS usrt1"; $order = "usrt1 {$this->sortorder1}"; if (!empty($this->sortfield2)) { $ofields .= ", u.{$this->sortfield2} AS usrt2"; $order .= ", usrt2 {$this->sortorder2}"; } if ($this->sortfield1 != 'id' and $this->sortfield2 != 'id') { // User order MUST be the same in both queries, // must include the only unique user->id if not already present. $ofields .= ", u.id AS usrt"; $order .= ", usrt ASC"; } } $userfields = 'u.*'; $customfieldssql = ''; if ($this->allowusercustomfields && !empty($CFG->grade_export_customprofilefields)) { $customfieldscount = 0; $customfieldsarray = grade_helper::get_user_profile_fields($this->course->id, $this->allowusercustomfields); foreach ($customfieldsarray as $field) { if (!empty($field->customid)) { $customfieldssql .= "\n LEFT JOIN (SELECT * FROM {user_info_data}\n WHERE fieldid = :cf{$customfieldscount}) cf{$customfieldscount}\n ON u.id = cf{$customfieldscount}.userid"; $userfields .= ", cf{$customfieldscount}.data AS customfield_{$field->shortname}"; $params['cf' . $customfieldscount] = $field->customid; $customfieldscount++; } } } $users_sql = "SELECT {$userfields} {$ofields}\n FROM {user} u\n JOIN ({$enrolledsql}) je ON je.id = u.id\n {$groupsql} {$customfieldssql}\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookroles_sql}\n AND ra.contextid {$relatedctxsql}\n ) rainner ON rainner.userid = u.id\n WHERE u.deleted = 0\n {$groupwheresql}\n ORDER BY {$order}"; $this->users_rs = $DB->get_recordset_sql($users_sql, $params); if (!$this->onlyactive) { $context = context_course::instance($this->course->id); $this->suspendedusers = get_suspended_userids($context); } else { $this->suspendedusers = array(); } if (!empty($this->grade_items)) { $itemids = array_keys($this->grade_items); list($itemidsql, $grades_params) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED, 'items'); $params = array_merge($params, $grades_params); $grades_sql = "SELECT g.* {$ofields}\n FROM {grade_grades} g\n JOIN {user} u ON g.userid = u.id\n JOIN ({$enrolledsql}) je ON je.id = u.id\n {$groupsql}\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookroles_sql}\n AND ra.contextid {$relatedctxsql}\n ) rainner ON rainner.userid = u.id\n WHERE u.deleted = 0\n AND g.itemid {$itemidsql}\n {$groupwheresql}\n ORDER BY {$order}, g.itemid ASC"; $this->grades_rs = $DB->get_recordset_sql($grades_sql, $params); } else { $this->grades_rs = false; } return true; }
/** * Set up the courses grades data for the report. * * @param bool $studentcoursesonly Only show courses that the user is a student of. * @return array of course grades information */ public function setup_courses_data($studentcoursesonly) { global $USER, $DB; $coursesdata = array(); $numusers = $this->get_numusers(false); foreach ($this->courses as $course) { if (!$course->showgrades) { continue; } // If we are only showing student courses and this course isn't part of the group, then move on. if ($studentcoursesonly && !isset($this->studentcourseids[$course->id])) { continue; } $coursecontext = context_course::instance($course->id); if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { // The course is hidden and the user isn't allowed to see it. continue; } if (!has_capability('moodle/user:viewuseractivitiesreport', context_user::instance($this->user->id)) && ((!has_capability('moodle/grade:view', $coursecontext) || $this->user->id != $USER->id) && !has_capability('moodle/grade:viewall', $coursecontext))) { continue; } $coursesdata[$course->id]['course'] = $course; $coursesdata[$course->id]['context'] = $coursecontext; $canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext); // Get course grade_item. $courseitem = grade_item::fetch_course_item($course->id); // Get the stored grade. $coursegrade = new grade_grade(array('itemid' => $courseitem->id, 'userid' => $this->user->id)); $coursegrade->grade_item =& $courseitem; $finalgrade = $coursegrade->finalgrade; if (!$canviewhidden and !is_null($finalgrade)) { if ($coursegrade->is_hidden()) { $finalgrade = null; } else { $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($course->id, $courseitem, $finalgrade); // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $finalgrade = $adjustedgrade['grade']; $courseitem->grademax = $adjustedgrade['grademax']; $courseitem->grademin = $adjustedgrade['grademin']; } } else { // We must use the specific max/min because it can be different for // each grade_grade when items are excluded from sum of grades. if (!is_null($finalgrade)) { $courseitem->grademin = $coursegrade->get_grade_min(); $courseitem->grademax = $coursegrade->get_grade_max(); } } $coursesdata[$course->id]['finalgrade'] = $finalgrade; $coursesdata[$course->id]['courseitem'] = $courseitem; if ($this->showrank['any'] && $this->showrank[$course->id] && !is_null($finalgrade)) { // Find the number of users with a higher grade. // Please note this can not work if hidden grades involved :-( to be fixed in 2.0. $params = array($finalgrade, $courseitem->id); $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade IS NOT NULL AND finalgrade > ?\n AND itemid = ?"; $rank = $DB->count_records_sql($sql, $params) + 1; $coursesdata[$course->id]['rank'] = $rank; $coursesdata[$course->id]['numusers'] = $numusers; } } return $coursesdata; }