backup MDL-22254 Added backup and restore of course completion information
authorSam Hemelryk <sam@moodle.com>
Fri, 17 Sep 2010 03:04:55 +0000 (03:04 +0000)
committerSam Hemelryk <sam@moodle.com>
Fri, 17 Sep 2010 03:04:55 +0000 (03:04 +0000)
backup/moodle2/backup_activity_task.class.php
backup/moodle2/backup_final_task.class.php
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_final_task.class.php
backup/moodle2/restore_stepslib.php
backup/util/plan/backup_plan.class.php
backup/util/plan/backup_task.class.php

index 0695d52..0a9124c 100644 (file)
@@ -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();
         }
index f690739..320442b 100644 (file)
@@ -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'));
 
index a62acc5..e46634f 100644 (file)
@@ -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
index 69a639c..9febd72 100644 (file)
@@ -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'));
index 7875273..9834e43 100644 (file)
@@ -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
index 39fb6f8..bf38ec0 100644 (file)
@@ -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());
     }
index f7840ef..d6313a1 100644 (file)
@@ -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();
+    }
 }
 
 /*