mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 00:12:56 +02:00
MDL-46696 backup: Include grade history in backups
This commit is contained in:
parent
e609e6cf02
commit
167eb0335b
@ -181,6 +181,9 @@ abstract class backup_activity_task extends backup_task {
|
||||
// Generate the grading file (conditionally)
|
||||
$this->add_step(new backup_activity_grading_structure_step('activity_grading', 'grading.xml'));
|
||||
|
||||
// Generate the grade history file. The setting 'grade_histories' is handled in the step.
|
||||
$this->add_step(new backup_activity_grade_history_structure_step('activity_grade_history', 'grade_history.xml'));
|
||||
|
||||
// Annotate the scales used in already annotated outcomes
|
||||
$this->add_step(new backup_annotate_scales_from_outcomes('annotate_scales'));
|
||||
|
||||
|
@ -89,6 +89,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 grade history file, conditionally.
|
||||
$this->add_step(new backup_grade_history_structure_step('course_grade_history','grade_history.xml'));
|
||||
|
||||
// Generate the course completion
|
||||
$this->add_step(new backup_course_completion_structure_step('course_completion', 'completion.xml'));
|
||||
|
||||
|
@ -152,6 +152,9 @@ class backup_root_task extends backup_task {
|
||||
$gradehistories->set_ui(new backup_setting_ui_checkbox($gradehistories, get_string('rootsettinggradehistories', 'backup')));
|
||||
$this->add_setting($gradehistories);
|
||||
$users->add_dependency($gradehistories);
|
||||
// The restore does not process the grade histories when some activities are ignored.
|
||||
// So let's define a dependency to prevent false expectations from our users.
|
||||
$activities->add_dependency($gradehistories);
|
||||
|
||||
// Define question bank inclusion setting.
|
||||
$questionbank = new backup_generic_setting('questionbank', base_setting::IS_BOOLEAN, true);
|
||||
|
@ -1022,6 +1022,65 @@ class backup_gradebook_structure_step extends backup_structure_step {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step in charge of constructing the grade_history.xml file containing the grade histories.
|
||||
*/
|
||||
class backup_grade_history_structure_step extends backup_structure_step {
|
||||
|
||||
/**
|
||||
* Limit the execution.
|
||||
*
|
||||
* This applies the same logic than the one applied to {@link backup_gradebook_structure_step},
|
||||
* because we do not want to save the history of items which are not backed up. At least for now.
|
||||
*/
|
||||
protected function execute_condition() {
|
||||
return backup_plan_dbops::require_gradebook_backup($this->get_courseid(), $this->get_backupid());
|
||||
}
|
||||
|
||||
protected function define_structure() {
|
||||
|
||||
// Settings to use.
|
||||
$userinfo = $this->get_setting_value('users');
|
||||
$history = $this->get_setting_value('grade_histories');
|
||||
|
||||
// Create the nested elements.
|
||||
$bookhistory = new backup_nested_element('grade_history');
|
||||
$grades = new backup_nested_element('grade_grades');
|
||||
$grade = new backup_nested_element('grade_grade', array('id'), array(
|
||||
'action', 'oldid', 'source', 'loggeduser', 'itemid', 'userid',
|
||||
'rawgrade', 'rawgrademax', 'rawgrademin', 'rawscaleid',
|
||||
'usermodified', 'finalgrade', 'hidden', 'locked', 'locktime', 'exported', 'overridden',
|
||||
'excluded', 'feedback', 'feedbackformat', 'information',
|
||||
'informationformat', 'timemodified'));
|
||||
|
||||
// Build the tree.
|
||||
$bookhistory->add_child($grades);
|
||||
$grades->add_child($grade);
|
||||
|
||||
// This only happens if we are including user info and history.
|
||||
if ($userinfo && $history) {
|
||||
// Only keep the history of grades related to items which have been backed up, The query is
|
||||
// similar (but not identical) to the one used in backup_gradebook_structure_step::define_structure().
|
||||
$gradesql = "SELECT ggh.*
|
||||
FROM {grade_grades_history} ggh
|
||||
JOIN {grade_items} gi ON ggh.itemid = gi.id
|
||||
WHERE gi.courseid = :courseid
|
||||
AND (gi.itemtype = 'manual' OR gi.itemtype = 'course' OR gi.itemtype = 'category')";
|
||||
$grade->set_source_sql($gradesql, array('courseid' => backup::VAR_COURSEID));
|
||||
}
|
||||
|
||||
// Annotations. (Final annotations as this step is part of the final task).
|
||||
$grade->annotate_ids('scalefinal', 'rawscaleid');
|
||||
$grade->annotate_ids('userfinal', 'loggeduser');
|
||||
$grade->annotate_ids('userfinal', 'userid');
|
||||
$grade->annotate_ids('userfinal', 'usermodified');
|
||||
|
||||
// Return the root element.
|
||||
return $bookhistory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* structure step in charge if constructing the completion.xml file for all the users completion
|
||||
* information in a given activity
|
||||
@ -2162,6 +2221,56 @@ class backup_activity_grades_structure_step extends backup_structure_step {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Structure step in charge of constructing the grade history of an activity.
|
||||
*
|
||||
* This step is added to the task regardless of the setting 'grade_histories'.
|
||||
* The reason is to allow for a more flexible step in case the logic needs to be
|
||||
* split accross different settings to control the history of items and/or grades.
|
||||
*/
|
||||
class backup_activity_grade_history_structure_step extends backup_structure_step {
|
||||
|
||||
protected function define_structure() {
|
||||
|
||||
// Settings to use.
|
||||
$userinfo = $this->get_setting_value('userinfo');
|
||||
$history = $this->get_setting_value('grade_histories');
|
||||
|
||||
// Create the nested elements.
|
||||
$bookhistory = new backup_nested_element('grade_history');
|
||||
$grades = new backup_nested_element('grade_grades');
|
||||
$grade = new backup_nested_element('grade_grade', array('id'), array(
|
||||
'action', 'oldid', 'source', 'loggeduser', 'itemid', 'userid',
|
||||
'rawgrade', 'rawgrademax', 'rawgrademin', 'rawscaleid',
|
||||
'usermodified', 'finalgrade', 'hidden', 'locked', 'locktime', 'exported', 'overridden',
|
||||
'excluded', 'feedback', 'feedbackformat', 'information',
|
||||
'informationformat', 'timemodified'));
|
||||
|
||||
// Build the tree.
|
||||
$bookhistory->add_child($grades);
|
||||
$grades->add_child($grade);
|
||||
|
||||
// This only happens if we are including user info and history.
|
||||
if ($userinfo && $history) {
|
||||
// Define sources. Only select the history related to existing activity items.
|
||||
$grade->set_source_sql("SELECT ggh.*
|
||||
FROM {grade_grades_history} ggh
|
||||
JOIN {backup_ids_temp} bi ON ggh.itemid = bi.itemid
|
||||
WHERE bi.backupid = ?
|
||||
AND bi.itemname = 'grade_item'", array(backup::VAR_BACKUPID));
|
||||
}
|
||||
|
||||
// Annotations.
|
||||
$grade->annotate_ids('scalefinal', 'rawscaleid'); // Straight as scalefinal because it's > 0.
|
||||
$grade->annotate_ids('user', 'loggeduser');
|
||||
$grade->annotate_ids('user', 'userid');
|
||||
$grade->annotate_ids('user', 'usermodified');
|
||||
|
||||
// Return the root element.
|
||||
return $bookhistory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backups up the course completion information for the course.
|
||||
*/
|
||||
|
@ -163,6 +163,9 @@ abstract class restore_activity_task extends restore_task {
|
||||
// Advanced grading methods attached to the module
|
||||
$this->add_step(new restore_activity_grading_structure_step('activity_grading', 'grading.xml'));
|
||||
|
||||
// Grade history. The setting 'grade_history' is handled in the step.
|
||||
$this->add_step(new restore_activity_grade_history_structure_step('activity_grade_history', 'grade_history.xml'));
|
||||
|
||||
// Userscompletion (conditionally)
|
||||
if ($this->get_setting_value('userscompletion')) {
|
||||
$this->add_step(new restore_userscompletion_structure_step('activity_userscompletion', 'completion.xml'));
|
||||
|
@ -58,6 +58,7 @@ class restore_final_task extends restore_task {
|
||||
// Gradebook. Don't restore the gradebook unless activities are being restored.
|
||||
if ($this->get_setting_value('activities')) {
|
||||
$this->add_step(new restore_gradebook_structure_step('gradebook_step','gradebook.xml'));
|
||||
$this->add_step(new restore_grade_history_structure_step('grade_history', 'grade_history.xml'));
|
||||
}
|
||||
|
||||
// Course completion, executed conditionally if restoring to new course
|
||||
|
@ -246,5 +246,9 @@ class restore_root_task extends restore_task {
|
||||
$gradehistories->get_ui()->set_changeable($changeable);
|
||||
$this->add_setting($gradehistories);
|
||||
$users->add_dependency($gradehistories);
|
||||
|
||||
// The restore does not process the grade histories when some activities are ignored.
|
||||
// So let's define a dependency to prevent false expectations from our users.
|
||||
$activities->add_dependency($gradehistories);
|
||||
}
|
||||
}
|
||||
|
@ -224,6 +224,7 @@ class restore_gradebook_structure_step extends restore_structure_step {
|
||||
global $DB;
|
||||
|
||||
$data = (object)$data;
|
||||
$oldid = $data->id;
|
||||
$olduserid = $data->userid;
|
||||
|
||||
$data->itemid = $this->get_new_parentid('grade_item');
|
||||
@ -243,6 +244,7 @@ class restore_gradebook_structure_step extends restore_structure_step {
|
||||
$this->log($message, backup::LOG_DEBUG);
|
||||
} else {
|
||||
$newitemid = $DB->insert_record('grade_grades', $data);
|
||||
$this->set_mapping('grade_grades', $oldid, $newitemid);
|
||||
}
|
||||
} else {
|
||||
$message = "Mapped user id not found for user id '{$olduserid}', grade item id '{$data->itemid}'";
|
||||
@ -445,6 +447,85 @@ class restore_gradebook_structure_step extends restore_structure_step {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step in charge of restoring the grade history of a course.
|
||||
*
|
||||
* The execution conditions are itendical to {@link restore_gradebook_structure_step} because
|
||||
* we do not want to restore the history if the gradebook and its content has not been
|
||||
* restored. At least for now.
|
||||
*/
|
||||
class restore_grade_history_structure_step extends restore_structure_step {
|
||||
|
||||
protected function execute_condition() {
|
||||
global $CFG, $DB;
|
||||
|
||||
// No gradebook info found, don't execute.
|
||||
$fullpath = $this->task->get_taskbasepath();
|
||||
$fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
|
||||
if (!file_exists($fullpath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some module present in backup file isn't available to restore in this site, don't execute.
|
||||
if ($this->task->is_missing_modules()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some activity has been excluded to be restored, don't execute.
|
||||
if ($this->task->is_excluding_activities()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// There should only be one grade category (the 1 associated with the course itself).
|
||||
$category = new stdclass();
|
||||
$category->courseid = $this->get_courseid();
|
||||
$catcount = $DB->count_records('grade_categories', (array)$category);
|
||||
if ($catcount > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Arrived here, execute the step.
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function define_structure() {
|
||||
$paths = array();
|
||||
|
||||
// Settings to use.
|
||||
$userinfo = $this->get_setting_value('users');
|
||||
$history = $this->get_setting_value('grade_histories');
|
||||
|
||||
if ($userinfo && $history) {
|
||||
$paths[] = new restore_path_element('grade_grade',
|
||||
'/grade_history/grade_grades/grade_grade');
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
protected function process_grade_grade($data) {
|
||||
global $DB;
|
||||
|
||||
$data = (object)($data);
|
||||
$olduserid = $data->userid;
|
||||
unset($data->id);
|
||||
|
||||
$data->userid = $this->get_mappingid('user', $data->userid, null);
|
||||
if (!empty($data->userid)) {
|
||||
// Do not apply the date offsets as this is history.
|
||||
$data->itemid = $this->get_mappingid('grade_item', $data->itemid);
|
||||
$data->oldid = $this->get_mappingid('grade_grades', $data->oldid);
|
||||
$data->usermodified = $this->get_mappingid('user', $data->usermodified, null);
|
||||
$data->rawscaleid = $this->get_mappingid('scale', $data->rawscaleid);
|
||||
$DB->insert_record('grade_grades_history', $data);
|
||||
} else {
|
||||
$message = "Mapped user id not found for user id '{$olduserid}', grade item id '{$data->itemid}'";
|
||||
$this->log($message, backup::LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* decode all the interlinks present in restored content
|
||||
* relying 100% in the restore_decode_processor that handles
|
||||
@ -2895,6 +2976,7 @@ class restore_activity_grades_structure_step extends restore_structure_step {
|
||||
protected function process_grade_grade($data) {
|
||||
$data = (object)($data);
|
||||
$olduserid = $data->userid;
|
||||
$oldid = $data->id;
|
||||
unset($data->id);
|
||||
|
||||
$data->itemid = $this->get_new_parentid('grade_item');
|
||||
@ -2908,7 +2990,7 @@ class restore_activity_grades_structure_step extends restore_structure_step {
|
||||
|
||||
$grade = new grade_grade($data, false);
|
||||
$grade->insert('restore');
|
||||
// no need to save any grade_grade mapping
|
||||
$this->set_mapping('grade_grades', $oldid, $grade->id);
|
||||
} else {
|
||||
debugging("Mapped user id not found for user id '{$olduserid}', grade item id '{$data->itemid}'");
|
||||
}
|
||||
@ -2941,6 +3023,52 @@ class restore_activity_grades_structure_step extends restore_structure_step {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step in charge of restoring the grade history of an activity.
|
||||
*
|
||||
* This step is added to the task regardless of the setting 'grade_histories'.
|
||||
* The reason is to allow for a more flexible step in case the logic needs to be
|
||||
* split accross different settings to control the history of items and/or grades.
|
||||
*/
|
||||
class restore_activity_grade_history_structure_step extends restore_structure_step {
|
||||
|
||||
protected function define_structure() {
|
||||
$paths = array();
|
||||
|
||||
// Settings to use.
|
||||
$userinfo = $this->get_setting_value('userinfo');
|
||||
$history = $this->get_setting_value('grade_histories');
|
||||
|
||||
if ($userinfo && $history) {
|
||||
$paths[] = new restore_path_element('grade_grade',
|
||||
'/grade_history/grade_grades/grade_grade');
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
protected function process_grade_grade($data) {
|
||||
global $DB;
|
||||
|
||||
$data = (object) $data;
|
||||
$olduserid = $data->userid;
|
||||
unset($data->id);
|
||||
|
||||
$data->userid = $this->get_mappingid('user', $data->userid, null);
|
||||
if (!empty($data->userid)) {
|
||||
// Do not apply the date offsets as this is history.
|
||||
$data->itemid = $this->get_mappingid('grade_item', $data->itemid);
|
||||
$data->oldid = $this->get_mappingid('grade_grades', $data->oldid);
|
||||
$data->usermodified = $this->get_mappingid('user', $data->usermodified, null);
|
||||
$data->rawscaleid = $this->get_mappingid('scale', $data->rawscaleid);
|
||||
$DB->insert_record('grade_grades_history', $data);
|
||||
} else {
|
||||
$message = "Mapped user id not found for user id '{$olduserid}', grade item id '{$data->itemid}'";
|
||||
$this->log($message, backup::LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This structure steps restores one instance + positions of one block
|
||||
|
Loading…
x
Reference in New Issue
Block a user