From 6ab7d000491af82130b633b195726920deae90fd Mon Sep 17 00:00:00 2001 From: Neill Magill Date: Thu, 1 Jun 2017 14:42:26 +0100 Subject: [PATCH] MDL-52501 assign: Fix group submissions without submission record Based on work by Tim Lock While upgrading to Moodle 2.8 or above it is possible for it to fail if the site has multiple graded attempts for a user without an associated submission record. This caused the upgrade to violate the databases unique key rules. This same issue could apply to a backup that was created of an assignment that had this issue, when it is restored into Moodle 2.8 or later. This change adds the attempt number from the grade table into the query that is inserting the new submission record (thanks to Tim). It also moves the insert query before the latest flag is set on submission records to ensure that only one has it, without this it is possible that multiple submission records would be marked as the latest after an upgrade or restore. --- .../moodle2/restore_assign_stepslib.php | 54 ++++++++++--------- mod/assign/db/upgrade.php | 46 ++++++++-------- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/mod/assign/backup/moodle2/restore_assign_stepslib.php b/mod/assign/backup/moodle2/restore_assign_stepslib.php index 2f5a0a86863..16b37020d3f 100644 --- a/mod/assign/backup/moodle2/restore_assign_stepslib.php +++ b/mod/assign/backup/moodle2/restore_assign_stepslib.php @@ -279,6 +279,34 @@ class restore_assign_activity_structure_step extends restore_activity_structure_ require_once($CFG->dirroot . '/mod/assign/locallib.php'); $assignmentid = $this->get_new_parentid('assign'); + + // First check for records with a grade, but no submission record. + // This happens when a teacher marks a student before they have submitted anything. + $records = $DB->get_recordset_sql('SELECT g.id, g.userid, g.attemptnumber + FROM {assign_grades} g + LEFT JOIN {assign_submission} s + ON s.assignment = g.assignment + AND s.userid = g.userid + WHERE s.id IS NULL AND g.assignment = ?', array($assignmentid)); + + $submissions = array(); + foreach ($records as $record) { + $submission = new stdClass(); + $submission->assignment = $assignmentid; + $submission->userid = $record->userid; + $submission->attemptnumber = $record->attemptnumber; + $submission->status = ASSIGN_SUBMISSION_STATUS_NEW; + $submission->groupid = 0; + $submission->latest = 0; + $submission->timecreated = time(); + $submission->timemodified = time(); + array_push($submissions, $submission); + } + + $records->close(); + + $DB->insert_records('assign_submission', $submissions); + // This code could be rewritten as a monster SQL - but the point of adding this "latest" field // to the submissions table in the first place was to get away from those hard to maintain SQL queries. @@ -316,32 +344,6 @@ class restore_assign_activity_structure_step extends restore_activity_structure_ $DB->update_record('assign_submission', $submission); } } - - // Now check for records with a grade, but no submission record. - // This happens when a teacher marks a student before they have submitted anything. - $records = $DB->get_recordset_sql('SELECT g.id, g.userid - FROM {assign_grades} g - LEFT JOIN {assign_submission} s - ON s.assignment = g.assignment - AND s.userid = g.userid - WHERE s.id IS NULL AND g.assignment = ?', array($assignmentid)); - - $submissions = array(); - foreach ($records as $record) { - $submission = new stdClass(); - $submission->assignment = $assignmentid; - $submission->userid = $record->userid; - $submission->status = ASSIGN_SUBMISSION_STATUS_NEW; - $submission->groupid = 0; - $submission->latest = 1; - $submission->timecreated = time(); - $submission->timemodified = time(); - array_push($submissions, $submission); - } - - $records->close(); - - $DB->insert_records('assign_submission', $submissions); } /** diff --git a/mod/assign/db/upgrade.php b/mod/assign/db/upgrade.php index 193b330f60d..035aa88468a 100644 --- a/mod/assign/db/upgrade.php +++ b/mod/assign/db/upgrade.php @@ -79,6 +79,29 @@ function xmldb_assign_upgrade($oldversion) { $count = $DB->count_records_sql($countsql, array(1)); if ($count == 0) { + // Look for grade records with no submission record. + // This is when a teacher has marked a student before they submitted anything. + $records = $DB->get_records_sql('SELECT g.id, g.assignment, g.userid, g.attemptnumber + FROM {assign_grades} g + LEFT JOIN {assign_submission} s + ON s.assignment = g.assignment + AND s.userid = g.userid + WHERE s.id IS NULL'); + $submissions = array(); + foreach ($records as $record) { + $submission = new stdClass(); + $submission->assignment = $record->assignment; + $submission->userid = $record->userid; + $submission->attemptnumber = $record->attemptnumber; + $submission->status = 'new'; + $submission->groupid = 0; + $submission->latest = 0; + $submission->timecreated = time(); + $submission->timemodified = time(); + array_push($submissions, $submission); + } + + $DB->insert_records('assign_submission', $submissions); // Mark the latest attempt for every submission in mod_assign. $maxattemptsql = 'SELECT assignment, userid, groupid, max(attemptnumber) AS maxattempt @@ -105,29 +128,6 @@ function xmldb_assign_upgrade($oldversion) { $select = 'id IN(' . $maxattemptidssql . ')'; $DB->set_field_select('assign_submission', 'latest', 1, $select); } - - // Look for grade records with no submission record. - // This is when a teacher has marked a student before they submitted anything. - $records = $DB->get_records_sql('SELECT g.id, g.assignment, g.userid - FROM {assign_grades} g - LEFT JOIN {assign_submission} s - ON s.assignment = g.assignment - AND s.userid = g.userid - WHERE s.id IS NULL'); - $submissions = array(); - foreach ($records as $record) { - $submission = new stdClass(); - $submission->assignment = $record->assignment; - $submission->userid = $record->userid; - $submission->status = 'new'; - $submission->groupid = 0; - $submission->latest = 1; - $submission->timecreated = time(); - $submission->timemodified = time(); - array_push($submissions, $submission); - } - - $DB->insert_records('assign_submission', $submissions); } // Assign savepoint reached.