/** * Given the grade tree and an array of element ids (e.g. c15, i42), and expecting the 'moveafter' URL param, * moves the selected items to the requested location. Then redirects the user to the given $returnurl * * @param object $gtree The grade tree (a recursive representation of the grade categories and grade items) * @param array $eids * @param string $returnurl */ function move_elements($eids, $returnurl) { $moveafter = required_param('moveafter', PARAM_INT); if (!is_array($eids)) { $eids = array($eids); } if (!($after_el = $this->gtree->locate_element("cg{$moveafter}"))) { print_error('invalidelementid', '', $returnurl); } $after = $after_el['object']; $parent = $after; $sortorder = $after->get_sortorder(); foreach ($eids as $eid) { if (!($element = $this->gtree->locate_element($eid))) { print_error('invalidelementid', '', $returnurl); } $object = $element['object']; $object->set_parent($parent->id); $object->move_after_sortorder($sortorder); $sortorder++; } redirect($returnurl, '', 0); }
$data->aggregation = $aggregationtype; grade_category::set_properties($grade_category, $data); $grade_category->update(); grade_regrade_final_grades($courseid); } //first make sure we have proper final grades - we need it for locking changes $normalisationmessage = null; $originalweights = grade_helper::fetch_all_natural_weights_for_course($courseid); grade_regrade_final_grades($courseid); $alteredweights = grade_helper::fetch_all_natural_weights_for_course($courseid); if (array_diff($originalweights, $alteredweights)) { $normalisationmessage = get_string('weightsadjusted', 'grades'); } // get the grading tree object // note: total must be first for moving to work correctly, if you want it last moving code must be rewritten! $gtree = new grade_tree($courseid, false, false); if (empty($eid)) { $element = null; $object = null; } else { if (!($element = $gtree->locate_element($eid))) { print_error('invalidelementid', '', $returnurl); } $object = $element['object']; } $switch = grade_get_setting($course->id, 'aggregationposition', $CFG->grade_aggregationposition); $strgrades = get_string('grades'); $strgraderreport = get_string('graderreport', 'grades'); $moving = false; $movingeid = false; if ($action == 'moveselect') {
/** * Static recursive helper - add colspan information into categories * * @param array &$element The seed of the recursion * * @return int */ public function inject_colspans(&$element) { if (empty($element['children'])) { return 1; } $count = 0; foreach ($element['children'] as $key => $child) { $count += grade_tree::inject_colspans($element['children'][$key]); } $element['colspan'] = $count; return $count; }
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]); } } }
/** * Static recursive helper - add colspan information into categories * * @param array &$element The seed of the recursion * * @return int */ public function inject_colspans(&$element) { if (empty($element['children'])) { return 1; } $count = 0; foreach ($element['children'] as $key => $child) { if (!self::can_output_item($child)) { continue; } $count += grade_tree::inject_colspans($element['children'][$key]); } $element['colspan'] = $count; return $count; }
require_once $CFG->dirroot . '/grade/lib.php'; $courseid = required_param('id', PARAM_INT); $action = required_param('action', PARAM_ALPHA); $eid = required_param('eid', PARAM_ALPHANUM); $PAGE->set_url('/grade/edit/tree/action.php', array('id' => $courseid, 'action' => $action, 'eid' => $eid)); /// Make sure they can even access this course if (!($course = $DB->get_record('course', array('id' => $courseid)))) { print_error('nocourseid'); } require_login($course); $context = context_course::instance($course->id); // default return url $gpr = new grade_plugin_return(); $returnurl = $gpr->get_return_url($CFG->wwwroot . '/grade/edit/tree/index.php?id=' . $course->id); // get the grading tree object $gtree = new grade_tree($courseid, false, false); // what are we working with? if (!($element = $gtree->locate_element($eid))) { print_error('invalidelementid', '', $returnurl); } $object = $element['object']; $type = $element['type']; switch ($action) { case 'hide': if ($eid and confirm_sesskey()) { if (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:hide', $context)) { print_error('nopermissiontohide', '', $returnurl); } if ($type == 'grade' and empty($object->id)) { $object->insert(); }
$eid = optional_param('eid', 0, PARAM_ALPHANUM); /// Make sure they can even access this course if (!($course = get_record('course', 'id', $courseid))) { print_error('nocourseid'); } require_login($course); $context = get_context_instance(CONTEXT_COURSE, $course->id); require_capability('moodle/grade:manage', $context); /// return tracking object $gpr = new grade_plugin_return(array('type' => 'edit', 'plugin' => 'tree', 'courseid' => $courseid)); $returnurl = $gpr->get_return_url(null); //first make sure we have proper final grades - we need it for locking changes grade_regrade_final_grades($courseid); // get the grading tree object // note: total must be first for moving to work correctly, if you want it last moving code must be rewritten! $gtree = new grade_tree($courseid, false, false); if (empty($eid)) { $element = null; $object = null; } else { if (!($element = $gtree->locate_element($eid))) { error('Incorrect element id!', $returnurl); } $object = $element['object']; } $switch = grade_report::get_pref('aggregationposition'); $strgrades = get_string('grades'); $strgraderreport = get_string('graderreport', 'grades'); $strcategoriesedit = get_string('categoriesedit', 'grades'); $strcategoriesanditems = get_string('categoriesanditems', 'grades'); $navigation = grade_build_nav(__FILE__, $strcategoriesanditems, array('courseid' => $courseid));
/** * Builds and returns the rows that will make up the right part of the grader report * @param boolean $displayaverages whether to display average rows in the table * @return array Array of html_table_row objects */ public function get_right_rows($displayaverages) { global $CFG, $USER, $OUTPUT, $DB, $PAGE; $rows = array(); $this->rowcount = 0; $numrows = count($this->gtree->get_levels()); $numusers = count($this->users); $gradetabindex = 1; $columnstounset = array(); $strgrade = $this->get_lang_string('grade'); $strfeedback = $this->get_lang_string("feedback"); $arrows = $this->get_sort_arrows(); $jsarguments = array('cfg' => array('ajaxenabled' => false), 'items' => array(), 'users' => array(), 'feedback' => array(), 'grades' => array()); $jsscales = array(); // Get preferences once. $showactivityicons = $this->get_pref('showactivityicons'); $quickgrading = $this->get_pref('quickgrading'); $showquickfeedback = $this->get_pref('showquickfeedback'); $enableajax = $this->get_pref('enableajax'); $showanalysisicon = $this->get_pref('showanalysisicon'); // Get strings which are re-used inside the loop. $strftimedatetimeshort = get_string('strftimedatetimeshort'); $strexcludedgrades = get_string('excluded', 'grades'); $strerror = get_string('error'); foreach ($this->gtree->get_levels() as $key => $row) { $headingrow = new html_table_row(); $headingrow->attributes['class'] = 'heading_name_row'; foreach ($row as $columnkey => $element) { $sortlink = clone $this->baseurl; if (isset($element['object']->id)) { $sortlink->param('sortitemid', $element['object']->id); } $eid = $element['eid']; $object = $element['object']; $type = $element['type']; $categorystate = @$element['categorystate']; if (!empty($element['colspan'])) { $colspan = $element['colspan']; } else { $colspan = 1; } if (!empty($element['depth'])) { $catlevel = 'catlevel' . $element['depth']; } else { $catlevel = ''; } // Element is a filler if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') { $fillercell = new html_table_cell(); $fillercell->attributes['class'] = $type . ' ' . $catlevel; $fillercell->colspan = $colspan; $fillercell->text = ' '; // This is a filler cell; don't use a <th>, it'll confuse screen readers. $fillercell->header = false; $headingrow->cells[] = $fillercell; } else { if ($type == 'category') { // Make sure the grade category has a grade total or at least has child grade items. if (grade_tree::can_output_item($element)) { // Element is a category. $categorycell = new html_table_cell(); $categorycell->attributes['class'] = 'category ' . $catlevel; $categorycell->colspan = $colspan; $categorycell->text = $this->get_course_header($element); $categorycell->header = true; $categorycell->scope = 'col'; // Print icons. if ($USER->gradeediting[$this->courseid]) { $categorycell->text .= $this->get_icons($element); } $headingrow->cells[] = $categorycell; } } else { // Element is a grade_item if ($element['object']->id == $this->sortitemid) { if ($this->sortorder == 'ASC') { $arrow = $this->get_sort_arrow('up', $sortlink); } else { $arrow = $this->get_sort_arrow('down', $sortlink); } } else { $arrow = $this->get_sort_arrow('move', $sortlink); } $headerlink = $this->gtree->get_element_header($element, true, $showactivityicons, false, false, true); $itemcell = new html_table_cell(); $itemcell->attributes['class'] = $type . ' ' . $catlevel . ' highlightable' . ' i' . $element['object']->id; $itemcell->attributes['data-itemid'] = $element['object']->id; if ($element['object']->is_hidden()) { $itemcell->attributes['class'] .= ' dimmed_text'; } $singleview = ''; // FIXME: MDL-52678 This is extremely hacky we should have an API for inserting grade column links. if (get_capability_info('gradereport/singleview:view')) { if (has_all_capabilities(array('gradereport/singleview:view', 'moodle/grade:viewall', 'moodle/grade:edit'), $this->context)) { $url = new moodle_url('/grade/report/singleview/index.php', array('id' => $this->course->id, 'item' => 'grade', 'itemid' => $element['object']->id)); $singleview = $OUTPUT->action_icon($url, new pix_icon('t/editstring', get_string('singleview', 'grades', $element['object']->get_name()))); } } $itemcell->colspan = $colspan; $itemcell->text = shorten_text($headerlink) . $arrow . $singleview; $itemcell->header = true; $itemcell->scope = 'col'; $headingrow->cells[] = $itemcell; } } } $rows[] = $headingrow; } $rows = $this->get_right_icons_row($rows); // Preload scale objects for items with a scaleid and initialize tab indices $scaleslist = array(); $tabindices = array(); foreach ($this->gtree->get_items() as $itemid => $item) { $scale = null; if (!empty($item->scaleid)) { $scaleslist[] = $item->scaleid; $jsarguments['items'][$itemid] = array('id' => $itemid, 'name' => $item->get_name(true), 'type' => 'scale', 'scale' => $item->scaleid, 'decimals' => $item->get_decimals()); } else { $jsarguments['items'][$itemid] = array('id' => $itemid, 'name' => $item->get_name(true), 'type' => 'value', 'scale' => false, 'decimals' => $item->get_decimals()); } $tabindices[$item->id]['grade'] = $gradetabindex; $tabindices[$item->id]['feedback'] = $gradetabindex + $numusers; $gradetabindex += $numusers * 2; } $scalesarray = array(); if (!empty($scaleslist)) { $scalesarray = $DB->get_records_list('scale', 'id', $scaleslist); } $jsscales = $scalesarray; // Get all the grade items if the user can not view hidden grade items. // It is possible that the user is simply viewing the 'Course total' by switching to the 'Aggregates only' view // and that this user does not have the ability to view hidden items. In this case we still need to pass all the // grade items (in case one has been hidden) as the course total shown needs to be adjusted for this particular // user. if (!$this->canviewhidden) { $allgradeitems = grade_item::fetch_all(array('courseid' => $this->courseid)); } foreach ($this->users as $userid => $user) { if ($this->canviewhidden) { $altered = array(); $unknown = array(); } else { $usergrades = $this->allgrades[$userid]; $hidingaffected = grade_grade::get_hiding_affected($usergrades, $allgradeitems); $altered = $hidingaffected['altered']; $unknown = $hidingaffected['unknown']; unset($hidingaffected); } $itemrow = new html_table_row(); $itemrow->id = 'user_' . $userid; $fullname = fullname($user); $jsarguments['users'][$userid] = $fullname; foreach ($this->gtree->items as $itemid => $unused) { $item =& $this->gtree->items[$itemid]; $grade = $this->grades[$userid][$item->id]; $itemcell = new html_table_cell(); $itemcell->id = 'u' . $userid . 'i' . $itemid; $itemcell->attributes['data-itemid'] = $itemid; // Get the decimal points preference for this item $decimalpoints = $item->get_decimals(); if (in_array($itemid, $unknown)) { $gradeval = null; } else { if (array_key_exists($itemid, $altered)) { $gradeval = $altered[$itemid]; } else { $gradeval = $grade->finalgrade; } } if (!empty($grade->finalgrade)) { $gradevalforjs = null; if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { $gradevalforjs = (int) $gradeval; } else { $gradevalforjs = format_float($gradeval, $decimalpoints); } $jsarguments['grades'][] = array('user' => $userid, 'item' => $itemid, 'grade' => $gradevalforjs); } // MDL-11274 // Hide grades in the grader report if the current grader doesn't have 'moodle/grade:viewhidden' if (!$this->canviewhidden and $grade->is_hidden()) { if (!empty($CFG->grade_hiddenasdate) and $grade->get_datesubmitted() and !$item->is_category_item() and !$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 $itemcell->text = "<span class='datesubmitted'>" . userdate($grade->get_datesubmitted(), $strftimedatetimeshort) . "</span>"; } else { $itemcell->text = '-'; } $itemrow->cells[] = $itemcell; continue; } // emulate grade element $eid = $this->gtree->get_grade_eid($grade); $element = array('eid' => $eid, 'object' => $grade, 'type' => 'grade'); $itemcell->attributes['class'] .= ' grade i' . $itemid; if ($item->is_category_item()) { $itemcell->attributes['class'] .= ' cat'; } if ($item->is_course_item()) { $itemcell->attributes['class'] .= ' course'; } if ($grade->is_overridden()) { $itemcell->attributes['class'] .= ' overridden'; $itemcell->attributes['aria-label'] = get_string('overriddengrade', 'gradereport_grader'); } if (!empty($grade->feedback)) { $feedback = wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br>'); $itemcell->attributes['data-feedback'] = $feedback; $jsarguments['feedback'][] = array('user' => $userid, 'item' => $itemid, 'content' => $feedback); } if ($grade->is_excluded()) { // Adding white spaces before and after to prevent a screenreader from // thinking that the words are attached to the next/previous <span> or text. $itemcell->text .= " <span class='excludedfloater'>" . $strexcludedgrades . "</span> "; } // Do not show any icons if no grade (no record in DB to match) if (!$item->needsupdate and $USER->gradeediting[$this->courseid]) { $itemcell->text .= $this->get_icons($element); } $hidden = ''; if ($grade->is_hidden()) { $hidden = ' dimmed_text '; } $gradepass = '******'; if ($grade->is_passed($item)) { $gradepass = '******'; } else { if (is_null($grade->is_passed($item))) { $gradepass = ''; } } // if in editing mode, we need to print either a text box // or a drop down (for scales) // grades in item of type grade category or course are not directly editable if ($item->needsupdate) { $itemcell->text .= "<span class='gradingerror{$hidden}'>" . $strerror . "</span>"; } else { if ($USER->gradeediting[$this->courseid]) { if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { $itemcell->attributes['class'] .= ' grade_type_scale'; } else { if ($item->gradetype == GRADE_TYPE_VALUE) { $itemcell->attributes['class'] .= ' grade_type_value'; } else { if ($item->gradetype == GRADE_TYPE_TEXT) { $itemcell->attributes['class'] .= ' grade_type_text'; } } } if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { $scale = $scalesarray[$item->scaleid]; $gradeval = (int) $gradeval; // scales use only integers $scales = explode(",", $scale->scale); // reindex because scale is off 1 // MDL-12104 some previous scales might have taken up part of the array // so this needs to be reset $scaleopt = array(); $i = 0; foreach ($scales as $scaleoption) { $i++; $scaleopt[$i] = $scaleoption; } if ($quickgrading and $grade->is_editable()) { $oldval = empty($gradeval) ? -1 : $gradeval; if (empty($item->outcomeid)) { $nogradestr = $this->get_lang_string('nograde'); } else { $nogradestr = $this->get_lang_string('nooutcome', 'grades'); } $attributes = array('tabindex' => $tabindices[$item->id]['grade'], 'id' => 'grade_' . $userid . '_' . $item->id); $gradelabel = $fullname . ' ' . $item->itemname; $itemcell->text .= html_writer::label(get_string('useractivitygrade', 'gradereport_grader', $gradelabel), $attributes['id'], false, array('class' => 'accesshide')); $itemcell->text .= html_writer::select($scaleopt, 'grade[' . $userid . '][' . $item->id . ']', $gradeval, array(-1 => $nogradestr), $attributes); } else { if (!empty($scale)) { $scales = explode(",", $scale->scale); // invalid grade if gradeval < 1 if ($gradeval < 1) { $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>-</span>"; } else { $gradeval = $grade->grade_item->bounded_grade($gradeval); //just in case somebody changes scale $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>{$scales[$gradeval - 1]}</span>"; } } } } else { if ($item->gradetype != GRADE_TYPE_TEXT) { // Value type if ($quickgrading and $grade->is_editable()) { $value = format_float($gradeval, $decimalpoints); $gradelabel = $fullname . ' ' . $item->itemname; $itemcell->text .= '<label class="accesshide" for="grade_' . $userid . '_' . $item->id . '">' . get_string('useractivitygrade', 'gradereport_grader', $gradelabel) . '</label>'; $itemcell->text .= '<input size="6" tabindex="' . $tabindices[$item->id]['grade'] . '" type="text" class="text" title="' . $strgrade . '" name="grade[' . $userid . '][' . $item->id . ']" id="grade_' . $userid . '_' . $item->id . '" value="' . $value . '" />'; } else { $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>" . format_float($gradeval, $decimalpoints) . "</span>"; } } } // If quickfeedback is on, print an input element if ($showquickfeedback and $grade->is_editable()) { $feedbacklabel = $fullname . ' ' . $item->itemname; $itemcell->text .= '<label class="accesshide" for="feedback_' . $userid . '_' . $item->id . '">' . get_string('useractivityfeedback', 'gradereport_grader', $feedbacklabel) . '</label>'; $itemcell->text .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback'] . '" id="feedback_' . $userid . '_' . $item->id . '" size="6" title="' . $strfeedback . '" type="text" name="feedback[' . $userid . '][' . $item->id . ']" value="' . s($grade->feedback) . '" />'; } } else { // Not editing $gradedisplaytype = $item->get_displaytype(); if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { $itemcell->attributes['class'] .= ' grade_type_scale'; } else { if ($item->gradetype == GRADE_TYPE_VALUE) { $itemcell->attributes['class'] .= ' grade_type_value'; } else { if ($item->gradetype == GRADE_TYPE_TEXT) { $itemcell->attributes['class'] .= ' grade_type_text'; } } } // Only allow edting if the grade is editable (not locked, not in a unoverridable category, etc). if ($enableajax && $grade->is_editable()) { // If a grade item is type text, and we don't have show quick feedback on, it can't be edited. if ($item->gradetype != GRADE_TYPE_TEXT || $showquickfeedback) { $itemcell->attributes['class'] .= ' clickable'; } } if ($item->needsupdate) { $itemcell->text .= "<span class='gradingerror{$hidden}{$gradepass}'>" . $error . "</span>"; } else { // The max and min for an aggregation may be different to the grade_item. if (!is_null($gradeval)) { $item->grademax = $grade->get_grade_max(); $item->grademin = $grade->get_grade_min(); } $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>" . grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null) . "</span>"; if ($showanalysisicon) { $itemcell->text .= $this->gtree->get_grade_analysis_icon($grade); } } } } // Enable keyboard navigation if the grade is editable (not locked, not in a unoverridable category, etc). if ($enableajax && $grade->is_editable()) { // If a grade item is type text, and we don't have show quick feedback on, it can't be edited. if ($item->gradetype != GRADE_TYPE_TEXT || $showquickfeedback) { $itemcell->attributes['class'] .= ' gbnavigable'; } } if (!empty($this->gradeserror[$item->id][$userid])) { $itemcell->text .= $this->gradeserror[$item->id][$userid]; } $itemrow->cells[] = $itemcell; } $rows[] = $itemrow; } if ($enableajax) { $jsarguments['cfg']['ajaxenabled'] = true; $jsarguments['cfg']['scales'] = array(); foreach ($jsscales as $scale) { // Trim the scale values, as they may have a space that is ommitted from values later. $jsarguments['cfg']['scales'][$scale->id] = array_map('trim', explode(',', $scale->scale)); } $jsarguments['cfg']['feedbacktrunclength'] = $this->feedback_trunc_length; // Student grades and feedback are already at $jsarguments['feedback'] and $jsarguments['grades'] } $jsarguments['cfg']['isediting'] = (bool) $USER->gradeediting[$this->courseid]; $jsarguments['cfg']['courseid'] = $this->courseid; $jsarguments['cfg']['studentsperpage'] = $this->get_students_per_page(); $jsarguments['cfg']['showquickfeedback'] = (bool) $showquickfeedback; $module = array('name' => 'gradereport_grader', 'fullpath' => '/grade/report/grader/module.js', 'requires' => array('base', 'dom', 'event', 'event-mouseenter', 'event-key', 'io-queue', 'json-parse', 'overlay')); $PAGE->requires->js_init_call('M.gradereport_grader.init_report', $jsarguments, false, $module); $PAGE->requires->strings_for_js(array('addfeedback', 'feedback', 'grade'), 'grades'); $PAGE->requires->strings_for_js(array('ajaxchoosescale', 'ajaxclicktoclose', 'ajaxerror', 'ajaxfailedupdate', 'ajaxfieldchanged'), 'gradereport_grader'); if (!$enableajax && $USER->gradeediting[$this->courseid]) { $PAGE->requires->yui_module('moodle-core-formchangechecker', 'M.core_formchangechecker.init', array(array('formid' => 'gradereport_grader'))); $PAGE->requires->string_for_js('changesmadereallygoaway', 'moodle'); } $rows = $this->get_right_range_row($rows); if ($displayaverages) { $rows = $this->get_right_avg_row($rows, true); $rows = $this->get_right_avg_row($rows); } return $rows; }
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]); } } }
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 = ''; // 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; $class .= " itemcenter "; if ($this->showweight) { $data['weight']['class'] = $class; $data['weight']['content'] = '-'; // 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); } } } // Range if ($this->showrange) { $data['range']['class'] = $class; $data['range']['content'] = $grade_grade->grade_item->get_formatted_range(GRADE_DISPLAY_TYPE_REAL, $this->rangedecimals); } // 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); } } } // 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); } } } // 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 } } } // 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'] = '-'; } } // Feedback if ($this->showfeedback) { if ($grade_grade->overridden > 0 and ($type == 'categoryitem' or $type == 'courseitem')) { $data['feedback']['class'] = $class . ' 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'] = $class . ' feedbacktext'; $data['feedback']['content'] = ' '; } else { $data['feedback']['class'] = $class . ' feedbacktext'; $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat); } } } } } /// 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]); } } }
function generate_jwc_xml($jwc_courses, $export_users, $include_cats = false, $dryrun = true) { global $course, $output, $jwc, $DB, $USER, $nonexist_users; if ($include_cats) { $heading = '导出分项成绩及总分到教务处'; } else { $heading = '导出总分到教务处'; } if ($dryrun) { $heading .= '(模拟)'; } else { $heading .= '(正式)'; } echo $output->heading($heading); //first make sure we have proper final grades - this must be done before constructing of the grade tree grade_regrade_final_grades($course->id); // 获得成绩类别和项信息 $tree = new grade_tree($course->id, true, true); $levels = $tree->get_levels(); // 总分 $total_item = normalize_grade_item($levels[0][0]['object']->grade_item); $sub_items = array(); $extra_items = array(); $items = array(); // 顶级成绩分类和项 if (array_key_exists(1, $levels) && $include_cats) { foreach ($levels[1] as $element) { if ($element['type'] == 'item') { $grade_item = normalize_grade_item($element['object']); } else { if ($element['type'] == 'category') { $tmp = array_pop($element['children']); $grade_item = normalize_grade_item($tmp['object']); //用类别名做成绩名 $grade_item->itemname = $element['object']->fullname; } else { // ignore unused fillers continue; } } if ($grade_item->grademax <= 0) { // 不计分成绩项/类别 continue; } if ($grade_item->aggregationcoef) { // 额外加分 $extra_items[$grade_item->id] = $grade_item; } else { $sub_items[$grade_item->id] = $grade_item; } } } /// 验证成绩项是否符合教务处要求 $result = true; // 总成绩满分必须是100分 if ($total_item->grademax != MAX_TOTAL_GRADE) { echo $output->require_max_total_grade($total_item->grademax); $result = false; } if ($include_cats) { // 总成绩算法必须是“简单加权平均分” $total_aggregation = $levels[0][0]['object']->aggregation; if ($total_aggregation != GRADE_AGGREGATE_WEIGHTED_MEAN2 and $total_aggregation != GRADE_AGGREGATE_SUM) { echo $output->require_aggregation($total_aggregation); $result = false; } // 子成绩项权重和必须为100 // 所有非加分的分项相加为100,才合法,除非不包含子类别 $weight_sum = 0; foreach ($sub_items as $item) { $weight_sum += $item->grademax; } if ($include_cats and $weight_sum != MAX_TOTAL_GRADE) { echo $output->require_100_weight($weight_sum); $result = false; } // 子成绩项数量不能超过8 if (count($sub_items) > MAX_SUB_GRADE_COUNT) { echo $output->require_max_subitems(count($sub_items)); $result = false; } // 加分成绩项数量不能超过2 if (count($extra_items) > MAX_EXTRA_SUB_GRADE_COUNT) { echo $output->require_max_extraitems(count($extra_items)); $result = false; } } if (!$result) { echo $output->modify_items_link(); return false; } $xml = new gradebook_xml(); if ($dryrun) { echo $output->box_start(); echo $output->heading('可导出成绩项', 3); } $itemtable = new html_table(); $itemtable->head = array('成绩分项名称', '权重', '加分'); foreach ($sub_items as $item) { $itemtable->data[] = new html_table_row(array($item->itemname, $item->grademax . '%', '否')); $xml->add_weight_item($item->id, $item->itemname, $item->grademax, $item->grademax); } foreach ($extra_items as $item) { $itemtable->data[] = new html_table_row(array($item->itemname, $item->grademax . '%', '是')); $xml->add_weight_item($item->id, $item->itemname, $item->grademax, $item->grademax, true); } $itemtable->data[] = new html_table_row(array($total_item->itemname, $total_item->grademax . '%', '-')); if ($dryrun) { echo html_writer::table($itemtable); } // 本地不存在的用户 if (!empty($nonexist_users)) { echo $output->heading('教务处有记录而本站无对应用户的学生', 3); $usertable = new html_table(); $usertable->head = array('序号', '姓名', '学号'); $count = 0; foreach ($nonexist_users as $user) { $row = array(); $count++; $row[] = new html_table_cell($count); $row[] = new html_table_cell($user->name); $row[] = new html_table_cell($user->code); $usertable->data[] = new html_table_row($row); } echo html_writer::table($usertable); } // 用户成绩 if ($dryrun) { echo $output->heading('可导出成绩', 3); } $items = $sub_items + $extra_items; $items[$total_item->id] = $total_item; $geub = new grade_export_update_buffer(); $gui = new graded_users_iterator($course, $items); $gui->init(); $usertable = new html_table(); $usertable->head = array('序号', '姓名', '学号'); foreach ($items as $item) { $usertable->head[] = $item->itemname; } $count = 0; while ($userdata = $gui->next_user()) { $user = $userdata->user; if ($user->auth != 'cas' || empty($user->idnumber)) { // 非cas用户成绩不可导出 continue; } if (!array_key_exists($user->id, $export_users)) { // 教务处无记录用户不导出 continue; } $row = array(); $count++; $row[] = new html_table_cell($count); $row[] = new html_table_cell($user->firstname); $row[] = new html_table_cell($user->idnumber); $grades = array(); foreach ($userdata->grades as $itemid => $grade) { if ($itemid == $total_item->id) { // 总分 $finalgrade = round($grade->finalgrade); $grades[0] = $finalgrade; } else { $finalgrade = round($grade->finalgrade, 1); $grades[$itemid] = $finalgrade; } $row[] = new html_table_cell($finalgrade); } $xml->add_user($user->idnumber, $user->firstname, $grades); $usertable->data[] = new html_table_row($row); } $gui->close(); $geub->close(); if ($dryrun) { echo html_writer::table($usertable); echo $output->box_end(); } // 存入数据库 foreach ($jwc_courses as $jwc_course) { $xml->set_xkid($jwc_course->xkid); $new = new stdClass(); $new->xml = $xml->asXML(); $new->requestkey = md5($new->xml); $new->expiredtime = time() + KEY_EXPIRED_TIME; $new->user = $USER->id; $new->course = $course->id; if (!$dryrun) { if ($old = $DB->get_record('grade_export_jwc', array('requestkey' => $new->requestkey))) { $old->expiredtime = time() + KEY_EXPIRED_TIME; $DB->update_record('grade_export_jwc', $old); } else { $DB->insert_record('grade_export_jwc', $new); } // real export $errormsg = ''; if (!$jwc->export($jwc_course->xkid, $new->requestkey, $errormsg)) { $errormsg = textlib_get_instance()->convert($errormsg, 'gbk'); echo $output->notification('导出过程出错(' . $errormsg . ')。请将这串字符串报告给管理员:' . $new->requestkey); return false; } } } return true; }
/** * Test can_output_item. */ public function test_can_output_item() { $this->resetAfterTest(); $generator = $this->getDataGenerator(); // Course level grade category. $course = $generator->create_course(); // Grade tree looks something like: // - Test course (Rendered). $gradetree = grade_category::fetch_course_tree($course->id); $this->assertTrue(grade_tree::can_output_item($gradetree)); // Add a grade category with default settings. $generator->create_grade_category(array('courseid' => $course->id)); // Grade tree now looks something like: // - Test course n (Rendered). // -- Grade category n (Rendered). $gradetree = grade_category::fetch_course_tree($course->id); $this->assertNotEmpty($gradetree['children']); foreach ($gradetree['children'] as $child) { $this->assertTrue(grade_tree::can_output_item($child)); } // Add a grade category with grade type = None. $nototalcategory = 'No total category'; $nototalparams = ['courseid' => $course->id, 'fullname' => $nototalcategory, 'aggregation' => GRADE_AGGREGATE_WEIGHTED_MEAN]; $nototal = $generator->create_grade_category($nototalparams); $catnototal = grade_category::fetch(array('id' => $nototal->id)); // Set the grade type of the grade item associated to the grade category. $catitemnototal = $catnototal->load_grade_item(); $catitemnototal->gradetype = GRADE_TYPE_NONE; $catitemnototal->update(); // Grade tree looks something like: // - Test course n (Rendered). // -- Grade category n (Rendered). // -- No total category (Not rendered). $gradetree = grade_category::fetch_course_tree($course->id); foreach ($gradetree['children'] as $child) { if ($child['object']->fullname == $nototalcategory) { $this->assertFalse(grade_tree::can_output_item($child)); } else { $this->assertTrue(grade_tree::can_output_item($child)); } } // Add another grade category with default settings under 'No total category'. $normalinnototalparams = ['courseid' => $course->id, 'fullname' => 'Normal category in no total category', 'parent' => $nototal->id]; $generator->create_grade_category($normalinnototalparams); // Grade tree looks something like: // - Test course n (Rendered). // -- Grade category n (Rendered). // -- No total category (Rendered). // --- Normal category in no total category (Rendered). $gradetree = grade_category::fetch_course_tree($course->id); foreach ($gradetree['children'] as $child) { // All children are now visible. $this->assertTrue(grade_tree::can_output_item($child)); if (!empty($child['children'])) { foreach ($child['children'] as $grandchild) { $this->assertTrue(grade_tree::can_output_item($grandchild)); } } } // Add a grade category with grade type = None. $nototalcategory2 = 'No total category 2'; $nototal2params = ['courseid' => $course->id, 'fullname' => $nototalcategory2, 'aggregation' => GRADE_AGGREGATE_WEIGHTED_MEAN]; $nototal2 = $generator->create_grade_category($nototal2params); $catnototal2 = grade_category::fetch(array('id' => $nototal2->id)); // Set the grade type of the grade item associated to the grade category. $catitemnototal2 = $catnototal2->load_grade_item(); $catitemnototal2->gradetype = GRADE_TYPE_NONE; $catitemnototal2->update(); // Add a category with no total under 'No total category'. $nototalinnototalcategory = 'Category with no total in no total category'; $nototalinnototalparams = ['courseid' => $course->id, 'fullname' => $nototalinnototalcategory, 'aggregation' => GRADE_AGGREGATE_WEIGHTED_MEAN, 'parent' => $nototal2->id]; $nototalinnototal = $generator->create_grade_category($nototalinnototalparams); $catnototalinnototal = grade_category::fetch(array('id' => $nototalinnototal->id)); // Set the grade type of the grade item associated to the grade category. $catitemnototalinnototal = $catnototalinnototal->load_grade_item(); $catitemnototalinnototal->gradetype = GRADE_TYPE_NONE; $catitemnototalinnototal->update(); // Grade tree looks something like: // - Test course n (Rendered). // -- Grade category n (Rendered). // -- No total category (Rendered). // --- Normal category in no total category (Rendered). // -- No total category 2 (Not rendered). // --- Category with no total in no total category (Not rendered). $gradetree = grade_category::fetch_course_tree($course->id); foreach ($gradetree['children'] as $child) { if ($child['object']->fullname == $nototalcategory2) { $this->assertFalse(grade_tree::can_output_item($child)); } else { $this->assertTrue(grade_tree::can_output_item($child)); } if (!empty($child['children'])) { foreach ($child['children'] as $grandchild) { if ($grandchild['object']->fullname == $nototalinnototalcategory) { $this->assertFalse(grade_tree::can_output_item($grandchild)); } else { $this->assertTrue(grade_tree::can_output_item($grandchild)); } } } } }
/** * Re-sorts the gradebook to put all MTG grade items first. * * Gets all of the grade items for the specifed course. Iterates over the array * of items and moves the MTG items to the front of the array. Then does a * second pass to renumber all the sortorders to make the items sequential from * 2 upwards (1 will be the course item). * * @param object $course Database record for course, containing the id. */ function sort_gradebook($course) { global $CFG, $DB; require_once $CFG->dirroot . '/grade/lib.php'; require_once $CFG->dirroot . '/grade/edit/tree/lib.php'; $gtree = new \grade_tree($course->id, false, false); $fields = array('alis_avgcse', 'alis_alisnum', 'alis_alis', 'alis_mtg', 'alis_cpg'); $params = array($course->id); list($in_sql, $in_params) = $DB->get_in_or_equal($params); $params = \array_merge($params, $in_params); $where = 'courseid = ? AND idnumber ' . $in_sql; $gradeitems = $DB->get_records_select('grade_items', $where, $params, 'itemnumber DESC'); $courseitem = $DB->get_record('grade_items', array('courseid' => $course->id, 'itemtype' => 'course')); //$mtgitems = count_records_select('grade_items', $where); // First, move the MTG grade items to the front $offset = 0; foreach ($gradeitems as $item) { if (!($element = $gtree->locate_element('i' . $item->id))) { \print_error('invalidelementid'); } $object = $element['object']; $moveafter = 'c' . $courseitem->iteminstance; $first = 1; // If First is set to 1, it means the target is the first child of the category $moveafter if (!($after_el = $gtree->locate_element($moveafter))) { \print_error('invalidelementid'); } $after = $after_el['object']; $sortorder = $after->get_sortorder(); if (!$first) { $parent = $after->get_parent_category(); $object->set_parent($parent->id); } else { $object->set_parent($after->id); } $object->move_after_sortorder($sortorder); } }
/** * 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); } }