Merge branch 'MDL-28006-convert-gradebook' of git://github.com/mudrd8mz/moodle

This commit is contained in:
Eloy Lafuente (stronk7) 2011-07-01 00:09:50 +02:00
commit 7ace3a2199
5 changed files with 349 additions and 10 deletions

View File

@ -52,6 +52,8 @@ abstract class moodle1_handlers_factory {
new moodle1_roles_definition_handler($converter), new moodle1_roles_definition_handler($converter),
new moodle1_question_bank_handler($converter), new moodle1_question_bank_handler($converter),
new moodle1_scales_handler($converter), new moodle1_scales_handler($converter),
new moodle1_outcomes_handler($converter),
new moodle1_gradebook_handler($converter),
); );
$handlers = array_merge($handlers, self::get_plugin_handlers('mod', $converter)); $handlers = array_merge($handlers, self::get_plugin_handlers('mod', $converter));
@ -927,10 +929,23 @@ class moodle1_course_outline_handler extends moodle1_xml_handler {
$this->write_xml('module', $cminfo, array('/module/id', '/module/version')); $this->write_xml('module', $cminfo, array('/module/id', '/module/version'));
$this->close_xml_writer(); $this->close_xml_writer();
// todo: write proper grades.xml and roles.xml, for now we just make // write grades.xml
// sure that those files are present $this->open_xml_writer($directory.'/grades.xml');
$this->xmlwriter->begin_tag('activity_gradebook');
$gradeitems = $this->converter->get_stash_or_default('gradebook_modgradeitem_'.$modname, $modinstanceid, array());
if (!empty($gradeitems)) {
$this->xmlwriter->begin_tag('grade_items');
foreach ($gradeitems as $gradeitem) {
$this->write_xml('grade_item', $gradeitem, array('/grade_item/id'));
}
$this->xmlwriter->end_tag('grade_items');
}
$this->write_xml('grade_letters', array()); // no grade_letters in module context in Moodle 1.9
$this->xmlwriter->end_tag('activity_gradebook');
$this->close_xml_writer();
// todo: write proper roles.xml, for now we just make sure the file is present
$this->make_sure_xml_exists($directory.'/roles.xml', 'roles'); $this->make_sure_xml_exists($directory.'/roles.xml', 'roles');
$this->make_sure_xml_exists($directory.'/grades.xml', 'activity_gradebook');
} }
} }
} }
@ -1319,7 +1334,7 @@ class moodle1_scales_handler extends moodle1_handler {
protected $fileman = null; protected $fileman = null;
/** /**
* Registers path that are not qtype-specific * Registers paths
*/ */
public function get_paths() { public function get_paths() {
return array( return array(
@ -1372,6 +1387,272 @@ class moodle1_scales_handler extends moodle1_handler {
} }
/**
* Handles the conversion of the outcomes
*/
class moodle1_outcomes_handler extends moodle1_xml_handler {
/** @var moodle1_file_manager instance used to convert images embedded into outcome descriptions */
protected $fileman = null;
/**
* Registers paths
*/
public function get_paths() {
return array(
new convert_path('gradebook_grade_outcomes', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_OUTCOMES'),
new convert_path(
'gradebook_grade_outcome', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_OUTCOMES/GRADE_OUTCOME',
array(
'addfields' => array(
'descriptionformat' => FORMAT_MOODLE,
),
)
),
);
}
/**
* Prepares the file manager and starts writing outcomes.xml
*/
public function on_gradebook_grade_outcomes_start() {
$syscontextid = $this->converter->get_contextid(CONTEXT_SYSTEM);
$this->fileman = $this->converter->get_file_manager($syscontextid, 'grade', 'outcome');
$this->open_xml_writer('outcomes.xml');
$this->xmlwriter->begin_tag('outcomes_definition');
}
/**
* Processes GRADE_OUTCOME tags progressively
*/
public function process_gradebook_grade_outcome(array $data, array $raw) {
global $CFG;
// replay the upgrade step 2009110400
if ($CFG->texteditors !== 'textarea') {
$data['description'] = text_to_html($data['description'], false, false, true);
$data['descriptionformat'] = FORMAT_HTML;
}
// convert course files embedded into the outcome description field
$this->fileman->itemid = $data['id'];
$data['description'] = moodle1_converter::migrate_referenced_files($data['description'], $this->fileman);
// write the outcome data
$this->write_xml('outcome', $data, array('/outcome/id'));
return $data;
}
/**
* Closes outcomes.xml
*/
public function on_gradebook_grade_outcomes_end() {
$this->xmlwriter->end_tag('outcomes_definition');
$this->close_xml_writer();
}
}
/**
* Handles the conversion of the gradebook structures in the moodle.xml file
*/
class moodle1_gradebook_handler extends moodle1_xml_handler {
/** @var array of (int)gradecategoryid => (int|null)parentcategoryid */
protected $categoryparent = array();
/**
* Registers paths
*/
public function get_paths() {
return array(
new convert_path('gradebook', '/MOODLE_BACKUP/COURSE/GRADEBOOK'),
new convert_path('gradebook_grade_letter', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_LETTERS/GRADE_LETTER'),
new convert_path(
'gradebook_grade_category', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_CATEGORIES/GRADE_CATEGORY',
array(
'addfields' => array(
'hidden' => 0, // upgrade step 2010011200
),
)
),
new convert_path('gradebook_grade_item', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_ITEMS/GRADE_ITEM'),
new convert_path('gradebook_grade_item_grades', '/MOODLE_BACKUP/COURSE/GRADEBOOK/GRADE_ITEMS/GRADE_ITEM/GRADE_GRADES'),
);
}
/**
* Initializes the in-memory structures
*
* This should not be needed actually as the moodle.xml contains just one GRADEBOOK
* element. But who knows - maybe someone will want to write a mass conversion
* tool in the future (not me definitely ;-)
*/
public function on_gradebook_start() {
$this->categoryparent = array();
}
/**
* Processes one GRADE_LETTER data
*
* In Moodle 1.9, all grade_letters are from course context only. Therefore
* we put them here.
*/
public function process_gradebook_grade_letter(array $data, array $raw) {
$this->converter->set_stash('gradebook_gradeletter', $data, $data['id']);
}
/**
* Processes one GRADE_CATEGORY data
*/
public function process_gradebook_grade_category(array $data, array $raw) {
$this->categoryparent[$data['id']] = $data['parent'];
$this->converter->set_stash('gradebook_gradecategory', $data, $data['id']);
}
/**
* Processes one GRADE_ITEM data
*/
public function process_gradebook_grade_item(array $data, array $raw) {
// here we use get_nextid() to get a nondecreasing sequence
$data['sortorder'] = $this->converter->get_nextid();
if ($data['itemtype'] === 'mod') {
return $this->process_mod_grade_item($data, $raw);
} else if (in_array($data['itemtype'], array('manual', 'course', 'category'))) {
return $this->process_nonmod_grade_item($data, $raw);
} else {
$this->log('unsupported grade_item type', backup::LOG_ERROR, $data['itemtype']);
}
}
/**
* Processes one GRADE_ITEM of the type 'mod'
*/
protected function process_mod_grade_item(array $data, array $raw) {
$stashname = 'gradebook_modgradeitem_'.$data['itemmodule'];
$stashitemid = $data['iteminstance'];
$gradeitems = $this->converter->get_stash_or_default($stashname, $stashitemid, array());
// typically there will be single item with itemnumber 0
$gradeitems[$data['itemnumber']] = $data;
$this->converter->set_stash($stashname, $gradeitems, $stashitemid);
return $data;
}
/**
* Processes one GRADE_ITEM of te type 'manual' or 'course' or 'category'
*/
protected function process_nonmod_grade_item(array $data, array $raw) {
$stashname = 'gradebook_nonmodgradeitem';
$stashitemid = $data['id'];
$this->converter->set_stash($stashname, $data, $stashitemid);
return $data;
}
/**
* @todo
*/
public function on_gradebook_grade_item_grades_start() {
}
/**
* Writes the collected information into gradebook.xml
*/
public function on_gradebook_end() {
$this->open_xml_writer('gradebook.xml');
$this->xmlwriter->begin_tag('gradebook');
$this->write_grade_categories();
$this->write_grade_items();
$this->write_grade_letters();
$this->xmlwriter->end_tag('gradebook');
$this->close_xml_writer();
}
/**
* Writes grade_categories
*/
protected function write_grade_categories() {
$this->xmlwriter->begin_tag('grade_categories');
foreach ($this->converter->get_stash_itemids('gradebook_gradecategory') as $gradecategoryid) {
$gradecategory = $this->converter->get_stash('gradebook_gradecategory', $gradecategoryid);
$path = $this->calculate_category_path($gradecategoryid);
$gradecategory['depth'] = count($path);
$gradecategory['path'] = '/'.implode('/', $path).'/';
$this->write_xml('grade_category', $gradecategory, array('/grade_category/id'));
}
$this->xmlwriter->end_tag('grade_categories');
}
/**
* Calculates the path to the grade_category
*
* Moodle 1.9 backup does not store the grade_category's depth and path. This method is used
* to repopulate this information using the $this->categoryparent values.
*
* @param int $categoryid
* @return array of ids including the categoryid
*/
protected function calculate_category_path($categoryid) {
if (!array_key_exists($categoryid, $this->categoryparent)) {
throw new moodle1_convert_exception('gradebook_unknown_categoryid', null, $categoryid);
}
$path = array($categoryid);
$parent = $this->categoryparent[$categoryid];
while (!is_null($parent)) {
array_unshift($path, $parent);
$parent = $this->categoryparent[$parent];
if (in_array($parent, $path)) {
throw new moodle1_convert_exception('circular_reference_in_categories_tree');
}
}
return $path;
}
/**
* Writes grade_items
*/
protected function write_grade_items() {
$this->xmlwriter->begin_tag('grade_items');
foreach ($this->converter->get_stash_itemids('gradebook_nonmodgradeitem') as $gradeitemid) {
$gradeitem = $this->converter->get_stash('gradebook_nonmodgradeitem', $gradeitemid);
$this->write_xml('grade_item', $gradeitem, array('/grade_item/id'));
}
$this->xmlwriter->end_tag('grade_items');
}
/**
* Writes grade_letters
*/
protected function write_grade_letters() {
$this->xmlwriter->begin_tag('grade_letters');
foreach ($this->converter->get_stash_itemids('gradebook_gradeletter') as $gradeletterid) {
$gradeletter = $this->converter->get_stash('gradebook_gradeletter', $gradeletterid);
$this->write_xml('grade_letter', $gradeletter, array('/grade_letter/id'));
}
$this->xmlwriter->end_tag('grade_letters');
}
}
/** /**
* Shared base class for activity modules, blocks and qtype handlers * Shared base class for activity modules, blocks and qtype handlers
*/ */

View File

@ -470,6 +470,22 @@ class moodle1_converter extends base_converter {
} }
} }
/**
* Restores a given stash or returns the given default if there is no such stash
*
* @param string $stashname name of the stash
* @param int $itemid optional id for multiple infos within the same stashname
* @param mixed $default information to return if the info has not been stashed previously
* @return mixed stashed data or the default value
*/
public function get_stash_or_default($stashname, $itemid = 0, $default = null) {
try {
return $this->get_stash($stashname, $itemid);
} catch (moodle1_convert_empty_storage_exception $e) {
return $default;
}
}
/** /**
* Returns the list of existing stashes * Returns the list of existing stashes
* *

View File

@ -160,6 +160,38 @@ class moodle1_converter_test extends UnitTestCase {
$converter->drop_stash_storage(); $converter->drop_stash_storage();
} }
public function test_get_stash_or_default() {
$converter = convert_factory::get_converter('moodle1', $this->tempdir);
$converter->create_stash_storage();
$this->assertTrue(is_null($converter->get_stash_or_default('stashname')));
$this->assertTrue(is_null($converter->get_stash_or_default('stashname', 7)));
$this->assertTrue('default' === $converter->get_stash_or_default('stashname', 0, 'default'));
$this->assertTrue(array('foo', 'bar') === $converter->get_stash_or_default('stashname', 42, array('foo', 'bar')));
//$converter->set_stash('stashname', 0);
//$this->assertFalse(is_null($converter->get_stash_or_default('stashname'))); // todo returns true now, this needs MDL-27713 to be fixed
//$converter->set_stash('stashname', '');
//$this->assertFalse(is_null($converter->get_stash_or_default('stashname'))); // todo returns true now, this needs MDL-27713 to be fixed
//$converter->set_stash('stashname', array());
//$this->assertFalse(is_null($converter->get_stash_or_default('stashname'))); // todo returns true now, this needs MDL-27713 to be fixed
$converter->set_stash('stashname', 42);
$this->assertTrue(42 === $converter->get_stash_or_default('stashname'));
$this->assertTrue(is_null($converter->get_stash_or_default('stashname', 1)));
$this->assertTrue(42 === $converter->get_stash_or_default('stashname', 0, 61));
$converter->set_stash('stashname', array(42 => (object)array('id' => 42)), 18);
$stashed = $converter->get_stash_or_default('stashname', 18, 1984);
$this->assertIsA($stashed, 'array');
$this->assertTrue(is_object($stashed[42]));
$this->assertTrue($stashed[42]->id === 42);
$converter->drop_stash_storage();
}
public function test_get_contextid() { public function test_get_contextid() {
$converter = convert_factory::get_converter('moodle1', $this->tempdir); $converter = convert_factory::get_converter('moodle1', $this->tempdir);

View File

@ -146,12 +146,18 @@ class restore_gradebook_structure_step extends restore_structure_step {
$data->courseid = $this->get_courseid(); $data->courseid = $this->get_courseid();
//manual grade items store category id in categoryid
if ($data->itemtype=='manual') { if ($data->itemtype=='manual') {
// manual grade items store category id in categoryid
$data->categoryid = $this->get_mappingid('grade_category', $data->categoryid, NULL); $data->categoryid = $this->get_mappingid('grade_category', $data->categoryid, NULL);
} //course and category grade items store their category id in iteminstance } else if ($data->itemtype=='course') {
else if ($data->itemtype=='course' || $data->itemtype=='category') { // course grade item stores their category id in iteminstance
$coursecat = grade_category::fetch_course_category($this->get_courseid());
$data->iteminstance = $coursecat->id;
} else if ($data->itemtype=='category') {
// category grade items store their category id in iteminstance
$data->iteminstance = $this->get_mappingid('grade_category', $data->iteminstance, NULL); $data->iteminstance = $this->get_mappingid('grade_category', $data->iteminstance, NULL);
} else {
throw new restore_step_exception('unexpected_grade_item_type', $data->itemtype);
} }
$data->scaleid = $this->get_mappingid('scale', $data->scaleid, NULL); $data->scaleid = $this->get_mappingid('scale', $data->scaleid, NULL);
@ -300,12 +306,16 @@ class restore_gradebook_structure_step extends restore_structure_step {
//if this is an activity grade item that needs to be put back in its correct category //if this is an activity grade item that needs to be put back in its correct category
if (!empty($grade_item_backup->parentitemid)) { if (!empty($grade_item_backup->parentitemid)) {
$updateobj->categoryid = $this->get_mappingid('grade_category', $grade_item_backup->parentitemid); $oldcategoryid = $this->get_mappingid('grade_category', $grade_item_backup->parentitemid, null);
if (!is_null($oldcategoryid)) {
$updateobj->categoryid = $oldcategoryid;
$DB->update_record('grade_items', $updateobj);
}
} else { } else {
//mark course and category items as needing to be recalculated //mark course and category items as needing to be recalculated
$updateobj->needsupdate=1; $updateobj->needsupdate=1;
$DB->update_record('grade_items', $updateobj);
} }
$DB->update_record('grade_items', $updateobj);
} }
} }
$rs->close(); $rs->close();

View File

@ -89,7 +89,7 @@ abstract class convert_helper {
$filepath = $dirpath . '/moodle_backup.xml'; $filepath = $dirpath . '/moodle_backup.xml';
if (!is_dir($dirpath)) { if (!is_dir($dirpath)) {
throw new converter_helper_exception('tmp_backup_directory_not_found', $dirpath); throw new convert_helper_exception('tmp_backup_directory_not_found', $dirpath);
} }
if (!file_exists($filepath)) { if (!file_exists($filepath)) {