mirror of
https://github.com/moodle/moodle.git
synced 2025-02-15 21:36:58 +01:00
225 lines
8.7 KiB
HTML
225 lines
8.7 KiB
HTML
<!DOCTYPE HTML PUBLIC>
|
|
<html>
|
|
<head>
|
|
<title>Question versioning</title>
|
|
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
|
|
<!--link rel="stylesheet" type="text/css" href="doc.css"-->
|
|
</head>
|
|
<body>
|
|
|
|
<h1>Question versioning</h1>
|
|
|
|
<h2>Contents</h2>
|
|
<ul>
|
|
<a href='#description'><li>Purpose and Description</li></a>
|
|
<a href='#database'><li>Database</li></a>
|
|
<a href='#adjustments'><li>Adjustments to the Data</li></a>
|
|
<a href='#issues'><li>Affected Code and Functionality</li></a>
|
|
</ul>
|
|
|
|
<a name='description'></a><h2>Purpose and Description</h2>
|
|
<p>
|
|
|
|
When questions that were already attempted by a student are edited, it can be
|
|
important to keep a copy of the question as it was before editing in order to
|
|
reconstruct the quiz as it was seen by the student. To provide this
|
|
functionality a question versioning mechanism was implemented.
|
|
|
|
</p><p>
|
|
|
|
The first goal, namely keeping around old questions, is easily achieved. They
|
|
are just not deleted any more. However, this is not enough; it is also necessary
|
|
to store which questions are versions of others. To achieve this goal, there is
|
|
an additional table, which stores the versioning information:
|
|
<code>quiz_question_versions</code>.
|
|
|
|
</p><p>
|
|
|
|
When a question is replaced for which there are already student attempts then
|
|
all the attempt data gets associated to the new version of the question and is
|
|
re-graded. This requires the question ids in the <code>quiz_attempts</code>,
|
|
<code>quiz_states</code> and <code>quiz_newest_states</code> tables to be
|
|
replaced by the new id. However we do also want to be able to reconstruct
|
|
the quiz the way the student saw it when he gave his answers. For that purpose
|
|
the id of the original question is always preserved in the 'originalquestion'
|
|
field of the <code>quiz_states</code> table.
|
|
|
|
</p><p>
|
|
|
|
If all
|
|
old versions of questions are kept around this could horribly clutter the
|
|
editing interface. Therefore a field called <code>hidden</code> was added to the
|
|
<code>quiz_questions</code> table and all old versions of edited questions are
|
|
automatically hidden. When this flag is set to 1 the question is not displayed
|
|
in the list of available questions, unless the user chooses to show them.
|
|
|
|
</p><p>
|
|
|
|
While the mechanism above should work as described, there is some additional
|
|
complexity in order to minimise the number of versions created. If a question is
|
|
created and has not been attempted by a student yet (this excludes teacher
|
|
previews of the individual question and the quiz!), the database record will be
|
|
reused (i.e. overwritten) and no new version will be created. This is especially
|
|
important when the question is created and the first 2 or 3 mistakes are only
|
|
noticed during preview.
|
|
|
|
</p><p>
|
|
|
|
On the editing screen for questions an additional set of options was introduced
|
|
(see image).<br />
|
|
<img src='versioning-01.gif' alt='Replacement Options'
|
|
/><br />
|
|
It shows which quizzes use the edited question and how many students have
|
|
attempted it in a particular quiz. Based on this information it is then
|
|
possible to choose in which quizzes the new version of the question should be
|
|
used and in which ones the old one should remain.
|
|
|
|
</p><p>
|
|
|
|
By default the 'replace' checkbox for all quizzes that don't have any students'
|
|
attempts are checked and in addition, if the question is edited out of a quiz
|
|
context (i.e. not in the category question list), the 'replace' option is
|
|
checked for that quiz as well.
|
|
|
|
</p>
|
|
|
|
<a name='database'></a><h2>Database</h2>
|
|
<p>
|
|
|
|
The changes to the database structure are limited to an
|
|
added field (<code>hidden</code>) in the
|
|
<code>quiz_questions</code> table and an additional table called
|
|
<code>quiz_question_versions</code>. However, dealing with the
|
|
<code>quiz_questions</code> table has become slightly more
|
|
complicated.
|
|
|
|
</p><p>
|
|
|
|
The <code>hidden</code> field in the <code>quiz_questions</code> table has no
|
|
implications for the core functionality. It is only used to determine, as the
|
|
name implies, whether the question is shown in the category list or not.
|
|
|
|
</p><p>
|
|
|
|
The table <code>quiz_question_versions</code> stores information about the
|
|
actual change. This information includes the ids of the old question and the new
|
|
question, the id of the user who did the change and a timestamp. Quite
|
|
importantly, the id of the quiz, in which the question was replaced is also
|
|
stored. This means that the versions table provides a history of the different
|
|
states the quiz went through until it was edited to be at the current state. The
|
|
information allows to recreate a quiz as it was at any point in time (from a
|
|
data perspective - this possibility is not used extensively by the code).
|
|
|
|
</p>
|
|
|
|
<a name='adjustments'></a><h2>Adjustments to the Data</h2>
|
|
<p>
|
|
|
|
When a question is replaced by a newer version, database records are updated in
|
|
the order shown below (compare with <strong>question.php</strong>):
|
|
|
|
<ul>
|
|
<li>
|
|
|
|
First a new record is inserted into the <code>quiz_question_versions</code>
|
|
table for each affected quiz (i.e. each quiz in which the question was
|
|
replaced).
|
|
|
|
</li><li>
|
|
|
|
Then, for each affected quiz, the comma separated list of question ids in the
|
|
<code>question</code> field is updated by replacing the old question id with
|
|
the new one.
|
|
|
|
</li><li>
|
|
|
|
In the <code>quiz_question_instances</code> table the record that links the
|
|
old question to the quiz is also updated to point to the new question.
|
|
|
|
</li><li>
|
|
|
|
In all attempts belonging to the old question the comma-separated list of
|
|
question ids in the <code>layout</code> field are changed by replacing the
|
|
old id by the new one.
|
|
|
|
</li><li>
|
|
|
|
All states belonging to the old question are made to belong to the new
|
|
version by changing the id in the 'question' field. However if we are
|
|
replacing the original question then the id of this original version
|
|
is stored in the <code>originalquestion</code> field.
|
|
|
|
</li><li>
|
|
|
|
We have to change the <code>questionid</code> field in <code>
|
|
quiz_newest_states</code>.
|
|
|
|
</li><li>
|
|
|
|
Finally we have to do any question-type specific changes. For example
|
|
question types that store student responses by storing the id of the
|
|
answer in the <code>quiz_answers</code> table will have to recode these
|
|
ids in all the states to point to the corresponding answers in the
|
|
new version. This is handled by the function replace_question_in_attempts()
|
|
in the question type class.
|
|
|
|
</li>
|
|
</ul>
|
|
|
|
<a name='issues'></a><h2>Affected Code and Functionality</h2>
|
|
<p>
|
|
|
|
Note: This section should still be considered under construction until the
|
|
question mark behind bug #3311 is taken off.
|
|
|
|
</p><p>
|
|
|
|
In the file <strong>review.php</strong> and potentially also in the file
|
|
<strong>attempt.php</strong>, if a question is edited during a student's
|
|
attempt, the data from <code>quiz_question_versions</code> needs to be taken
|
|
into account. If a student has attempted a quiz and a question was changed
|
|
afterwards (i.e. a new version of that question was created), the question id
|
|
of the old version remains in the comma separated list inside the
|
|
<code>attempt->layout</code> field. However, since the records in the
|
|
<code>quiz_question_instances</code> table get updated, we need to go forward in
|
|
the question history, by looping through entries from the
|
|
<code>quiz_question_versions</code> table, to find out the id of the question
|
|
version that is currently used in the quiz.
|
|
|
|
</p><p>
|
|
|
|
Suggestion: With a fairly simple change to the convention of what is stored in
|
|
the <code>quiz_question_versions</code> table we could get rid of the
|
|
requirement of looping through all the versions. If in the newquestion field we
|
|
store the id of the question that is currently used in the quiz, it would be
|
|
possible to get the complete history for a question quite simply by selecting by
|
|
quiz id and <code>newquestion</code>.
|
|
|
|
</p><p>
|
|
|
|
It should be fairly simple to write an upgrade script for this change.
|
|
Additionally, another <code>set_field</code> would need to be added to
|
|
<strong>question.php</strong> to change the <code>newquestion</code> field to
|
|
the new question id. The benefits would be a much simpler handling of the
|
|
question history, resulting in more efficient code than the current fix for bug
|
|
#3311 in review.php.
|
|
|
|
</p><p>
|
|
|
|
The place where all the versioning actually takes place is
|
|
<strong>question.php</strong>. Here the changes described in
|
|
<a href='#adjustments'>Adjustments to the Data</a> are carried out.
|
|
|
|
</p><p>
|
|
|
|
Obviously the backup and restore scripts also take
|
|
<code>quiz_question_versions</code> into account, however, they don't need to be
|
|
concerned with the ways the data is used.
|
|
|
|
</p>
|
|
|
|
</p>
|
|
|
|
</body>
|
|
</html>
|