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)); }
/** * 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); } } } } } }
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); }
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
/** * 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(); }
/** * 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; }
/** * 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"; }
* 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. */
function test_upgrade_calculated_grade_items_regrade() { global $DB, $CFG; $this->resetAfterTest(); // Create a user. $user = $this->getDataGenerator()->create_user(); // Create a course. $course = $this->getDataGenerator()->create_course(); // Enrol the user in the course. $studentrole = $DB->get_record('role', array('shortname' => 'student')); $maninstance1 = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST); $manual = enrol_get_plugin('manual'); $manual->enrol_user($maninstance1, $user->id, $studentrole->id); set_config('upgrade_calculatedgradeitemsonlyregrade', 1); // Creating a category for a grade item. $gradecategory = new grade_category(); $gradecategory->fullname = 'calculated grade category'; $gradecategory->courseid = $course->id; $gradecategory->insert(); $gradecategoryid = $gradecategory->id; // This is a manual grade item. $gradeitem = new grade_item(); $gradeitem->itemname = 'grade item one'; $gradeitem->itemtype = 'manual'; $gradeitem->categoryid = $gradecategoryid; $gradeitem->courseid = $course->id; $gradeitem->idnumber = 'gi1'; $gradeitem->insert(); // Changing the category into a calculated grade category. $gradecategoryitem = grade_item::fetch(array('iteminstance' => $gradecategory->id)); $gradecategoryitem->calculation = '=##gi' . $gradeitem->id . '##/2'; $gradecategoryitem->grademax = 50; $gradecategoryitem->grademin = 15; $gradecategoryitem->update(); // Setting a grade for the student. $grade = $gradeitem->get_grade($user->id, true); $grade->finalgrade = 50; $grade->update(); grade_regrade_final_grades($course->id); $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id)); $grade->rawgrademax = 100; $grade->rawgrademin = 0; $grade->update(); $this->assertNotEquals($gradecategoryitem->grademax, $grade->rawgrademax); $this->assertNotEquals($gradecategoryitem->grademin, $grade->rawgrademin); // This is the function that we are testing. If we comment out this line, then the test fails because the grade items // are not flagged for regrading. upgrade_calculated_grade_items(); grade_regrade_final_grades($course->id); $grade = grade_grade::fetch(array('itemid' => $gradecategoryitem->id, 'userid' => $user->id)); $this->assertEquals($gradecategoryitem->grademax, $grade->rawgrademax); $this->assertEquals($gradecategoryitem->grademin, $grade->rawgrademin); }
public function test_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); } }
/** * 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; }
/** * 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); } }