<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.moodle.org/311/en/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dooug</id>
	<title>MoodleDocs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.moodle.org/311/en/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dooug"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/311/en/Special:Contributions/Dooug"/>
	<updated>2026-04-21T03:27:28Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/311/en/index.php?title=Development:Changing_the_Moodle_Question_Engine&amp;diff=52878</id>
		<title>Development:Changing the Moodle Question Engine</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/311/en/index.php?title=Development:Changing_the_Moodle_Question_Engine&amp;diff=52878"/>
		<updated>2009-03-16T20:20:37Z</updated>

		<summary type="html">&lt;p&gt;Dooug: /* Code flow */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
&lt;br /&gt;
The Question Engine is the part of the question code in Moodle that deals with what happens when a question is attempted by a Student, or later reviewed by the Teacher or the student themselves or included in reports. Roughly speaking this is half the question code, the other part being the Question Bank, which allows people to create, edit, organise, import, export and share questions. The Question Engine is the part that has to perform when you have a large class of students taking a quiz simultaneously.&lt;br /&gt;
&lt;br /&gt;
[[Image:Sydney_harbour.jpg|frame|An irrelevant but pretty picture of Sydney, taken from the hotel room where most of this was written.]]This document is a by-product of three years working on the Moodle quiz and question code. During that time I have fixed hundreds of bugs and feature requests. Some of the bugs with the Question Engine have requried careful fixing in order to avoid regressions, and there are other issues that are hard to do anything about at the moment.&lt;br /&gt;
&lt;br /&gt;
As well as bug fixes, I have also overseen major developments in the quiz/question system. For example Jamie&#039;s changes to the Question Bank, Ollie&#039;s new quiz editing interface, and Mahmoud&#039;s new quiz navigation that I added to Core Moodle while refactoring the quiz attempt code.&lt;br /&gt;
&lt;br /&gt;
Over the three years, I have also followed the Moodle quiz forum and seen what it is that people struggle with, complain about, want to change (and also what they like!). For example, having coached some people when writing new question types I realise that creating a new questions type is more difficult than it needs to be.&lt;br /&gt;
&lt;br /&gt;
The changes proposed here fix parts of the question engine code that I have been dissatisfied with since about the first time I looked at it. There are several reasons why I have not addressed them before now.&lt;br /&gt;
&lt;br /&gt;
# To fix them properly, we must change how the data is stored in some of the key question tables. In some sites, these tables contain millions of rows, and the processing required on update is non-trivial and is different for different question types. This is not something to undertake lightly.&lt;br /&gt;
# It took me about two and a half years to work out this solution. Some parts of this proposal where clear from the start, but the exact notion of question interaction models, and how they would relate to question types, only crystallised recently in my mind. Because of 1. I did not want to change anything until I was sure what I was proposing was right, not just in general, but in all the details.&lt;br /&gt;
# The mundane reason that there have been plenty of other things to work on, and I have not had time to address this until now.&lt;br /&gt;
# These are not critical problems. There have been no major changes to the question engine since the 1.6 release. Clearly there are not fundamental flaws with the current system. It is just not as good as it could be.&lt;br /&gt;
&lt;br /&gt;
As this is a major change, I will spend some time justifying it, before describing the change itself. My estimate is that doing what I propose here is a couple of month&#039;s solid work.&lt;br /&gt;
&lt;br /&gt;
==Goals of an online assessment system==&lt;br /&gt;
&lt;br /&gt;
Before going further, I think it is worth being clear about what properties we require of a question engine. In my view, we want:&lt;br /&gt;
# Correctness&lt;br /&gt;
# Robustness&lt;br /&gt;
# Richness&lt;br /&gt;
# Efficiency&lt;br /&gt;
&lt;br /&gt;
Let me explain in more detail, and summarise how I thing Moodle currently performs.&lt;br /&gt;
&lt;br /&gt;
===1. Correctness===&lt;br /&gt;
&lt;br /&gt;
This says that, assuming nothing untoward happens, given certain inputs (question definitions, student responses, ...); and an understanding (specification) of what is supposed to happen; then the quiz module will always produce the right outputs (feedback, grades, ...). This is most important, because without this, you can&#039;t rely on anything.&lt;br /&gt;
&lt;br /&gt;
I think Moodle does quite well in this regard, mainly due to many committed users who will report bugs as they are encountered, and smaller group of developers who then fix those bugs.&lt;br /&gt;
&lt;br /&gt;
===2. Robustness===&lt;br /&gt;
&lt;br /&gt;
The previous section started, &amp;quot;assuming nothing untoward happens.&amp;quot; Robustness is about what happens if something does go wrong. What if the server crashes in the middle of a test? What if the connection to the database is lost in the middle of processing one student&#039;s submission? What if the PHP process runs out of memory in the middle of a re-grade? What if an impatient user clicks submit repeatedly or closes their browser window? And so on.&lt;br /&gt;
&lt;br /&gt;
In some of these cases, it is inevitable that some data will be lost. But robustness says that no matter what happens, the student&#039;s quiz attempt must be left in a consistent state (presumably either just before, or just after the last submission) and no more than the last submission&#039;s worth of data is lost.&lt;br /&gt;
&lt;br /&gt;
I think that this is currently a weakness for Moodle. If the server goes down at the end of a timed test, bad things happen. See the list of bugs below.&lt;br /&gt;
&lt;br /&gt;
===3. Richness===&lt;br /&gt;
&lt;br /&gt;
This is about supporting a rich range of educational experiences. One of Martin&#039;s &#039;five laws&#039; is that we learn when creating something for another person to see. An online assessment system can sometimes take the place of the other person. The question is, how effective a substitute can it be in how many situations?&lt;br /&gt;
&lt;br /&gt;
Moodle does quite well here, with adaptive mode, plug-in-able question types and so on. However, we could do better. At the moment, writing a new question type is harder than it need be. Also, the interaction model like adaptive or non-adaptive mode is hard coded into the question engine which makes it harder to support new interactions like certainty based marking.&lt;br /&gt;
&lt;br /&gt;
===4. Efficiency===&lt;br /&gt;
&lt;br /&gt;
Given all the above, how many simultaneous users can be supported with a particular amount of hardware? &lt;br /&gt;
&lt;br /&gt;
Moodle is neither flagrantly inefficient, nor as efficient as it could be. We currently lack easy-to-perform performance measurement. We should establish some, so we can estimate current hardware requirements, and observe the effect of other developments on efficiency.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Why things need to change==&lt;br /&gt;
&lt;br /&gt;
I have just summarised how well Moodle meets my four goals. I will now presents a different picture, by listing bugs and feature requests in the Moodle tracker that I think can best be resolved by the changes I propose. &lt;br /&gt;
&lt;br /&gt;
===Correctness issues===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Key !! Votes !! Watchers !! Summary&lt;br /&gt;
|-&lt;br /&gt;
| MDL-3030 || 16 || 7 || attempts should be auto-closed&lt;br /&gt;
|-&lt;br /&gt;
| MDL-3936 || 2 || 1 || Essay Question used in score before grading&lt;br /&gt;
|-&lt;br /&gt;
| MDL-9303 || 2 || 5 || General Feedback not shown for essay questions on review screen&lt;br /&gt;
|-&lt;br /&gt;
| MDL-9327 || 4 || 3 || Text in Essay type questions is not saved in second or more attempts  when the teacher has not graded previous attempts&lt;br /&gt;
|-&lt;br /&gt;
| MDL-13289 || 0 || 0 || Essay question with adaptive mode not show feedback&lt;br /&gt;
|-&lt;br /&gt;
| MDL-17681 || 0 || 0 || Manually assigned grades cannot easily by re-scaled when the marks for a question is changed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Robustness issues===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Key !! Votes !! Watchers !! Summary&lt;br /&gt;
|-&lt;br /&gt;
| MDL-11852 || 0 || 1 || Quiz does not score properly&lt;br /&gt;
|-&lt;br /&gt;
| MDL-12344 || 0 || 0 || Student takes quiz, upon completion of quiz receives the following&lt;br /&gt;
|-&lt;br /&gt;
| MDL-12665 || 0 || 0 || error: Must Specify course id, shorname, or idnumber quiz attempts sometimes show zero despite correct answers&lt;br /&gt;
|-&lt;br /&gt;
| MDL-13073 || 0 || 0 || Regrading (really) messes up grades&lt;br /&gt;
|-&lt;br /&gt;
| MDL-13246 || 0 || 1 || Quiz does not save score. Shows 0/20&lt;br /&gt;
|-&lt;br /&gt;
| MDL-14873 || 0 || 2 || quiz answers submit freezes (occationally)&lt;br /&gt;
|-&lt;br /&gt;
| MDL-15305 || 1 || 0 || Quiz doesn&#039;t work after submit all and finish button is selected - get a &amp;quot;No quiz with id=0&amp;quot; or &amp;quot;No attempt with id=0&amp;quot; error message&lt;br /&gt;
|-&lt;br /&gt;
| MDL-16451 || 0 || 1 || Quiz scores 0 with secure window; Grades do not transfer&lt;br /&gt;
|-&lt;br /&gt;
| MDL-16490 || 0 || 1 || timed quiz generates bad 2nd attempt&lt;br /&gt;
|-&lt;br /&gt;
| MDL-16663 || 0 || 1 || errors and missing work&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
(Many of these are intermittent and only affect some people. Sometimes bugs like this turn out to be the result of a corrupt table in MySQL. Still, we really should not be getting problems like this with a system that is sometimes relied on as much as the quiz is.)&lt;br /&gt;
&lt;br /&gt;
===Richness issues===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Key !! Votes !! Watchers !! Summary&lt;br /&gt;
|-&lt;br /&gt;
| MDL-1647 || 4 || 6 || Allowing negative marks for questions&lt;br /&gt;
|-&lt;br /&gt;
|| MDL-4860 || 0 || 0 || Questiontype giving hints&lt;br /&gt;
|-&lt;br /&gt;
| MDL-12821 || 0 || 1 || Essay quiz question redundant submit button&lt;br /&gt;
|-&lt;br /&gt;
| MDL-17894 || 0 || 0 || Support certainty/Confidence based marking&lt;br /&gt;
|-&lt;br /&gt;
| MDL-17895 || 0 || 0 || Allow question types to validate responses before grading&lt;br /&gt;
|-&lt;br /&gt;
| MDL-17896 || 0 || 0 || Question types like Opaque should not require nasty hacks in question_process_responses&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Issues I don&#039;t propose to fix with this change===&lt;br /&gt;
&lt;br /&gt;
... but which would become easier to fix as a result:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Key !! Votes !! Watchers !! Summary&lt;br /&gt;
|-&lt;br /&gt;
| MDL-3452 || 0 || 0 || Use saved answers when marking late submissions&lt;br /&gt;
|-&lt;br /&gt;
| MDL-4309 || 0 || 3 || Ability for late quizzes (with penalty)&lt;br /&gt;
|-&lt;br /&gt;
| MDL-15596 || 0 || 0 || Allow a question to be used more than once in an attempt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==How this part of the quiz currently works==&lt;br /&gt;
&lt;br /&gt;
Before saying what I would like to change, I think it is best to summarise how the question engine currently works.&lt;br /&gt;
&lt;br /&gt;
===Database tables===&lt;br /&gt;
&lt;br /&gt;
There are three database tables that store data about students&#039; attempts at questions.&lt;br /&gt;
&lt;br /&gt;
====question_attempts====&lt;br /&gt;
&lt;br /&gt;
This table is basically used to generate unique ids for the other tables relating to question attempts, and allow one to link the data in those tables to the other parts of Moodle that use the question bank, for example from a quiz attempt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Column !! Type !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| id || INT(10) NOT NULL AUTO INCREMENT || Unique id used to link attempt question data to other things, e.g. quiz_attempts.uniqueid&lt;br /&gt;
|-&lt;br /&gt;
| modulename || NOT NULL VARCHAR(20) || e.g. &#039;quiz&#039; the thing linked to.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====question_sessions====&lt;br /&gt;
&lt;br /&gt;
There is one row in this table for each question in an attempt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Column !! Type !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| id || INT(10) NOT NULL AUTO INCREMENT || Unique id. Not used much.&lt;br /&gt;
|-&lt;br /&gt;
| attemptid || INT(10) NOT NULL REFERENCES question_attempts.id || Which attempt this data belongs to.&lt;br /&gt;
|-&lt;br /&gt;
| questionid || INT(10) NOT NULL REFERENCES question.id || Which question this is the attempt data for.&lt;br /&gt;
|-&lt;br /&gt;
| newest || INT(10) NOT NULL REFERENCES question_states.id || The latest state of this question in this attempt.&lt;br /&gt;
|-&lt;br /&gt;
| newgraded || INT(10) NOT NULL REFERENCES question_states.id || The latest graded state of this question in this attempt.&lt;br /&gt;
|-&lt;br /&gt;
| sumpenalty || NUMBER(12,7) NOT NULL || Used for adaptive mode scoring.&lt;br /&gt;
|-&lt;br /&gt;
| manualcomment || TEXT || A teacher&#039;s manual comment.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
(attemptid, questionid) is an alternate key, but that makes MDL-15596 impossible to satisfy.&lt;br /&gt;
&lt;br /&gt;
====question_states====&lt;br /&gt;
&lt;br /&gt;
There are several rows here for each question session, recording the different states that the question went through as the student attempted it. For example open, save, submit, manual_grade.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Column !! Type !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| id || INT(10) NOT NULL AUTO INCREMENT || Unique id. Not used much.&lt;br /&gt;
|-&lt;br /&gt;
| attempt || INT(10) NOT NULL REFERENCES question_attempts.id || Which attempt this data belongs to.&lt;br /&gt;
|-&lt;br /&gt;
| question || INT(10) NOT NULL REFERENCES question.id || Which question this is the attempt data for.&lt;br /&gt;
|-&lt;br /&gt;
| seq_number || INT(10) NOT NULL || Numbers the states within this question attempt.&lt;br /&gt;
|-&lt;br /&gt;
| answer || TEXT NOT NULL || A representation of the student&#039;s response.&lt;br /&gt;
|-&lt;br /&gt;
| timestamp || INT(10) NOT NULL || Timestamp of the event that lead to this state.&lt;br /&gt;
|-&lt;br /&gt;
| event || INT(4) NOT NULL || The type of state this is. One of the QUESTION_EVENTXXX constants from the top of questionlib.php.&lt;br /&gt;
|-&lt;br /&gt;
| grade || NUMBER(12,7) NOT NULL || The grade that had been earned by this point, after penalties had been taken into account.&lt;br /&gt;
|-&lt;br /&gt;
| raw_grade || NUMBER(12,7) NOT NULL || The grade that had been earned by this point, before penalties had been taken into account.&lt;br /&gt;
|-&lt;br /&gt;
| penalty || NUMBER(12,7) NOT NULL || Used by the adaptive mode scoring.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
(attempt, question, seq_number) is an alternate key (see the remark in the previous sub-section).&lt;br /&gt;
&lt;br /&gt;
The student&#039;s response is stored in the answer column. It is up to each question type to convert the data received from the student into a single string in whatever format seems best, even though the response come from a HTTP post, and so is just an array of key =&amp;gt; value pairs, which could easily be stored directly in the database without each question type having to write some nasty string concatenation/parsing code. &lt;br /&gt;
&lt;br /&gt;
===Code flow===&lt;br /&gt;
&lt;br /&gt;
A part of Moodle, for example the Quiz module, wishes to use questions. First, mod/quiz/attempt.php will render a page of questions by calling&lt;br /&gt;
# load_questions,&lt;br /&gt;
# load_question_states, and&lt;br /&gt;
# print_question one or more times.&lt;br /&gt;
All three of these functions delegate part of the work to the appropriate question type.&lt;br /&gt;
&lt;br /&gt;
This outputs each question in its current state. Any form controls that are output have a name=&amp;quot;&amp;quot; attribute that is a combination of a prefix determined by the question engine (e.q. q123_), and a main part determined by the question type.&lt;br /&gt;
&lt;br /&gt;
When this page is submitted, the response goes to whichever script the question using code designate. In the case of the quiz it is mod/quiz/processattemptattempt.php. This calls&lt;br /&gt;
# load_questions,&lt;br /&gt;
# load_question_states,&lt;br /&gt;
# question_extract_responses,&lt;br /&gt;
# question_process_responses, and&lt;br /&gt;
# save_question_state.&lt;br /&gt;
# In the case of the quiz, it then saves the quiz_attempts row, which has been updated by some of the above methods.&lt;br /&gt;
Again, steps 1., 2., 4. and 5. delegate part of the work to the question types.&lt;br /&gt;
&lt;br /&gt;
Processing manual grading by the teacher is similar. The sequence of calls is:&lt;br /&gt;
# load_questions,&lt;br /&gt;
# load_question_states,&lt;br /&gt;
# question_process_comment, and&lt;br /&gt;
# save_question_state.&lt;br /&gt;
# In the case of the quiz, it then saves the quiz_attempts row, which has been updated by some of the above methods.&lt;br /&gt;
&lt;br /&gt;
One thing to note here is that a lot of processing is done before any data can be stored in the database.&lt;br /&gt;
&lt;br /&gt;
Another point to note is that the question type classes are singleton classes. That is, only one instance of each question type is created. All the methods used by the question engine are passed a $question and possibly a $state object to tell the question type which question instance they should be processing. This is good design from several points of view. It works well with batch database queries (efficiency) and the question types to not have state, which might become inconsistent (robustness). However, there is a slight price here, since very rich question types cannot cache state in memory, so they may have to repeat processing (richness &amp;amp;lt;-&amp;gt; efficiency trade off) however, they can store state in the database since they can put whatever they like in the question_states.answer field.&lt;br /&gt;
&lt;br /&gt;
==Overview of the changes==&lt;br /&gt;
&lt;br /&gt;
===Clarifying question states===&lt;br /&gt;
&lt;br /&gt;
Althought there is a database table called question_states, the column there that stores the type of state is called event, and the values stored there are active verbs like open, save, grade. The question_process_responses function is written in terms of these actions too. One nasty part is that during the processing, the event type can be changed several times, before it is finally stored in the database as a state.&lt;br /&gt;
&lt;br /&gt;
I would like to clearly separate the concept of the current state that a question is in, and the various actions that lead to a change of state.&lt;br /&gt;
&lt;br /&gt;
(+Correctness, +Richness).&lt;br /&gt;
&lt;br /&gt;
===Question interaction models===&lt;br /&gt;
&lt;br /&gt;
At the moment, the various sequences of states that a question can move through is controlled by a combination of the compare_responses and grade_responses methods of the question types, and hard-coded logic in the question_process_responses function.&lt;br /&gt;
&lt;br /&gt;
I would like to separate out the control of how a question moves through different states, into what I will call question interaction models. Currently there are two models supported by Moodle, and the choice is controlled by the adaptive mode setting in the quiz.&lt;br /&gt;
&lt;br /&gt;
There is the deferred feedback model, which is what you get when adaptive mode is off. The student enters an answer to each question which is saved. Then, when they submit the quiz attempt, all the questions are graded. Then all the marks and feedback are available for review.&lt;br /&gt;
&lt;br /&gt;
Alternatively, there is the adaptive model, where the student can submit one question at a time, get some feedback, and if they were wrong, immediately change their answer, with a reduced grade if they get it right the second time.&lt;br /&gt;
&lt;br /&gt;
Currently, manually graded question use the same adaptive, or non-adaptive, models, with some slight changes. However, this does not work very well, as the number of essay question correctness bugs listed above shows. Really, manual grading should be treated as a separate interaction model.&lt;br /&gt;
&lt;br /&gt;
So, what exactly does the interaction model control? Given the current state of the question, it limits the range of actions that are possible. For example, in adaptive mode, there is a submit button next to each question. In non-adaptive mode, the only option is to enter an answer and have it saved, until the submit all and finish button is pressed. That is, it rougly replaces question_extract_responses, question_process_responses, question_process_comment and save_question_state in the current code.&lt;br /&gt;
&lt;br /&gt;
(+++Richness, ++Correctness, +Robustness)&lt;br /&gt;
&lt;br /&gt;
===Simplified API for question types===&lt;br /&gt;
&lt;br /&gt;
Although I said above &amp;quot;The student enters an answer to each question which is saved. Then when they submit the quiz attempt, all the questions are graded.&amp;quot; In fact, that was a lie. Whenever a response is saved, the grade_responses method of the question type is called, even in the state is only being saved. This is confusing, to say the least, and very bad for performance in the case of a question type like JUnit (in contrib) which takes code submitted by the student, and compiles it in order to grade it.&lt;br /&gt;
&lt;br /&gt;
So some of the API will only change to the extent that certian funcitons will in future only be called when one would expect them to be. I think this can be done in a backwards-compatible way.&lt;br /&gt;
&lt;br /&gt;
Another change will be that, at the moment, question types have to implement tricky load/save_question_state methods that, basically, have to unserialise/serialise some of the state data in a custom way, so it can be stored in the answer column. This is silly design, and leads to extra, unnecessary database queries. By changing the database structure so that the students&#039; responses are stored directly in a new table (after all, an HTTP POST request is really just an array of string key-value pairs, and there is a natural way to store that in a database) it should be posiblity to eliminate the need for these methods.&lt;br /&gt;
&lt;br /&gt;
(+Richness, +Correctness, +Efficiency)&lt;br /&gt;
&lt;br /&gt;
===Changed database structure===&lt;br /&gt;
&lt;br /&gt;
At the moment, the some parts of the question_sessions and question_states tables and their relations are not normalised. This is a technical term for describing database designs. Basically, if the tables are normalised, then each &#039;fact&#039; is stored in exactly one place. This makes it much less likely that the database can get into an inconsistent state. Changing to a normalised structure should therefore increase robustness.&lt;br /&gt;
&lt;br /&gt;
In addition, I wish to change the tables, so the the responses recieved from the student are stored in a much more &#039;raw&#039; form. That will mean that the responses can be saved much earlier in the sequence of processing, which will again increase robustness. It will also allow the sequence of responses from the student to be replayed more easily, making it easier to continue after a server crash, to regrade, and to write automatic test scripts.&lt;br /&gt;
&lt;br /&gt;
(+++Robustness, +Correctness)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Brief digression: who controls the model, the quiz or the question?==&lt;br /&gt;
&lt;br /&gt;
Before proceeding to decribe in detail the design for how I will implement these changes, I want to digress and discuss one of the design decisions that it took a long time for me to resolve.&lt;br /&gt;
&lt;br /&gt;
The question is, is the interaction model a property of the question, or the quiz?&lt;br /&gt;
&lt;br /&gt;
At the moment, there is a setting in the quiz that lets you change all the questions in the quiz from adaptive mode to non-adptive mode at the flick of a switch. Or, at least, it allows you to re-use the same questions in both adaptive and non-adaptive mode. This suggest that the interaction model is a property of the quiz.&lt;br /&gt;
&lt;br /&gt;
On the other hand, suppose that you wanted to introduce the feature that, in adaptive mode, the student gets different amounts of feedback after each try at getting the question right. For example, the first time they get it wrong the only get a brief hint. If they are wrong the second time they get a more detailed comment, and so on. In order to do this, you need more data (the various hints) in order to define an adaptive question that is irrelevant to a non-adaptive question. Also, certain question types (e.g. Opaque, from contrib) have to, of necessity, ignore the adaptive, non-adaptive setting. And I suggested above that, manually graded question types like Essay should really be considered to have a separate interaction model. This suggests that the interaction model is a properly of the individual question. (Although usability considerations suggest that a single quiz should probably be constructed from questions with similar interaction models.)&lt;br /&gt;
&lt;br /&gt;
I evenutally concluded that both answers are right, and more importantly, worked out how a question engine could allow that to happen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{{Work in progress}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;TODO finish the rest of this document.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Detailed design==&lt;br /&gt;
&lt;br /&gt;
===New database structure===&lt;br /&gt;
&lt;br /&gt;
====question_attempt_batches====&lt;br /&gt;
&lt;br /&gt;
This is just a rename of question_attempts.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Column !! Type !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| id || INT(10) NOT NULL AUTO INCREMENT || Unique id used to link attempt question data to other things, e.g. quiz_attempts.uniqueid and question_attempts.batchid&lt;br /&gt;
|-&lt;br /&gt;
| modulename || NOT NULL VARCHAR(255) || e.g. &#039;quiz&#039; the thing linked to.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====question_attempts====&lt;br /&gt;
&lt;br /&gt;
This replaces question_sessions. Question sessions is not a great name because session has other connotations in the context of web applications. I think it is right to use the question_attempt name here, because this tables has one row for each attempt at each question.&lt;br /&gt;
&lt;br /&gt;
There is now no requirement for (attemptid, questionid) to be unique.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Column !! Type !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| id || INT(10) NOT NULL AUTO INCREMENT || Unique id. Linked to from question_states.attemptid.&lt;br /&gt;
|-&lt;br /&gt;
| batchid || INT(10) NOT NULL REFERENCES question_batch.id || Which attempt this data belongs to.&lt;br /&gt;
|-&lt;br /&gt;
| interactionmodel || VARCHAR(32) NOT NULL || The question interaction model that is managing this question attempt.&lt;br /&gt;
|-&lt;br /&gt;
| questionid || INT(10) NOT NULL REFERENCES question.id || Which question this is the attempt data for.&lt;br /&gt;
|-&lt;br /&gt;
| maxgrade || NUMBER(12,7) NOT NULL || The grade this question is marked out of in this attempt.&lt;br /&gt;
|-&lt;br /&gt;
| responsesummary || TEXT || This is a textual summary of the student&#039;s response (basically what you would expect to in the Quiz responses report).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Need to store maxgrade becuase it could come from anywhere, (e.g. quiz_question_instances, question.defaultgrade, ...). We need it available at various times (e.g. when displaying a question) so it is better to store it explicitly here.&lt;br /&gt;
&lt;br /&gt;
====question_states====&lt;br /&gt;
&lt;br /&gt;
Same purpose as the old question_states table, but simplified.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Column !! Type !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| id || INT(10) NOT NULL AUTO INCREMENT || Unique id. Linked to from question_states.stateid.&lt;br /&gt;
|-&lt;br /&gt;
| attemptid || INT(10) NOT NULL REFERENCES question_attempts.id || Which question attempt this data belongs to.&lt;br /&gt;
|-&lt;br /&gt;
| timestamp || INT(10) NOT NULL || Timestamp of the event that lead to this state.&lt;br /&gt;
|-&lt;br /&gt;
| state || INT(4) NOT NULL || The type of state this is. One of the QUESTION_STATEXXX constants from the top of questionlib.php.&lt;br /&gt;
|-&lt;br /&gt;
| grade || NUMBER(12,7) || The grade the student has earned for this question, on a scale of 0..1. Needs to be multiplied by question_attempts.maxgrade to get the true grade.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* We store grade unscaled (as a value between 0.0 and 1.0) because that makes regrading easier. (You might think that you can adjust scaled grades later, and that is almost true, but if maxgrade used to be 0, then you can&#039;t change it to anything else.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====question_responses====&lt;br /&gt;
&lt;br /&gt;
This stores the data submitted by the student (a list of name =&amp;gt; value pairs) that lead to the state stateid. This replaces the old question_states.answer.&lt;br /&gt;
&lt;br /&gt;
There will be a convention that ordinary names like &#039;myvariable&#039; should be used for submitted data belonging to the question type; names prefixed with a !, like &#039;!myaction&#039; should be used for data belonging to the question interaction model; and names prefixed with a _ can be used for internal things, for example, the random question might store &#039;_realquestionid&#039; attached to the &#039;open&#039; state, or a question type that does a lot of expensive processing might store a &#039;_cachedresult&#039; value, so the expensive calculation does not need to be repeated when reviewing the attempt.&lt;br /&gt;
&lt;br /&gt;
Note that, the old question_states.answer field used to save a lot of repetitive information from one state to the next, for example the other questionid for random questions, and the choices order for multiple-choice questions with shuffle-answers on. In future, this sort of repetitive information will not be saved. Instead, during question processing, the question types will be given access to the full state history.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;nicetable&amp;quot;&lt;br /&gt;
! Column !! Type !! Comment&lt;br /&gt;
|-&lt;br /&gt;
| id || INT(10) NOT NULL AUTO INCREMENT || Unique id. Not used much.&lt;br /&gt;
|-&lt;br /&gt;
| stateid || INT(10) NOT NULL REFERENCES question_states.id || Which state the submission of this data lead to.&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(20) NOT NULL || The name of the parameter received from the student.&lt;br /&gt;
|-&lt;br /&gt;
| value || TEXT || The value of the parameter.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Upgrading the database===&lt;br /&gt;
&lt;br /&gt;
===New list of states that a question may be in===&lt;br /&gt;
&lt;br /&gt;
The aim here is to have as few states as necessary. What is necessary? To make it clear what is going on, for example in the quiz navigation. Of course, that is only one case to consider.&lt;br /&gt;
&lt;br /&gt;
; Incomplete&lt;br /&gt;
: This is the state that questions start in. They stay in this state as long as the student still needs to give this question attention. In deferred feedback (non-adaptive) mode, that is until the student has entered an answer. (For a short-answer question, any answer in the input box moves you out of this state; for a matching question, you only move out of this state when you have answered all the sub-questions.) In adaptive mode, the question stays in this state until either you have got it right, or you have run out of tries.&lt;br /&gt;
: In the state, the student can enter or change their answer.&lt;br /&gt;
; Complete&lt;br /&gt;
: This state is for questions where the student have done enough, but the attempt is still open, so they could change their answer if they wanted to. For example, this happens in deferred feedback mode when the student has entered a complete answer, and before they do submit all and finish. Also, a Description, after the student has seen it.&lt;br /&gt;
: In the state, the student can enter or change their answer.&lt;br /&gt;
; Graded(Correct/PartiallyCorrect/Incorrect)&lt;br /&gt;
: For computer-graded questions, once the student can no longer interact with the question, it goes to one of the sub-states of the graded state. &lt;br /&gt;
; Finished&lt;br /&gt;
: For questions that do not have a grade, for example descriptions, after the attempt is over, they go into this state.&lt;br /&gt;
; GaveUp&lt;br /&gt;
: This state is used for questions where it is impossible to assign a grade because the student did submit all and finish when the question was in the incomplete state. However, this does not necessarily happen, for example, we may choose to grade an incomplete matching question if the student has completed at least one sub-question.&lt;br /&gt;
; ManuallyGraded(Correct/PartiallyCorrect/Incorrect)&lt;br /&gt;
; Commented&lt;br /&gt;
; GaveUpCommented&lt;br /&gt;
: These three states correspond the the previous three states after the teacher has added a comment and/or manually graded.&lt;br /&gt;
&lt;br /&gt;
[[Image:Question_state_diagram.png]]&lt;br /&gt;
&lt;br /&gt;
===Interaction models to implement initially===&lt;br /&gt;
&lt;br /&gt;
It is the interaction model that controls how the student&#039;s attempt at the question moves through the state diagram in the previous section. When making the key decisions, that interaction model calls methods of the question type to supply question-type-specific information about what is happening.&lt;br /&gt;
&lt;br /&gt;
Note that the main blocker to supporting negative scoring in the quiz is that it is not compatible with adaptive mode. So separating the models will allow MDL-1647 to finally be resolved.&lt;br /&gt;
&lt;br /&gt;
====Deferred feedback====&lt;br /&gt;
&lt;br /&gt;
This is how the quiz currently work when adaptive mode is off. The student enters a response to each question, then does Submit all and finish at the end of their attempt. Only then do they get feedback and/or grades, depending on the review settings.&lt;br /&gt;
&lt;br /&gt;
====Legacy adaptive mode====&lt;br /&gt;
&lt;br /&gt;
This re-implements how adaptive mode currently works, so old attempts can still be reviewed. In future, adaptive mode will be replaced with ...&lt;br /&gt;
&lt;br /&gt;
====Interactive====&lt;br /&gt;
&lt;br /&gt;
This will replace adaptive mode. This is the model that has been used successfully for several years in the OU&#039;s OpenMark system. The OU has also  modified Moodle to work like this, but because of the way the quiz core currently works, I was not happy just merging the OU changes into Moodle core. In a sense, this whole document grew out of my thoughts about how to implement the OU changes in Moodle core properly.&lt;br /&gt;
&lt;br /&gt;
====Manually graded====&lt;br /&gt;
&lt;br /&gt;
This is a new mode, specifically tailored to manually graded questions. Should make it possible to fix a lot of the outstanding manual grading correctness bugs above.&lt;br /&gt;
&lt;br /&gt;
====Each attempt builds on last====&lt;br /&gt;
&lt;br /&gt;
This is for the benefit of the quiz feature of the same name. It is just like deferred feedback model, but subsequent attempts are seeded with the student&#039;s last response from the previous attempt. I think it leads to slightly cleaner code to do this as a separate interaction model. For example, it does not make any sense to use each attempt builds on last in combination with adaptive mode, although Moodle currently lets you do that.&lt;br /&gt;
&lt;br /&gt;
====Immediate feedback====&lt;br /&gt;
&lt;br /&gt;
This would be like cross between deferred feedback an interactive. Each question would have a submit button beside it, like in interactive mode, so the student can submit it immediately and get the feedback while they still remember their thought processes while answering the question. However, unlike the interactive model, they would not then be allowed to change their answer.&lt;br /&gt;
&lt;br /&gt;
====Certainty based marking with deferred feedback====&lt;br /&gt;
&lt;br /&gt;
This takes any question that can use the deferred feedback model, and adds three radio buttons to the UI (Higher, medium, lower) for the student to use to indicate how certain they are that their answer is correct. Their choice is used to alter the grade the receive for the question.&lt;br /&gt;
&lt;br /&gt;
====Certainty based marking with immediate feedback====&lt;br /&gt;
&lt;br /&gt;
This is like immediate feedback mode with the certainty based marking feature.&lt;br /&gt;
&lt;br /&gt;
===Changes to the question type API===&lt;br /&gt;
&lt;br /&gt;
Can this be backwards compatible?&lt;br /&gt;
&lt;br /&gt;
===The interaction model API===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Proposed robustness and performance testing system==&lt;br /&gt;
&lt;br /&gt;
A major change to the question engine should really only be contemplated in combination with the introduction of a test harness that makes it easy to run correctness, performance and reliablitity tests.&lt;br /&gt;
&lt;br /&gt;
One adavntage of the way data will be stored in the new system is that everything originally submitted by the user will be stored in the database in a format very close to the one in which it was originally received by the web server. Therefore, it should be easy to write a script that replays saved quiz attempts. This is the basis of a test harness. I will create such a test script as part of this work.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Time estimates==&lt;br /&gt;
&lt;br /&gt;
* Completing the above design: a few days.&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
===Time scales===&lt;br /&gt;
&lt;br /&gt;
It seems highly unlikely that this could be done before Moodle 2.0 branches. Now that I have worked out the details of this proposal, I would really like to do it as soon as possible, so that would be for Moodle 2.1. However, that is dependant on my employer (either moodle.com or The Open University). This change is sufficiently big that I would not want to undertake it on my own time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Summary==&lt;br /&gt;
&lt;br /&gt;
This change will cost about X months development. In exchange, the question engine will be more reliable, and will support a richer ranged of teaching and learning activities, through extending the range of interaction models supported, and maing it easier to write new question types.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* The place to discuss this is the [http://moodle.org/mod/forum/view.php?f=121 quiz forum].&lt;br /&gt;
*There is also a [http://moodle.org/mod/forum/discuss.php?d=114527#p502692 Thread in Lesson forum].&lt;br /&gt;
* See the lists of [[#Why_things_need_to_change|related tracker issues]] above.&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer]]&lt;br /&gt;
[[Category:Quiz]]&lt;/div&gt;</summary>
		<author><name>Dooug</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/311/en/index.php?title=Development:Quiz_database_structure&amp;diff=52161</id>
		<title>Development:Quiz database structure</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/311/en/index.php?title=Development:Quiz_database_structure&amp;diff=52161"/>
		<updated>2009-03-06T18:45:43Z</updated>

		<summary type="html">&lt;p&gt;Dooug: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Quiz developer docs}}&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The following diagram shows the database structure for the quiz and question bank in Moodle 1.9.&lt;br /&gt;
&lt;br /&gt;
There have been only small changes since Moodle 1.6. From memory, those changes are&lt;br /&gt;
* adding the quiz_feedback table in Moodle 1.7.&lt;br /&gt;
* renaming the question_sessions.comment column to manualcomment in Moodle 1.7.&lt;br /&gt;
* adding the column question.generalfeedback in Moodle 1.7.&lt;br /&gt;
* adding question.timecreated, timemodified, createdby and modifiedby. Replacing question_category.course with question_category.contextid. Removing question_category.published. This was Jamie Pratt&#039;s questionbank upgrade in Moodle 1.9.&lt;br /&gt;
&lt;br /&gt;
The rest of this page refers to Moodle 1.5. There were big changes in the quiz database between Moodle 1.5 and 1.6 (basically separating out the question bank stuff from the quiz module). I still don&#039;t have time to update it for now, but a lot of the changes were just renaming tables, so if you can deduce the old table names from the new ones, there is useful information in there.&lt;br /&gt;
&lt;br /&gt;
[[Image:Quiz_database.png]]&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
The quiz data model has a fairly large pool of database tables, so the first step in explaining them is to provide some order. Conceptually it is possible to distinguish between the tables holding the teacher-supplied data, defining quizzes and questions, and the tables storing all the data that is generated when students interact with the quizzes and questions. &lt;br /&gt;
&lt;br /&gt;
A further simplification is possible by separating out the questiontype specific tables. They are logical extensions to other tables and therefore are not necessary for understanding the general basic model. They are therefore explained on the developer documentation page for the individual [[Quiz developer docs#Question types|question types]].&lt;br /&gt;
&lt;br /&gt;
The diagram below shows how the most important tables are linked to one another.&lt;br /&gt;
&lt;br /&gt;
[[image:Quiz_database_tables.gif]]&lt;br /&gt;
&lt;br /&gt;
==Teacher-supplied data==&lt;br /&gt;
&lt;br /&gt;
===quiz===&lt;br /&gt;
&lt;br /&gt;
The quiz table contains the definition for all the quizzes. Each quiz belongs to a course, reflected by the course id, has a name and a short descriptive text (intro), an opening and a closing time and several fields that store the settings of various quiz options, each of which is explained in the quiz help that is linked to from the quiz settings page. &lt;br /&gt;
&lt;br /&gt;
The only field that requires additional information is the optionsflag, which... I will put an explanation here eventually --[[User:Gustav|Gustav]] 06:02, 5 February 2006 (WST)&lt;br /&gt;
&lt;br /&gt;
For quizzes that contain random questions the $quiz object may acquire an additional property&lt;br /&gt;
;questionsinuse&lt;br /&gt;
:A comma separated list of questions that are being used in the quiz. This is used by the random questiontype to avoid choosing a question that is already being used.&lt;br /&gt;
&lt;br /&gt;
===question===&lt;br /&gt;
All the questions code in moodle 1.6 has been transfered to the top-level question directory so that new question types could be more easily implemented as plug-ins and could be used in other components than just the quizzes. &lt;br /&gt;
The database has been modified so that the tables names do not refer to quiz. For example the quiz_questions table was renamed question.&lt;br /&gt;
Generally  the table items where maintained so that the old code can be used but there has been specific modifications.&lt;br /&gt;
The 1.6 question table contains the id,category,parent,name,questiontext,questiontextformat,image,defaultgrade,penalty,qtype,length,stamp,version,hidden as the old quiz_questions table with the following modifications.&lt;br /&gt;
&lt;br /&gt;
This table constitutes the item or question bank, i.e. the repository of defined questions. The quiz_questions table defines the data that is common to questions of all types. &lt;br /&gt;
&lt;br /&gt;
;id &lt;br /&gt;
:int(10) NOT NULL auto_increment,&lt;br /&gt;
:Primary key. This is used as a foreign key in many other tables. for example the quiz_answers table allows to define an arbitrary number of answers that are part of the question. And many questiontypes have their own tables that hold more information about the question.&lt;br /&gt;
&lt;br /&gt;
;category &lt;br /&gt;
:int(10) NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key: refers to an id in the table [[#question_categories|question_categories]]&lt;br /&gt;
&lt;br /&gt;
;parent &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key: refers to an id in the table [[#quiz_questions|quiz_questions]]. This field is set to zero except in the case of wrapped questions as they are used for example in the multianswer questiontype. When a question wraps around any number of subquestions the subquestions will have their parent id field set to the id of the main question, thus allowing the question to find all its sub-questions (or wrapped questions). If parent is not &#039;0&#039; the question is never shown in a list of questions, because it is not a valid question by itself. This is also used for hiding random questions from the question list. Their parent field is simply set to their own id. &lt;br /&gt;
&lt;br /&gt;
;name&lt;br /&gt;
:varchar(255) NOT NULL default &#039;&#039;,&#039;&#039;&#039;: in 1.6 utf8&#039;&#039;&#039;&lt;br /&gt;
:Question name that is used in question lists on the quiz editing pages&lt;br /&gt;
&lt;br /&gt;
;questiontext &lt;br /&gt;
:text NOT NULL, &#039;&#039;&#039;: in 1.6 utf8&#039;&#039;&#039;&lt;br /&gt;
:Main text of the question&lt;br /&gt;
&lt;br /&gt;
;questiontextformat &lt;br /&gt;
:tinyint(2) NOT NULL default &#039;0&#039;,&lt;br /&gt;
&lt;br /&gt;
;image &lt;br /&gt;
:varchar(255) NOT NULL default &#039;&#039;, &#039;&#039;&#039;: modified in 1.6 to utf8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;defaultgrade &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;1&#039;,&lt;br /&gt;
:Default grade for a question, usually &#039;1&#039;. In the wrapped parts of cloze questions defaultgrade represents the weight of the part.&lt;br /&gt;
&lt;br /&gt;
;penalty &lt;br /&gt;
:float NOT NULL default &#039;0.1&#039;,&lt;br /&gt;
:The penalty that is applied (if the respective quiz option is set) for an incorrect attempt at the question.&lt;br /&gt;
&lt;br /&gt;
;commentarytext &lt;br /&gt;
:text NOT NULL default &#039;&#039;,&lt;br /&gt;
:Extra feedback that is give to all studnets attempting the question, (as opposed to feedback which is different for each student depending on their repsonse).&lt;br /&gt;
&lt;br /&gt;
;qtype &lt;br /&gt;
:varchar(20) NOT NULL &#039;&#039;&#039;utf8&#039;&#039;&#039;&lt;br /&gt;
:The questiontype of the question as text i.e. &#039;truefalse&#039;, &#039;calculated&#039;  &lt;br /&gt;
&lt;br /&gt;
;length &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;1&#039;,&lt;br /&gt;
:This defines how many question numbers are required for this question. It is generally set to &amp;quot;1&amp;quot;, but the description questiontype, for example, sets it to &amp;quot;0&amp;quot;, reflecting the fact that it doesn&#039;t have a question number.&lt;br /&gt;
&lt;br /&gt;
;stamp &lt;br /&gt;
:varchar(255) NOT NULL default &#039;&#039;,&#039;&#039;&#039;: modified in 1.6 to  utf8&#039;&#039;&#039;&lt;br /&gt;
:Unique identifier&lt;br /&gt;
&lt;br /&gt;
;version &lt;br /&gt;
:int(10) NOT NULL default &#039;1&#039;,&#039;&#039;&#039;: &lt;br /&gt;
:A number representing how often the question was edited.&lt;br /&gt;
&#039;&#039;&#039;was modified to&#039;&#039;&#039;&lt;br /&gt;
:varchar(255) NOT NULL default &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;hidden &lt;br /&gt;
:int(1) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Controls whether to display a question in the question bank list in edit.php. Hiding questions is  a mechanism to &amp;quot;delete&amp;quot; questions without removing them from the database and thus to allow them to be restored or &amp;quot;unhidden&amp;quot; at a later stage. Also the (unfinished and disabled) [[Quiz_developer_docs#Question_versioning|versioning feature]] uses the hidden field to prevent older versions of a question from cluttering the user interface.&lt;br /&gt;
&lt;br /&gt;
In addition to these static fields, which are part of the generic question definitions, there are some additional fields that are only added to the object at runtime. There are two different kinds of fields: On the one hand there are questiontype specific fields, which are also part of the static question definition, but only apply to some questions. On the other hand there are fields that refer to the question instance, i.e. the question in the context of a particular quiz. The values for these later fields can differ when a question is used in different quizzes.&lt;br /&gt;
&lt;br /&gt;
The runtime fields are added to question objects with the function &amp;lt;code&amp;gt;quiz_get_questions_options()&amp;lt;/code&amp;gt; is called. This in turn calls the questiontype specific &amp;lt;code&amp;gt;get_question_options()&amp;lt;/code&amp;gt; method for to add the options field.&lt;br /&gt;
&lt;br /&gt;
;maxgrade&lt;br /&gt;
:This is the maximum grade that the teacher has assigned to this question in the context of the current quiz. This is by default equal to $questions-&amp;gt;defaultgrade but the teacher can change this when editing the quiz. In the database it is stored in the [[#quiz_question_instances|quiz_question_instances table]].&lt;br /&gt;
&lt;br /&gt;
;instance&lt;br /&gt;
:Foreign key: refers to an id in the table [[#quiz_question_instances|quiz_question_instances]]&lt;br /&gt;
&lt;br /&gt;
;options&lt;br /&gt;
:Optional field. It provides a namespace for any questiontype specific information that may be stored in this field. It is generally set by the &amp;lt;code&amp;gt;get_question_options&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
;name_prefix&lt;br /&gt;
:The prefix to be used on all interactions printed as part of the question.&lt;br /&gt;
&lt;br /&gt;
We now deal in alphabetical order with the remaining tables for the teacher-provided data.&lt;br /&gt;
&lt;br /&gt;
===question_answers===&lt;br /&gt;
&lt;br /&gt;
This table allows a common way to store one or more teacher-defined answers for each question. It is not mandatory for a questiontype to make use of this table however. A questiontype may choose to store it&#039;s teacher-defined answers in an entirely different way, or even to do away with teacher-defined answers and use some other method to mark the student-supplied answer. For example it could calculate the correct answer on the fly.&lt;br /&gt;
&lt;br /&gt;
;id &lt;br /&gt;
:int(10) unsigned NOT NULL auto_increment,&lt;br /&gt;
:Primary key&lt;br /&gt;
&lt;br /&gt;
;question &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key to [[#quiz_questions|quiz_questions]] table.&lt;br /&gt;
&lt;br /&gt;
;answer &lt;br /&gt;
:text NOT NULL,&lt;br /&gt;
:The string that represents the teacher-defined answer that will be compared to the student responses for grading and feedback purposes. The format of this field is entirely up to the question type.&lt;br /&gt;
&lt;br /&gt;
;fraction &lt;br /&gt;
:varchar(10) NOT NULL default &#039;0.0&#039;,&lt;br /&gt;
:The fraction of the total mark that the student should earn for giving this particular answer. This could be a negative number for wrong answers. Note that it is stored in a varchar(10) field.&lt;br /&gt;
&lt;br /&gt;
;feedback &lt;br /&gt;
:text NOT NULL,&lt;br /&gt;
:Text that can be displayed to student as feedback when the student&#039;s responses match this particular answer.&lt;br /&gt;
&lt;br /&gt;
In the past there was also a sequence number field was sometimes used to store the order of the different answers and was set to &#039;0&#039; if the order was of no importance.&lt;br /&gt;
&lt;br /&gt;
===question_categories===&lt;br /&gt;
&lt;br /&gt;
Categories are provided as a way to organize questions. Each category has a name and a descriptive text (info) and the sortorder as metadata. Categories allow hierarchical nesting via the parent id and can be private or published, i.e. they can be made available to teachers in other courses.&lt;br /&gt;
&lt;br /&gt;
Since categories are simply a means for organising questions they are not vital for understanding how the quiz module works.&lt;br /&gt;
&lt;br /&gt;
;id &lt;br /&gt;
:int(10) unsigned NOT NULL auto_increment,&lt;br /&gt;
:Primary key&lt;br /&gt;
&lt;br /&gt;
;course &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key to the course table&lt;br /&gt;
&lt;br /&gt;
;name &lt;br /&gt;
varchar(255) NOT NULL default &#039;&#039;,&lt;br /&gt;
&lt;br /&gt;
;info &lt;br /&gt;
:text NOT NULL,&lt;br /&gt;
&lt;br /&gt;
;publish &lt;br /&gt;
:tinyint(4) NOT NULL default &#039;0&#039;,&lt;br /&gt;
&lt;br /&gt;
;stamp &lt;br /&gt;
:varchar(255) NOT NULL default &#039;&#039;,&lt;br /&gt;
&lt;br /&gt;
;parent int(10) &lt;br /&gt;
:unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
&lt;br /&gt;
;sortorder int(10) &lt;br /&gt;
:unsigned NOT NULL default &#039;999&#039;,&lt;br /&gt;
&lt;br /&gt;
===quiz_question_instances===&lt;br /&gt;
&lt;br /&gt;
Questions can be assigned different grades in different quizzes. These are stored in this table. This table can also be used to find all the questions assigned to a particular quiz. But note that this is not the same as the questions that are actually used because if the quiz contains random questions then these in turn choose other questions to be used when an attempt is started and these actually used questions are not stored in this table.&lt;br /&gt;
&lt;br /&gt;
While, after a small extension, this table could also fulfill the purpose of storing the order of the questions in a quiz, this is currently still done in the questions field in the [[#quiz|quiz]] table.&lt;br /&gt;
&lt;br /&gt;
;id &lt;br /&gt;
:int(10) unsigned NOT NULL auto_increment,&lt;br /&gt;
:Primary key&lt;br /&gt;
&lt;br /&gt;
;quiz &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key to the id of the [[#quiz|quiz]] table.&lt;br /&gt;
&lt;br /&gt;
;question &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key to the id of the [[#quiz_questions|quiz_questions]] table.&lt;br /&gt;
&lt;br /&gt;
;grade &lt;br /&gt;
:smallint(6) NOT NULL default &#039;0&#039;,&lt;br /&gt;
:The maximum grade assigned to this question in this quiz. This grade was set by the teacher on the [[mod/quiz/edit|quiz editing page]]. At runtime this information is stored in the $question-&amp;gt;maxgrade field.&lt;br /&gt;
&lt;br /&gt;
===quiz_question_versions===&lt;br /&gt;
&lt;br /&gt;
This feature is not finished and disabled. The table structure may still change. See [[Quiz developer docs#Question versioning]] for a discussion.&lt;br /&gt;
&lt;br /&gt;
;id &lt;br /&gt;
:int(10) unsigned NOT NULL auto_increment,&lt;br /&gt;
&lt;br /&gt;
;quiz &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
&lt;br /&gt;
;oldquestion &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
&lt;br /&gt;
;newquestion &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
&lt;br /&gt;
;userid &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
&lt;br /&gt;
;timestamp &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
&lt;br /&gt;
==Student-created data==&lt;br /&gt;
&lt;br /&gt;
===quiz_attempts===&lt;br /&gt;
&lt;br /&gt;
In the quiz_attempts table a record is created each time a user starts an attempt at a quiz. This is one of the important tables and the corresponding $attempt object is passed between many of the quiz module functions and methods.&lt;br /&gt;
&lt;br /&gt;
;id :int(10) unsigned NOT NULL auto_increment,&lt;br /&gt;
:Primary key&lt;br /&gt;
;uniqueid :int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Primary key since Moodle 1.6 used by [[Quiz database structure#quiz_states|quiz_states table]]. See [[Separating_questions_from_quizzes]] for details.&lt;br /&gt;
;quiz &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&amp;lt;br /&amp;gt;The id of the quiz that is attempted&lt;br /&gt;
;userid &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&amp;lt;br /&amp;gt;The id of the user who is attempting the quiz&lt;br /&gt;
;attempt &lt;br /&gt;
:smallint(6) NOT NULL default &#039;0&#039;,&amp;lt;br&amp;gt;It is possible for a user to attempt a quiz several times, therefore the number of the attempt is stored in this field.&lt;br /&gt;
;sumgrades &lt;br /&gt;
:varchar(10) NOT NULL default &#039;0.0&#039;,&amp;lt;br&amp;gt;The (unscaled) grade for the attempt, i.e. if the grades assigned to the questions add up to 8, but the maximum grade for the quiz is set to 10, then the sumgrades field can contain 8 at maximum.&lt;br /&gt;
;timestart &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&amp;lt;br&amp;gt;The timestart field is set to the current time when an attempt is started and is never changed afterwards.&lt;br /&gt;
;timefinish &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&amp;lt;br&amp;gt;The timefinish field is set to &amp;quot;0&amp;quot; initially and to the current time when the attempt is closed. This is exploited at several places in the code to determine whether an attempt has been closed or not (i.e. closed = timefinish &amp;gt; 0). For all other modifications of an attempt record the timemodified field should be changed as well.&lt;br /&gt;
;timemodified &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&amp;lt;br&amp;gt;This should be changed each time data in the record is modified.&lt;br /&gt;
;layout &lt;br /&gt;
:text NOT NULL,&amp;lt;br&amp;gt;a comma separated list of question ids, with a &amp;quot;0&amp;quot; denoting a page break. Usually the comma separated list ends with &amp;quot;,0&amp;quot;.&lt;br /&gt;
;preview &lt;br /&gt;
:tinyint(3) unsigned NOT NULL default &#039;0&#039;,&amp;lt;br&amp;gt;A flag that marks a teacher preview (i.e. an attempt by a user with teacher privileges) that may be automatically deleted when the quiz is previewed again, and which is not taken into account when viewing statistics.&lt;br /&gt;
&lt;br /&gt;
===quiz_states===&lt;br /&gt;
&lt;br /&gt;
States are saved for each interaction with a question. This allows a review of the complete history of a user&#039;s interactions with individual questions. The current state is the most important object used by the quiz module run-time code. This $state run-time object has a few additional fields not in the database that will be explained further down. The database fields are:&lt;br /&gt;
&lt;br /&gt;
;id &lt;br /&gt;
:int(10) unsigned NOT NULL auto_increment,&lt;br /&gt;
:Primary key&lt;br /&gt;
&lt;br /&gt;
;attempt &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key: refers to an id in the table [[#quiz_attempts|quiz_attempts]]. &#039;&#039;&#039;In Moodle 1.5 it referred to the id field of that table, since Moodle 1.6 it refers to the uniqueid field.&#039;&#039;&#039; This change was done while [[Separating questions from quizzes]].&lt;br /&gt;
&lt;br /&gt;
;question &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key: refers to an id in the table [[#quiz_questions|quiz_questions]]. The attempt id and the question id together sufficiently identify a question instance that a state belongs to.&lt;br /&gt;
&lt;br /&gt;
;originalquestion &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key: refers to an id in the table quiz_questions. It identifies which question was in use when the student attempted the question. This is part of the [[Quiz_developer_docs#Question_versioning|question versioning]] code.&lt;br /&gt;
&lt;br /&gt;
;seq_number &lt;br /&gt;
:int(6) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:A counter that provides information about the sequence in which the states were created.&lt;br /&gt;
&lt;br /&gt;
;answer &lt;br /&gt;
:text NOT NULL,&lt;br /&gt;
:This field is deleted during runtime and replaced with the responses field. For legacy reasons it is still called answer in the database. Questiontypes can store students&#039; responses (usually in a serialized format) in this field using the method save_session_and_responses. Questiontypes are allowed to deviate from this and handle their response storage in any desired way. Some questiontypes do not use this field but instead store the student response in their own table extending this table.&lt;br /&gt;
&lt;br /&gt;
;timestamp &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:A time stamp to record when the state was created. This could mainly be useful for audit purposes.&lt;br /&gt;
&lt;br /&gt;
;event &lt;br /&gt;
:int(4) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Records the event or interaction type that lead from the previous state to the current one. The field can take the value of any of the following constants (defined in locallib.php):&lt;br /&gt;
:*EVENTOPEN: The attempt has just been opened and this is the initial state of a question, i.e. the user has seen the question did not interact with it yet.&lt;br /&gt;
:*EVENTSAVE: The responses are just being saved, either because the student requested this explicitly or because the student navigated to another quiz page.&lt;br /&gt;
:*EVENTVALIDATE: The student requested a validation of the responses. (This is not supported by all questiontypes.)&lt;br /&gt;
:*EVENTGRADE: The responses are being graded but the question session is not closed. This is generally the case for adaptive questions.&lt;br /&gt;
:*EVENTCLOSE: The responses are being graded and the question session is closed. Usually this happens because the whole attempt closes, either because the student requests it or because the time is up or because we are beyond the due date.&lt;br /&gt;
:*EVENTDUPLICATEGRADE: This is a strange one. It indicates that the responses would have been graded had they not been found to be identical to previous responses.&lt;br /&gt;
&lt;br /&gt;
;grade &lt;br /&gt;
:varchar(10) NOT NULL default &#039;0.0&#039;,&lt;br /&gt;
:Calculated from the raw grade by deducting the penalty and rescaling to the defaultgrade value of the question. Note that this is a varchar(10) field like most grade-related fields in the quiz module.&lt;br /&gt;
&lt;br /&gt;
;raw_grade &lt;br /&gt;
:varchar(10) NOT NULL default &#039;&#039;,&lt;br /&gt;
:The grade that was achieved for the question scaled to the question&#039;s weight or grade as assigned in the quiz_question_instances table&lt;br /&gt;
&lt;br /&gt;
;penalty &lt;br /&gt;
:varchar(10) NOT NULL default &#039;0.0&#039;,&lt;br /&gt;
:The penalty incurred during the transition from the previous state to this one. This is different to the cumulative penalty, which can be calculated by adding up all the penalties from previous marking events. &lt;br /&gt;
&lt;br /&gt;
In addition to the database fields a few fields are added to the states at run time. They are dealt with by the questiontypes with the methods &amp;lt;code&amp;gt;create_session_and_responses&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;restore_session_and_responses&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;save_session_and_responses&amp;lt;/code&amp;gt; and are defined as follows.&lt;br /&gt;
&lt;br /&gt;
;sumpenalty&lt;br /&gt;
:The cumulative penalty, which can be calculated by adding up all the penalties from previous marking events. For efficiency these cumulative penalties are stored in the table [[#quiz_newest_states|quiz_newest_states]] so that they do not need to be re-calculated each time.&lt;br /&gt;
&lt;br /&gt;
;changed&lt;br /&gt;
:This records whether a change has taken place to the run-time state. This is set to false when the state is restored from the database. Any code which changes the question state must set this field to true and must increment seq_number. The &amp;lt;code&amp;gt;quiz_save_question_session()&amp;lt;/code&amp;gt; function will save the new state object to the database if the field is set to true.&lt;br /&gt;
&lt;br /&gt;
;responses 	&lt;br /&gt;
:This is automatically set to the value of the database field &amp;lt;code&amp;gt;answer&amp;lt;/code&amp;gt; before that one is removed. The responses field contains an array of responses. In the default case of a single response the value can be found in -&amp;gt;responses[&#039;&#039;]. For questiontypes using several HTML form elements for their responses the array contains a value for each of the interaction elements. The indices are determined by the name of the elements after the name_prefix is stripped.&lt;br /&gt;
&lt;br /&gt;
;last_graded&lt;br /&gt;
:This field contains another state object representing the most recent graded state in the history of this question attempt. This is necessary, because if a state merely represents a save interaction, it should not affect the session in any way. Therefore another grade interaction has to proceed from the last graded state.&lt;br /&gt;
&lt;br /&gt;
;options&lt;br /&gt;
:Optional field. Similarly to the options field in the question object, this field provides a namespace for any questiontype specific information. The difference is that the information in the state-&amp;gt;options field should be state specific, whereas the question-&amp;gt;options field should be static.&lt;br /&gt;
&lt;br /&gt;
We now deal in alphabetical order with the remaining tables holding the student-created data.&lt;br /&gt;
&lt;br /&gt;
===quiz_grades===&lt;br /&gt;
&lt;br /&gt;
The quiz_grades table merely stores a student&#039;s awarded grade for a quiz. Since it is possible to allow several attempts on a quiz, the grade stored is calculated depending on the quiz setting grademethod. This table exists mainly for convenience, because the values of its fields can be recalculated.&lt;br /&gt;
&lt;br /&gt;
;id &lt;br /&gt;
:int(10) unsigned NOT NULL auto_increment,&lt;br /&gt;
:Primary key&lt;br /&gt;
&lt;br /&gt;
;quiz &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key refering to the id of the [[#quiz|quiz]] table&lt;br /&gt;
&lt;br /&gt;
;userid &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Foreign key refering to the id of the user table&lt;br /&gt;
&lt;br /&gt;
;grade &lt;br /&gt;
:double NOT NULL default &#039;0&#039;,&lt;br /&gt;
:The grade awarded for this quiz to this user&lt;br /&gt;
&lt;br /&gt;
;timemodified &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
:Unix timestamp to be updated each time the grade is changed&lt;br /&gt;
&lt;br /&gt;
===quiz_newest_states===&lt;br /&gt;
&lt;br /&gt;
This table exists only for efficiency reasons:&lt;br /&gt;
#Via its &#039;newest&#039; and &#039;newgraded&#039; fields it gives attempt.php a way to quickly find the newest state and the newest graded state for an attempt. It allows the construction of SQL to select all the states that need to be loaded on attempt.php or review.php.&lt;br /&gt;
#Via its &#039;sumpenalty&#039; field it gives quiz_apply_penalty() a quick way for getting at the accumulated penalty that needs to be applied. Without this field the penalties from all previous graded states would have to be added up each time. Not a big deal actually because this could be achieved with a single SQL query (using SUM) but this field was introduced when we still had the multiplicative penalty scheme around which would have been more difficult to recompute.&lt;br /&gt;
&lt;br /&gt;
This table was introduced in Moodle 1.5 and is not populated for all states during the upgrade because on sites with a lot of existing states that could take too long. Rather it is done whenever needed by quiz_upgrade_states().&lt;br /&gt;
&lt;br /&gt;
;id &lt;br /&gt;
:int(10) unsigned NOT NULL auto_increment,&lt;br /&gt;
Primary key&lt;br /&gt;
&lt;br /&gt;
;attemptid &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
Foreign key referring to the id in the [[#quiz_attempts|quiz_attempts]] table. In Moodle 1.5 it referred to the id field of that table, since Moodle 1.6 it refers to the uniqueid field. This change was done while [[Separating questions from quizzes]].&lt;br /&gt;
&lt;br /&gt;
;questionid &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
Foreign key referring to the id in the [[#quiz_questions|quiz_questions]] table&lt;br /&gt;
&lt;br /&gt;
;newest &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
Foreign key referring to the id in the [[#quiz_states|quiz_states]] table. This points to the newest state for this attempt and this question.&lt;br /&gt;
&lt;br /&gt;
;newgraded &lt;br /&gt;
:int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
Foreign key referring to the id in the [[#quiz_states|quiz_states]] table. This points to the newest state created by a grading event.&lt;br /&gt;
&lt;br /&gt;
;sumpenalty &lt;br /&gt;
:varchar(10) NOT NULL default &#039;0.0&#039;,&lt;br /&gt;
This stores the total penalty that this user has accumulated over previous graded responses to this question in this attempt.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Development:Question_database_structure|Question database structure]]&lt;br /&gt;
* [[Development:Database_schema_introduction|Database schema introduction]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer|Quiz database structure]]&lt;br /&gt;
[[Category:Quiz]]&lt;/div&gt;</summary>
		<author><name>Dooug</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/311/en/index.php?title=User:Douglas_Reith&amp;diff=51869</id>
		<title>User:Douglas Reith</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/311/en/index.php?title=User:Douglas_Reith&amp;diff=51869"/>
		<updated>2009-03-02T21:02:23Z</updated>

		<summary type="html">&lt;p&gt;Dooug: New page: New to Moodle.   I live/work in Ann Arbor, Michigan.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;New to Moodle. &lt;br /&gt;
&lt;br /&gt;
I live/work in Ann Arbor, Michigan.&lt;/div&gt;</summary>
		<author><name>Dooug</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/311/en/index.php?title=Development:Random_question_type&amp;diff=51868</id>
		<title>Development:Random question type</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/311/en/index.php?title=Development:Random_question_type&amp;diff=51868"/>
		<updated>2009-03-02T21:01:28Z</updated>

		<summary type="html">&lt;p&gt;Dooug: fixed  typos&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Questiontype developer docs}}&lt;br /&gt;
&lt;br /&gt;
The random question is not a real question but only a device to randomly choose a question from a category. The $question-&amp;gt;questiontext field is abused as a flag: 1 means choose question from the category and its subcategories, 0 means only use questions in the category itself.&lt;br /&gt;
&lt;br /&gt;
When a new session is started for a random question then a question is chosen from the category. We need to make sure that no question is used more than once in the quiz. Therefore the following need to be excluded:&lt;br /&gt;
#All questions that are explicitly assigned to the quiz&lt;br /&gt;
#All questions that are already chosen by an other random question&lt;br /&gt;
#Random questions&lt;br /&gt;
#Other explicitly excluded question types&lt;br /&gt;
#Wrapped questions&lt;br /&gt;
&lt;br /&gt;
To do the first the question type uses an additional property $quiz-&amp;gt;questionsinuse that holds a comma separated list of all questions used in the current quiz. To do the second the questiontype class has a property &#039;catrandoms&#039; which is an array indexed by category id and by $question-&amp;gt;questiontext. Each entry is a randomized array of questions in that category which can be used.&lt;br /&gt;
&lt;br /&gt;
==Explicitly excluded question types==&lt;br /&gt;
&lt;br /&gt;
See &amp;lt;code&amp;gt;question/type/random/questiontype.php&amp;lt;/code&amp;gt; for the list of explicitly excluded types:&lt;br /&gt;
&lt;br /&gt;
 var $excludedtypes = array(&amp;quot;&#039;random&#039;&amp;quot;, &amp;quot;&#039;randomsamatch&#039;&amp;quot;, &amp;quot;&#039;essay&#039;&amp;quot;, &amp;quot;&#039;description&#039;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Please note that because of problems with grading, the [[Essay_question_developer_docs|essay question]] is not supported at the moment. See more information at the thread [http://moodle.org/mod/forum/discuss.php?d=50903 Random Question Test].&lt;br /&gt;
&lt;br /&gt;
==Response storage==&lt;br /&gt;
&lt;br /&gt;
The answer field for random questions come in two flavours:&lt;br /&gt;
&lt;br /&gt;
*For responses stored by Moodle version 1.5 and later the answer field has the pattern random#-* where the # part is the numeric question id of the actual question shown in the quiz attempt and * represents the student response to that actual question.&lt;br /&gt;
&lt;br /&gt;
*For responses stored by older Moodle versions - the answer field is simply the question id of the actual question. The student response to the actual question is stored in a separate record.&lt;br /&gt;
&lt;br /&gt;
This means that prior to Moodle version 1.5, random questions needed two records for storing the response to a single question. From version 1.5 and later the question type random works like all the other question types in that it now only needs one record per question.&lt;br /&gt;
&lt;br /&gt;
==$question-&amp;gt;options==&lt;br /&gt;
&lt;br /&gt;
The random question type does not have any options for $question-&amp;gt;options.&lt;br /&gt;
&lt;br /&gt;
==$state-&amp;gt;options==&lt;br /&gt;
&lt;br /&gt;
This has one property &#039;question&#039; which is set to the fully instantiated question object for the randomly chosen question.&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer|Random question type]]&lt;br /&gt;
[[Category:Quiz]]&lt;/div&gt;</summary>
		<author><name>Dooug</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/311/en/index.php?title=Development:vim&amp;diff=51454</id>
		<title>Development:vim</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/311/en/index.php?title=Development:vim&amp;diff=51454"/>
		<updated>2009-02-23T14:50:08Z</updated>

		<summary type="html">&lt;p&gt;Dooug: typo fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Setting up Vim for Moodle Development =&lt;br /&gt;
&lt;br /&gt;
There are many powerful features available in vim, here are a few tips &lt;br /&gt;
to ensure you get powerful use out of vim with moodle development&lt;br /&gt;
&lt;br /&gt;
== Indentation ==&lt;br /&gt;
Moodle [[Coding|Coding Guidelines]] state that indentation should be spaces of 4 spaces (not Tabs). Some .vimrc options will help you adhere to these guidelines with ease:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot; insert space characters whenever the tab key is presse&lt;br /&gt;
set expandtab&lt;br /&gt;
&lt;br /&gt;
&amp;quot; insert 4 spaces characters when tab key is pressed&lt;br /&gt;
set tabstop=4&lt;br /&gt;
&lt;br /&gt;
&amp;quot; insert 4 spaces wen autoindent indents&lt;br /&gt;
set shiftwidth=4&lt;br /&gt;
&lt;br /&gt;
&amp;quot; automatically indent files&lt;br /&gt;
set autoindent&lt;br /&gt;
&lt;br /&gt;
&amp;quot; Do smart indentation depending on code syntax (e.g. change after { }, keywords etc)&lt;br /&gt;
set smartindent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Syntax Highlighting ==&lt;br /&gt;
Recent versions of vim have robust syntax highlighting for php. In order to switch syntax highlighting on add the following to your .vimrc:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot; set syntax highlighting on&lt;br /&gt;
syntax on&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other Useful vimrc settings ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot; show a ruler with line number, % through file on status line&lt;br /&gt;
set ruler&lt;br /&gt;
&amp;quot; show line number&lt;br /&gt;
set nu&lt;br /&gt;
&amp;quot; PHP syntax check&lt;br /&gt;
set makeprg=php\ -l\ %&lt;br /&gt;
set errorformat=%m\ in\ %f\ on\ line\ %l&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== File encoding and decoding ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set fileencodings=ucs-bom,utf-8,gbk,big5,latin1&lt;br /&gt;
set termencoding=utf-8,gbk&lt;br /&gt;
if has (&#039;multi_byte&#039;) &amp;amp;&amp;amp; v:version &amp;gt; 601&lt;br /&gt;
  if v:lang =~? &#039;^\(zh\)\|\(ja\)\|\(ko\)&#039;&lt;br /&gt;
    set ambiwidth=double&lt;br /&gt;
  endif&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Status Line ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set statusline=%&amp;lt;%f\ %h%m%r%=%k[%{(&amp;amp;fenc==\&amp;quot;\&amp;quot;)?&amp;amp;enc:&amp;amp;fenc}%{(&amp;amp;bomb?\&amp;quot;,BOM\&amp;quot;:\&amp;quot;\&amp;quot;)}]\ %-14.(%l,%c%V%)\ %P&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Ctags ==&lt;br /&gt;
See the [[Development:ctags|Ctags Article]] for details on how to setup ctags usage with moodle and vim&lt;/div&gt;</summary>
		<author><name>Dooug</name></author>
	</entry>
</feed>