示例#1
0
 function test_grade_category_construct()
 {
     $course_category = grade_category::fetch_course_category($this->courseid);
     $params = new stdClass();
     $params->courseid = $this->courseid;
     $params->fullname = 'unittestcategory4';
     $grade_category = new grade_category($params, false);
     $grade_category->insert();
     $this->assertEqual($params->courseid, $grade_category->courseid);
     $this->assertEqual($params->fullname, $grade_category->fullname);
     $this->assertEqual(2, $grade_category->depth);
     $this->assertEqual("/{$course_category->id}/{$grade_category->id}/", $grade_category->path);
     $parentpath = $grade_category->path;
     // Test a child category
     $params->parent = $grade_category->id;
     $params->fullname = 'unittestcategory5';
     $grade_category = new grade_category($params, false);
     $grade_category->insert();
     $this->assertEqual(3, $grade_category->depth);
     $this->assertEqual($parentpath . $grade_category->id . "/", $grade_category->path);
     $parentpath = $grade_category->path;
     // Test a third depth category
     $params->parent = $grade_category->id;
     $params->fullname = 'unittestcategory6';
     $grade_category = new grade_category($params, false);
     $grade_category->insert();
     $this->assertEqual(4, $grade_category->depth);
     $this->assertEqual($parentpath . $grade_category->id . "/", $grade_category->path);
 }
 /**
  * Adds a grade category and moves the specified item into it.
  * Uses locks to prevent race conditions (see MDL-37055).
  */
 public function execute()
 {
     $customdata = $this->get_custom_data();
     // Get lock timeout.
     $timeout = 5;
     // A namespace for the locks.
     $locktype = 'block_mhaairs_add_category';
     // Resource key - course id and category name.
     $resource = "course: {$customdata->courseid}; catname: {$customdata->catname}";
     // Get an instance of the currently configured lock_factory.
     $lockfactory = \core\lock\lock_config::get_lock_factory($locktype);
     // Open a lock.
     $lock = $lockfactory->get_lock($resource, $timeout);
     // Add the category.
     $catparams = array('fullname' => $customdata->catname, 'courseid' => $customdata->courseid);
     if (!($category = \grade_category::fetch($catparams))) {
         // If the category does not exist we create it.
         $gradeaggregation = get_config('core', 'grade_aggregation');
         if ($gradeaggregation === false) {
             $gradeaggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2;
         }
         // Parent category is automatically added(created) during insert.
         $catparams['hidden'] = false;
         $catparams['aggregation'] = $gradeaggregation;
         try {
             $category = new \grade_category($catparams, false);
             $category->id = $category->insert();
         } catch (Exception $e) {
             // Must release the locks.
             $lock->release();
             // Rethrow to reschedule task.
             throw $e;
         }
     }
     // Release locks.
     $lock->release();
     // Add the item to the category.
     $gitem = \grade_item::fetch(array('id' => $customdata->itemid));
     $gitem->categoryid = $category->id;
     $gitem->update();
 }
 /**
  * Tests the gradebookservice update grade function for adding an item in a category,
  * where multiple categories with the same name already exist. The item should be assigned
  * to the oldest category. When moved to a category with a different name, the item should
  * be assigned to the new category. No tasks should be created and no locking should be triggered.
  *
  * @return void
  */
 public function test_update_item_multiple_categories()
 {
     global $DB, $CFG;
     $this->set_user('admin');
     // No tasks.
     $this->assertEquals(0, $DB->count_records('task_adhoc'));
     $catname = 'testcat';
     $catname1 = 'testcat1';
     $catparams = array('fullname' => $catname, 'courseid' => $this->course->id, 'hidden' => false);
     // Create category.
     $category = new \grade_category($catparams, false);
     $categoryid1 = $category->insert();
     // Create second category with the same name.
     $category = new \grade_category($catparams, false);
     $categoryid2 = $category->insert();
     // Create another category with the different name.
     $catparams['fullname'] = $catname1;
     $category = new \grade_category($catparams, false);
     $categoryid3 = $category->insert();
     // Add item 101 to $catname.
     $this->add_grade_item('101', $catname);
     // No tasks.
     $this->assertEquals(0, $DB->count_records('task_adhoc'));
     // The items should be assigned to category 1.
     $params = array('iteminstance' => '101', 'categoryid' => $categoryid1, 'courseid' => $this->course->id);
     $this->assertEquals(1, $DB->count_records('grade_items', $params));
     // Add item 102 to $catname.
     $this->add_grade_item('102', $catname);
     // No tasks.
     $this->assertEquals(0, $DB->count_records('task_adhoc'));
     // The item should be assigned to the category 1.
     $params = array('iteminstance' => '102', 'categoryid' => $categoryid1, 'courseid' => $this->course->id);
     $this->assertEquals(1, $DB->count_records('grade_items', $params));
     // Add item 103 to $catname1.
     $this->add_grade_item('103', $catname1);
     // No tasks.
     $this->assertEquals(0, $DB->count_records('task_adhoc'));
     // The item should be assigned to the category 3.
     $params = array('iteminstance' => '103', 'categoryid' => $categoryid3, 'courseid' => $this->course->id);
     $this->assertEquals(1, $DB->count_records('grade_items', $params));
     // Move item 102 to $catname1.
     $this->add_grade_item('102', $catname1);
     // No tasks.
     $this->assertEquals(0, $DB->count_records('task_adhoc'));
     // There should be two items assigned to the category 3.
     $params = array('categoryid' => $categoryid3, 'courseid' => $this->course->id);
     $this->assertEquals(2, $DB->count_records('grade_items', $params));
     // The item should be assigned to the category 3.
     $params['iteminstance'] = '102';
     $this->assertEquals(1, $DB->count_records('grade_items', $params));
 }
示例#4
0
/**
 * Common create/update module module actions that need to be processed as soon as a module is created/updaded.
 * For example:create grade parent category, add outcomes, rebuild caches, regrade, save plagiarism settings...
 * Please note this api does not trigger events as of MOODLE 2.6. Please trigger events before calling this api.
 *
 * @param object $moduleinfo the module info
 * @param object $course the course of the module
 *
 * @return object moduleinfo update with grading management info
 */
function edit_module_post_actions($moduleinfo, $course)
{
    global $CFG;
    require_once $CFG->libdir . '/gradelib.php';
    $modcontext = context_module::instance($moduleinfo->coursemodule);
    $hasgrades = plugin_supports('mod', $moduleinfo->modulename, FEATURE_GRADE_HAS_GRADE, false);
    $hasoutcomes = plugin_supports('mod', $moduleinfo->modulename, FEATURE_GRADE_OUTCOMES, true);
    // Sync idnumber with grade_item.
    if ($hasgrades && ($grade_item = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => $moduleinfo->modulename, 'iteminstance' => $moduleinfo->instance, 'itemnumber' => 0, 'courseid' => $course->id)))) {
        if ($grade_item->idnumber != $moduleinfo->cmidnumber) {
            $grade_item->idnumber = $moduleinfo->cmidnumber;
            $grade_item->update();
        }
    }
    if ($hasgrades) {
        $items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $moduleinfo->modulename, 'iteminstance' => $moduleinfo->instance, 'courseid' => $course->id));
    } else {
        $items = array();
    }
    // Create parent category if requested and move to correct parent category.
    if ($items and isset($moduleinfo->gradecat)) {
        if ($moduleinfo->gradecat == -1) {
            $grade_category = new grade_category();
            $grade_category->courseid = $course->id;
            $grade_category->fullname = $moduleinfo->name;
            $grade_category->insert();
            if ($grade_item) {
                $parent = $grade_item->get_parent_category();
                $grade_category->set_parent($parent->id);
            }
            $moduleinfo->gradecat = $grade_category->id;
        }
        $gradecategory = $grade_item->get_parent_category();
        foreach ($items as $itemid => $unused) {
            $items[$itemid]->set_parent($moduleinfo->gradecat);
            if ($itemid == $grade_item->id) {
                // Use updated grade_item.
                $grade_item = $items[$itemid];
            }
            if (!empty($moduleinfo->add)) {
                if (grade_category::aggregation_uses_aggregationcoef($gradecategory->aggregation)) {
                    if ($gradecategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
                        $grade_item->aggregationcoef = 1;
                    } else {
                        $grade_item->aggregationcoef = 0;
                    }
                    $grade_item->update();
                }
            }
        }
    }
    require_once $CFG->libdir . '/grade/grade_outcome.php';
    // Add outcomes if requested.
    if ($hasoutcomes && ($outcomes = grade_outcome::fetch_all_available($course->id))) {
        $grade_items = array();
        // Outcome grade_item.itemnumber start at 1000, there is nothing above outcomes.
        $max_itemnumber = 999;
        if ($items) {
            foreach ($items as $item) {
                if ($item->itemnumber > $max_itemnumber) {
                    $max_itemnumber = $item->itemnumber;
                }
            }
        }
        foreach ($outcomes as $outcome) {
            $elname = 'outcome_' . $outcome->id;
            if (property_exists($moduleinfo, $elname) and $moduleinfo->{$elname}) {
                // So we have a request for new outcome grade item?
                if ($items) {
                    $outcomeexists = false;
                    foreach ($items as $item) {
                        if ($item->outcomeid == $outcome->id) {
                            $outcomeexists = true;
                            break;
                        }
                    }
                    if ($outcomeexists) {
                        continue;
                    }
                }
                $max_itemnumber++;
                $outcome_item = new grade_item();
                $outcome_item->courseid = $course->id;
                $outcome_item->itemtype = 'mod';
                $outcome_item->itemmodule = $moduleinfo->modulename;
                $outcome_item->iteminstance = $moduleinfo->instance;
                $outcome_item->itemnumber = $max_itemnumber;
                $outcome_item->itemname = $outcome->fullname;
                $outcome_item->outcomeid = $outcome->id;
                $outcome_item->gradetype = GRADE_TYPE_SCALE;
                $outcome_item->scaleid = $outcome->scaleid;
                $outcome_item->insert();
                // Move the new outcome into correct category and fix sortorder if needed.
                if ($grade_item) {
                    $outcome_item->set_parent($grade_item->categoryid);
                    $outcome_item->move_after_sortorder($grade_item->sortorder);
                } else {
                    if (isset($moduleinfo->gradecat)) {
                        $outcome_item->set_parent($moduleinfo->gradecat);
                    }
                }
                $gradecategory = $outcome_item->get_parent_category();
                if ($outcomeexists == false) {
                    if (grade_category::aggregation_uses_aggregationcoef($gradecategory->aggregation)) {
                        if ($gradecategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
                            $outcome_item->aggregationcoef = 1;
                        } else {
                            $outcome_item->aggregationcoef = 0;
                        }
                        $outcome_item->update();
                    }
                }
            }
        }
    }
    if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_ADVANCED_GRADING, false) and has_capability('moodle/grade:managegradingforms', $modcontext)) {
        require_once $CFG->dirroot . '/grade/grading/lib.php';
        $gradingman = get_grading_manager($modcontext, 'mod_' . $moduleinfo->modulename);
        $showgradingmanagement = false;
        foreach ($gradingman->get_available_areas() as $areaname => $aretitle) {
            $formfield = 'advancedgradingmethod_' . $areaname;
            if (isset($moduleinfo->{$formfield})) {
                $gradingman->set_area($areaname);
                $methodchanged = $gradingman->set_active_method($moduleinfo->{$formfield});
                if (empty($moduleinfo->{$formfield})) {
                    // Going back to the simple direct grading is not a reason to open the management screen.
                    $methodchanged = false;
                }
                $showgradingmanagement = $showgradingmanagement || $methodchanged;
            }
        }
        // Update grading management information.
        $moduleinfo->gradingman = $gradingman;
        $moduleinfo->showgradingmanagement = $showgradingmanagement;
    }
    rebuild_course_cache($course->id, true);
    if ($hasgrades) {
        grade_regrade_final_grades($course->id);
    }
    require_once $CFG->libdir . '/plagiarismlib.php';
    plagiarism_save_form_elements($moduleinfo);
    return $moduleinfo;
}
function RWSUQGrades($r_qiz)
{
    $r_gi = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => $r_qiz->modulename, 'iteminstance' => $r_qiz->instance, 'itemnumber' => 0, 'courseid' => $r_qiz->course));
    if ($r_gi && $r_gi->idnumber != $r_qiz->cmidnumber) {
        $r_gi->idnumber = $r_qiz->cmidnumber;
        $r_gi->update();
    }
    $r_its = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $r_qiz->modulename, 'iteminstance' => $r_qiz->instance, 'courseid' => $r_qiz->course));
    if ($r_its && isset($r_qiz->gradecat)) {
        if ($r_qiz->gradecat == -1) {
            $r_gcat = new grade_category();
            $r_gcat->courseid = $r_qiz->course;
            $r_gcat->fullname = $r_qiz->name;
            $r_gcat->insert();
            if ($r_gi) {
                $r_par = $r_gi->get_parent_category();
                $r_gcat->set_parent($r_par->id);
            }
            $r_qiz->gradecat = $r_gcat->id;
        }
        foreach ($r_its as $r_iti => $r_un) {
            $r_its[$r_iti]->set_parent($r_qiz->gradecat);
            if ($r_iti == $r_gi->id) {
                $r_gi = $r_its[$r_iti];
            }
        }
    }
    if ($r_ocs = grade_outcome::fetch_all_available($r_qiz->course)) {
        $r_gis = array();
        $r_mit = 999;
        if ($r_its) {
            foreach ($r_its as $r_it) {
                if ($r_it->itemnumber > $r_mit) {
                    $r_mit = $r_it->itemnumber;
                }
            }
        }
        foreach ($r_ocs as $r_oc) {
            $r_eln = 'outcome_' . $r_oc->id;
            if (property_exists($r_qiz, $r_eln) and $r_qiz->{$r_eln}) {
                if ($r_its) {
                    foreach ($r_its as $r_it) {
                        if ($r_it->outcomeid == $r_oc->id) {
                            continue 2;
                        }
                    }
                }
                $r_mit++;
                $r_oi = new grade_item();
                $r_oi->courseid = $r_qiz->course;
                $r_oi->itemtype = 'mod';
                $r_oi->itemmodule = $r_qiz->modulename;
                $r_oi->iteminstance = $r_qiz->instance;
                $r_oi->itemnumber = $r_mit;
                $r_oi->itemname = $r_oc->fullname;
                $r_oi->outcomeid = $r_oc->id;
                $r_oi->gradetype = GRADE_TYPE_SCALE;
                $r_oi->scaleid = $r_oc->scaleid;
                $r_oi->insert();
                if ($r_gi) {
                    $r_oi->set_parent($r_gi->categoryid);
                    $r_oi->move_after_sortorder($r_gi->sortorder);
                } else {
                    if (isset($r_qiz->gradecat)) {
                        $r_oi->set_parent($r_qiz->gradecat);
                    }
                }
            }
        }
    }
}
示例#6
0
 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;
 }
 /**
  * Tests update grade service with existing regular manual items with the same name
  * as requested. Should update the first item and turn it into mhaairs item. Should not change
  * any item property other than the item instance.
  *
  * @return void
  */
 public function test_update_item_with_manual_item()
 {
     global $DB;
     $this->set_user('admin');
     $this->assertEquals(0, $DB->count_records('grade_items'));
     $catname = 'Existing grade category';
     $itemname = 'Existing grade item';
     // Create category.
     $catparams = array('fullname' => $catname, 'courseid' => $this->course->id, 'hidden' => false);
     $category = new \grade_category($catparams, false);
     $category->id = $category->insert();
     // Add two manual grade item directly.
     $itemparams = array('courseid' => $this->course->id, 'itemtype' => 'manual', 'itemname' => $itemname, 'categoryid' => $category->id);
     $gitem = new \grade_item($itemparams, false);
     $gitem->insert('manual');
     $gitem = new \grade_item($itemparams, false);
     $gitem->insert('manual');
     // There should be 4 items (including course and category items).
     $itemcount = $DB->count_records('grade_items');
     $this->assertEquals(4, $itemcount);
     $service = 'block_mhaairs_gradebookservice_external::update_grade';
     $itemdetails = array('itemname' => $itemname, 'grademax' => 90, 'useexisting' => 1, 'categoryid' => 'New grade category');
     $itemdetailsjson = urlencode(json_encode($itemdetails));
     // Update item via service.
     $servicedata = array();
     $servicedata['source'] = 'mhaairs';
     $servicedata['courseid'] = 'tc1';
     $servicedata['itemtype'] = 'manual';
     $servicedata['itemmodule'] = 'mhaairs';
     $servicedata['iteminstance'] = 345;
     $servicedata['itemnumber'] = 0;
     $servicedata['grades'] = null;
     $servicedata['itemdetails'] = $itemdetailsjson;
     $result = call_user_func_array($service, $servicedata);
     // 2 grade categories overall.
     $itemcount = $DB->count_records('grade_categories');
     $this->assertEquals(2, $itemcount);
     // 4 grade items overall.
     $itemcount = $DB->count_records('grade_items');
     $this->assertEquals(4, $itemcount);
     // 2 manual items remaining.
     $itemcount = $DB->count_records('grade_items', array('itemtype' => 'manual'));
     $this->assertEquals(2, $itemcount);
     // 1 mhaairs item.
     $itemcount = $DB->count_records('grade_items', array('itemtype' => 'manual', 'itemmodule' => 'mhaairs'));
     $this->assertEquals(1, $itemcount);
     // 1 mhaairs item with the item instance and original grade.
     $itemcount = $DB->count_records('grade_items', array('itemtype' => 'manual', 'itemmodule' => 'mhaairs', 'iteminstance' => 345, 'grademax' => 100.0, 'categoryid' => $category->id));
     $this->assertEquals(1, $itemcount);
 }
 /**
  * Tests the calculation of grades using the various aggregation methods with and without hidden grades
  * This will not work entirely until MDL-11837 is done
  * @global type $DB
  */
 protected function sub_test_grade_category_generate_grades()
 {
     global $DB;
     //inserting some special grade items to make testing the final grade calculation easier
     $params->courseid = $this->courseid;
     $params->fullname = 'unittestgradecalccategory';
     $params->aggregation = GRADE_AGGREGATE_MEAN;
     $params->aggregateonlygraded = 0;
     $grade_category = new grade_category($params, false);
     $grade_category->insert();
     $this->assertTrue(method_exists($grade_category, 'generate_grades'));
     $grade_category->load_grade_item();
     $cgi = $grade_category->get_grade_item();
     $cgi->grademin = 0;
     $cgi->grademax = 20;
     //3 grade items out of 10 but category is out of 20 to force scaling to occur
     $cgi->update();
     //3 grade items each with a maximum grade of 10
     $grade_items = array();
     for ($i = 0; $i < 3; $i++) {
         $grade_items[$i] = new grade_item();
         $grade_items[$i]->courseid = $this->courseid;
         $grade_items[$i]->categoryid = $grade_category->id;
         $grade_items[$i]->itemname = 'manual grade_item ' . $i;
         $grade_items[$i]->itemtype = 'manual';
         $grade_items[$i]->itemnumber = 0;
         $grade_items[$i]->needsupdate = false;
         $grade_items[$i]->gradetype = GRADE_TYPE_VALUE;
         $grade_items[$i]->grademin = 0;
         $grade_items[$i]->grademax = 10;
         $grade_items[$i]->iteminfo = 'Manual grade item used for unit testing';
         $grade_items[$i]->timecreated = time();
         $grade_items[$i]->timemodified = time();
         //used as the weight by weighted mean and as extra credit by mean with extra credit
         //Will be 0, 1 and 2
         $grade_items[$i]->aggregationcoef = $i;
         $grade_items[$i]->insert();
     }
     //a grade for each grade item
     $grade_grades = array();
     for ($i = 0; $i < 3; $i++) {
         $grade_grades[$i] = new grade_grade();
         $grade_grades[$i]->itemid = $grade_items[$i]->id;
         $grade_grades[$i]->userid = $this->userid;
         $grade_grades[$i]->rawgrade = ($i + 1) * 2;
         //produce grade grades of 2, 4 and 6
         $grade_grades[$i]->finalgrade = ($i + 1) * 2;
         $grade_grades[$i]->timecreated = time();
         $grade_grades[$i]->timemodified = time();
         $grade_grades[$i]->information = '1 of 2 grade_grades';
         $grade_grades[$i]->informationformat = FORMAT_PLAIN;
         $grade_grades[$i]->feedback = 'Good, but not good enough..';
         $grade_grades[$i]->feedbackformat = FORMAT_PLAIN;
         $grade_grades[$i]->insert();
     }
     //3 grade items with 1 grade_grade each.
     //grade grades have the values 2, 4 and 6
     //First correct answer is the aggregate with all 3 grades
     //Second correct answer is with the first grade (value 2) hidden
     $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEDIAN, 'GRADE_AGGREGATE_MEDIAN', 8, 8);
     $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MAX, 'GRADE_AGGREGATE_MAX', 12, 12);
     $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MODE, 'GRADE_AGGREGATE_MODE', 12, 12);
     //weighted mean. note grade totals are rounded to an int to prevent rounding discrepancies. correct final grade isnt actually exactly 10
     //3 items with grades 2, 4 and 6 with weights 0, 1 and 2 and all out of 10. then doubled to be out of 20.
     $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN, 'GRADE_AGGREGATE_WEIGHTED_MEAN', 10, 10);
     //simple weighted mean
     //3 items with grades 2, 4 and 6 equally weighted and all out of 10. then doubled to be out of 20.
     $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN2, 'GRADE_AGGREGATE_WEIGHTED_MEAN2', 8, 10);
     //mean of grades with extra credit
     //3 items with grades 2, 4 and 6 with extra credit 0, 1 and 2 equally weighted and all out of 10. then doubled to be out of 20.
     $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_EXTRACREDIT_MEAN, 'GRADE_AGGREGATE_EXTRACREDIT_MEAN', 10, 13);
     //aggregation tests the are affected by a hidden grade currently dont work as we dont store the altered grade in the database
     //instead an in memory recalculation is done. This should be remedied by MDL-11837
     //fails with 1 grade hidden. still reports 8 as being correct
     $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEAN, 'GRADE_AGGREGATE_MEAN', 8, 10);
     //fails with 1 grade hidden. still reports 4 as being correct
     $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MIN, 'GRADE_AGGREGATE_MIN', 4, 8);
     //fails with 1 grade hidden. still reports 12 as being correct
     $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_SUM, 'GRADE_AGGREGATE_SUM', 12, 10);
 }
示例#9
0
 private function create_grade_category($course)
 {
     static $cnt = 0;
     $cnt++;
     $grade_category = new grade_category(array('courseid' => $course->id, 'fullname' => 'Cat ' . $cnt), false);
     $grade_category->apply_default_settings();
     $grade_category->apply_forced_settings();
     $grade_category->insert();
     return $grade_category;
 }
 }
 // sync idnumber with grade_item
 if ($grade_item = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => $fromform->modulename, 'iteminstance' => $fromform->instance, 'itemnumber' => 0, 'courseid' => $course->id))) {
     if ($grade_item->idnumber != $fromform->cmidnumber) {
         $grade_item->idnumber = $fromform->cmidnumber;
         $grade_item->update();
     }
 }
 $items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $fromform->modulename, 'iteminstance' => $fromform->instance, 'courseid' => $course->id));
 // create parent category if requested and move to correct parent category
 if ($items and isset($fromform->gradecat)) {
     if ($fromform->gradecat == -1) {
         $grade_category = new grade_category();
         $grade_category->courseid = $course->id;
         $grade_category->fullname = $fromform->name;
         $grade_category->insert();
         if ($grade_item) {
             $parent = $grade_item->get_parent_category();
             $grade_category->set_parent($parent->id);
         }
         $fromform->gradecat = $grade_category->id;
     }
     foreach ($items as $itemid => $unused) {
         $items[$itemid]->set_parent($fromform->gradecat);
         if ($itemid == $grade_item->id) {
             // use updated grade_item
             $grade_item = $items[$itemid];
         }
     }
 }
 // add outcomes if requested
示例#11
0
 /**
  * Create a grade_category.
  *
  * @param array|stdClass $record
  * @return stdClass the grade category record
  */
 public function create_grade_category($record = null)
 {
     global $CFG;
     $this->gradecategorycounter++;
     $i = $this->gradecategorycounter;
     if (!isset($record['fullname'])) {
         $record['fullname'] = 'Grade category ' . $i;
     }
     // For gradelib classes.
     require_once $CFG->libdir . '/gradelib.php';
     // Create new grading category in this course.
     $gradecategory = new grade_category($record, false);
     $gradecategory->apply_default_settings();
     $gradecategory->apply_forced_settings();
     $gradecategory->insert();
     // This creates a default grade item for the category
     $gradeitem = $gradecategory->load_grade_item();
     if (isset($record->parentcategory)) {
         $gradecategory->set_parent($data->parentcategory);
     }
     $gradecategory->update_from_db();
     return $gradecategory->get_record_data();
 }
示例#12
0
    /**
     * Test the upgrade function for flagging courses with calculated grade item problems.
     */
    public function test_upgrade_calculated_grade_items() {
        global $DB, $CFG;
        $this->resetAfterTest();

        // Create a user.
        $user = $this->getDataGenerator()->create_user();

        // Create a couple of courses.
        $course1 = $this->getDataGenerator()->create_course();
        $course2 = $this->getDataGenerator()->create_course();
        $course3 = $this->getDataGenerator()->create_course();

        // Enrol the user in the courses.
        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
        $maninstance1 = $DB->get_record('enrol', array('courseid' => $course1->id, 'enrol' => 'manual'), '*', MUST_EXIST);
        $maninstance2 = $DB->get_record('enrol', array('courseid' => $course2->id, 'enrol' => 'manual'), '*', MUST_EXIST);
        $maninstance3 = $DB->get_record('enrol', array('courseid' => $course3->id, 'enrol' => 'manual'), '*', MUST_EXIST);
        $manual = enrol_get_plugin('manual');
        $manual->enrol_user($maninstance1, $user->id, $studentrole->id);
        $manual->enrol_user($maninstance2, $user->id, $studentrole->id);
        $manual->enrol_user($maninstance3, $user->id, $studentrole->id);

        // To create the data we need we freeze the grade book to use the old behaviour.
        set_config('gradebook_calculations_freeze_' . $course1->id, 20150627);
        set_config('gradebook_calculations_freeze_' . $course2->id, 20150627);
        set_config('gradebook_calculations_freeze_' . $course3->id, 20150627);
        $CFG->grade_minmaxtouse = 2;

        // Creating a category for a grade item.
        $gradecategory = new grade_category();
        $gradecategory->fullname = 'calculated grade category';
        $gradecategory->courseid = $course1->id;
        $gradecategory->insert();
        $gradecategoryid = $gradecategory->id;

        // This is a manual grade item.
        $gradeitem = new grade_item();
        $gradeitem->itemname = 'grade item one';
        $gradeitem->itemtype = 'manual';
        $gradeitem->categoryid = $gradecategoryid;
        $gradeitem->courseid = $course1->id;
        $gradeitem->idnumber = 'gi1';
        $gradeitem->insert();

        // Changing the category into a calculated grade category.
        $gradecategoryitem = grade_item::fetch(array('iteminstance' => $gradecategory->id));
        $gradecategoryitem->calculation = '=##gi' . $gradeitem->id . '##/2';
        $gradecategoryitem->update();

        // Setting a grade for the student.
        $grade = $gradeitem->get_grade($user->id, true);
        $grade->finalgrade = 50;
        $grade->update();
        // Creating all the grade_grade items.
        grade_regrade_final_grades($course1->id);
        // Updating the grade category to a new grade max and min.
        $gradecategoryitem->grademax = 50;
        $gradecategoryitem->grademin = 5;
        $gradecategoryitem->update();

        // Different manual grade item for course 2. We are creating a course with a calculated grade item that has a grade max of
        // 50. The grade_grade will have a rawgrademax of 100 regardless.
        $gradeitem = new grade_item();
        $gradeitem->itemname = 'grade item one';
        $gradeitem->itemtype = 'manual';
        $gradeitem->courseid = $course2->id;
        $gradeitem->idnumber = 'gi1';
        $gradeitem->grademax = 25;
        $gradeitem->insert();

        // Calculated grade item for course 2.
        $calculatedgradeitem = new grade_item();
        $calculatedgradeitem->itemname = 'calculated grade';
        $calculatedgradeitem->itemtype = 'manual';
        $calculatedgradeitem->courseid = $course2->id;
        $calculatedgradeitem->calculation = '=##gi' . $gradeitem->id . '##*2';
        $calculatedgradeitem->grademax = 50;
        $calculatedgradeitem->insert();

        // Assigning a grade for the user.
        $grade = $gradeitem->get_grade($user->id, true);
        $grade->finalgrade = 10;
        $grade->update();

        // Setting all of the grade_grade items.
        grade_regrade_final_grades($course2->id);

        // Different manual grade item for course 3. We are creating a course with a calculated grade item that has a grade max of
        // 50. The grade_grade will have a rawgrademax of 100 regardless.
        $gradeitem = new grade_item();
        $gradeitem->itemname = 'grade item one';
        $gradeitem->itemtype = 'manual';
        $gradeitem->courseid = $course3->id;
        $gradeitem->idnumber = 'gi1';
        $gradeitem->grademax = 25;
        $gradeitem->insert();

        // Calculated grade item for course 2.
        $calculatedgradeitem = new grade_item();
        $calculatedgradeitem->itemname = 'calculated grade';
        $calculatedgradeitem->itemtype = 'manual';
        $calculatedgradeitem->courseid = $course3->id;
        $calculatedgradeitem->calculation = '=##gi' . $gradeitem->id . '##*2';
        $calculatedgradeitem->grademax = 50;
        $calculatedgradeitem->insert();

        // Assigning a grade for the user.
        $grade = $gradeitem->get_grade($user->id, true);
        $grade->finalgrade = 10;
        $grade->update();

        // Setting all of the grade_grade items.
        grade_regrade_final_grades($course3->id);
        // Need to do this first before changing the other courses, otherwise they will be flagged too early.
        set_config('gradebook_calculations_freeze_' . $course3->id, null);
        upgrade_calculated_grade_items($course3->id);
        $this->assertEquals(20150627, $CFG->{'gradebook_calculations_freeze_' . $course3->id});

        // Change the setting back to null.
        set_config('gradebook_calculations_freeze_' . $course1->id, null);
        set_config('gradebook_calculations_freeze_' . $course2->id, null);
        // Run the upgrade.
        upgrade_calculated_grade_items();
        // The setting should be set again after the upgrade.
        $this->assertEquals(20150627, $CFG->{'gradebook_calculations_freeze_' . $course1->id});
        $this->assertEquals(20150627, $CFG->{'gradebook_calculations_freeze_' . $course2->id});
    }
 /**
  * Saves a gradebook (a set of grade items and scores related to a course),
  * also creates the categories based on the item type
  *
  * @param object $gradebook an object with at least course_id and items set
  * items should contain grade_items (courseid. categoryid, name, scores)
  * scores should contain grade_grade (user_id, score)
  * @return stdClass the saved gradebook with all items and scores in the same structure,
  * errors are recorded as grade_item->errors and score->error
  * @throws InvalidArgumentException
  */
 public static function save_gradebook($gradebook)
 {
     global $CFG;
     if (!$gradebook) {
         throw new InvalidArgumentException("gradebook must be set");
     }
     if (!isset($gradebook->course_id)) {
         throw new InvalidArgumentException("gradebook->course_id must be set");
     }
     if (!isset($gradebook->items) || empty($gradebook->items)) {
         throw new InvalidArgumentException("gradebook->items must be set and include items");
     }
     $gb_saved = new stdClass();
     $gb_saved->items = array();
     $gb_saved->course_id = $gradebook->course_id;
     $course = self::get_course($gradebook->course_id);
     if (!$course) {
         throw new InvalidArgumentException("No course found with course_id ({$gradebook->course_id})");
     }
     $gb_saved->course = $course;
     // extra permissions check on gradebook manage/update
     $user_id = self::require_user();
     if (class_exists('context_course')) {
         // for Moodle 2.2+
         $context = context_course::instance($course->id);
     } else {
         /** @noinspection PhpDeprecationInspection */
         $context = get_context_instance(CONTEXT_COURSE, $course->id);
         // deprecated
     }
     if (!$context || !has_capability('moodle/grade:manage', $context, $user_id)) {
         throw new InvalidArgumentException("User ({$user_id}) cannot manage the gradebook in course id={$course->id} (" . var_export($course, true) . "), context: " . var_export($context, true));
     }
     // attempt to get the default iclicker category first
     $default_iclicker_category = grade_category::fetch(array('courseid' => $gradebook->course_id, 'fullname' => self::GRADE_CATEGORY_NAME));
     $default_iclicker_category_id = $default_iclicker_category ? $default_iclicker_category->id : null;
     //echo "\n\nGRADEBOOK: ".var_export($gradebook);
     // iterate through and save grade items by calling other method
     if (!empty($gradebook->items)) {
         $saved_items = array();
         $number = 0;
         foreach ($gradebook->items as $grade_item) {
             // check for this category
             $item_category_name = self::GRADE_CATEGORY_NAME;
             if (!empty($grade_item->type) && self::GRADE_CATEGORY_NAME != $grade_item->type) {
                 $item_category_name = $grade_item->type;
                 $item_category = grade_category::fetch(array('courseid' => $gradebook->course_id, 'fullname' => $item_category_name));
                 if (!$item_category) {
                     // create the category
                     $params = array('courseid' => $gradebook->course_id, 'fullname' => $item_category_name);
                     $grade_category = new grade_category($params, false);
                     // Use default aggregation type.
                     $grade_category->aggregation = $CFG->grade_aggregation;
                     $grade_category->insert(self::GRADE_LOCATION_STR);
                     $item_category_id = $grade_category->id;
                 } else {
                     $item_category_id = $item_category->id;
                 }
             } else {
                 // use default
                 if (!$default_iclicker_category_id) {
                     // create the category
                     $params = array('courseid' => $gradebook->course_id, 'fullname' => self::GRADE_CATEGORY_NAME);
                     $grade_category = new grade_category($params, false);
                     // Use default aggregation type.
                     $grade_category->aggregation = $CFG->grade_aggregation;
                     $grade_category->insert(self::GRADE_LOCATION_STR);
                     $default_iclicker_category_id = $grade_category->id;
                 }
                 $item_category_id = $default_iclicker_category_id;
             }
             $grade_item->categoryid = $item_category_id;
             $grade_item->typename = $item_category_name;
             $grade_item->courseid = $gradebook->course_id;
             $grade_item->item_number = $number;
             $saved_grade_item = self::save_grade_item($grade_item);
             $saved_items[] = $saved_grade_item;
             $number++;
         }
         $gb_saved->items = $saved_items;
     }
     $gb_saved->default_category_id = $default_iclicker_category_id;
     //echo "\n\nRESULT: ".var_export($gb_saved);
     return $gb_saved;
 }
示例#14
0
 /**
  * This is the function which runs when cron calls.
  */
 public function execute()
 {
     global $CFG, $DB;
     echo "BEGIN >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n";
     /**
      * A script, to be run overnight, to pull L3VA scores from Leap and generate
      * the MAG, for each student on specifically-tagged courses, and add it into
      * our live Moodle.
      *
      * @copyright 2014-2015 Paul Vaughan
      * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      */
     // Script start time.
     $time_start = microtime(true);
     // Null or an int (course's id): run the script only for this course. For testing or one-offs.
     // TODO: Consider changing $thiscourse as an array, not an integer.
     $thiscourse = null;
     // null or e.g. 1234
     // TODO: can we use *all* the details in version.php? It would make a lot more sense.
     $version = '1.0.20';
     //$build      = '20150128';
     $build = get_config('block_leap', 'version');
     // Debugging.
     define('DEBUG', true);
     // Debugging.
     define('TRUNCATE_LOG', true);
     // Truncate the log table.
     if (TRUNCATE_LOG) {
         echo 'Truncating block_leap_log...';
         $DB->delete_records('block_leap_log', null);
         echo " done.\n";
     }
     overnight::tlog('GradeTracker script, v' . $version . ', ' . $build . '.', 'hiya');
     overnight::tlog('Started at ' . date('c', $time_start) . '.', ' go ');
     if ($thiscourse) {
         overnight::tlog('IMPORTANT! Processing only course \'' . $thiscourse . '\'.', 'warn');
     }
     overnight::tlog('', '----');
     // Before almost anything has the chance to fail, reset the fail delay setting back to 0.
     if (DEBUG) {
         if (!($reset = $DB->set_field('task_scheduled', 'faildelay', 0, array('component' => 'block_leap', 'classname' => '\\block_leap\\task\\overnight')))) {
             overnight::tlog('Scheduled task "fail delay" could not be reset.', 'warn');
         } else {
             overnight::tlog('Scheduled task "fail delay" reset to 0.', 'dbug');
         }
     }
     $leap_url = get_config('block_leap', 'leap_url');
     $auth_token = get_config('block_leap', 'auth_token');
     define('LEAP_API_URL', $leap_url . '/people/%s/views/courses.json?token=' . $auth_token);
     //overnight::tlog( 'Leap API URL: ' . LEAP_API_URL, 'dbug' );
     // Number of decimal places in the processed targets (and elsewhere).
     define('DECIMALS', 3);
     // Search term to use when searching for courses to process.
     define('IDNUMBERLIKE', 'leapcore_%');
     //define( 'IDNUMBERLIKE', 'leapcore_test' );
     // Category details for the above columns to go into.
     define('CATNAME', get_string('gradebook:category_title', 'block_leap'));
     // Include some details.
     require dirname(__FILE__) . '/../../details.php';
     // Logging array for the end-of-script summary.
     $logging = array('courses' => array(), 'students_processed' => array(), 'students_unique' => array(), 'no_l3va' => array(), 'not_updated' => array(), 'grade_types' => array('btec' => 0, 'a level' => 0, 'gcse' => 0, 'refer and pass' => 0, 'noscale' => 0, 'develop, pass' => 0), 'poor_grades' => array(), 'num' => array('courses' => 0, 'students_processed' => 0, 'students_unique' => 0, 'no_l3va' => 0, 'not_updated' => 0, 'grade_types' => 0, 'grade_types_in_use' => 0, 'poor_grades' => 0));
     // Small array to store the GCSE English and maths grades from the JSON.
     $gcse = array('english' => null, 'maths' => null);
     // Just for internal use, defines the grade type (int) and what it is (string).
     $gradetypes = array(0 => 'None', 1 => 'Value', 2 => 'Scale', 3 => 'Text');
     // Define the wanted column names (will appear in this order in the Gradebook, initially).
     // These column names are an integral part of this plugin and should not be changed.
     $column_names = array(get_string('gradebook:tag', 'block_leap') => get_string('gradebook:tag_desc', 'block_leap'));
     // Make an array keyed to the column names to store the grades in.
     $targets = array();
     foreach ($column_names as $name => $desc) {
         $targets[strtolower($name)] = '';
     }
     /**
      * The next section looks through all courses for those with a properly configured Leap block
      * and adds it (and the tracking configuration) to the $courses array.
      */
     overnight::tlog('', '----');
     $courses = $DB->get_records('course', null, null, 'id,shortname,fullname');
     $allcourses = array();
     foreach ($courses as $course) {
         if ($course->id != 1) {
             $coursecontext = \context_course::instance($course->id);
             if (!($blockrecord = $DB->get_record('block_instances', array('blockname' => 'leap', 'parentcontextid' => $coursecontext->id)))) {
                 if (DEBUG) {
                     overnight::tlog('No Leap block found for course "' . $course->id . '" (' . $course->shortname . ')', 'dbug');
                 }
                 continue;
             }
             if (!($blockinstance = block_instance('leap', $blockrecord))) {
                 if (DEBUG) {
                     overnight::tlog('No Leap block instance found for course "' . $course->id . '" (' . $course->shortname . ')', 'dbug');
                 }
                 continue;
             }
             if (isset($blockinstance->config->trackertype) && !empty($blockinstance->config->trackertype)) {
                 $course->trackertype = $blockinstance->config->trackertype;
                 $course->scalename = null;
                 $course->scaleid = null;
                 $course->gradeid = null;
                 $allcourses[] = $course;
                 if (DEBUG) {
                     overnight::tlog('Tracker "' . $blockinstance->config->trackertype . '" found in course ' . $course->id . ' (' . $course->shortname . ')', 'dbug');
                 }
             } else {
                 if (DEBUG) {
                     overnight::tlog('<Tracker not found in course ' . $course->id . ' (' . $course->shortname . ')', 'dbug');
                 }
             }
         }
         // END if $course != 1
     }
     // END foreach $courses as $course
     /*
             foreach ( $allcourses as $course ) {
                 // Ignore the course with id = 1, as it's the front page.
                 if ( $course->id == 1 ) {
                     continue;
                 } else {
                     // First get course context.
                     $coursecontext  = \context_course::instance( $course->id );
                     $blockrecord    = $DB->get_record( 'block_instances', array( 'blockname' => 'leap', 'parentcontextid' => $coursecontext->id ) );
                     $blockinstance  = block_instance( 'leap', $blockrecord );
                     // Check and add trackertype and coursetype to the $course object.
                     if (
                         isset( $blockinstance->config->trackertype ) &&
                         !empty( $blockinstance->config->trackertype ) )
                         //!empty( $blockinstance->config->trackertype ) &&
                         //isset( $blockinstance->config->coursetype ) &&
                         //!empty( $blockinstance->config->coursetype ) )
                     {
                         $course->trackertype    = $blockinstance->config->trackertype;
                         //$course->coursetype     = $blockinstance->config->coursetype;
                         // Setting some more variables we'll need in due course.
                         $course->scalename      = null;
                         $course->scaleid        = null;
                         $course->gradeid        = null;
                         // All good, so...
                         $courses[] = $course;
                         overnight::tlog( 'Course \'' . $course->fullname . '\' (' . $course->shortname . ') [' . $course->id . '] added to process list.', 'info');
                         if ( DEBUG ) {
                             overnight::tlog( json_encode( $course ), 'dbug');
                         }
                     }
                 }
             }
     */
     //var_dump($courses); exit(0);
     /* Example $courses array.
     array(2) {
       [0]=>
       object(stdClass)#91 (7) {
         ["id"]=>
         string(1) "2"
         ["shortname"]=>
         string(5) "TC101"
         ["fullname"]=>
         string(15) "Test Course 101"
         ["trackertype"]=>
         string(7) "english"
         ["coursetype"]=>
         string(14) "a2_englishlang"
         ["scalename"]=>
         string(0) ""
         ["scaleid"]=>
         NULL
       }
       [1]=>
       object(stdClass)#92 (7) {
         ["id"]=>
         string(1) "3"
         ["shortname"]=>
         string(5) "TC201"
         ["fullname"]=>
         string(15) "Test course 201"
         ["trackertype"]=>
         string(7) "english"
         ["coursetype"]=>
         string(6) "a2_law"
         ["scalename"]=>
         string(0) ""
         ["scaleid"]=>
         NULL
       }
     }
     
     */
     $num_courses = count($allcourses);
     $cur_courses = 0;
     if ($num_courses == 0) {
         overnight::tlog('No courses found to process, so halting.', 'EROR');
         // Returning false indicates failure. We didn't fail, just found no courses to process.
         return true;
     }
     overnight::tlog('', '----');
     /**
      * Sets up each configured course with a category and columns within it.
      */
     foreach ($allcourses as $course) {
         $cur_courses++;
         overnight::tlog('Processing course (' . $cur_courses . '/' . $num_courses . ') ' . $course->fullname . ' (' . $course->shortname . ') [' . $course->id . '] at ' . date('c', time()) . '.', 'info');
         $logging['courses'][] = $course->fullname . ' (' . $course->shortname . ') [' . $course->id . '].';
         //overnight::tlog( $course->coursetype, 'PVDB' );
         /*
         We need to give serious thought to NOT doing this, as it's basically impossible to set the correct scale at this point.
         Grades and that:
         
         Develop / Pass - would be used for all pass only type qualifications but develop will be used instead of refer, retake, fail etc.
         U, G, F, E, D, C, B, A, A*  - to be used for GCSE / A-level plus any others that have letter grades.
         Refer, Pass, Merit, Distinction.
         Refer, PP, PM, MM, MD, DD
         Refer, PPP, PPM, PMM, MMM, MMD, MDD, DDD
         Numbers from 0 - 100 - in traffic light systems the numbers will be compared and anything lower than the target will be red, higher than target will be green.
         */
         /*
                     // Work out the scale from the course type.
                     if ( stristr( $course->coursetype, 'as_' ) || stristr( $course->coursetype, 'a2_' ) ) {
                         $course->scalename  = 'A Level';
                     } else if ( stristr( $course->coursetype, 'gcse_' ) ) {
                         $course->scalename  = 'GCSE';
                     } else if ( stristr( $course->coursetype, 'btec_' ) ) {
                         $course->scalename  = 'BTEC';
                     }
                     overnight::tlog( 'Course ' . $course->id . ' appears to be a ' . $course->scalename . ' course.', 'info' );
         
                     // Get the scale ID.
                     if ( !$moodlescale = $DB->get_record( 'scale', array( 'name' => $course->scalename ), 'id' ) ) {
                         overnight::tlog( '- Could not find a scale called \'' . $course->scalename . '\' for course ' . $course->id . '.', 'warn' );
         
                     } else {
                         // Scale located.
                         $course->scaleid = $moodlescale->id;
                         overnight::tlog( '- Scale called \'' . $course->scalename . '\' found with ID ' . $moodlescale->id . '.', 'info' );
                     }
         
                     overnight::tlog( json_encode( $course ), '>dbg');
                     //var_dump($courses); exit(0);
         */
         overnight::tlog(json_encode($course), '>dbg');
         // Figure out the grade type and scale here, pulled directly from the course's gradebook's course itemtype.
         $coursegradescale = $DB->get_record('grade_items', array('courseid' => $course->id, 'itemtype' => 'course'), 'gradetype, scaleid');
         $course->gradeid = $coursegradescale->gradetype;
         $course->scaleid = $coursegradescale->scaleid;
         overnight::tlog(json_encode($course), 'PVDB');
         if ($course->gradeid == 2) {
             if ($coursescale = $DB->get_record('scale', array('id' => $course->scaleid))) {
                 $course->scalename = $coursescale->name;
                 $tolog = '- Scale \'' . $course->scaleid . '\' (' . $coursescale->name . ') found [' . $coursescale->scale . ']';
                 $tolog .= $coursescale->courseid ? ' (which is specific to course ' . $coursescale->courseid . ').' : ' (which is global).';
                 overnight::tlog($tolog, 'info');
             } else {
                 // If the scale doesn't exist that the course is using, this is a problem.
                 overnight::tlog('- Gradetype \'2\' set, but no matching scale found.', 'warn');
             }
         }
         overnight::tlog(json_encode($course), 'PVDB');
         /*
                     if ( $coursegradescale = $DB->get_record( 'grade_items', array( 'courseid' => $course->id, 'itemtype' => 'course' ), 'gradetype, scaleid' ) ) {
         
            $course->gradeid = $coursegradescale->gradetype;
            $course->scaleid = $coursegradescale->scaleid;
         
            overnight::tlog( 'Gradetype \'' . $course->gradeid . '\' (' . $gradetypes[$course->gradeid] . ') found.', 'info' );
         
            // If the grade type is 2 / scale.
            if ( $course->gradeid == 2 ) {
                if ( $coursescale = $DB->get_record( 'scale', array( 'id' => $course->scaleid ) ) ) {
         
                    $course->scalename  = $coursescale->name;
                    //$course->scaleid    = $scaleid;
         
                    //$course->coursetype = $coursescale->name;
         
                    $tolog = '- Scale \'' . $course->scaleid . '\' (' . $course->scalename . ') found';
                    $tolog .= ( $coursescale->courseid ) ? ' (which is specific to course ' . $coursescale->courseid . ').' : ' (which is global).';
                    overnight::tlog( $tolog, 'info' );
         
                } else {
         
                    // If the scale doesn't exist that the course is using, this is a problem.
                    overnight::tlog( '- Gradetype \'2\' set, but no matching scale found.', 'warn' );
         
                }
         
            } else if ( $course->gradeid == 1 ) {
                // If the grade type is 1 / value.
                $course->scalename  = 'noscale';
                $course->scaleid    = 1;
                // Already set, above.
                //$course->coursetype = 'Value';
         
                $tolog = ' Using \'' . $gradetypes[$gradeid] . '\' gradetype.';
         
            }
         
         
                     } else {
            // Set it to default if no good scale could be found/used.
            $gradeid = 0;
            $scaleid = 0;
            overnight::tlog('No \'gradetype\' found, so using defaults instead.', 'info');
                     }
         
         
                     // You may get errors here (unknown index IIRC) if no scalename is generated because a scale (or anything) hasn't been set
                     // for that course (e.g. 'cos it's a new course). Catch this earlier!
                     $logging['grade_types'][strtolower($course->scalename)]++;
         
                     /**
         * Category checking: create or skip.
         */
         if ($DB->get_record('grade_categories', array('courseid' => $course->id, 'fullname' => CATNAME))) {
             // Category exists, so skip creation.
             overnight::tlog('Category \'' . CATNAME . '\' already exists for course ' . $course->id . '.', 'skip');
         } else {
             $grade_category = new \grade_category();
             // Create a category for this course.
             $grade_category->courseid = $course->id;
             // Course id.
             $grade_category->fullname = CATNAME;
             // Set the category name (no description).
             $grade_category->sortorder = 1;
             // Need a better way of changing column order.
             $grade_category->hidden = 1;
             // Attempting to hide the totals.
             // Save all that...
             if (!($gc = $grade_category->insert())) {
                 overnight::tlog('Category \'' . CATNAME . '\' could not be inserted for course ' . $course->id . '.', 'EROR');
                 return false;
             } else {
                 overnight::tlog('Category \'' . CATNAME . '\' (' . $gc . ') created for course ' . $course->id . '.');
             }
         }
         // We've either checked a category exists or created one, so this *should* always work.
         $cat_id = $DB->get_record('grade_categories', array('courseid' => $course->id, 'fullname' => CATNAME));
         $cat_id = $cat_id->id;
         // One thing we need to do is set 'gradetype' to 0 on that newly created category, which prevents a category total showing
         // and the grades counting towards the total course grade.
         $DB->set_field_select('grade_items', 'gradetype', 0, "courseid = " . $course->id . " AND itemtype = 'category' AND iteminstance = " . $cat_id);
         /**
          * Column checking: create or update.
          */
         // Step through each column name.
         foreach ($column_names as $col_name => $col_desc) {
             // Need to check for previously-created columns and force an update if they already exist.
             //if ( $DB->get_record('grade_items', array( 'courseid' => $course->id, 'itemname' => $col_name, 'itemtype' => 'manual' ) ) ) {
             //    // Column exists, so update instead.
             //    overnight::tlog('- Column \'' . $col_name . '\' already exists for course ' . $course->id . '.', 'skip');
             //} else {
             $grade_item = new \grade_item();
             // Create a new item object.
             $grade_item->courseid = $course->id;
             // Course id.
             $grade_item->itemtype = 'manual';
             // Set the category name (no description).
             $grade_item->itemname = $col_name;
             // The item's name.
             $grade_item->iteminfo = $col_desc;
             // Description of the item.
             $grade_item->categoryid = $cat_id;
             // Set the immediate parent category.
             $grade_item->hidden = 0;
             // Don't want it hidden (by default).
             $grade_item->locked = 1;
             // Lock it (by default).
             // Per-column specifics.
             if ($col_name == 'TAG') {
                 $grade_item->sortorder = 1;
                 // In-category sort order.
                 //$grade_item->gradetype  = $course->gradeid;
                 $grade_item->gradetype = 3;
                 // Text field.
                 //$grade_item->scaleid    = $course->scaleid;
                 //$grade_item->display    = 1;        // 'Real'. MIGHT need to seperate out options for BTEC and A Level.
                 $grade_item->display = 0;
                 // No frills.
             }
             //if ( $col_name == 'L3VA' ) {
             //    // Lock the L3VA col as it's calculated elsewhere.
             //    $grade_item->sortorder  = 2;
             //    $grade_item->locked     = 1;
             //    $grade_item->decimals   = 0;
             //    $grade_item->display    = 1; // 'Real'.
             //}
             //if ( $col_name == 'MAG' ) {
             //    $grade_item->sortorder  = 3;
             //    //$grade_item->locked     = 1;
             //    $grade_item->gradetype  = $gradeid;
             //    $grade_item->scaleid    = $scaleid;
             //    $grade_item->display    = 1; // 'Real'.
             //}
             // Scale ID, generated earlier. An int, 0 or greater.
             // TODO: Check if we need this any more!!
             //$grade_item->scale = $course->scaleid;
             // Check to see if this record already exists, determining if we insert or update.
             if (!($grade_items_exists = $DB->get_record('grade_items', array('courseid' => $course->id, 'itemname' => $col_name, 'itemtype' => 'manual')))) {
                 // INSERT a new record.
                 if (!($gi = $grade_item->insert())) {
                     overnight::tlog('- Column \'' . $col_name . '\' could not be inserted for course ' . $course->id . '.', 'EROR');
                     return false;
                 } else {
                     overnight::tlog('- Column \'' . $col_name . '\' created for course ' . $course->id . '.');
                 }
             } else {
                 // UPDATE the existing record.
                 $grade_item->id = $grade_items_exists->id;
                 if (!($gi = $grade_item->update())) {
                     overnight::tlog('- Column \'' . $col_name . '\' could not be updated for course ' . $course->id . '.', 'EROR');
                     return false;
                 } else {
                     overnight::tlog('- Column \'' . $col_name . '\' updated for course ' . $course->id . '.');
                 }
             }
             //} // END skip processing if manual column(s) already found in course.
         }
         // END while working through each rquired column.
         // Good to here.
         /**
          * Move the category to the first location in the gradebook if it isn't already.
          */
         //$gtree = new grade_tree($course->id, false, false);
         //$temp = grade_edit_tree::move_elements(1, '')
         /**
          * Collect enrolments based on each of those courses
          */
         // EPIC 'get enrolled students' query from Stack Overflow:
         // http://stackoverflow.com/questions/22161606/sql-query-for-courses-enrolment-on-moodle
         // Only selects manually enrolled, not self-enrolled student roles (redacted!).
         $sql = "SELECT DISTINCT u.id AS userid, firstname, lastname, username\n                FROM mdl_user u\n                    JOIN mdl_user_enrolments ue ON ue.userid = u.id\n                    JOIN mdl_enrol e ON e.id = ue.enrolid\n                        -- AND e.enrol = 'leap'\n                    JOIN mdl_role_assignments ra ON ra.userid = u.id\n                    JOIN mdl_context ct ON ct.id = ra.contextid\n                        AND ct.contextlevel = 50\n                    JOIN mdl_course c ON c.id = ct.instanceid\n                        AND e.courseid = c.id\n                    JOIN mdl_role r ON r.id = ra.roleid\n                        AND r.shortname = 'student'\n                WHERE courseid = " . $course->id . "\n                    AND e.status = 0\n                    AND u.suspended = 0\n                    AND u.deleted = 0\n                    AND (\n                        ue.timeend = 0\n                        OR ue.timeend > NOW()\n                    )\n                    AND ue.status = 0\n                ORDER BY userid ASC;";
         if (!($enrollees = $DB->get_records_sql($sql))) {
             overnight::tlog('No enrolled students found for course ' . $course->id . '.', 'warn');
         } else {
             $num_enrollees = count($enrollees);
             overnight::tlog('Found ' . $num_enrollees . ' students enrolled onto course ' . $course->id . '.', 'info');
             // A variable to store which enrollee we're processing.
             $cur_enrollees = 0;
             foreach ($enrollees as $enrollee) {
                 $cur_enrollees++;
                 // Attempt to extract the student ID from the username.
                 $tmp = explode('@', $enrollee->username);
                 $enrollee->studentid = $tmp[0];
                 // A proper student, hopefully.
                 overnight::tlog('- Processing user (' . $cur_enrollees . '/' . $num_enrollees . ') ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->userid . ') [' . $enrollee->studentid . '] on course ' . $course->id . '.', 'info');
                 $logging['students_processed'][] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . '.';
                 $logging['students_unique'][$enrollee->userid] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '].';
                 // Assemble the URL with the correct data.
                 $leapdataurl = sprintf(LEAP_API_URL, $enrollee->studentid);
                 //if ( DEBUG ) {
                 //    overnight::tlog('-- Leap URL: ' . $leapdataurl, 'dbug');
                 //}
                 // Use fopen to read from the API.
                 if (!($handle = fopen($leapdataurl, 'r'))) {
                     // If the API can't be reached for some reason.
                     overnight::tlog('- Cannot open ' . $leapdataurl . '.', 'EROR');
                 } else {
                     // API reachable, get the data.
                     $leapdata = fgets($handle);
                     fclose($handle);
                     if (DEBUG) {
                         overnight::tlog('-- Returned JSON: ' . $leapdata, 'dbug');
                     }
                     // Handle an empty result from the API.
                     if (strlen($leapdata) == 0) {
                         overnight::tlog('-- API returned 0 bytes.', 'EROR');
                     } else {
                         // Decode the JSON into an object.
                         $leapdata = json_decode($leapdata);
                         // Checking for JSON decoding errors, seems only right.
                         if (json_last_error()) {
                             overnight::tlog('-- JSON decoding returned error code ' . json_last_error() . ' for user ' . $enrollee->studentid . '.', 'EROR');
                         } else {
                             // We have a L3VA score! And possibly GCSE English and maths grades too.
                             //$targets['l3va']    = number_format( $leapdata->person->l3va, DECIMALS );
                             //$gcse['english']    = $leapdata->person->gcse_english;
                             //$gcse['maths']      = $leapdata->person->gcse_maths;
                             //if ( $targets['l3va'] == '' || !is_numeric( $targets['l3va'] ) || $targets['l3va'] <= 0 ) {
                             //    // If the L3VA isn't good.
                             //    overnight::tlog('-- L3VA is not good: \'' . $targets['l3va'] . '\'.', 'warn');
                             //    $logging['no_l3va'][$enrollee->userid] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '].';
                             //
                             //} else {
                             //overnight::tlog('-- ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->userid . ') [' . $enrollee->studentid . '] L3VA score: ' . $targets['l3va'] . '.', 'info');
                             overnight::tlog('-- ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->userid . ') [' . $enrollee->studentid . '].', 'info');
                             // If this course is tagged as a GCSE English or maths course, use the grades supplied in the JSON.
                             /*
                                                                 if ( $course->coursetype == 'leapcore_gcse_english' ) {
                                                                     $magtemp        = overnight::make_mag( $gcse['english'], $course->coursetype, $course->scalename );
                                                                     $tagtemp        = array( null, null );
                             
                                                                 } else if ( $course->coursetype == 'leapcore_gcse_maths' ) {
                                                                     $magtemp        = overnight::make_mag( $gcse['maths'], $course->coursetype, $course->scalename );
                                                                     $tagtemp        = array( null, null );
                             
                                                                 } else {
                                                                     // Make the MAG from the L3VA.
                                                                     $magtemp        = overnight::make_mag( $targets['l3va'], $course->coursetype, $course->scalename );
                                                                     // Make the TAG in the same way, setting 'true' at the end for the next grade up.
                                                                     $tagtemp        = overnight::make_mag( $targets['l3va'], $course->coursetype, $course->scalename, true );
                             
                                                                 }
                                                                 $targets['mag'] = $magtemp[0];
                                                                 $targets['tag'] = $tagtemp[0];
                             */
                             /*
                                                                 if ( $course->coursetype == 'leapcore_gcse_english' || $course->coursetype == 'leapcore_gcse_maths' ) {
                                                                     overnight::tlog('--- GCSEs passed through from Leap JSON: MAG: \'' . $targets['mag'] . '\' ['. $magtemp[1] .']. TAG: \'' . $targets['tag'] . '\' ['. $tagtemp[1] .'].', 'info');
                                                                 } else {
                                                                     overnight::tlog('--- Generated data: MAG: \'' . $targets['mag'] . '\' ['. $magtemp[1] .']. TAG: \'' . $targets['tag'] . '\' ['. $tagtemp[1] .'].', 'info');
                                                                 }
                             
                                                                 if ( $targets['mag'] == '0' || $targets['mag'] == '1' ) {
                                                                     $logging['poor_grades'][] = 'MAG ' . $targets['mag'] . ' assigned to ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . '.';
                                                                 }
                                                                 if ( $targets['tag'] == '0' || $targets['tag'] == '1' ) {
                                                                     $logging['poor_grades'][] = 'TAG ' . $targets['tag'] . ' assigned to ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . '.';
                                                                 }
                             */
                             // Loop through all settable, updateable grades.
                             foreach ($targets as $target => $score) {
                                 // Need the grade_items.id for grade_grades.itemid.
                                 $gradeitem = $DB->get_record('grade_items', array('courseid' => $course->id, 'itemname' => strtoupper($target)), 'id, categoryid');
                                 // Check to see if this data already exists in the database, so we can insert or update.
                                 $gradegrade = $DB->get_record('grade_grades', array('itemid' => $gradeitem->id, 'userid' => $enrollee->userid), 'id');
                                 // New grade_grade object.
                                 $grade = new \grade_grade();
                                 $grade->userid = $enrollee->userid;
                                 $grade->itemid = $gradeitem->id;
                                 $grade->categoryid = $gradeitem->categoryid;
                                 $grade->rawgrade = $score;
                                 // Will stay as set.
                                 $grade->finalgrade = $score;
                                 // Will change with the grade, e.g. 3.
                                 $grade->timecreated = time();
                                 $grade->timemodified = $grade->timecreated;
                                 // TODO: "excluded" is a thing and prevents a grade being aggregated.
                                 $grade->excluded = true;
                                 // If no id exists, INSERT.
                                 if (!$gradegrade) {
                                     if (!($gl = $grade->insert())) {
                                         overnight::tlog('--- ' . strtoupper($target) . ' insert failed for user ' . $enrollee->userid . ' on course ' . $course->id . '.', 'EROR');
                                     } else {
                                         overnight::tlog('--- ' . strtoupper($target) . ' (' . $score . ') inserted for user ' . $enrollee->userid . ' on course ' . $course->id . '.');
                                     }
                                 } else {
                                     // If the row already exists, UPDATE, but don't ever *update* the TAG.
                                     //if ( $target == 'mag' && !$score ) {
                                     //    // For MAGs, we don't want to update to a zero or null score as that may overwrite a manually-entered MAG.
                                     //    overnight::tlog('--- ' . strtoupper( $target ) . ' of 0 or null (' . $score . ') purposefully not updated for user ' . $enrollee->userid . ' on course ' . $course->id . '.' );
                                     //    $logging['not_updated'][] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . ': ' . strtoupper( $target ) . ' of \'' . $score . '\'.';
                                     //} else if ( $target != 'tag' ) {
                                     $grade->id = $gradegrade->id;
                                     // We don't want to set this again, but we do want the modified time set.
                                     unset($grade->timecreated);
                                     $grade->timemodified = time();
                                     if (!($gl = $grade->update())) {
                                         overnight::tlog('--- ' . strtoupper($target) . ' update failed for user ' . $enrollee->userid . ' on course ' . $course->id . '.', 'EROR');
                                     } else {
                                         overnight::tlog('--- ' . strtoupper($target) . ' (' . $score . ') update for user ' . $enrollee->userid . ' on course ' . $course->id . '.');
                                     }
                                     //} else {
                                     //    overnight::tlog('--- ' . strtoupper( $target ) . ' purposefully not updated for user ' . $enrollee->userid . ' on course ' . $course->id . '.', 'skip' );
                                     //    $logging['not_updated'][] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . ': ' . strtoupper( $target ) . ' of \'' . $score . '\'.';
                                     //} // END ignore updating the TAG.
                                 }
                                 // END insert or update check.
                             }
                             // END foreach loop.
                             //} // END L3VA check.
                         }
                         // END any json_decode errors.
                     }
                     // END empty API result.
                 }
                 // END open leap API for reading.
             }
             // END cycle through each course enrollee.
         }
         // END enrollee query.
         // Final blank-ish log entry to separate out one course from another.
         overnight::tlog('', '----');
     }
     // END foreach course tagged 'leapcore_*'.
     // Sort and dump the summary log.
     overnight::tlog('Summary of all performed operations.', 'smry');
     asort($logging['courses']);
     asort($logging['students_processed']);
     asort($logging['students_unique']);
     asort($logging['no_l3va']);
     asort($logging['not_updated']);
     arsort($logging['grade_types']);
     asort($logging['poor_grades']);
     // Processing.
     $logging['num']['courses'] = count($logging['courses']);
     $logging['num']['students_processed'] = count($logging['students_processed']);
     $logging['num']['students_unique'] = count($logging['students_unique']);
     $logging['num']['no_l3va'] = count($logging['no_l3va']);
     $logging['num']['not_updated'] = count($logging['not_updated']);
     $logging['num']['grade_types'] = count($logging['grade_types']);
     foreach ($logging['grade_types'] as $value) {
         $logging['num']['grade_types_in_use'] += $value;
     }
     $logging['num']['poor_grades'] = count($logging['poor_grades']);
     if ($logging['num']['courses']) {
         overnight::tlog($logging['num']['courses'] . ' courses:', 'smry');
         $count = 0;
         foreach ($logging['courses'] as $course) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $course, 'smry');
         }
     } else {
         overnight::tlog('No courses processed.', 'warn');
     }
     if ($logging['num']['students_processed']) {
         overnight::tlog($logging['num']['students_processed'] . ' student-courses processed:', 'smry');
         $count = 0;
         foreach ($logging['students_processed'] as $student) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $student, 'smry');
         }
     } else {
         overnight::tlog('No student-courses processed.', 'warn');
     }
     if ($logging['num']['students_unique']) {
         overnight::tlog($logging['num']['students_unique'] . ' unique students:', 'smry');
         $count = 0;
         foreach ($logging['students_unique'] as $student) {
             echo sprintf('%4s', ++$count) . ': ' . $student . "\n";
             overnight::tlog('- ' . sprintf('%4s', $count) . ': ' . $student, 'smry');
         }
     } else {
         overnight::tlog('No unique students processed.', 'warn');
     }
     if ($logging['num']['no_l3va']) {
         overnight::tlog($logging['num']['no_l3va'] . ' students with no L3VA:', 'smry');
         $count = 0;
         foreach ($logging['no_l3va'] as $no_l3va) {
             echo sprintf('%4s', ++$count) . ': ' . $no_l3va . "\n";
             overnight::tlog('- ' . sprintf('%4s', $count) . ': ' . $no_l3va, 'smry');
         }
     } else {
         overnight::tlog('No missing L3VAs.', 'warn');
     }
     if ($logging['num']['not_updated']) {
         overnight::tlog($logging['num']['not_updated'] . ' students purposefully not updated (0 or null grade):', 'smry');
         $count = 0;
         foreach ($logging['not_updated'] as $not_updated) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $not_updated, 'smry');
         }
     } else {
         overnight::tlog('No students purposefully not updated.', 'warn');
     }
     if ($logging['num']['grade_types']) {
         overnight::tlog($logging['num']['grade_types'] . ' grade types with ' . $logging['num']['grade_types_in_use'] . ' grades set:', 'smry');
         $count = 0;
         foreach ($logging['grade_types'] as $grade_type => $num_grades) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $grade_type . ': ' . $num_grades, 'smry');
         }
     } else {
         overnight::tlog('No grade_types found.', 'warn');
     }
     if ($logging['num']['poor_grades']) {
         overnight::tlog($logging['num']['poor_grades'] . ' poor grades:', 'smry');
         $count = 0;
         foreach ($logging['poor_grades'] as $poorgrade) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $poorgrade, 'smry');
         }
     } else {
         overnight::tlog('No poor grades found. Good!', 'smry');
     }
     // Finish time.
     $time_end = microtime(true);
     $duration = $time_end - $time_start;
     $mins = floor($duration / 60) == 0 ? '' : floor($duration / 60) . ' minutes';
     $secs = $duration % 60 == 0 ? '' : $duration % 60 . ' seconds';
     $secs = $mins == '' ? $secs : ' ' . $secs;
     overnight::tlog('', '----');
     overnight::tlog('Finished at ' . date('c', $time_end) . ', took ' . $mins . $secs . ' (' . number_format($duration, DECIMALS) . ' seconds).', 'byby');
     //exit(0);
     return true;
     echo "\nEND   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n";
 }
示例#15
0
  * Category checking or creation.
  */
 if ($DB->get_record('grade_categories', array('courseid' => $course->id, 'fullname' => CATNAME))) {
     // Category exists, so skip creation.
     tlog('Category \'' . CATNAME . '\' already exists for course ' . $course->id . '.', 'skip');
 } else {
     // Create a category for this course.
     $grade_category = new grade_category();
     // Course id.
     $grade_category->courseid = $course->id;
     // Set the category name (no description).
     $grade_category->fullname = CATNAME;
     // Set the sort order (making this the first category in the gradebook, hopefully).
     $grade_category->sortorder = 1;
     // Save all that...
     if (!($gc = $grade_category->insert())) {
         tlog('Category \'' . CATNAME . '\' could not be inserted for course ' . $course->id . '.', 'EROR');
         exit(1);
     } else {
         tlog('Category \'' . CATNAME . '\' (' . $gc . ') created for course ' . $course->id . '.');
     }
 }
 // We've either checked a category exists or created one, so this *should* always work.
 $cat_id = $DB->get_record('grade_categories', array('courseid' => $course->id, 'fullname' => CATNAME));
 $cat_id = $cat_id->id;
 // One thing we need to do is set 'gradetype' to 0 on that newly created category, which prevents a category total showing
 // and the grades counting towards the total course grade.
 $DB->set_field_select('grade_items', 'gradetype', 0, "courseid = " . $course->id . " AND itemtype = 'category' AND iteminstance = " . $cat_id);
 /**
  * Column checking or creation.
  */
示例#16
0
 function test_upgrade_calculated_grade_items_regrade()
 {
     global $DB, $CFG;
     $this->resetAfterTest();
     // Create a user.
     $user = $this->getDataGenerator()->create_user();
     // Create a course.
     $course = $this->getDataGenerator()->create_course();
     // Enrol the user in the course.
     $studentrole = $DB->get_record('role', array('shortname' => 'student'));
     $maninstance1 = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST);
     $manual = enrol_get_plugin('manual');
     $manual->enrol_user($maninstance1, $user->id, $studentrole->id);
     set_config('upgrade_calculatedgradeitemsonlyregrade', 1);
     // Creating a category for a grade item.
     $gradecategory = new grade_category();
     $gradecategory->fullname = 'calculated grade category';
     $gradecategory->courseid = $course->id;
     $gradecategory->insert();
     $gradecategoryid = $gradecategory->id;
     // This is a manual grade item.
     $gradeitem = new grade_item();
     $gradeitem->itemname = 'grade item one';
     $gradeitem->itemtype = 'manual';
     $gradeitem->categoryid = $gradecategoryid;
     $gradeitem->courseid = $course->id;
     $gradeitem->idnumber = 'gi1';
     $gradeitem->insert();
     // Changing the category into a calculated grade category.
     $gradecategoryitem = grade_item::fetch(array('iteminstance' => $gradecategory->id));
     $gradecategoryitem->calculation = '=##gi' . $gradeitem->id . '##/2';
     $gradecategoryitem->grademax = 50;
     $gradecategoryitem->grademin = 15;
     $gradecategoryitem->update();
     // Setting a grade for the student.
     $grade = $gradeitem->get_grade($user->id, true);
     $grade->finalgrade = 50;
     $grade->update();
     grade_regrade_final_grades($course->id);
     $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id));
     $grade->rawgrademax = 100;
     $grade->rawgrademin = 0;
     $grade->update();
     $this->assertNotEquals($gradecategoryitem->grademax, $grade->rawgrademax);
     $this->assertNotEquals($gradecategoryitem->grademin, $grade->rawgrademin);
     // This is the function that we are testing. If we comment out this line, then the test fails because the grade items
     // are not flagged for regrading.
     upgrade_calculated_grade_items();
     grade_regrade_final_grades($course->id);
     $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id));
     $this->assertEquals($gradecategoryitem->grademax, $grade->rawgrademax);
     $this->assertEquals($gradecategoryitem->grademin, $grade->rawgrademin);
 }
示例#17
0
 public function test_create_module()
 {
     global $CFG, $SITE, $DB;
     if (!file_exists("{$CFG->dirroot}/mod/page/")) {
         $this->markTestSkipped('Can not find standard Page module');
     }
     $this->resetAfterTest(true);
     $generator = $this->getDataGenerator();
     $page = $generator->create_module('page', array('course' => $SITE->id));
     $this->assertNotEmpty($page);
     $cm = get_coursemodule_from_instance('page', $page->id, $SITE->id, true);
     $this->assertEquals(0, $cm->sectionnum);
     $page = $generator->create_module('page', array('course' => $SITE->id), array('section' => 3));
     $this->assertNotEmpty($page);
     $cm = get_coursemodule_from_instance('page', $page->id, $SITE->id, true);
     $this->assertEquals(3, $cm->sectionnum);
     // Prepare environment to generate modules with all possible options.
     // Enable advanced functionality.
     $CFG->enablecompletion = 1;
     $CFG->enableavailability = 1;
     $CFG->enableoutcomes = 1;
     require_once $CFG->libdir . '/gradelib.php';
     require_once $CFG->libdir . '/completionlib.php';
     require_once $CFG->dirroot . '/rating/lib.php';
     // Create a course with enabled completion.
     $course = $generator->create_course(array('enablecompletion' => true));
     // Create new grading category in this course.
     $grade_category = new grade_category();
     $grade_category->courseid = $course->id;
     $grade_category->fullname = 'Grade category';
     $grade_category->insert();
     // Create group and grouping.
     $group = $generator->create_group(array('courseid' => $course->id));
     $grouping = $generator->create_grouping(array('courseid' => $course->id));
     $generator->create_grouping_group(array('groupid' => $group->id, 'groupingid' => $grouping->id));
     // Prepare arrays with properties that we can both use for creating modules and asserting the data in created modules.
     // General properties.
     $optionsgeneral = array('visible' => 0, 'section' => 3, 'cmidnumber' => 'IDNUM', 'groupmode' => SEPARATEGROUPS, 'groupingid' => $grouping->id);
     // In case completion is enabled on site and for course every module can have manual completion.
     $featurecompletionmanual = array('completion' => COMPLETION_TRACKING_MANUAL, 'completionexpected' => time() + 7 * DAYSECS);
     // Automatic completion is possible if module supports FEATURE_COMPLETION_TRACKS_VIEWS or FEATURE_GRADE_HAS_GRADE.
     // Note: completionusegrade is stored in DB and can be found in cm_info as 'completiongradeitemnumber' - either NULL or 0.
     // Note: module can have more autocompletion rules as defined in moodleform_mod::add_completion_rules().
     $featurecompletionautomatic = array('completion' => COMPLETION_TRACKING_AUTOMATIC, 'completionview' => 1, 'completionusegrade' => 1);
     // Module supports FEATURE_RATE:
     $featurerate = array('assessed' => RATING_AGGREGATE_AVERAGE, 'scale' => 100, 'ratingtime' => 1, 'assesstimestart' => time() - DAYSECS, 'assesstimefinish' => time() + DAYSECS);
     // Module supports FEATURE_GRADE_HAS_GRADE:
     $featuregrade = array('grade' => 10, 'gradecat' => $grade_category->id);
     // Now let's create several modules with different options.
     $m1 = $generator->create_module('assign', array('course' => $course->id) + $optionsgeneral);
     $m2 = $generator->create_module('data', array('course' => $course->id) + $featurecompletionmanual + $featurerate);
     $m3 = $generator->create_module('assign', array('course' => $course->id) + $featurecompletionautomatic + $featuregrade);
     // We need id of the grading item for the second module to create availability dependency in the 3rd module.
     $gradingitem = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $m3->id));
     // Now prepare option to create the 4th module with an availability condition.
     $optionsavailability = array('availability' => '{"op":"&","showc":[true],"c":[' . '{"type":"date","d":">=","t":' . (time() - WEEKSECS) . '}]}');
     // Create module with conditional availability.
     $m4 = $generator->create_module('assign', array('course' => $course->id) + $optionsavailability);
     // Verifying that everything is generated correctly.
     $modinfo = get_fast_modinfo($course->id);
     $cm1 = $modinfo->cms[$m1->cmid];
     $this->assertEquals($optionsgeneral['visible'], $cm1->visible);
     $this->assertEquals($optionsgeneral['section'], $cm1->sectionnum);
     // Note difference in key.
     $this->assertEquals($optionsgeneral['cmidnumber'], $cm1->idnumber);
     // Note difference in key.
     $this->assertEquals($optionsgeneral['groupmode'], $cm1->groupmode);
     $this->assertEquals($optionsgeneral['groupingid'], $cm1->groupingid);
     $cm2 = $modinfo->cms[$m2->cmid];
     $this->assertEquals($featurecompletionmanual['completion'], $cm2->completion);
     $this->assertEquals($featurecompletionmanual['completionexpected'], $cm2->completionexpected);
     $this->assertEquals(null, $cm2->completiongradeitemnumber);
     // Rating info is stored in the module's table (in our test {data}).
     $data = $DB->get_record('data', array('id' => $m2->id));
     $this->assertEquals($featurerate['assessed'], $data->assessed);
     $this->assertEquals($featurerate['scale'], $data->scale);
     $this->assertEquals($featurerate['assesstimestart'], $data->assesstimestart);
     $this->assertEquals($featurerate['assesstimefinish'], $data->assesstimefinish);
     // No validation for 'ratingtime'. It is only used in to enable/disable assesstime* when adding module.
     $cm3 = $modinfo->cms[$m3->cmid];
     $this->assertEquals($featurecompletionautomatic['completion'], $cm3->completion);
     $this->assertEquals($featurecompletionautomatic['completionview'], $cm3->completionview);
     $this->assertEquals(0, $cm3->completiongradeitemnumber);
     // Zero instead of default null since 'completionusegrade' was set.
     $gradingitem = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $m3->id));
     $this->assertEquals(0, $gradingitem->grademin);
     $this->assertEquals($featuregrade['grade'], $gradingitem->grademax);
     $this->assertEquals($featuregrade['gradecat'], $gradingitem->categoryid);
     $cm4 = $modinfo->cms[$m4->cmid];
     $this->assertEquals($optionsavailability['availability'], $cm4->availability);
 }
 /**
  * Tests grade_report_user::inject_rowspans()
  *
  * inject_rowspans() returns the count of the number of elements, sets maxdepth on the
  *  report object and sets the rowspan property on any element that has children.
  */
 public function test_inject_rowspans()
 {
     global $CFG, $USER, $DB;
     parent::setUp();
     $this->resetAfterTest(true);
     $CFG->enableavailability = 1;
     $CFG->enablecompletion = 1;
     // Create a course.
     $course = $this->getDataGenerator()->create_course();
     $coursecategory = grade_category::fetch_course_category($course->id);
     $coursecontext = context_course::instance($course->id);
     // Create and enrol test users.
     $student = $this->getDataGenerator()->create_user(array('username' => 'Student Sam'));
     $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
     $this->getDataGenerator()->enrol_user($student->id, $course->id, $role->id);
     $teacher = $this->getDataGenerator()->create_user(array('username' => 'Teacher T'));
     $role = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
     $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $role->id);
     // An array so we can test with both users in a loop.
     $users = array($student, $teacher);
     // Make the student the current user.
     $this->setUser($student);
     // Test an empty course.
     $report = $this->create_report($course, $student, $coursecontext);
     // a lead column that spans all children + course grade item = 2
     $this->assertEquals(2, $report->inject_rowspans($report->gtree->top_element));
     $this->assertEquals(2, $report->gtree->top_element['rowspan']);
     $this->assertEquals(2, $report->maxdepth);
     // Only elements with children should have rowspan set.
     if (array_key_exists('rowspan', $report->gtree->top_element['children'][1])) {
         $this->fail('Elements without children should not have rowspan set');
     }
     // Add 2 activities.
     $data1 = $this->getDataGenerator()->create_module('data', array('assessed' => 1, 'scale' => 100, 'course' => $course->id));
     $forum1 = $this->getDataGenerator()->create_module('forum', array('assessed' => 1, 'scale' => 100, 'course' => $course->id));
     $forum1cm = get_coursemodule_from_id('forum', $forum1->cmid);
     // Switch the stdClass instance for a grade item instance so grade_item::set_parent() is available.
     $forum1 = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'forum', 'iteminstance' => $forum1->id, 'courseid' => $course->id));
     $report = $this->create_report($course, $student, $coursecontext);
     // Lead column + course + (2 x activity) = 4
     $this->assertEquals(4, $report->inject_rowspans($report->gtree->top_element));
     $this->assertEquals(4, $report->gtree->top_element['rowspan']);
     // Lead column + 1 level (course + 2 activities) = 2
     $this->assertEquals(2, $report->maxdepth);
     // Only elements with children should have rowspan set.
     if (array_key_exists('rowspan', $report->gtree->top_element['children'][1])) {
         $this->fail('Elements without children should not have rowspan set');
     }
     // Hide the forum activity.
     set_coursemodule_visible($forum1cm->id, 0);
     foreach ($users as $user) {
         $this->setUser($user);
         $message = 'Testing with ' . $user->username;
         accesslib_clear_all_caches_for_unit_testing();
         $report = $this->create_report($course, $user, $coursecontext);
         // Lead column + course + (2 x activity) = 4 (element count isn't affected by hiding)
         $this->assertEquals(4, $report->inject_rowspans($report->gtree->top_element), $message);
         $this->assertEquals(4, $report->gtree->top_element['rowspan'], $message);
         // Lead column -> 1 level containing the course + 2 activities = 2
         $this->assertEquals(2, $report->maxdepth, $message);
     }
     // Unhide the forum activity.
     set_coursemodule_visible($forum1cm->id, 1);
     // Create a category and put the forum in it.
     $params = new stdClass();
     $params->courseid = $course->id;
     $params->fullname = 'unittestcategory';
     $params->parent = $coursecategory->id;
     $gradecategory = new grade_category($params, false);
     $gradecategory->insert();
     $forum1->set_parent($gradecategory->id);
     $report = $this->create_report($course, $student, $coursecontext);
     // Lead column + course + (category + category grade item) + (2 x activity) = 6
     $this->assertEquals(6, $report->inject_rowspans($report->gtree->top_element));
     $this->assertEquals(6, $report->gtree->top_element['rowspan']);
     // Lead column -> the category -> the forum activity = 3
     $this->assertEquals(3, $report->maxdepth);
     // Check rowspan on the category. The category itself + category grade item + forum = 3
     $this->assertEquals(3, $report->gtree->top_element['children'][4]['rowspan']);
     // check the forum doesn't have rowspan set
     if (array_key_exists('rowspan', $report->gtree->top_element['children'][4]['children'][3])) {
         $this->fail('The forum has no children so should not have rowspan set');
     }
     // Conditional activity tests.
     $DB->insert_record('course_modules_availability', (object) array('coursemoduleid' => $forum1cm->id, 'gradeitemid' => 37, 'grademin' => 5.5));
     $cm = (object) array('id' => $forum1cm->id);
     $test = new condition_info($cm, CONDITION_MISSING_EVERYTHING);
     $fullcm = $test->get_full_course_module();
     foreach ($users as $user) {
         $this->setUser($user);
         $message = 'Testing with ' . $user->username;
         accesslib_clear_all_caches_for_unit_testing();
         $report = $this->create_report($course, $user, $coursecontext);
         // Lead column + course + (category + category grade item) + (2 x activity) = 6
         $this->assertEquals(6, $report->inject_rowspans($report->gtree->top_element), $message);
         $this->assertEquals(6, $report->gtree->top_element['rowspan'], $message);
         // Lead column -> the category -> the forum activity = 3
         $this->assertEquals(3, $report->maxdepth, $message);
     }
 }
示例#19
0
/**
 * This function creates all the gradebook data from xml
 */
function restore_create_gradebook($restore, $xml_file)
{
    global $CFG;
    $status = true;
    //Check it exists
    if (!file_exists($xml_file)) {
        return false;
    }
    // Get info from xml
    // info will contain the number of record to process
    $info = restore_read_xml_gradebook($restore, $xml_file);
    // If we have info, then process
    if (empty($info)) {
        return $status;
    }
    if (empty($CFG->disablegradehistory) and isset($info->gradebook_histories) and $info->gradebook_histories == "true") {
        $restore_histories = true;
    } else {
        $restore_histories = false;
    }
    // make sure top course category exists
    $course_category = grade_category::fetch_course_category($restore->course_id);
    $course_category->load_grade_item();
    // we need to know if all grade items that were backed up are being restored
    // if that is not the case, we do not restore grade categories nor gradeitems of category type or course type
    // i.e. the aggregated grades of that category
    $restoreall = true;
    // set to false if any grade_item is not selected/restored or already exist
    $importing = !empty($SESSION->restore->importing);
    if ($importing) {
        $restoreall = false;
    } else {
        $prev_grade_items = grade_item::fetch_all(array('courseid' => $restore->course_id));
        $prev_grade_cats = grade_category::fetch_all(array('courseid' => $restore->course_id));
        // if any categories already present, skip restore of categories from backup - course item or category already exist
        if (count($prev_grade_items) > 1 or count($prev_grade_cats) > 1) {
            $restoreall = false;
        }
        unset($prev_grade_items);
        unset($prev_grade_cats);
        if ($restoreall) {
            if ($recs = get_records_select("backup_ids", "table_name = 'grade_items' AND backup_code = {$restore->backup_unique_code}", "", "old_id")) {
                foreach ($recs as $rec) {
                    if ($data = backup_getid($restore->backup_unique_code, 'grade_items', $rec->old_id)) {
                        $info = $data->info;
                        // do not restore if this grade_item is a mod, and
                        $itemtype = backup_todb($info['GRADE_ITEM']['#']['ITEMTYPE']['0']['#']);
                        if ($itemtype == 'mod') {
                            $olditeminstance = backup_todb($info['GRADE_ITEM']['#']['ITEMINSTANCE']['0']['#']);
                            $itemmodule = backup_todb($info['GRADE_ITEM']['#']['ITEMMODULE']['0']['#']);
                            if (empty($restore->mods[$itemmodule]->granular)) {
                                continue;
                            } else {
                                if (!empty($restore->mods[$itemmodule]->instances[$olditeminstance]->restore)) {
                                    continue;
                                }
                            }
                            // at least one activity should not be restored - do not restore categories and manual items at all
                            $restoreall = false;
                            break;
                        }
                    }
                }
            }
        }
    }
    // Start ul
    if (!defined('RESTORE_SILENTLY')) {
        echo '<ul>';
    }
    // array of restored categories - speedup ;-)
    $cached_categories = array();
    $outcomes = array();
    /// Process letters
    $context = get_context_instance(CONTEXT_COURSE, $restore->course_id);
    // respect current grade letters if defined
    if ($status and $restoreall and !record_exists('grade_letters', 'contextid', $context->id)) {
        if (!defined('RESTORE_SILENTLY')) {
            echo '<li>' . get_string('gradeletters', 'grades') . '</li>';
        }
        // Fetch recordset_size records in each iteration
        $recs = get_records_select("backup_ids", "table_name = 'grade_letters' AND backup_code = {$restore->backup_unique_code}", "", "old_id");
        if ($recs) {
            foreach ($recs as $rec) {
                // Get the full record from backup_ids
                $data = backup_getid($restore->backup_unique_code, 'grade_letters', $rec->old_id);
                if ($data) {
                    $info = $data->info;
                    $dbrec = new object();
                    $dbrec->contextid = $context->id;
                    $dbrec->lowerboundary = backup_todb($info['GRADE_LETTER']['#']['LOWERBOUNDARY']['0']['#']);
                    $dbrec->letter = backup_todb($info['GRADE_LETTER']['#']['LETTER']['0']['#']);
                    insert_record('grade_letters', $dbrec);
                }
            }
        }
    }
    /// Preprocess outcomes - do not store them yet!
    if ($status and !$importing and $restoreall) {
        if (!defined('RESTORE_SILENTLY')) {
            echo '<li>' . get_string('gradeoutcomes', 'grades') . '</li>';
        }
        $recs = get_records_select("backup_ids", "table_name = 'grade_outcomes' AND backup_code = '{$restore->backup_unique_code}'", "", "old_id");
        if ($recs) {
            foreach ($recs as $rec) {
                //Get the full record from backup_ids
                $data = backup_getid($restore->backup_unique_code, 'grade_outcomes', $rec->old_id);
                if ($data) {
                    $info = $data->info;
                    //first find out if outcome already exists
                    $shortname = backup_todb($info['GRADE_OUTCOME']['#']['SHORTNAME']['0']['#']);
                    if ($candidates = get_records_sql("SELECT *\n                                                             FROM {$CFG->prefix}grade_outcomes\n                                                            WHERE (courseid IS NULL OR courseid = {$restore->course_id})\n                                                                  AND shortname = '{$shortname}'\n                                                         ORDER BY courseid ASC, id ASC")) {
                        $grade_outcome = reset($candidates);
                        $outcomes[$rec->old_id] = $grade_outcome;
                        continue;
                    }
                    $dbrec = new object();
                    if (has_capability('moodle/grade:manageoutcomes', get_context_instance(CONTEXT_SYSTEM))) {
                        $oldoutcome = backup_todb($info['GRADE_OUTCOME']['#']['COURSEID']['0']['#']);
                        if (empty($oldoutcome)) {
                            //site wide
                            $dbrec->courseid = null;
                        } else {
                            //course only
                            $dbrec->courseid = $restore->course_id;
                        }
                    } else {
                        // no permission to add site outcomes
                        $dbrec->courseid = $restore->course_id;
                    }
                    //Get the fields
                    $dbrec->shortname = backup_todb($info['GRADE_OUTCOME']['#']['SHORTNAME']['0']['#'], false);
                    $dbrec->fullname = backup_todb($info['GRADE_OUTCOME']['#']['FULLNAME']['0']['#'], false);
                    $dbrec->scaleid = backup_todb($info['GRADE_OUTCOME']['#']['SCALEID']['0']['#'], false);
                    $dbrec->description = backup_todb($info['GRADE_OUTCOME']['#']['DESCRIPTION']['0']['#'], false);
                    $dbrec->timecreated = backup_todb($info['GRADE_OUTCOME']['#']['TIMECREATED']['0']['#'], false);
                    $dbrec->timemodified = backup_todb($info['GRADE_OUTCOME']['#']['TIMEMODIFIED']['0']['#'], false);
                    $dbrec->usermodified = backup_todb($info['GRADE_OUTCOME']['#']['USERMODIFIED']['0']['#'], false);
                    //Need to recode the scaleid
                    if ($scale = backup_getid($restore->backup_unique_code, 'scale', $dbrec->scaleid)) {
                        $dbrec->scaleid = $scale->new_id;
                    }
                    //Need to recode the usermodified
                    if ($modifier = backup_getid($restore->backup_unique_code, 'user', $dbrec->usermodified)) {
                        $dbrec->usermodified = $modifier->new_id;
                    }
                    $grade_outcome = new grade_outcome($dbrec, false);
                    $outcomes[$rec->old_id] = $grade_outcome;
                }
            }
        }
    }
    /// Process grade items and grades
    if ($status) {
        if (!defined('RESTORE_SILENTLY')) {
            echo '<li>' . get_string('gradeitems', 'grades') . '</li>';
        }
        $counter = 0;
        //Fetch recordset_size records in each iteration
        $recs = get_records_select("backup_ids", "table_name = 'grade_items' AND backup_code = '{$restore->backup_unique_code}'", "id", "old_id");
        if ($recs) {
            foreach ($recs as $rec) {
                //Get the full record from backup_ids
                $data = backup_getid($restore->backup_unique_code, 'grade_items', $rec->old_id);
                if ($data) {
                    $info = $data->info;
                    // first find out if category or normal item
                    $itemtype = backup_todb($info['GRADE_ITEM']['#']['ITEMTYPE']['0']['#'], false);
                    if ($itemtype == 'course' or $itemtype == 'category') {
                        if (!$restoreall or $importing) {
                            continue;
                        }
                        $oldcat = backup_todb($info['GRADE_ITEM']['#']['ITEMINSTANCE']['0']['#'], false);
                        if (!($cdata = backup_getid($restore->backup_unique_code, 'grade_categories', $oldcat))) {
                            continue;
                        }
                        $cinfo = $cdata->info;
                        unset($cdata);
                        if ($itemtype == 'course') {
                            $course_category->fullname = backup_todb($cinfo['GRADE_CATEGORY']['#']['FULLNAME']['0']['#'], false);
                            $course_category->aggregation = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATION']['0']['#'], false);
                            $course_category->keephigh = backup_todb($cinfo['GRADE_CATEGORY']['#']['KEEPHIGH']['0']['#'], false);
                            $course_category->droplow = backup_todb($cinfo['GRADE_CATEGORY']['#']['DROPLOW']['0']['#'], false);
                            $course_category->aggregateonlygraded = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATEONLYGRADED']['0']['#'], false);
                            $course_category->aggregateoutcomes = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATEOUTCOMES']['0']['#'], false);
                            $course_category->aggregatesubcats = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATESUBCATS']['0']['#'], false);
                            $course_category->timecreated = backup_todb($cinfo['GRADE_CATEGORY']['#']['TIMECREATED']['0']['#'], false);
                            $course_category->update('restore');
                            $status = backup_putid($restore->backup_unique_code, 'grade_categories', $oldcat, $course_category->id) && $status;
                            $cached_categories[$oldcat] = $course_category;
                            $grade_item = $course_category->get_grade_item();
                        } else {
                            $oldparent = backup_todb($cinfo['GRADE_CATEGORY']['#']['PARENT']['0']['#'], false);
                            if (empty($cached_categories[$oldparent])) {
                                debugging('parent not found ' . $oldparent);
                                continue;
                                // parent not found, sorry
                            }
                            $grade_category = new grade_category();
                            $grade_category->courseid = $restore->course_id;
                            $grade_category->parent = $cached_categories[$oldparent]->id;
                            $grade_category->fullname = backup_todb($cinfo['GRADE_CATEGORY']['#']['FULLNAME']['0']['#'], false);
                            $grade_category->aggregation = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATION']['0']['#'], false);
                            $grade_category->keephigh = backup_todb($cinfo['GRADE_CATEGORY']['#']['KEEPHIGH']['0']['#'], false);
                            $grade_category->droplow = backup_todb($cinfo['GRADE_CATEGORY']['#']['DROPLOW']['0']['#'], false);
                            $grade_category->aggregateonlygraded = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATEONLYGRADED']['0']['#'], false);
                            $grade_category->aggregateoutcomes = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATEOUTCOMES']['0']['#'], false);
                            $grade_category->aggregatesubcats = backup_todb($cinfo['GRADE_CATEGORY']['#']['AGGREGATESUBCATS']['0']['#'], false);
                            $grade_category->timecreated = backup_todb($cinfo['GRADE_CATEGORY']['#']['TIMECREATED']['0']['#'], false);
                            $grade_category->insert('restore');
                            $status = backup_putid($restore->backup_unique_code, 'grade_categories', $oldcat, $grade_category->id) && $status;
                            $cached_categories[$oldcat] = $grade_category;
                            $grade_item = $grade_category->get_grade_item();
                            // creates grade_item too
                        }
                        unset($cinfo);
                        $idnumber = backup_todb($info['GRADE_ITEM']['#']['IDNUMBER']['0']['#'], false);
                        if (grade_verify_idnumber($idnumber, $restore->course_id)) {
                            $grade_item->idnumber = $idnumber;
                        }
                        $grade_item->itemname = backup_todb($info['GRADE_ITEM']['#']['ITEMNAME']['0']['#'], false);
                        $grade_item->iteminfo = backup_todb($info['GRADE_ITEM']['#']['ITEMINFO']['0']['#'], false);
                        $grade_item->gradetype = backup_todb($info['GRADE_ITEM']['#']['GRADETYPE']['0']['#'], false);
                        $grade_item->calculation = backup_todb($info['GRADE_ITEM']['#']['CALCULATION']['0']['#'], false);
                        $grade_item->grademax = backup_todb($info['GRADE_ITEM']['#']['GRADEMAX']['0']['#'], false);
                        $grade_item->grademin = backup_todb($info['GRADE_ITEM']['#']['GRADEMIN']['0']['#'], false);
                        $grade_item->gradepass = backup_todb($info['GRADE_ITEM']['#']['GRADEPASS']['0']['#'], false);
                        $grade_item->multfactor = backup_todb($info['GRADE_ITEM']['#']['MULTFACTOR']['0']['#'], false);
                        $grade_item->plusfactor = backup_todb($info['GRADE_ITEM']['#']['PLUSFACTOR']['0']['#'], false);
                        $grade_item->aggregationcoef = backup_todb($info['GRADE_ITEM']['#']['AGGREGATIONCOEF']['0']['#'], false);
                        $grade_item->display = backup_todb($info['GRADE_ITEM']['#']['DISPLAY']['0']['#'], false);
                        $grade_item->decimals = backup_todb($info['GRADE_ITEM']['#']['DECIMALS']['0']['#'], false);
                        $grade_item->hidden = backup_todb($info['GRADE_ITEM']['#']['HIDDEN']['0']['#'], false);
                        $grade_item->locked = backup_todb($info['GRADE_ITEM']['#']['LOCKED']['0']['#'], false);
                        $grade_item->locktime = backup_todb($info['GRADE_ITEM']['#']['LOCKTIME']['0']['#'], false);
                        $grade_item->timecreated = backup_todb($info['GRADE_ITEM']['#']['TIMECREATED']['0']['#'], false);
                        if (backup_todb($info['GRADE_ITEM']['#']['SCALEID']['0']['#'], false)) {
                            $scale = backup_getid($restore->backup_unique_code, "scale", backup_todb($info['GRADE_ITEM']['#']['SCALEID']['0']['#'], false));
                            $grade_item->scaleid = $scale->new_id;
                        }
                        if (backup_todb($info['GRADE_ITEM']['#']['OUTCOMEID']['0']['#'], false)) {
                            $outcome = backup_getid($restore->backup_unique_code, "grade_outcomes", backup_todb($info['GRADE_ITEM']['#']['OUTCOMEID']['0']['#'], false));
                            $grade_item->outcomeid = $outcome->new_id;
                        }
                        $grade_item->update('restore');
                        $status = backup_putid($restore->backup_unique_code, "grade_items", $rec->old_id, $grade_item->id) && $status;
                    } else {
                        if ($itemtype != 'mod' and (!$restoreall or $importing)) {
                            // not extra gradebook stuff if restoring individual activities or something already there
                            continue;
                        }
                        $dbrec = new object();
                        $dbrec->courseid = $restore->course_id;
                        $dbrec->itemtype = backup_todb($info['GRADE_ITEM']['#']['ITEMTYPE']['0']['#'], false);
                        $dbrec->itemmodule = backup_todb($info['GRADE_ITEM']['#']['ITEMMODULE']['0']['#'], false);
                        if ($itemtype == 'mod') {
                            // iteminstance should point to new mod
                            $olditeminstance = backup_todb($info['GRADE_ITEM']['#']['ITEMINSTANCE']['0']['#'], false);
                            $mod = backup_getid($restore->backup_unique_code, $dbrec->itemmodule, $olditeminstance);
                            $dbrec->iteminstance = $mod->new_id;
                            if (!($cm = get_coursemodule_from_instance($dbrec->itemmodule, $mod->new_id))) {
                                // item not restored - no item
                                continue;
                            }
                            // keep in sync with activity idnumber
                            $dbrec->idnumber = $cm->idnumber;
                        } else {
                            $idnumber = backup_todb($info['GRADE_ITEM']['#']['IDNUMBER']['0']['#'], false);
                            if (grade_verify_idnumber($idnumber, $restore->course_id)) {
                                //make sure the new idnumber is unique
                                $dbrec->idnumber = $idnumber;
                            }
                        }
                        $dbrec->itemname = backup_todb($info['GRADE_ITEM']['#']['ITEMNAME']['0']['#'], false);
                        $dbrec->itemtype = backup_todb($info['GRADE_ITEM']['#']['ITEMTYPE']['0']['#'], false);
                        $dbrec->itemmodule = backup_todb($info['GRADE_ITEM']['#']['ITEMMODULE']['0']['#'], false);
                        $dbrec->itemnumber = backup_todb($info['GRADE_ITEM']['#']['ITEMNUMBER']['0']['#'], false);
                        $dbrec->iteminfo = backup_todb($info['GRADE_ITEM']['#']['ITEMINFO']['0']['#'], false);
                        $dbrec->gradetype = backup_todb($info['GRADE_ITEM']['#']['GRADETYPE']['0']['#'], false);
                        $dbrec->calculation = backup_todb($info['GRADE_ITEM']['#']['CALCULATION']['0']['#'], false);
                        $dbrec->grademax = backup_todb($info['GRADE_ITEM']['#']['GRADEMAX']['0']['#'], false);
                        $dbrec->grademin = backup_todb($info['GRADE_ITEM']['#']['GRADEMIN']['0']['#'], false);
                        $dbrec->gradepass = backup_todb($info['GRADE_ITEM']['#']['GRADEPASS']['0']['#'], false);
                        $dbrec->multfactor = backup_todb($info['GRADE_ITEM']['#']['MULTFACTOR']['0']['#'], false);
                        $dbrec->plusfactor = backup_todb($info['GRADE_ITEM']['#']['PLUSFACTOR']['0']['#'], false);
                        $dbrec->aggregationcoef = backup_todb($info['GRADE_ITEM']['#']['AGGREGATIONCOEF']['0']['#'], false);
                        $dbrec->display = backup_todb($info['GRADE_ITEM']['#']['DISPLAY']['0']['#'], false);
                        $dbrec->decimals = backup_todb($info['GRADE_ITEM']['#']['DECIMALS']['0']['#'], false);
                        $dbrec->hidden = backup_todb($info['GRADE_ITEM']['#']['HIDDEN']['0']['#'], false);
                        $dbrec->locked = backup_todb($info['GRADE_ITEM']['#']['LOCKED']['0']['#'], false);
                        $dbrec->locktime = backup_todb($info['GRADE_ITEM']['#']['LOCKTIME']['0']['#'], false);
                        $dbrec->timecreated = backup_todb($info['GRADE_ITEM']['#']['TIMECREATED']['0']['#'], false);
                        if (backup_todb($info['GRADE_ITEM']['#']['SCALEID']['0']['#'], false)) {
                            $scale = backup_getid($restore->backup_unique_code, "scale", backup_todb($info['GRADE_ITEM']['#']['SCALEID']['0']['#'], false));
                            $dbrec->scaleid = $scale->new_id;
                        }
                        if (backup_todb($info['GRADE_ITEM']['#']['OUTCOMEID']['0']['#'])) {
                            $oldoutcome = backup_todb($info['GRADE_ITEM']['#']['OUTCOMEID']['0']['#']);
                            if (empty($outcomes[$oldoutcome])) {
                                continue;
                                // error!
                            }
                            if (empty($outcomes[$oldoutcome]->id)) {
                                $outcomes[$oldoutcome]->insert('restore');
                                $outcomes[$oldoutcome]->use_in($restore->course_id);
                                backup_putid($restore->backup_unique_code, "grade_outcomes", $oldoutcome, $outcomes[$oldoutcome]->id);
                            }
                            $dbrec->outcomeid = $outcomes[$oldoutcome]->id;
                        }
                        $grade_item = new grade_item($dbrec, false);
                        $grade_item->insert('restore');
                        if ($restoreall) {
                            // set original parent if restored
                            $oldcat = $info['GRADE_ITEM']['#']['CATEGORYID']['0']['#'];
                            if (!empty($cached_categories[$oldcat])) {
                                $grade_item->set_parent($cached_categories[$oldcat]->id);
                            }
                        }
                        $status = backup_putid($restore->backup_unique_code, "grade_items", $rec->old_id, $grade_item->id) && $status;
                    }
                    // no need to restore grades if user data is not selected or importing activities
                    if ($importing or $grade_item->itemtype == 'mod' and !restore_userdata_selected($restore, $grade_item->itemmodule, $olditeminstance)) {
                        // module instance not selected when restored using granular
                        // skip this item
                        continue;
                    }
                    /// now, restore grade_grades
                    if (!empty($info['GRADE_ITEM']['#']['GRADE_GRADES']['0']['#']['GRADE'])) {
                        //Iterate over items
                        foreach ($info['GRADE_ITEM']['#']['GRADE_GRADES']['0']['#']['GRADE'] as $g_info) {
                            $grade = new grade_grade();
                            $grade->itemid = $grade_item->id;
                            $olduser = backup_todb($g_info['#']['USERID']['0']['#'], false);
                            $user = backup_getid($restore->backup_unique_code, "user", $olduser);
                            $grade->userid = $user->new_id;
                            $grade->rawgrade = backup_todb($g_info['#']['RAWGRADE']['0']['#'], false);
                            $grade->rawgrademax = backup_todb($g_info['#']['RAWGRADEMAX']['0']['#'], false);
                            $grade->rawgrademin = backup_todb($g_info['#']['RAWGRADEMIN']['0']['#'], false);
                            // need to find scaleid
                            if (backup_todb($g_info['#']['RAWSCALEID']['0']['#'])) {
                                $scale = backup_getid($restore->backup_unique_code, "scale", backup_todb($g_info['#']['RAWSCALEID']['0']['#'], false));
                                $grade->rawscaleid = $scale->new_id;
                            }
                            if (backup_todb($g_info['#']['USERMODIFIED']['0']['#'])) {
                                if ($modifier = backup_getid($restore->backup_unique_code, "user", backup_todb($g_info['#']['USERMODIFIED']['0']['#'], false))) {
                                    $grade->usermodified = $modifier->new_id;
                                }
                            }
                            $grade->finalgrade = backup_todb($g_info['#']['FINALGRADE']['0']['#'], false);
                            $grade->hidden = backup_todb($g_info['#']['HIDDEN']['0']['#'], false);
                            $grade->locked = backup_todb($g_info['#']['LOCKED']['0']['#'], false);
                            $grade->locktime = backup_todb($g_info['#']['LOCKTIME']['0']['#'], false);
                            $grade->exported = backup_todb($g_info['#']['EXPORTED']['0']['#'], false);
                            $grade->overridden = backup_todb($g_info['#']['OVERRIDDEN']['0']['#'], false);
                            $grade->excluded = backup_todb($g_info['#']['EXCLUDED']['0']['#'], false);
                            $grade->feedback = backup_todb($g_info['#']['FEEDBACK']['0']['#'], false);
                            $grade->feedbackformat = backup_todb($g_info['#']['FEEDBACKFORMAT']['0']['#'], false);
                            $grade->information = backup_todb($g_info['#']['INFORMATION']['0']['#'], false);
                            $grade->informationformat = backup_todb($g_info['#']['INFORMATIONFORMAT']['0']['#'], false);
                            $grade->timecreated = backup_todb($g_info['#']['TIMECREATED']['0']['#'], false);
                            $grade->timemodified = backup_todb($g_info['#']['TIMEMODIFIED']['0']['#'], false);
                            $grade->insert('restore');
                            backup_putid($restore->backup_unique_code, "grade_grades", backup_todb($g_info['#']['ID']['0']['#']), $grade->id);
                            $counter++;
                            if ($counter % 20 == 0) {
                                if (!defined('RESTORE_SILENTLY')) {
                                    echo ".";
                                    if ($counter % 400 == 0) {
                                        echo "<br />";
                                    }
                                }
                                backup_flush(300);
                            }
                        }
                    }
                }
            }
        }
    }
    /// add outcomes that are not used when doing full restore
    if ($status and $restoreall) {
        foreach ($outcomes as $oldoutcome => $grade_outcome) {
            if (empty($grade_outcome->id)) {
                $grade_outcome->insert('restore');
                $grade_outcome->use_in($restore->course_id);
                backup_putid($restore->backup_unique_code, "grade_outcomes", $oldoutcome, $grade_outcome->id);
            }
        }
    }
    if ($status and !$importing and $restore_histories) {
        /// following code is very inefficient
        $gchcount = count_records('backup_ids', 'backup_code', $restore->backup_unique_code, 'table_name', 'grade_categories_history');
        $gghcount = count_records('backup_ids', 'backup_code', $restore->backup_unique_code, 'table_name', 'grade_grades_history');
        $gihcount = count_records('backup_ids', 'backup_code', $restore->backup_unique_code, 'table_name', 'grade_items_history');
        $gohcount = count_records('backup_ids', 'backup_code', $restore->backup_unique_code, 'table_name', 'grade_outcomes_history');
        // Number of records to get in every chunk
        $recordset_size = 2;
        // process histories
        if ($gchcount && $status) {
            if (!defined('RESTORE_SILENTLY')) {
                echo '<li>' . get_string('gradecategoryhistory', 'grades') . '</li>';
            }
            $counter = 0;
            while ($counter < $gchcount) {
                //Fetch recordset_size records in each iteration
                $recs = get_records_select("backup_ids", "table_name = 'grade_categories_history' AND backup_code = '{$restore->backup_unique_code}'", "old_id", "old_id", $counter, $recordset_size);
                if ($recs) {
                    foreach ($recs as $rec) {
                        //Get the full record from backup_ids
                        $data = backup_getid($restore->backup_unique_code, 'grade_categories_history', $rec->old_id);
                        if ($data) {
                            //Now get completed xmlized object
                            $info = $data->info;
                            //traverse_xmlize($info);                            //Debug
                            //print_object ($GLOBALS['traverse_array']);         //Debug
                            //$GLOBALS['traverse_array']="";                     //Debug
                            $oldobj = backup_getid($restore->backup_unique_code, "grade_categories", backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['OLDID']['0']['#']));
                            if (empty($oldobj->new_id)) {
                                // if the old object is not being restored, can't restoring its history
                                $counter++;
                                continue;
                            }
                            $dbrec->oldid = $oldobj->new_id;
                            $dbrec->action = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['ACTION']['0']['#']);
                            $dbrec->source = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['SOURCE']['0']['#']);
                            $dbrec->timemodified = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['TIMEMODIFIED']['0']['#']);
                            // loggeduser might not be restored, e.g. admin
                            if ($oldobj = backup_getid($restore->backup_unique_code, "user", backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['LOGGEDUSER']['0']['#']))) {
                                $dbrec->loggeduser = $oldobj->new_id;
                            }
                            // this item might not have a parent at all, do not skip it if no parent is specified
                            if (backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['PARENT']['0']['#'])) {
                                $oldobj = backup_getid($restore->backup_unique_code, "grade_categories", backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['PARENT']['0']['#']));
                                if (empty($oldobj->new_id)) {
                                    // if the parent category not restored
                                    $counter++;
                                    continue;
                                }
                            }
                            $dbrec->parent = $oldobj->new_id;
                            $dbrec->depth = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['DEPTH']['0']['#']);
                            // path needs to be rebuilt
                            if ($path = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['PATH']['0']['#'])) {
                                // to preserve the path and make it work, we need to replace the categories one by one
                                // we first get the list of categories in current path
                                if ($paths = explode("/", $path)) {
                                    $newpath = '';
                                    foreach ($paths as $catid) {
                                        if ($catid) {
                                            // find the new corresponding path
                                            $oldpath = backup_getid($restore->backup_unique_code, "grade_categories", $catid);
                                            $newpath .= "/{$oldpath->new_id}";
                                        }
                                    }
                                    $dbrec->path = $newpath;
                                }
                            }
                            $dbrec->fullname = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['FULLNAME']['0']['#']);
                            $dbrec->aggregation = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['AGGRETGATION']['0']['#']);
                            $dbrec->keephigh = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['KEEPHIGH']['0']['#']);
                            $dbrec->droplow = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['DROPLOW']['0']['#']);
                            $dbrec->aggregateonlygraded = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['AGGREGATEONLYGRADED']['0']['#']);
                            $dbrec->aggregateoutcomes = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['AGGREGATEOUTCOMES']['0']['#']);
                            $dbrec->aggregatesubcats = backup_todb($info['GRADE_CATEGORIES_HISTORY']['#']['AGGREGATESUBCATS']['0']['#']);
                            $dbrec->courseid = $restore->course_id;
                            insert_record('grade_categories_history', $dbrec);
                            unset($dbrec);
                        }
                        //Increment counters
                        $counter++;
                        //Do some output
                        if ($counter % 1 == 0) {
                            if (!defined('RESTORE_SILENTLY')) {
                                echo ".";
                                if ($counter % 20 == 0) {
                                    echo "<br />";
                                }
                            }
                            backup_flush(300);
                        }
                    }
                }
            }
        }
        // process histories
        if ($gghcount && $status) {
            if (!defined('RESTORE_SILENTLY')) {
                echo '<li>' . get_string('gradegradeshistory', 'grades') . '</li>';
            }
            $counter = 0;
            while ($counter < $gghcount) {
                //Fetch recordset_size records in each iteration
                $recs = get_records_select("backup_ids", "table_name = 'grade_grades_history' AND backup_code = '{$restore->backup_unique_code}'", "old_id", "old_id", $counter, $recordset_size);
                if ($recs) {
                    foreach ($recs as $rec) {
                        //Get the full record from backup_ids
                        $data = backup_getid($restore->backup_unique_code, 'grade_grades_history', $rec->old_id);
                        if ($data) {
                            //Now get completed xmlized object
                            $info = $data->info;
                            //traverse_xmlize($info);                            //Debug
                            //print_object ($GLOBALS['traverse_array']);         //Debug
                            //$GLOBALS['traverse_array']="";                     //Debug
                            $oldobj = backup_getid($restore->backup_unique_code, "grade_grades", backup_todb($info['GRADE_GRADES_HISTORY']['#']['OLDID']['0']['#']));
                            if (empty($oldobj->new_id)) {
                                // if the old object is not being restored, can't restoring its history
                                $counter++;
                                continue;
                            }
                            $dbrec->oldid = $oldobj->new_id;
                            $dbrec->action = backup_todb($info['GRADE_GRADES_HISTORY']['#']['ACTION']['0']['#']);
                            $dbrec->source = backup_todb($info['GRADE_GRADES_HISTORY']['#']['SOURCE']['0']['#']);
                            $dbrec->timemodified = backup_todb($info['GRADE_GRADES_HISTORY']['#']['TIMEMODIFIED']['0']['#']);
                            if ($oldobj = backup_getid($restore->backup_unique_code, "user", backup_todb($info['GRADE_GRADES_HISTORY']['#']['LOGGEDUSER']['0']['#']))) {
                                $dbrec->loggeduser = $oldobj->new_id;
                            }
                            $oldobj = backup_getid($restore->backup_unique_code, "grade_items", backup_todb($info['GRADE_GRADES_HISTORY']['#']['ITEMID']['0']['#']));
                            $dbrec->itemid = $oldobj->new_id;
                            if (empty($dbrec->itemid)) {
                                $counter++;
                                continue;
                                // grade item not being restored
                            }
                            $oldobj = backup_getid($restore->backup_unique_code, "user", backup_todb($info['GRADE_GRADES_HISTORY']['#']['USERID']['0']['#']));
                            $dbrec->userid = $oldobj->new_id;
                            $dbrec->rawgrade = backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWGRADE']['0']['#']);
                            $dbrec->rawgrademax = backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWGRADEMAX']['0']['#']);
                            $dbrec->rawgrademin = backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWGRADEMIN']['0']['#']);
                            if ($oldobj = backup_getid($restore->backup_unique_code, "user", backup_todb($info['GRADE_GRADES_HISTORY']['#']['USERMODIFIED']['0']['#']))) {
                                $dbrec->usermodified = $oldobj->new_id;
                            }
                            if (backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWSCALEID']['0']['#'])) {
                                $scale = backup_getid($restore->backup_unique_code, "scale", backup_todb($info['GRADE_GRADES_HISTORY']['#']['RAWSCALEID']['0']['#']));
                                $dbrec->rawscaleid = $scale->new_id;
                            }
                            $dbrec->finalgrade = backup_todb($info['GRADE_GRADES_HISTORY']['#']['FINALGRADE']['0']['#']);
                            $dbrec->hidden = backup_todb($info['GRADE_GRADES_HISTORY']['#']['HIDDEN']['0']['#']);
                            $dbrec->locked = backup_todb($info['GRADE_GRADES_HISTORY']['#']['LOCKED']['0']['#']);
                            $dbrec->locktime = backup_todb($info['GRADE_GRADES_HISTORY']['#']['LOCKTIME']['0']['#']);
                            $dbrec->exported = backup_todb($info['GRADE_GRADES_HISTORY']['#']['EXPORTED']['0']['#']);
                            $dbrec->overridden = backup_todb($info['GRADE_GRADES_HISTORY']['#']['OVERRIDDEN']['0']['#']);
                            $dbrec->excluded = backup_todb($info['GRADE_GRADES_HISTORY']['#']['EXCLUDED']['0']['#']);
                            $dbrec->feedback = backup_todb($info['GRADE_TEXT_HISTORY']['#']['FEEDBACK']['0']['#']);
                            $dbrec->feedbackformat = backup_todb($info['GRADE_TEXT_HISTORY']['#']['FEEDBACKFORMAT']['0']['#']);
                            $dbrec->information = backup_todb($info['GRADE_TEXT_HISTORY']['#']['INFORMATION']['0']['#']);
                            $dbrec->informationformat = backup_todb($info['GRADE_TEXT_HISTORY']['#']['INFORMATIONFORMAT']['0']['#']);
                            insert_record('grade_grades_history', $dbrec);
                            unset($dbrec);
                        }
                        //Increment counters
                        $counter++;
                        //Do some output
                        if ($counter % 1 == 0) {
                            if (!defined('RESTORE_SILENTLY')) {
                                echo ".";
                                if ($counter % 20 == 0) {
                                    echo "<br />";
                                }
                            }
                            backup_flush(300);
                        }
                    }
                }
            }
        }
        // process histories
        if ($gihcount && $status) {
            if (!defined('RESTORE_SILENTLY')) {
                echo '<li>' . get_string('gradeitemshistory', 'grades') . '</li>';
            }
            $counter = 0;
            while ($counter < $gihcount) {
                //Fetch recordset_size records in each iteration
                $recs = get_records_select("backup_ids", "table_name = 'grade_items_history' AND backup_code = '{$restore->backup_unique_code}'", "old_id", "old_id", $counter, $recordset_size);
                if ($recs) {
                    foreach ($recs as $rec) {
                        //Get the full record from backup_ids
                        $data = backup_getid($restore->backup_unique_code, 'grade_items_history', $rec->old_id);
                        if ($data) {
                            //Now get completed xmlized object
                            $info = $data->info;
                            //traverse_xmlize($info);                            //Debug
                            //print_object ($GLOBALS['traverse_array']);         //Debug
                            //$GLOBALS['traverse_array']="";                     //Debug
                            $oldobj = backup_getid($restore->backup_unique_code, "grade_items", backup_todb($info['GRADE_ITEM_HISTORY']['#']['OLDID']['0']['#']));
                            if (empty($oldobj->new_id)) {
                                // if the old object is not being restored, can't restoring its history
                                $counter++;
                                continue;
                            }
                            $dbrec->oldid = $oldobj->new_id;
                            $dbrec->action = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ACTION']['0']['#']);
                            $dbrec->source = backup_todb($info['GRADE_ITEM_HISTORY']['#']['SOURCE']['0']['#']);
                            $dbrec->timemodified = backup_todb($info['GRADE_ITEM_HISTORY']['#']['TIMEMODIFIED']['0']['#']);
                            if ($oldobj = backup_getid($restore->backup_unique_code, "user", backup_todb($info['GRADE_ITEM_HISTORY']['#']['LOGGEDUSER']['0']['#']))) {
                                $dbrec->loggeduser = $oldobj->new_id;
                            }
                            $dbrec->courseid = $restore->course_id;
                            $oldobj = backup_getid($restore->backup_unique_code, 'grade_categories', backup_todb($info['GRADE_ITEM_HISTORY']['#']['CATEGORYID']['0']['#']));
                            $oldobj->categoryid = $category->new_id;
                            if (empty($oldobj->categoryid)) {
                                $counter++;
                                continue;
                                // category not restored
                            }
                            $dbrec->itemname = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMNAME']['0']['#']);
                            $dbrec->itemtype = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMTYPE']['0']['#']);
                            $dbrec->itemmodule = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMMODULE']['0']['#']);
                            // code from grade_items restore
                            $iteminstance = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMINSTANCE']['0']['#']);
                            // do not restore if this grade_item is a mod, and
                            if ($dbrec->itemtype == 'mod') {
                                if (!restore_userdata_selected($restore, $dbrec->itemmodule, $iteminstance)) {
                                    // module instance not selected when restored using granular
                                    // skip this item
                                    $counter++;
                                    continue;
                                }
                                // iteminstance should point to new mod
                                $mod = backup_getid($restore->backup_unique_code, $dbrec->itemmodule, $iteminstance);
                                $dbrec->iteminstance = $mod->new_id;
                            } else {
                                if ($dbrec->itemtype == 'category') {
                                    // the item instance should point to the new grade category
                                    // only proceed if we are restoring all grade items
                                    if ($restoreall) {
                                        $category = backup_getid($restore->backup_unique_code, 'grade_categories', $iteminstance);
                                        $dbrec->iteminstance = $category->new_id;
                                    } else {
                                        // otherwise we can safely ignore this grade item and subsequent
                                        // grade_raws, grade_finals etc
                                        continue;
                                    }
                                } elseif ($dbrec->itemtype == 'course') {
                                    // We don't restore course type to avoid duplicate course items
                                    if ($restoreall) {
                                        // TODO any special code needed here to restore course item without duplicating it?
                                        // find the course category with depth 1, and course id = current course id
                                        // this would have been already restored
                                        $cat = get_record('grade_categories', 'depth', 1, 'courseid', $restore->course_id);
                                        $dbrec->iteminstance = $cat->id;
                                    } else {
                                        $counter++;
                                        continue;
                                    }
                                }
                            }
                            $dbrec->itemnumber = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMNUMBER']['0']['#']);
                            $dbrec->iteminfo = backup_todb($info['GRADE_ITEM_HISTORY']['#']['ITEMINFO']['0']['#']);
                            $dbrec->idnumber = backup_todb($info['GRADE_ITEM_HISTORY']['#']['IDNUMBER']['0']['#']);
                            $dbrec->calculation = backup_todb($info['GRADE_ITEM_HISTORY']['#']['CALCULATION']['0']['#']);
                            $dbrec->gradetype = backup_todb($info['GRADE_ITEM_HISTORY']['#']['GRADETYPE']['0']['#']);
                            $dbrec->grademax = backup_todb($info['GRADE_ITEM_HISTORY']['#']['GRADEMAX']['0']['#']);
                            $dbrec->grademin = backup_todb($info['GRADE_ITEM_HISTORY']['#']['GRADEMIN']['0']['#']);
                            if ($oldobj = backup_getid($restore->backup_unique_code, "scale", backup_todb($info['GRADE_ITEM_HISTORY']['#']['SCALEID']['0']['#']))) {
                                // scaleid is optional
                                $dbrec->scaleid = $oldobj->new_id;
                            }
                            if ($oldobj = backup_getid($restore->backup_unique_code, "grade_outcomes", backup_todb($info['GRADE_ITEM_HISTORY']['#']['OUTCOMEID']['0']['#']))) {
                                // outcome is optional
                                $dbrec->outcomeid = $oldobj->new_id;
                            }
                            $dbrec->gradepass = backup_todb($info['GRADE_ITEM_HISTORY']['#']['GRADEPASS']['0']['#']);
                            $dbrec->multfactor = backup_todb($info['GRADE_ITEM_HISTORY']['#']['MULTFACTOR']['0']['#']);
                            $dbrec->plusfactor = backup_todb($info['GRADE_ITEM_HISTORY']['#']['PLUSFACTOR']['0']['#']);
                            $dbrec->aggregationcoef = backup_todb($info['GRADE_ITEM_HISTORY']['#']['AGGREGATIONCOEF']['0']['#']);
                            $dbrec->sortorder = backup_todb($info['GRADE_ITEM_HISTORY']['#']['SORTORDER']['0']['#']);
                            $dbrec->display = backup_todb($info['GRADE_ITEM_HISTORY']['#']['DISPLAY']['0']['#']);
                            $dbrec->decimals = backup_todb($info['GRADE_ITEM_HISTORY']['#']['DECIMALS']['0']['#']);
                            $dbrec->hidden = backup_todb($info['GRADE_ITEM_HISTORY']['#']['HIDDEN']['0']['#']);
                            $dbrec->locked = backup_todb($info['GRADE_ITEM_HISTORY']['#']['LOCKED']['0']['#']);
                            $dbrec->locktime = backup_todb($info['GRADE_ITEM_HISTORY']['#']['LOCKTIME']['0']['#']);
                            $dbrec->needsupdate = backup_todb($info['GRADE_ITEM_HISTORY']['#']['NEEDSUPDATE']['0']['#']);
                            insert_record('grade_items_history', $dbrec);
                            unset($dbrec);
                        }
                        //Increment counters
                        $counter++;
                        //Do some output
                        if ($counter % 1 == 0) {
                            if (!defined('RESTORE_SILENTLY')) {
                                echo ".";
                                if ($counter % 20 == 0) {
                                    echo "<br />";
                                }
                            }
                            backup_flush(300);
                        }
                    }
                }
            }
        }
        // process histories
        if ($gohcount && $status) {
            if (!defined('RESTORE_SILENTLY')) {
                echo '<li>' . get_string('gradeoutcomeshistory', 'grades') . '</li>';
            }
            $counter = 0;
            while ($counter < $gohcount) {
                //Fetch recordset_size records in each iteration
                $recs = get_records_select("backup_ids", "table_name = 'grade_outcomes_history' AND backup_code = '{$restore->backup_unique_code}'", "old_id", "old_id", $counter, $recordset_size);
                if ($recs) {
                    foreach ($recs as $rec) {
                        //Get the full record from backup_ids
                        $data = backup_getid($restore->backup_unique_code, 'grade_outcomes_history', $rec->old_id);
                        if ($data) {
                            //Now get completed xmlized object
                            $info = $data->info;
                            //traverse_xmlize($info);                            //Debug
                            //print_object ($GLOBALS['traverse_array']);         //Debug
                            //$GLOBALS['traverse_array']="";                     //Debug
                            $oldobj = backup_getid($restore->backup_unique_code, "grade_outcomes", backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['OLDID']['0']['#']));
                            if (empty($oldobj->new_id)) {
                                // if the old object is not being restored, can't restoring its history
                                $counter++;
                                continue;
                            }
                            $dbrec->oldid = $oldobj->new_id;
                            $dbrec->action = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['ACTION']['0']['#']);
                            $dbrec->source = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['SOURCE']['0']['#']);
                            $dbrec->timemodified = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['TIMEMODIFIED']['0']['#']);
                            if ($oldobj = backup_getid($restore->backup_unique_code, "user", backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['LOGGEDUSER']['0']['#']))) {
                                $dbrec->loggeduser = $oldobj->new_id;
                            }
                            $dbrec->courseid = $restore->course_id;
                            $dbrec->shortname = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['SHORTNAME']['0']['#']);
                            $dbrec->fullname = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['FULLNAME']['0']['#']);
                            $oldobj = backup_getid($restore->backup_unique_code, "scale", backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['SCALEID']['0']['#']));
                            $dbrec->scaleid = $oldobj->new_id;
                            $dbrec->description = backup_todb($info['GRADE_OUTCOME_HISTORY']['#']['DESCRIPTION']['0']['#']);
                            insert_record('grade_outcomes_history', $dbrec);
                            unset($dbrec);
                        }
                        //Increment counters
                        $counter++;
                        //Do some output
                        if ($counter % 1 == 0) {
                            if (!defined('RESTORE_SILENTLY')) {
                                echo ".";
                                if ($counter % 20 == 0) {
                                    echo "<br />";
                                }
                            }
                            backup_flush(300);
                        }
                    }
                }
            }
        }
    }
    if (!defined('RESTORE_SILENTLY')) {
        //End ul
        echo '</ul>';
    }
    return $status;
}
示例#20
0
 /**
  * Tests grade_report_user::inject_rowspans()
  *
  * inject_rowspans() returns the count of the number of elements, sets maxdepth on the
  *  report object and sets the rowspan property on any element that has children.
  */
 public function test_inject_rowspans()
 {
     global $CFG, $USER, $DB;
     parent::setUp();
     $this->resetAfterTest(true);
     $CFG->enableavailability = 1;
     $CFG->enablecompletion = 1;
     // Create a course.
     $course = $this->getDataGenerator()->create_course();
     $coursecategory = grade_category::fetch_course_category($course->id);
     $coursecontext = context_course::instance($course->id);
     // Create and enrol test users.
     $student = $this->getDataGenerator()->create_user(array('username' => 'Student Sam'));
     $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
     $this->getDataGenerator()->enrol_user($student->id, $course->id, $role->id);
     $teacher = $this->getDataGenerator()->create_user(array('username' => 'Teacher T'));
     $role = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
     $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $role->id);
     // An array so we can test with both users in a loop.
     $users = array($student, $teacher);
     // Make the student the current user.
     $this->setUser($student);
     // Test an empty course.
     $report = $this->create_report($course, $student, $coursecontext);
     // a lead column that spans all children + course grade item = 2
     $this->assertEquals(2, $report->inject_rowspans($report->gtree->top_element));
     $this->assertEquals(2, $report->gtree->top_element['rowspan']);
     $this->assertEquals(2, $report->maxdepth);
     // Only elements with children should have rowspan set.
     if (array_key_exists('rowspan', $report->gtree->top_element['children'][1])) {
         $this->fail('Elements without children should not have rowspan set');
     }
     // Add 2 activities.
     $data1 = $this->getDataGenerator()->create_module('data', array('assessed' => 1, 'scale' => 100, 'course' => $course->id));
     $forum1 = $this->getDataGenerator()->create_module('forum', array('assessed' => 1, 'scale' => 100, 'course' => $course->id));
     $forum1cm = get_coursemodule_from_id('forum', $forum1->cmid);
     // Switch the stdClass instance for a grade item instance so grade_item::set_parent() is available.
     $forum1 = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'forum', 'iteminstance' => $forum1->id, 'courseid' => $course->id));
     $report = $this->create_report($course, $student, $coursecontext);
     // Lead column + course + (2 x activity) = 4
     $this->assertEquals(4, $report->inject_rowspans($report->gtree->top_element));
     $this->assertEquals(4, $report->gtree->top_element['rowspan']);
     // Lead column + 1 level (course + 2 activities) = 2
     $this->assertEquals(2, $report->maxdepth);
     // Only elements with children should have rowspan set.
     if (array_key_exists('rowspan', $report->gtree->top_element['children'][1])) {
         $this->fail('Elements without children should not have rowspan set');
     }
     // Hide the forum activity.
     set_coursemodule_visible($forum1cm->id, 0);
     foreach ($users as $user) {
         $this->setUser($user);
         $message = 'Testing with ' . $user->username;
         accesslib_clear_all_caches_for_unit_testing();
         $report = $this->create_report($course, $user, $coursecontext);
         // Lead column + course + (2 x activity) = 4 (element count isn't affected by hiding)
         $this->assertEquals(4, $report->inject_rowspans($report->gtree->top_element), $message);
         $this->assertEquals(4, $report->gtree->top_element['rowspan'], $message);
         // Lead column -> 1 level containing the course + 2 activities = 2
         $this->assertEquals(2, $report->maxdepth, $message);
     }
     // Unhide the forum activity.
     set_coursemodule_visible($forum1cm->id, 1);
     // Create a category and put the forum in it.
     $params = new stdClass();
     $params->courseid = $course->id;
     $params->fullname = 'unittestcategory';
     $params->parent = $coursecategory->id;
     $gradecategory = new grade_category($params, false);
     $gradecategory->insert();
     $forum1->set_parent($gradecategory->id);
     $report = $this->create_report($course, $student, $coursecontext);
     // Lead column + course + (category + category grade item) + (2 x activity) = 6
     $this->assertEquals(6, $report->inject_rowspans($report->gtree->top_element));
     $this->assertEquals(6, $report->gtree->top_element['rowspan']);
     // Lead column -> the category -> the forum activity = 3
     $this->assertEquals(3, $report->maxdepth);
     // Check rowspan on the category. The category itself + category grade item + forum = 3
     $this->assertEquals(3, $report->gtree->top_element['children'][4]['rowspan']);
     // check the forum doesn't have rowspan set
     if (array_key_exists('rowspan', $report->gtree->top_element['children'][4]['children'][3])) {
         $this->fail('The forum has no children so should not have rowspan set');
     }
     // Conditional activity tests.
     // Note: I have ported this test to the new conditional availability
     // system, but it does not appear to actually test anything - in fact,
     // if you remove the code that sets the condition, it still passes
     // because it apparently is intended to have the same number of rows
     // even when some are hidden. The  same is true of the
     // set_coursemodule_visible test above. I don't feel this is a very
     // good test; somebody with more knowledge of this report might want to
     // fix it to check that the row actually is being hidden.
     $DB->set_field('course_modules', 'availability', '{"op":"|","show":false,"c":[' . '{"type":"grade","min":5.5,"id":37}]}', array('id' => $forum1cm->id));
     get_fast_modinfo($course->id, 0, true);
     foreach ($users as $user) {
         $this->setUser($user);
         $message = 'Testing with ' . $user->username;
         accesslib_clear_all_caches_for_unit_testing();
         $report = $this->create_report($course, $user, $coursecontext);
         // Lead column + course + (category + category grade item) + (2 x activity) = 6
         $this->assertEquals(6, $report->inject_rowspans($report->gtree->top_element), $message);
         $this->assertEquals(6, $report->gtree->top_element['rowspan'], $message);
         // Lead column -> the category -> the forum activity = 3
         $this->assertEquals(3, $report->maxdepth, $message);
     }
 }