From bd39b6f226b4842aa53f064457b3d522a5eecb55 Mon Sep 17 00:00:00 2001 From: Sam Hemelryk Date: Fri, 17 Sep 2010 03:04:55 +0000 Subject: [PATCH] backup MDL-22254 Added backup and restore of course completion information --- backup/moodle2/backup_activity_task.class.php | 2 +- backup/moodle2/backup_final_task.class.php | 3 + backup/moodle2/backup_stepslib.php | 79 ++++++ backup/moodle2/restore_final_task.class.php | 3 + backup/moodle2/restore_stepslib.php | 247 ++++++++++++++++++ backup/util/plan/backup_plan.class.php | 9 + backup/util/plan/backup_task.class.php | 4 + 7 files changed, 346 insertions(+), 1 deletion(-) diff --git a/backup/moodle2/backup_activity_task.class.php b/backup/moodle2/backup_activity_task.class.php index 0695d525bc3..0a9124c92c4 100644 --- a/backup/moodle2/backup_activity_task.class.php +++ b/backup/moodle2/backup_activity_task.class.php @@ -173,7 +173,7 @@ abstract class backup_activity_task extends backup_task { // Find activity_included_setting if (!$this->get_setting_value('included')) { $this->log('activity skipped by _included setting', backup::LOG_DEBUG, $this->name); - + $this->plan->set_excluding_activities(); } else { // Setting tells us it's ok to execute parent::execute(); } diff --git a/backup/moodle2/backup_final_task.class.php b/backup/moodle2/backup_final_task.class.php index f690739ead5..320442b5737 100644 --- a/backup/moodle2/backup_final_task.class.php +++ b/backup/moodle2/backup_final_task.class.php @@ -73,6 +73,9 @@ class backup_final_task extends backup_task { // execute_condition() so only will be excuted if ALL module grade_items in course have been exported $this->add_step(new backup_gradebook_structure_step('course_gradebook','gradebook.xml')); + // Generate the course completion + $this->add_step(new backup_course_completion_structure_step('course_completion', 'completion.xml')); + // Generate the scales file with all the (final) annotated scales $this->add_step(new backup_final_scales_structure_step('scaleslist', 'scales.xml')); diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index a62acc5d126..e46634f8155 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -1544,3 +1544,82 @@ class backup_activity_grades_structure_step extends backup_structure_step { return $book; } } + +/** + * Backups up the course completion information for the course. + */ +class backup_course_completion_structure_step extends backup_structure_step { + + protected function execute_condition() { + // Check that all activities have been included + if ($this->task->is_excluding_activities()) { + return false; + } + return true; + } + + /** + * The structure of the course completion backup + * + * @return backup_nested_element + */ + protected function define_structure() { + + // To know if we are including user completion info + $userinfo = $this->get_setting_value('userscompletion'); + + $cc = new backup_nested_element('course_completion'); + + $criteria = new backup_nested_element('course_completion_criteria', array('id'), array( + 'course','criteriatype', 'module', 'moduleinstance', 'courseinstanceshortname', 'enrolperiod', 'timeend', 'gradepass', 'role' + )); + + $criteriacompletions = new backup_nested_element('course_completion_crit_completions'); + + $criteriacomplete = new backup_nested_element('course_completion_crit_compl', array('id'), array( + 'criteriaid', 'userid','gradefinal','unenrolled','deleted','timecompleted' + )); + + $coursecompletions = new backup_nested_element('course_completions', array('id'), array( + 'userid', 'course', 'deleted', 'timenotified', 'timeenrolled','timestarted','timecompleted','reaggregate' + )); + + $notify = new backup_nested_element('course_completion_notify', array('id'), array( + 'course','role','message','timesent' + )); + + $aggregatemethod = new backup_nested_element('course_completion_aggr_methd', array('id'), array( + 'course','criteriatype','method','value' + )); + + $cc->add_child($criteria); + $criteria->add_child($criteriacompletions); + $criteriacompletions->add_child($criteriacomplete); + $cc->add_child($coursecompletions); + $cc->add_child($notify); + $cc->add_child($aggregatemethod); + + // We need to get the courseinstances shortname rather than an ID for restore + $criteria->set_source_sql("SELECT ccc.*, c.shortname courseinstanceshortname + FROM {course_completion_criteria} ccc + LEFT JOIN {course} c ON c.id = ccc.courseinstance + WHERE ccc.course = ?", array(backup::VAR_COURSEID)); + + + $notify->set_source_table('course_completion_notify', array('course' => backup::VAR_COURSEID)); + $aggregatemethod->set_source_table('course_completion_aggr_methd', array('course' => backup::VAR_COURSEID)); + + if ($userinfo) { + $criteriacomplete->set_source_table('course_completion_crit_compl', array('criteriaid' => backup::VAR_PARENTID)); + $coursecompletions->set_source_table('course_completions', array('course' => backup::VAR_COURSEID)); + } + + $criteria->annotate_ids('role', 'role'); + $criteriacomplete->annotate_ids('user', 'userid'); + $coursecompletions->annotate_ids('user', 'userid'); + $notify->annotate_ids('role', 'role'); + + return $cc; + + } +} \ No newline at end of file diff --git a/backup/moodle2/restore_final_task.class.php b/backup/moodle2/restore_final_task.class.php index 69a639c17cd..9febd726954 100644 --- a/backup/moodle2/restore_final_task.class.php +++ b/backup/moodle2/restore_final_task.class.php @@ -47,6 +47,9 @@ class restore_final_task extends restore_task { $this->add_step(new restore_gradebook_structure_step('gradebook_step','gradebook.xml')); } + // Course completion + $this->add_step(new restore_course_completion_structure_step('course_completion', 'completion.xml')); + // Review all the module_availability records in backup_ids in order // to match them with existing modules / grade items. $this->add_step(new restore_process_course_modules_availability('process_modules_availability')); diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index 7875273421d..9834e432d53 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -1226,6 +1226,253 @@ class restore_comments_structure_step extends restore_structure_step { } } +class restore_course_completion_structure_step extends restore_structure_step { + + /** + * Conditionally decide if this step should be executed. + * + * This function checks parameters that are not immediate settings to ensure + * that the enviroment is suitable for the restore of course completion info. + * + * This function checks the following four parameters: + * + * 1. Course completion is enabled on the site + * 2. The backup includes course completion information + * 3. All modules are restorable + * 4. All modules are marked for restore. + * + * @return bool True is safe to execute, false otherwise + */ + protected function execute_condition() { + global $CFG; + + // First check course completion is enabled on this site + if (empty($CFG->enablecompletion)) { + // Disabled, don't restore course completion + return false; + } + + // Check it is included in the backup + $fullpath = $this->task->get_taskbasepath(); + $fullpath = rtrim($fullpath, '/') . '/' . $this->filename; + if (!file_exists($fullpath)) { + // Not found, can't restore course completion + return false; + } + + // Check we are able to restore all backed up modules + if ($this->task->is_missing_modules()) { + return false; + } + + // Finally check all modules within the backup are being restored. + if ($this->task->is_excluding_activities()) { + return false; + } + + return true; + } + + /** + * Define the course completion structure + * + * @return array Array of restore_path_element + */ + protected function define_structure() { + + // To know if we are including user completion info + $userinfo = $this->get_setting_value('userscompletion'); + + $paths = array(); + $paths[] = new restore_path_element('course_completion_criteria', '/course_completion/course_completion_criteria'); + $paths[] = new restore_path_element('course_completion_notify', '/course_completion/course_completion_notify'); + $paths[] = new restore_path_element('course_completion_aggr_methd', '/course_completion/course_completion_aggr_methd'); + + if ($userinfo) { + $paths[] = new restore_path_element('course_completion_crit_compl', '/course_completion/course_completion_criteria/course_completion_crit_completions/course_completion_crit_compl'); + $paths[] = new restore_path_element('course_completions', '/course_completion/course_completions'); + } + + return $paths; + + } + + /** + * Process course completion criteria + * + * @global moodle_database $DB + * @param stdClass $data + */ + public function process_course_completion_criteria($data) { + global $DB; + + $data = (object)$data; + $data->course = $this->get_courseid(); + + // Apply the date offset to the time end field + $data->timeend = $this->apply_date_offset($data->timeend); + + // Map the role from the criteria + if (!empty($data->role)) { + $data->role = $this->get_mappingid('role', $data->role); + } + + // If the completion criteria is for a module we need to map the module instance + // to the new module id. + if (!empty($data->moduleinstance) && !empty($data->module)) { + $data->moduleinstance = $this->get_mappingid('course_module', $data->moduleinstance); + } else { + $data->module = null; + $data->moduleinstance = null; + } + + // We backup the course shortname rather than the ID so that we can match back to the course + $skipcriteria = false; + if (!empty($data->courseinstanceshortname)) { + $courseinstanceid = $DB->get_field('course', 'id', array('shortname'=>$data->courseinstanceshortname)); + if (!$courseinstanceid) { + $skipcriteria = true; + } + } else { + $courseinstanceid = null; + } + $data->courseinstance = $courseinstanceid; + + if (!$skipcriteria) { + $params = array( + 'course' => $data->course, + 'criteriatype' => $data->criteriatype, + 'enrolperiod' => $data->enrolperiod, + 'courseinstance' => $data->courseinstance, + 'module' => $data->module, + 'moduleinstance' => $data->moduleinstance, + 'timeend' => $data->timeend, + 'gradepass' => $data->gradepass, + 'role' => $data->role + ); + $newid = $DB->insert_record('course_completion_criteria', $params); + $this->set_mapping('course_completion_criteria', $data->id, $newid); + } + } + + /** + * Processes course compltion criteria complete records + * + * @global moodle_database $DB + * @param stdClass $data + */ + public function process_course_completion_crit_compl($data) { + global $DB; + + $data = (object)$data; + + // This may be empty if criteria could not be restored + $data->criteriaid = $this->get_mappingid('course_completion_criteria', $data->criteriaid); + + $data->course = $this->get_courseid(); + $data->userid = $this->get_mappingid('user', $data->userid); + + if (!empty($data->criteriaid) && !empty($data->userid)) { + $params = array( + 'userid' => $data->userid, + 'course' => $data->course, + 'criteriaid' => $data->criteriaid, + 'timecompleted' => $this->apply_date_offset($data->timecompleted) + ); + if (isset($data->gradefinal)) { + $params['gradefinal'] = $data->gradefinal; + } + if (isset($data->unenroled)) { + $params['unenroled'] = $data->unenroled; + } + if (isset($data->deleted)) { + $params['deleted'] = $data->deleted; + } + $DB->insert_record('course_completion_crit_compl', $params); + } + } + + /** + * Process course completions + * + * @global moodle_database $DB + * @param stdClass $data + */ + public function process_course_completions($data) { + global $DB; + + $data = (object)$data; + + $data->course = $this->get_courseid(); + $data->userid = $this->get_mappingid('user', $data->userid); + + if (!empty($data->userid)) { + $params = array( + 'userid' => $data->userid, + 'course' => $data->course, + 'deleted' => $data->deleted, + 'timenotified' => $this->apply_date_offset($data->timenotified), + 'timeenrolled' => $this->apply_date_offset($data->timeenrolled), + 'timestarted' => $this->apply_date_offset($data->timestarted), + 'timecompleted' => $this->apply_date_offset($data->timecompleted), + 'reaggregate' => $data->reaggregate + ); + $DB->insert_record('course_completions', $params); + } + } + + /** + * Process course completion notification records. + * + * Note: As of Moodle 2.0 this table is not being used however it has been + * left in in the hopes that one day the functionality there will be completed + * + * @global moodle_database $DB + * @param stdClass $data + */ + public function process_course_completion_notify($data) { + global $DB; + + $data = (object)$data; + + $data->course = $this->get_courseid(); + if (!empty($data->role)) { + $data->role = $this->get_mappingid('role', $data->role); + } + + $params = array( + 'course' => $data->course, + 'role' => $data->role, + 'message' => $data->message, + 'timesent' => $this->apply_date_offset($data->timesent), + ); + $DB->insert_record('course_completion_notify', $params); + } + + /** + * Process course completion aggregate methods + * + * @global moodle_database $DB + * @param stdClass $data + */ + public function process_course_completion_aggr_methd($data) { + global $DB; + + $data = (object)$data; + + $data->course = $this->get_courseid(); + + $params = array( + 'course' => $data->course, + 'criteriatype' => $data->criteriatype, + 'method' => $data->method, + 'value' => $data->value, + ); + $DB->insert_record('course_completion_aggr_methd', $params); + } + +} + /** * This structure step restores the grade items associated with one activity * All the grade items are made child of the "course" grade item but the original diff --git a/backup/util/plan/backup_plan.class.php b/backup/util/plan/backup_plan.class.php index 39fb6f8cdcf..bf38ec0365d 100644 --- a/backup/util/plan/backup_plan.class.php +++ b/backup/util/plan/backup_plan.class.php @@ -31,6 +31,7 @@ class backup_plan extends base_plan implements loggable { protected $controller; // The backup controller building/executing this plan protected $basepath; // Fullpath to dir where backup is created + protected $excludingdactivities; /** * Constructor - instantiates one object of this class @@ -67,6 +68,14 @@ class backup_plan extends base_plan implements loggable { return $this->controller->get_logger(); } + public function is_excluding_activities() { + return $this->excludingdactivities; + } + + public function set_excluding_activities() { + $this->excludingdactivities = true; + } + public function log($message, $level, $a = null, $depth = null, $display = false) { backup_helper::log($message, $level, $a, $depth, $display, $this->get_logger()); } diff --git a/backup/util/plan/backup_task.class.php b/backup/util/plan/backup_task.class.php index f7840ef326d..d6313a1f2b6 100644 --- a/backup/util/plan/backup_task.class.php +++ b/backup/util/plan/backup_task.class.php @@ -42,6 +42,10 @@ abstract class backup_task extends base_task { public function get_backupid() { return $this->plan->get_backupid(); } + + public function is_excluding_activities() { + return $this->plan->is_excluding_activities(); + } } /* -- 2.43.0