From 4d30b3ebf426475e7badefeb17d4f25d6b94b1ef Mon Sep 17 00:00:00 2001 From: Josh Willcock Date: Tue, 23 Jan 2024 14:47:21 +0000 Subject: [PATCH] Update SCORM reset & backup for 4.3 structure. --- ...backup_local_recompletion_plugin.class.php | 20 +++-- ...estore_local_recompletion_plugin.class.php | 29 +++++-- classes/plugins/mod_scorm.php | 26 ++++-- classes/privacy/provider.php | 19 +++-- db/install.xml | 36 +++++++-- db/upgrade.php | 80 ++++++++++++++++++- lang/en/local_recompletion.php | 8 +- version.php | 8 +- 8 files changed, 182 insertions(+), 44 deletions(-) diff --git a/backup/moodle2/backup_local_recompletion_plugin.class.php b/backup/moodle2/backup_local_recompletion_plugin.class.php index 41b9e5d..5564e8d 100644 --- a/backup/moodle2/backup_local_recompletion_plugin.class.php +++ b/backup/moodle2/backup_local_recompletion_plugin.class.php @@ -116,20 +116,30 @@ protected function define_course_plugin_structure() { $grade->annotate_ids('user', 'userid'); // Now deal with SCORM archive tables. + $scormattempts = new backup_nested_element('scormattempts'); + + $scormattempt = new backup_nested_element('scormattempt', array('id'), array( + 'scormid', 'userid', 'attempt', 'courseid')); + + $recompletion->add_child($scormattempts); + $scormattempts->add_child($scormattempt); + + if ($usercompletion) { + $scormattempt->set_source_table('local_recompletion_sa', array('courseid' => backup::VAR_COURSEID)); + } + $scormattempt->annotate_ids('user', 'userid'); + $scotracks = new backup_nested_element('scormtracks'); $scotrack = new backup_nested_element('sco_track', array('id'), array( - 'userid', 'attempt', 'element', 'value', - 'timemodified', 'course', 'scormid', 'scoid')); + 'scoid', 'attemptid', 'elementid', 'value', 'courseid', 'timemodified')); $recompletion->add_child($scotracks); $scotracks->add_child($scotrack); if ($usercompletion) { - $scotrack->set_source_table('local_recompletion_sst', array('course' => backup::VAR_COURSEID)); + $scotrack->set_source_table('local_recompletion_ssv', array('courseid' => backup::VAR_COURSEID)); } - $scotrack->annotate_ids('user', 'userid'); - // Now deal with choice archive tables. $choiceanswers = new backup_nested_element('choiceanswers'); diff --git a/backup/moodle2/restore_local_recompletion_plugin.class.php b/backup/moodle2/restore_local_recompletion_plugin.class.php index f914d87..f87a300 100644 --- a/backup/moodle2/restore_local_recompletion_plugin.class.php +++ b/backup/moodle2/restore_local_recompletion_plugin.class.php @@ -46,7 +46,8 @@ protected function define_course_plugin_structure() { $paths[] = new restore_path_element('recompletion_completion', $elepath.'/course_completion/completions/completion'); $paths[] = new restore_path_element('recompletion_qa', $elepath.'/quizattempts/attempt'); $paths[] = new restore_path_element('recompletion_qg', $elepath.'/quizgrades/grade'); - $paths[] = new restore_path_element('recompletion_sst', $elepath.'/scormtracks/sco_track'); + $paths[] = new restore_path_element('recompletion_sa', $elepath.'/scormattempts/scormattempt'); + $paths[] = new restore_path_element('recompletion_ssv', $elepath.'/scormtracks/sco_track'); $paths[] = new restore_path_element('recompletion_cha', $elepath.'/choiceanswers/choiceanswer'); $paths[] = new restore_path_element('recompletion_hvp', $elepath.'/hvpattempts/hvpattempt'); $paths[] = new restore_path_element('recompletion_h5p', $elepath.'/h5ps/h5p'); @@ -147,17 +148,30 @@ public function process_recompletion_qg($data) { } /** - * Process local_recompletion_sst table. + * Process local_recompletion_sa table. * @param stdClass $data */ - public function process_recompletion_sst($data) { + public function process_recompletion_sa($data) { global $DB; $data = (object) $data; - $data->course = $this->task->get_courseid(); + $data->courseid = $this->task->get_courseid(); $data->userid = $this->get_mappingid('user', $data->userid); - $DB->insert_record('local_recompletion_sst', $data); + $DB->insert_record('local_recompletion_sa', $data); + } + + /** + * Process local_recompletion_sst table. + * @param stdClass $data + */ + public function process_recompletion_ssv($data) { + global $DB; + + $data = (object) $data; + $data->courseid = $this->task->get_courseid(); + + $DB->insert_record('local_recompletion_ssv', $data); } /** @@ -339,11 +353,10 @@ protected function after_restore_course() { $rcm->close(); // Fix SCORM tracks. - $rcm = $DB->get_recordset('local_recompletion_sst', array('course' => $this->task->get_courseid())); + $rcm = $DB->get_recordset('local_recompletion_sa', array('courseid' => $this->task->get_courseid())); foreach ($rcm as $rc) { $rc->scormid = $this->get_mappingid('scorm', $rc->scormid); - $rc->scoid = $this->get_mappingid('scorm_sco', $rc->scoid); - $DB->update_record('local_recompletion_sst', $rc); + $DB->update_record('local_recompletion_sa', $rc); } $rcm->close(); diff --git a/classes/plugins/mod_scorm.php b/classes/plugins/mod_scorm.php index c60001d..24fe8bf 100644 --- a/classes/plugins/mod_scorm.php +++ b/classes/plugins/mod_scorm.php @@ -96,16 +96,26 @@ public static function reset($userid, $course, $config) { } else if ($config->scorm == LOCAL_RECOMPLETION_DELETE) { $params = array('userid' => $userid, 'course' => $course->id); $selectsql = 'userid = ? AND scormid IN (SELECT id FROM {scorm} WHERE course = ?)'; - if ($config->archivescorm) { - $scormscoestrack = $DB->get_records_select('scorm_scoes_track', $selectsql, $params); - // Strictly not part of #78 but eliminates unused local variable violation. - foreach (array_keys($scormscoestrack) as $sid) { - // Add courseid to records to help with restore process. - $scormscoestrack[$sid]->course = $course->id; + + $scormattempt = $DB->get_records_select('scorm_attempt', $selectsql, $params); + // Strictly not part of #78 but eliminates unused local variable violation. + foreach (array_keys($scormattempt) as $sid) { + // Add courseid to records to help with restore process. + $scormattempt[$sid]->courseid = $course->id; + $scormscoesvalue = $DB->get_records('scorm_scoes_value', ['attemptid' => $sid]); + if ($config->archivescorm) { + foreach (array_keys($scormscoesvalue) as $ssvid) { + $scormscoesvalue[$ssvid]->courseid = $course->id; + + } + $DB->insert_records('local_recompletion_ssv', $scormscoesvalue); } - $DB->insert_records('local_recompletion_sst', $scormscoestrack); + $DB->delete_records('scorm_scoes_value', ['attemptid' => $sid]); + } + if ($config->archivescorm) { + $DB->insert_records('local_recompletion_sa', $scormattempt); } - $DB->delete_records_select('scorm_scoes_track', $selectsql, $params); + $DB->delete_records_select('scorm_attempt', $selectsql, $params); $DB->delete_records_select('scorm_aicc_session', $selectsql, $params); } } diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index ead3974..b57d2aa 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -101,13 +101,20 @@ public static function get_metadata(collection $collection) : collection { 'timemodified' => 'privacy:metadata:quiz_grades:timemodified', ], 'privacy:metadata:quiz_grades'); - $collection->add_database_table('local_recompletion_sst', [ + $collection->add_database_table('local_recompletion_sa', [ 'userid' => 'privacy:metadata:userid', - 'attempt' => 'privacy:metadata:attempt', - 'element' => 'privacy:metadata:scoes_track:element', - 'value' => 'privacy:metadata:scoes_track:value', - 'timemodified' => 'privacy:metadata:timemodified' - ], 'privacy:metadata:scorm_scoes_track'); + 'scormid' => 'privacy:metadata:scormid', + 'courseid' => 'privacy:metadata:course', + ], 'privacy:metadata:scorm_attempt'); + + $collection->add_database_table('local_recompletion_ssv', [ + 'userid' => 'privacy:metadata:userid', + 'attemptid' => 'privacy:metadata:attempt', + 'elementid' => 'privacy:metadata:scoes_value:element', + 'value' => 'privacy:metadata:scoes_value:value', + 'timemodified' => 'privacy:metadata:timemodified', + 'courseid' => 'privacy:metadata:course' + ], 'privacy:metadata:scorm_scoes_value'); $collection->add_database_table('local_recompletion_ltia', [ 'toolid' => 'privacy:metadata:local_recompletion_ltia:toolid', diff --git a/db/install.xml b/db/install.xml index 9a5a8ec..19b267d 100644 --- a/db/install.xml +++ b/db/install.xml @@ -123,27 +123,47 @@ - +
+ + + + + + + + + + + + + + +
+ + + - - + + - + - + + + - - - + + +
diff --git a/db/upgrade.php b/db/upgrade.php index 6f38cc1..1678b9a 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -987,5 +987,83 @@ function xmldb_local_recompletion_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2023112600, 'local', 'recompletion'); } + if ($oldversion < 2024011601) { + + // Define table local_recompletion_sa to be created. + $table = new xmldb_table('local_recompletion_sa'); + + // Adding fields to table local_recompletion_ssv. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('scormid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('attempt', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table local_recompletion_ssv. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id')); + $table->add_key('scormid', XMLDB_KEY_FOREIGN, array('scormid'), 'scorm', array('id')); + $table->add_key('courseid', XMLDB_KEY_FOREIGN, array('courseid'), 'course', array('id')); + + // Conditionally launch create table for local_recompletion_ssv. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Define table local_recompletion_ssv to be created. + $table = new xmldb_table('local_recompletion_ssv'); + + // Adding fields to table local_recompletion_ssv. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('scoid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('attemptid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('elementid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('value', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table local_recompletion_ssv. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + $table->add_key('scoid', XMLDB_KEY_FOREIGN, array('scoid'), 'scorm_scoes', array('id')); + $table->add_key('attemptid', XMLDB_KEY_FOREIGN, array('attemptid'), 'scorm_attempt', array('id')); + $table->add_key('elementid', XMLDB_KEY_FOREIGN, array('elementid'), 'scorm_element', array('id')); + $table->add_key('courseid', XMLDB_KEY_FOREIGN, array('courseid'), 'course', array('id')); + // Conditionally launch create table for local_recompletion_ssv. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + // Migrate Data to new format. + $total = $DB->count_records('local_recompletion_sst'); + if ($total > 500000) { + // This site has a large number of user track records, lets warn that this next part may take some time. + $notification = new \core\output\notification( + get_string('largetrackupgrade', 'scorm', format_float($total, 0)), + \core\output\notification::NOTIFY_WARNING + ); + $notification->set_show_closebutton(false); + echo $OUTPUT->render($notification); + } + // Fill backup attempt table. + $sql = "INSERT INTO {local_recompletion_sa} (userid, scormid, attempt, courseid) + SELECT userid, scormid, attempt, course FROM {local_recompletion_sst} sst GROUP BY userid,scormid,attempt"; + $DB->execute($sql); + // Fill backup value table. + $sql = "INSERT INTO {local_recompletion_ssv} (attemptid, scoid, elementid, value, courseid, timemodified) + SELECT a.id as attemptid, t.scoid as scoid, e.id as elementid, t.value as value, s.course as courseid, t.timemodified + FROM {local_recompletion_sst} t + JOIN {scorm_element} e ON e.element = t.element + LEFT JOIN {scorm} s ON s.id = t.scormid + JOIN {local_recompletion_sa} a ON (t.userid = a.userid AND t.scormid = a.scormid AND a.attempt = t.attempt)"; + $DB->execute($sql); + // Remove old table. + $table = new xmldb_table('local_recompletion_sst'); + if ($dbman->table_exists($table)) { + $dbman->drop_table($table); + } + + // Recompletion savepoint reached. + upgrade_plugin_savepoint(true, 2024011601, 'local', 'recompletion'); + } + return true; -} +} \ No newline at end of file diff --git a/lang/en/local_recompletion.php b/lang/en/local_recompletion.php index 4eb963b..44b5040 100644 --- a/lang/en/local_recompletion.php +++ b/lang/en/local_recompletion.php @@ -135,13 +135,14 @@ $string['privacy:metadata:quiz_grades:quiz'] = 'The quiz that was graded.'; $string['privacy:metadata:quiz_grades:timemodified'] = 'The time that the grade was modified.'; $string['privacy:metadata:quiz_grades:userid'] = 'The user who was graded.'; -$string['privacy:metadata:scoes_track:element'] = 'The name of the element to be tracked'; -$string['privacy:metadata:scoes_track:value'] = 'The value of the given element'; +$string['privacy:metadata:scoes_value:element'] = 'The ID of the element to be tracked'; +$string['privacy:metadata:scoes_value:value'] = 'The value of the given element'; $string['privacy:metadata:coursemoduleid'] = 'The activity ID'; $string['privacy:metadata:completionstate'] = 'If the activity has been completed'; $string['privacy:metadata:viewed'] = 'If the activity was viewed'; $string['privacy:metadata:attempt'] = 'The attempt number'; -$string['privacy:metadata:scorm_scoes_track'] = 'Archive of the tracked data of the SCOes belonging to the activity'; +$string['privacy:metadata:scorm_attempt'] = 'Archive of previous SCORM attempts.'; +$string['privacy:metadata:scorm_scoes_value'] = 'Archive of the tracked data of the SCOes belonging to the activity'; $string['privacy:metadata:local_recompletion_qr:questionnaireid'] = 'Questionnaire id'; $string['privacy:metadata:local_recompletion_qr:submitted'] = 'Submitted'; $string['privacy:metadata:local_recompletion_qr:complete'] = 'complete'; @@ -159,7 +160,6 @@ $string['privacy:metadata:rawscore'] = 'The score obtained'; $string['privacy:metadata:timecreated'] = 'The time when the tracked element was created'; $string['privacy:metadata:timemodified'] = 'The last time element was tracked'; -$string['privacy:metadata:userid'] = 'The ID of the user who accessed the H5P activity'; $string['privacy:metadata:local_recompletion_la'] = 'Archive for lesson_attempts'; $string['privacy:metadata:correct'] = 'Correct answer?'; $string['privacy:metadata:useranswer'] = 'Answer'; diff --git a/version.php b/version.php index b0db438..232eb1c 100644 --- a/version.php +++ b/version.php @@ -24,9 +24,9 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2023112700; -$plugin->release = 2023112700; +$plugin->version = 2024011601; +$plugin->release = 2024011601; $plugin->maturity = MATURITY_STABLE; -$plugin->requires = 2022112805; // Requires 4.1. +$plugin->requires = 2023100900; // Requires 4.3. $plugin->component = 'local_recompletion'; -$plugin->supported = [401, 402]; +$plugin->supported = [403,403]; \ No newline at end of file