moodle/mod/quiz/doc/responsestorage.html
2005-07-16 09:43:49 +00:00

334 lines
14 KiB
HTML

<!DOCTYPE HTML PUBLIC>
<html>
<head>
<title>Response Storage</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>Response Storage</h1>
<h2>Contents</h2>
<ul class="navigation">
<li><a href='#description'>Purpose and Description</a></li>
<li><a href='#qtypemethods'>Questiontype Methods</a></li>
<li><a href='#storage'>Response Storage</a></li>
<li><a href='#runtime'>Runtime Model</a></li>
<li><a href='#qtypes'>Responses by Questiontype</a></li>
</ul>
<a name='description'></a><h2>Purpose and Description</h2>
<p>
<a href="terminology.html#responses">Responses</a> are an attribute of the
<code>$state</code> object. Questiontypes are completely free to implement the
storage mechanism of their responses (and other state information) the way they
want. Still, the standard <a href="terminology.html#responses">questiontypes</a>
all follow a similar model. The default storage model and the questiontype
specific variations are explained below.
</p><p>
The <a href="#runtime">runtime model</a> needs some more standardisation to work
generically. Therefore it imposes some constraints by convention, which are
explained later on the page.
</p>
<a name='qtypemethods'></a><h2>Questiontype Methods</h2>
<p>
The flexibility for the questiontypes to choose their response storage mechanism
freely and to convert from the storage model to the runtime model is provided by
a set of three functions, which allow to initialise the runtime
<code>$state->responses</code> field, to convert from the runtime to the storage
model and vice versa:
<ul>
<li><code>create_session_and_responses</code></li>
<li><code>restore_session_and_responses</code></li>
<li><code>save_session_and_responses</code></li>
</ul>
The following method descriptions are adapted from the phpdoc comments in
locallib.php.
</p>
<h3><code>create_session_and_responses</code></h3>
<p>
Initializes the <code>$state</code> object, in particular the
<code>$state->responses</code> field, but also any other state (or session)
information.
</p><p>
This function is called to start a question session. Empty question type
specific session data (if any) and empty response data is added to the
<code>$state</code> object. Session data is any data which must persist
throughout the quiz attempt, possibly with updates as the user interacts with
the question. This function does <strong>not</strong> create new entries in the
database for the session; a call to the <code>save_session_and_responses</code>
method will occur to do this.
</p>
<h3><code>restore_session_and_responses</code></h3>
<p>
Restores the session data and most recent responses for the given state.
</p><p>
This function loads any session data associated with the question session in the
given state from the database into the <code>$state</code> object. In particular
it loads the responses that have been saved for the given <code>$state</code>
into the <code>$state->responses</code> field.
</p><p>
Questiontypes with only a single form field for the student's response will not
need to restore the responses; the value of the <code>answer</code> field in the
<code>quiz_states</code> table is restored to <code>$state->responses['']</code>
automatically before this function is called. Questiontypes with more response
fields should override this method and set the <code>$state->responses</code>
field to an associative array of responses.
</p>
<h3><code>save_session_and_responses</code></h3>
<p>
Saves the session data and responses for the given question and state.
</p><p>
This function saves the question type specific session data from the
<code>$state</code> object to the database. In particular, for most
questiontypes, it saves the responses from the <code>$state->responses</code>
field of the <code>$state</code> object. The questiontype non-specific data for
the state has already been saved in the <code>quiz_states</code> table and the
<code>$state</code> object contains the corresponding id and sequence number,
which may be used to index a question type specific table.
</p><p>
Questiontypes with only a single form field for the student's response, which is
contained in <code>$state->responses['']</code> will not have to save this
response. It will already have been saved to the <code>answer</code> field of
the <code>quiz_states</code> table. Questiontypes with more response fields
should override this method and save the responses in their own database tables.
</p>
<a name='storage'></a><h2>Response Storage</h2>
<p>
The generic quiz module code saves the contents form the
<code>$state->responses['']</code> field to the <code>answer</code> field in the
<code>quiz_states</code> table and also automatically restores the contents of
this field to <code>$state->responses['']</code>. This means that any
questiontype, which only expects a single value as its response can skip the
implementation of the three methods described above. All questiontypes that have
multiple value responses need to implement these methods. The default
questiontypes handle this problem by serializing/de-serializing the responses
to/from the <code>answer</code> field in the <code>quiz_states</code> table.
However, it is also possible (and may be better practice) to extend the
<code>quiz_states</code> table with a questiontype specific table, i.e. take the
id of the <code>quiz_states</code> record as a foreign key in the questiontype
specific table. Because the value of <code>$state->responses['']</code> is set
to the value of the <code>answer</code> field, questiontypes that serialize
their response need to overwrite (in <code>save_session_and_responses</code>)
whatever value the generic code set this field to with their serialized value
(usually achieved with a simple <code>set_field</code>). In the method
<code>restore_session_and_responses</code> the serialized value can be read from
<code>$state->responses['']</code>. Care needs to be taken that this array value
is then unset or the whole array overwritten, so that the array does not
accidentally contain a value with the empty string index.
</p>
<a name='runtime'></a><h2>Runtime Model</h2>
<p>
The runtime model for responses dictates the structure of the
<code>$state->responses</code> array. Starting with the names of the
form elements this section goes through the relevant processing
steps and thus attempts to clarify why the keys of the
<code>$state->responses</code> array can differ for different
questiontypes; even more, it explains how the array keys are chosen and
set.
</p><p>
Although it may initially seem strange to start with the naming convention of
the form fields, the reason for this will become clear later on. The controls
(i.e. the form fields) of a question get printed by the method
<code>print_question_formulation_and_controls()</code>. The convention only
dictates that the name of the control element(s) <strong>must</strong> begin
with the value of <code>$question->name_prefix</code>. The
<code>$question->name_prefix</code> is a string starting with "resp" followed by
the question id and an underscore, e.g. resp56_. In the default case, when
there is only a single control element (this includes the case of a list of equally named
radio buttons), no postfix is appended to the name prefix. For questiontypes
that allow or require multiple form elements, an arbitrary string can be appended
to the name prefix to form the name of these form elements. The postfix must not
include any relational data
(i.e. ids of records in the <code>quiz_answers</code> table), because this can
lead to problems with regrading of versioned questions.
</p><p>
After the printing of the question the server only sees it again when it is
submitted. So the submitted data will contain several values indexed by strings
starting with respXX_. Upon submission, the function
<code>quiz_process_responses()</code> is called, which assigns the submitted
responses to the state of the question with id XX, using the postfix (i.e.
everything after the underscore) as array keys. In the default case with
only one control element the name only consists of the name prefix. This
explains why the default index of the <code>$state->responses</code> array is
the empty string. The value of each array element is obviously the value that
was submitted by the form, basically a raw response.
</p><p>
The function <code>quiz_process_responses()</code> in turn calls the
questiontype specific method <code>grade_responses()</code> to assign
a grade to the submitted responses and
<code>compare_responses()</code> to determine whether the response was
identical to the previous submission and to avoid regrading the same
responses repeatedly. These questiontype specific functions need to
be aware of the expected keys of the
<code>$state->responses</code> array.
</p><p>
Finally, the methods <code>restore_session_and_responses()</code> and
<code>save_session_and_responses()</code> also need to know the questiontype
specific layout of the <code>$state->responses</code> array and restore or save
the information, e.g. by converting from or to the data representation.
<a name='qtypes'></a><h2>Responses by Questiontype</h2>
<h3>Calculated</h3>
<p>
The calculated questiontype inherits its methods
<code>create_session_and_responses()</code>,
<code>restore_session_and_responses()</code> and
<code>save_session_and_responses()</code> from the abstract dataset
dependent questiontype. This questiontype serializes the information
of the chosen dataset together with the (single) response value into
a string of the format datasetXX-RESPONSE, where dataset is the
actual string "dataset", XX is the number of the chosen dataset item
and RESPONSE is the value that was received from the submitted form.
</p>
<h3>Description</h3>
<p>The description questiontype does not have responses.</p>
<h3>Match</h3>
<p>
The matching questiontype stores needs to store information about the order of
its subquestions and the selected responses. Because each subquestion is
identified by one record in the <code>quiz_match_sub</code> table, the ids of
this table are used to identify both the question and the response part. For
each subquestion a '-'-separated pair is created, giving first the id of the
record that provided the subquestion and then the id of the record that provided
the selected response. E.g. 1-3 means that the answer of the third question was
selected for the first subquestion. Correct responses are always pairs of
identical ids, e.g 2-2. If no response is selected, the second id in the pair is
set to '0'. These pairs are then put into a comma separated list to allow each
subquestion to store a response.
</p>
<h3>Multianswer</h3>
<p>
The serialized format of multianswer questions is a comma separated
list of pairs. Each pair saves the response for one of the
subquestions and is itself separated with a hyphen ('-'). In front
of the hyphen there is the sequence number of the subquestion
(starting at 1 incremented by the order the subquestions appear in
the text). After the hyphen is the raw response, that was submitted
by the student. The serialized format could look like the following
example:<br /> 1-34,2-Napoleon,3-4,4-correct response,5-1<br /> All
commas and hyphens that are part of the raw answer are HTML entity
encoded to avoid problems.
</p>
<h3>Multichoice</h3>
<p>The multichoice questiontype stores both the order of the choices and the
selected choices. Storing the order is optional (mainly to provide backward
compatibility with previous versions of this questiontype). The order is stored
as a comma separated list of answer ids (i.e. form the <code>quiz_answers</code>
table). It is separated with a colon (':') from the selected responses, which
are also stored as a comma separated list of answer ids. Note that the list of
selected responses is usually shorter (and often contains only one id) than the
list that provides the order.</p>
<h3>Numerical</h3>
<p>
The numerical questiontype, which inherits the function
<code>print_question_formulation_and_controls()</code> from the shortanswer
questiontype, has only one response field, so its responses are handled by the
default questiontype. The response is stored without any modifications in the
<code>answer</code> field of the table <code>quiz_states</code>.
</p>
<h3>Random</h3>
<p>
The random questiontype saves one state with a question id in the
<code>answer</code> field to indicate which question was randomly selected. From
that point onwards only states for the chosen question are saved.
</p>
<h3>RandomSAMatch</h3>
<p>
The random shortanswer matching questiontype reuses the <code>
save_session_and_responses</code> method of the matching questiontype and
therefore stores its responses in the same format. The difference is that the
first id in the pair is the question id (<code>quiz_questions</code>) of the
selected shortanswer and the second id is the id of the first correct answer
that is found ofr that shortanswer question in the <code>quiz_answers</code>
table.
</p>
<h3>Shortanswer</h3>
<p>
The shortanswer questiontype is the ideal example of the default case. It does
not overwrite any of the default implementations of the three methods mentioned
above, because it uses the default <code>$state->responses</code> array indexed
with the empty string. The value entered into the shortanswer input field is
entered directly into the <code>answer</code> field.
</p>
<h3>Truefalse</h3>
<p>
The true/false questiontype stores a single answer id (from the table <code>
quiz_answers</code>) in the <code>answer</code> field. Each true/false question
defines two answer records, one for the 'true' option, one for the 'false'
option, so either of these two ids gets stored as response.
</p>
</body>
</html>