Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Quiz: Difference between revisions

From MoodleDocs
(Added info about change for Moodle 1.6)
mNo edit summary
(46 intermediate revisions by 10 users not shown)
Line 1: Line 1:
The quiz module is a complex module with its own modular structure to allow question type plug-ins. The module has grown organically and in spite of a lot of rewriting for Moodle 1.5 the code is not very simple to understand.
{{Quiz developer docs}}


Cross-referenced code for the
The quiz is a complex module, although attempts have been made re-organise the code to keep things managable.
*[http://moodle.org/xref/stable/mod/quiz/index.html stable version]
*[http://moodle.org/xref/development/mod/quiz/index.html development version]


On this page I am planning to give an overview of how the module works. --[[User:Gustav|Gustav]] 07:12, 28 January 2006 (WST)
This page is only intended to give a high level overview. To really understand the quiz, you will have to look at the code, much of which should be clear and well commented.


==Changes for Moodle 1.5: adaptive questions==


During the first half of 2005 the quiz module code has undergone a considerable rewrite to allow for adaptive questions in which a question session can consist of several sequential student responses. The question can adapt itself to the student answers. For example in response to certain answers the question could provide feedback or hints and then ask the student to answer again or give the student a simpler or related question.
==What the quiz does and does not do==


Unfortunately many changes had to be made to the question type methods. This has however resulted in improved efficiency and has made the writing of question types easier. It also allows question types with more powerful features and has fixed some bugs / annoying behaviour.  
The quiz module uses the [[Question bank]] to manage the questions that teachers create, and the [[Question engine]] to display and process questions when students attempt them. You are particularly advised to read [[Using the question engine from module]].


For details see:
The quiz module itself is only responsible for
*[[Quiz rewrite|Quiz module rewrite]]
# letting teachers assemble questions into quizzes.
*[[Quiz conversion|How to convert existing question types]]
# controlling which students can attempt the quiz when.
# providing access so that students can review their past attempts and the feedback (if the teacher permits it).
# providing reports on the outcomes for teachers.


Of course there were countless other changes to the quiz module going from Moodle 1.4 to 1.5, especially to the teacher interface. However in spite of the fact that these changes are a lot more visible they were much less drastic from the point of view of the code.
Displaying the results is delegated to [[Quiz reports|quiz report]] sub-plugins. Note that some fairly core functionality is implemented in the reports. For example deleting and regrading attempts is handled by <tt>quiz_overview</tt> report, and bulk manual grading is handled by <tt>quiz_grading</tt>.


==Changes for Moodle 1.6: separating questions from quizzes==
A lot of the details of when and if students can access the quiz are handled by [[Quiz access rules|quiz access rule]] sub-plugins.


The quiz module is not the only activity module in Moodle that uses questions. The lesson does too and potentially questions could be useful in many modules. Therefore we have started to rewrite some  of the quiz module functions and move them from locallib.php to questionlib.php so that eventually they could be moved into a central library and be used by other modules.


The details of the changes are explained on the page [[Separating questions from quizzes]].
==Code overview==


==Objects and data structures==
All the code has PHPdoc comments which provides a lot of detailed explanation. That is not repeated here.


Key to understanding how the quiz module works is to understand the different kinds of object work together. There most important ones are:
This is a [[Modules|standard Moodle activity module]], so inside <tt>mod/quiz</tt> there are all the things you would expect to see like <tt>db/</tt>, <tt>lib.php</tt>, <tt>locallib.php</tt>, <tt>view.php</tt>, <tt>version.php</tt> and so on.


*Quizzes
The scripts that are directly accessed by the user are listed on [[Quiz user interface overview]], and I will not list them again here. It would probably also help to look at the [[Quiz database structure]] before trying to understand the back-end code.
*Questions
*Attempts
*States


Quizzes and Questions are data created by the teacher when setting up and editing a quiz. Attempts and States are data created by the student when interacting with a quiz.  
The back-end code is organised thus:
; lib.php
: All the functions that are called by the Moodle core. For performance reasons it is important that this does not include any other files.
; locallib.php
: This contains all the other quiz library functions that do not have a more specific home. Including this file also includes all the other quiz libraries that you might need.
; mod_form.php
: The module settings form, as you would expect for any Moodle activity module.
; editlib.php
: This defines the functions that are used when at teacher edits the quiz. Thus, they are mostly called from <tt>edit.php</tt>.
; attemptlib.php
: This defines the <tt>quiz</tt> and <tt>quiz_attempt</tt> classes. These are all about what happens when a student (or other user) is looking at the quiz, so they provide a personalised view of the quiz data from the point of view of that user. Thus these classes are mainly used by the <tt>view.php</tt>, <tt>startattempt.php</tt>, <tt>attempt.php</tt>, <tt>processattempt.php</tt>, <tt>summary.php</tt> and <tt>review.php</tt> scripts.
; accessmanager.php
: This provides the interface point that the rest of the quiz code uses to access the [[Quiz access rules|quiz access rule]] sub-plugins.
; renderer.php
: the quiz uses the [[Themes 2.0 overriding a renderer|Moodle 2.x renderer system]] to generate the HTML for all the student-visible parts of the UI. This means that theme designer have a lot of freedom as to how the quiz is displayed to students. Perhaps one day the editing UI will also be re-factored. As is standard for a Moodle plug-in, this file defines the renderer class.
; settings.php & settingslib.php
: define the admin settings for the quiz.
; accessmanager_form.php, addrandomform.php & override_form.php
: Used by <tt>accessmanager.php</tt>, <tt>addrandom.php</tt> and <tt>overridedit.php</tt> respectively.
; module.js
: JavaScript used by <tt>attempt.php</tt> and to a lesser extent <tt>view.php</tt>, <tt>summary.php</tt> and <tt>review.php</tt>.
; edit.js
:JavaScript used by <tt>edit.php</tt>.


Moodle allows students to make several attempts at a quiz. Data about such an attempt is stored in an attempt object. This holds for example how the quiz was randomized for this attempt and the ordering of the questions and answers. So attempts are indexed by 1) user, 2) quiz


Moodle allows students to interact repeatedly with a single question. So for example the student might initially just save an answer, later mark it, then correct it if it was marked incorrect. Each time the student interacts with a particular question inside a particular attempt at a quiz a new state is created. So states are indexed by 1) user, 2) attempt, 3) question.
==Quiz navigation fake block==


===Database structure===
During the quiz attempt (so, on <tt>attempt.php</tt>, <tt>summary,php</tt> (from Moodle 2.2 onwards) and <tt>review.php</tt>) we display a navigation UI that looks like a block. This uses the <tt>block_manager::add_fake_block</tt> feature.
All this data needs to be kept in Moodle's database. How this is achieved is explained on a separate page about the '''[[Quiz database structure]]''', which also contains a useful diagram.


===Runtime objects===
The block contents is produce by classes in <tt>attemptlib.php</tt> working with methods in the renderer. The navigation relies on some JavaScript in <tt>module.js</tt> because it is vitally important that every time the user moves from one page of the quiz attempt to another, we save their answers. The javascript turns click on the navigation links into form submissions.
In most scripts the quiz module deals with the same objects. In some cases these are not identical to the records from the database, but they have some fields that are added at runtime. The objects concerned are questions and states; both are described below. Also the options object, purely a runtime object, is described. Many functions that are used to process these objects make use of the additional fields and it is therefore necessary to use the correct functions for creating these objects. The structure of these objects is explained in detail on a separate page about the '''[[Quiz runtime objects]]'''.


The main script of the quiz module is attempt.php which will have to deal with all these objects. Studying the '''[[Quiz attempt|explanation of attempt.php]]''' is therefore a good way to start to study the quiz module code.


==Terminology==
==Quiz layout==


In order to describe and understand the quiz module, it is necessary to
Information about the structure of a quiz is stored in the <tt>quiz.qestions</tt> column and the <tt>quiz_question_instances</tt> table. The layout is a comma-separated list of question ids, with 0s to represent page-breaks.
understand the terminology that is used to describe its aspects. Below
some important terms are listed and explained and in some cases linked to more
detailed documentation.


;adaptive question (adaptive item)
There are methods in <tt>locallib.php</tt> and <tt>editlib.php</tt> to manipulate this structure.


:Adaptive questions are questions that walk the user through a directed graph of question states depending on the user's responses. E.g. a complicated mathematical question that is answered incorrectly, but is likely to be incorrect because of a common mistake, could provide the user with a hint towards this mistake, apply a penalty and allow a second attempt at this question. In the QTI specifications an adaptive question is referred to as an adaptive item
When a quiz attempt is stared, the questions are added to a <tt>question_usage_by_activity</tt> object ($quba) that is managed by the question bank. The $quba indexes things by 'slot' rather than question.id, so the quiz layout gets rewritten and stored in <tt>quiz_attempts.layout</tt>. This is done in <tt>startattempt.php</tt>.


:Quizzes can be run in adaptive mode, which provides buttons to mark each question individually.
One of the options is to randomise the order of the question in the quiz. This is done by shuffling <tt>quiz_attempts.layout</tt>.


;answer(s)
<tt>startattempt.php</tt> also handles selecting a real question to go in each place where the teacher has added a 'Random question from category X' to the quiz.


:The term answer is used exclusively for the teacher defined answers of a question. These answers are usually stored in the <code>quiz_answers</code> table and are compared to the responses for grading.


;attempt(s)
==See also==
 
:The term attempt is used in the sense of "Attempt at the quiz". Depending on the quiz settings, a student may be allowed several attempts at a quiz. An attempt is finished when the student clicks on the corresponding button on the attempt page. Students do not have to complete an attempt in one visit. They can navigate away from the quiz page and return later to continue the same attempt.
 
:Within one and the same quiz attempt a student may make several attempts        at answering a particular question, at least if the questiontype allows        it and the quiz is set up in adaptive mode. These will always be        referred to as &quot;attempts at a question&quot;, never just as        &quot;attempts&quot;.
 
;event
 
:The $stateobject (and the <code>quiz_states</code> table) has a field <code>event</code>, which indicates the event that led to the creation of the state. The field can take the value of any of the following constants (defined in locallib.php):
:*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.
:*EVENTSAVE: The responses are just being saved, either because the student requested this explicitly or because the student navigated to another quiz page.
:*EVENTVALIDATE: The student requested a validation of the responses. (This is not supported by all questiontypes.)
:*EVENTGRADE: The responses are being graded but the question session is not closed. This is generally the case for adaptive questions.
:*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.
:*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.
 
:When new responses are being processed by the function <code>quiz_process_responses</code>, then this function is being passed the event type in <code>$action->event</code> while the responses are in <code>$action->responses</code>.
 
;question(s)
 
:A question in the context of the quiz module is the set of definitions (question name, question text, possible answers, feedback, etc.) that constitute a reusable assessment item. In the terminology of the QTI specification what we refer to as a question is more appropriately called an '''item'''.
 
:A question itself is a static entity. All data related to interactions with a particular question (in a particular attempt on a particular quiz) is stored in its corresponding states.
 
;questiontype
 
:There are several questiontypes built into the quiz module, however,      due to the plugin architecture of the quiz module, it is possible to        define custom questiontypes. Existing questiontypes include true/false,        multiple choice, short answer, numerical and  calculated questions.
 
;responses
 
:Conceptually these are the students' responses to a question. This is always used in plural, although for some questiontypes there is only one possible response. In the runtime <code>$state</code> object there is a field <code>$state-&gt;responses</code>, which holds an array of a student's responses to a question. See also: [[Quiz response storage]]
 
;state
 
:Question states are saved in the table <code>quiz_states</code>. States always "belong" to a question within a particular attempt and are therefore also user specific. The first state in a question's life cycle during an attempt records the fact that the user has seen the question. This state is never modified, however, it can be superseded by a new state, which represents a user interaction. Different user interactions are defined by the events, a set of constants, one for each possible type of user interaction.
 
;session
 
:A question session is the complete history of question states that the question is taken through. Usually only the most recent state and the last graded state are of interest though.
 
==Penalty mechanism==
 
===What it is for===
 
When the quiz is run in adaptive mode the student can interact with a question repeatedly. So in particular the student can try again when he gets a wrong answer. Clearly the final mark for the question must reflect the fact that the student did not get it right originally. Therefore a penalty is subtracted from the final mark.
 
===How the penalty is determined===
 
First of all penalties are relevant only if a quiz is run in adaptive mode. Only in this case can a student have a second attempt and therefore only in this mode can there be any occasion to subtract a penalty.
 
Even in adaptive mode the penalty mechanism is only used when it is selected in the quiz options. If "Apply penalties" is set to "No" then the final mark for the question is the mark for the last graded response.
 
Each question has a 'penalty' field (which should really be called 'penaltyfactor') which is a number between 0 and 1. The penalty for a wrong response is calculated as the product ($quiz->penalty * $quiz->grade), i.e., as the product of the penaltyfactor with the maximum achievable grade for the question. This product is stored in $state->penalty. So $quiz->penalty is the fraction of the maximum grade that is subtracted as a penalty for each wrong response.
 
The $quiz->penalty field has a default value of 0.1, both in the database and in mod/quiz/defaults.php. This default can of course be overwritten by the admin on the quiz configuration page. This admin-selected default is (as usual for admin defaults) stored in $CFG->quiz_penalty. The teacher can choose a different penalty factor for each individual question when adding or editing a question.
 
Now if a student makes repeated wrong attempts (or partially correct attempts) the penalties for all these attempts are added up in $state->sumpenalties. The mark for the question is then calculated as the mark for the last graded response minus the sum of the penalties.
 
One curious fact about $state->sumpenalties is that, for efficiency reasons, it is not stored in the quiz_states table but instead in the 'sumpenalty' field of the quiz_newest_states table. That way it only has to be stored once per attempt rather than once per response.
 
===Where it is done in the code===
 
The function quiz_apply_penalty_and_timelimit() subtracts the penalty in $state->sumpenalty from the raw grade in $state->raw_grade to obtain $state->grade for the response. However it is ensured that the grade of a new attempt at the question never falls below the previously achieved grade. This function also increases $state->sumpenalty by the amount in $state->penalty. The assumption is that $state->penalty has just been set appropriately by the code calling this function, e.g., quiz_process_responses.
 
==Time limit==
 
A quiz can have a time limit. This is stored in minutes in $quiz->timelimit. So before using this in time calculations it always has to be multiplied by 60 to turn it into seconds like all other timestamps in moodle and php. If $quiz->timelimit is zero it means there is no timelimit.
 
If a student asks to start an attempt on view.php for a quiz with a timelimit then he is shown a javascript message alerting him to the timelimit and is asked to confirm.
 
For quizzes with timelimit attempt.php shows a javascript timer that counts down and automatically submits and closes the attempt when the time is up.
 
Confusingly there are two javascript timers in the quiz module. jsclock.php provides a countdown in the title bar that counts down to the quiz closing time if this is less than a day away. This has nothing to do with the timelimit. jstimer.php provides the countdown timer that implements the timelimit. It in turn uses timer.js.
 
The time a response was submitted by the student is recorded by attempt.php right at the top of the page and is then passed on to quiz_process_responses in $action->timestamp. This puts it into $state->timestamp. Finally, after the responses have been graded, the function quiz_apply_penalty_and_timelimit() checks that the responses are within the timelimit to within 5% and if not it sets the grade to zero (or the previously obtained grade, if that is higher).
 
==Pagination==
 
Quiz attempts can be paginated, i.e., spread over several pages. The student can navigate between the pages using the standard Moodle paging bar. When the student navigates to a different quiz page the answers on the current page are automatically submitted for saving (but not grading).
 
To do this automatic submission the paging bar needs some javascript. It is therefore not produced with Moodle's standard print_paging_bar() function from weblib.php but with quiz_print_navigation_panel() which is defined in mod/quiz/locallib.php and produces something that looks the same.
 
The teacher has complete control via the edit interface on edit.php over where the page breaks should occur. He can repaginate the quiz with any chosen number of questions per page. He can also move the page-breaks up and down using the arrows.
 
Internally page breaks are stored in the $quiz->questions field (which now should really be called $quiz->layout). This field contains a comma separated list of questionids and pagebreaks where the pagebreaks are represented by the id 0. For example 23,12,0,11, 0 means that the two questions with ids 23 and 12 are on the first page and the question with id 11 is on the second page. The last page break is invisible and Moodle sometimes puts it there itself for its own convenience.
 
Because the quiz has an option $quiz->shufflequestions to shuffle questions the layout that the student sees in a particular attempt does not necessarily have to be the same as that stored in $quiz->questions. Therefore each attempt has its own $attemp->layout field. If $quiz->shufflequestions is false then this just contains a copy of $quiz->questions but if it is true then during the creation of a new attempt by quiz_create_attempt() the function quiz_repaginate() is used to produce a layout with $quiz->questionsperpage number of questions per page that are randomly ordered.
 
Both attempt.php and review.php use the $attempt->layout field to determine what questions to show on a particular page. That way we can guarantee that the student will, for a particular attempt, always see the questions in the same order and with the same pagination, both while attempting and during review. Also a teacher when reviewing a student's attempt sees the pages the same way they were shown to the student. However the teacher is also given the option to see all questions on one page.
 
There are some functions in locallib.php dedicated to handling the layout fields: quiz_questions_on_page(), quiz_questions_in_quiz(), quiz_number_of_pages(), quiz_first_questionnumber(), quiz_repaginate(). They are very short functions. The function quiz_first_questionnumber() that determines the number of the first question on a particular page makes use of the $question->length field. To allow this calculation to be fast is the main reason why that field is in the question table even though it could also be determined easily from the question type.
 
 
==Question versioning==
 
'''Note:''' Question versioning is currently disabled until it is re-developed to fix all reported issues.
 
When questions that were already attempted by a student are edited, it can be important to keep a copy of the question as it was before editing in order to reconstruct the quiz as it was seen by the student. To provide this functionality a question versioning mechanism was implemented.
 
The first goal, namely keeping around old questions, is easily achieved. They are just not deleted any more. However, this is not enough; it is also necessary to store which questions are versions of others. To achieve this goal, there is an additional table, which stores the versioning information: quiz_question_versions.
 
When a question is replaced for which there are already student attempts then all the attempt data gets associated to the new version of the question and is re-graded. This requires the question ids in the quiz_attempts, quiz_states and quiz_newest_states tables to be replaced by the new id. However we do also want to be able to reconstruct the quiz the way the student saw it when he gave his answers. For that purpose the id of the original question is always preserved in the 'originalquestion' field of the quiz_states table.
 
If all old versions of questions are kept around this could horribly clutter the editing interface. Therefore a field called hidden was added to the quiz_questions table and all old versions of edited questions are automatically hidden. When this flag is set to 1 the question is not displayed in the list of available questions, unless the user chooses to show them.
 
While the mechanism above should work as described, there is some additional complexity in order to minimise the number of versions created. If a question is created and has not been attempted by a student yet (this excludes teacher previews of the individual question and the quiz!), the database record will be reused (i.e. overwritten) and no new version will be created. This is especially important when the question is created and the first 2 or 3 mistakes are only noticed during preview.
 
On the editing screen for questions an additional set of options was introduced (see image).
Replacement Options
It shows which quizzes use the edited question and how many students have attempted it in a particular quiz. Based on this information it is then possible to choose in which quizzes the new version of the question should be used and in which ones the old one should remain.
 
By default the 'replace' checkbox for all quizzes that don't have any students' attempts are checked and in addition, if the question is edited out of a quiz context (i.e. not in the category question list), the 'replace' option is checked for that quiz as well.
 
===Database===
 
The changes to the database structure are limited to an added field (hidden) in the quiz_questions table and an additional table called quiz_question_versions. However, dealing with the quiz_questions table has become slightly more complicated.
 
The hidden field in the quiz_questions table has no implications for the core functionality. It is only used to determine, as the name implies, whether the question is shown in the category list or not.
 
The table quiz_question_versions stores information about the actual change. This information includes the ids of the old question and the new question, the id of the user who did the change and a timestamp. Quite importantly, the id of the quiz, in which the question was replaced is also stored. This means that the versions table provides a history of the different states the quiz went through until it was edited to be at the current state. The information allows to recreate a quiz as it was at any point in time (from a data perspective - this possibility is not used extensively by the code).
 
===Adjustments to the Data===
 
When a question is replaced by a newer version, database records are updated in the order shown below (compare with question.php):
 
* First a new record is inserted into the quiz_question_versions table for each affected quiz (i.e. each quiz in which the question was replaced).
* Then, for each affected quiz, the comma separated list of question ids in the question field is updated by replacing the old question id with the new one.
* In the quiz_question_instances table the record that links the old question to the quiz is also updated to point to the new question.
* In all attempts belonging to the old question the comma-separated list of question ids in the layout field are changed by replacing the old id by the new one.
* All states belonging to the old question are made to belong to the new version by changing the id in the 'question' field. However if we are replacing the original question then the id of this original version is stored in the originalquestion field.
* We have to change the questionid field in quiz_newest_states.
* Finally we have to do any question-type specific changes. For example question types that store student responses by storing the id of the answer in the quiz_answers table will have to recode these ids in all the states to point to the corresponding answers in the new version. This is handled by the function replace_question_in_attempts() in the question type class.
 
===Affected Code and Functionality===
 
Note: This section should still be considered under construction until the question mark behind bug #3311 is taken off.
 
In the file review.php and potentially also in the file attempt.php, if a question is edited during a student's attempt, the data from quiz_question_versions needs to be taken into account. If a student has attempted a quiz and a question was changed afterwards (i.e. a new version of that question was created), the question id of the old version remains in the comma separated list inside the attempt->layout field. However, since the records in the quiz_question_instances table get updated, we need to go forward in the question history, by looping through entries from the quiz_question_versions table, to find out the id of the question version that is currently used in the quiz.
 
Suggestion: With a fairly simple change to the convention of what is stored in the quiz_question_versions table we could get rid of the requirement of looping through all the versions. If in the newquestion field we store the id of the question that is currently used in the quiz, it would be possible to get the complete history for a question quite simply by selecting by quiz id and newquestion.
 
It should be fairly simple to write an upgrade script for this change. Additionally, another set_field would need to be added to question.php to change the newquestion field to the new question id. The benefits would be a much simpler handling of the question history, resulting in more efficient code than the current fix for bug #3311 in review.php.
 
The place where all the versioning actually takes place is question.php. Here the changes described in Adjustments to the Data are carried out.
 
Obviously the backup and restore scripts also take quiz_question_versions into account, however, they don't need to be concerned with the ways the data is used.
 
 
==Future plans==
 
===Proposed new features===
 
The features below are in no particular order:
* New quiz statistics pages?: These pages should be built by using functions defined by the individual question types.
* Manual grade override: Teachers should be able to override the automatically calculated grades and should be able to make comments.
* Off-line questions: The answers to these are handed in off-line in the conventional way (e.g., on paper) and teachers enter marks on Moodle.
* Batch printing of quiz sheets: We want to be able to hand out question sheets to students so they can start working on the questions before going to the computer.
* Question preview from question edit page: so the teacher can try the question already before saving the changes.
* Show table of questions on view.php: gives teachers and students a bit of an overview of the quiz.
* Extending deadlines for individual students: for example when a student misses a deadline for good reasons.
* Filtering questions by quiz and by search: More ways to restrict which questions are shown on the quiz editing page.
* Re-open quizzes for revision: After the due date has passed the quiz could allow practice attempts.
 
===Completed===
 
The following developments have already been completed:
* Quiz results overview: suggestions for additional information and features on the overview page.
* Editing questions: This is about editing questions after students have already attempted them. There needs to be a mechanism to keep the old versions of the question around for auditing purposes.
* Reforming the quiz edit page: Changes on the quiz edit page should be saved straightaway, not only after Save button is pressed.
* Copying questions: The teacher can create a new question using a previous one as template.
* Moving questions: The teacher can now move selected questions to a different category.
* Automatic re-marking after question editing: if a teacher corrects a question that students have already attempted the teacher should be able to request a remark.
* Teacher preview: Teachers should have preview buttons for previewing a quiz.
* What students should see during review: The proposal is to replace the current options for this with an array of tickboxes.


* [[Goals of an online assessment system]] - a summary of what the quiz is trying to achieve, and the developments that might be done in the future.
* [[Question bank]]
* [[Question engine]] - also [[Question_Engine_2]], which needs to be merged into it. In particular, see [[Using the question engine from module]].
* [[Quiz Usability portal]] - docs from the project to re-design the editing interface in Moodle 2.1.
* [[Quiz support in the Mobile app]] - Quiz support in the Mobile app is one of the most requested features by users


[[Category:Quiz]]
[[Category:Quiz]]

Revision as of 13:44, 4 July 2016

The quiz is a complex module, although attempts have been made re-organise the code to keep things managable.

This page is only intended to give a high level overview. To really understand the quiz, you will have to look at the code, much of which should be clear and well commented.


What the quiz does and does not do

The quiz module uses the Question bank to manage the questions that teachers create, and the Question engine to display and process questions when students attempt them. You are particularly advised to read Using the question engine from module.

The quiz module itself is only responsible for

  1. letting teachers assemble questions into quizzes.
  2. controlling which students can attempt the quiz when.
  3. providing access so that students can review their past attempts and the feedback (if the teacher permits it).
  4. providing reports on the outcomes for teachers.

Displaying the results is delegated to quiz report sub-plugins. Note that some fairly core functionality is implemented in the reports. For example deleting and regrading attempts is handled by quiz_overview report, and bulk manual grading is handled by quiz_grading.

A lot of the details of when and if students can access the quiz are handled by quiz access rule sub-plugins.


Code overview

All the code has PHPdoc comments which provides a lot of detailed explanation. That is not repeated here.

This is a standard Moodle activity module, so inside mod/quiz there are all the things you would expect to see like db/, lib.php, locallib.php, view.php, version.php and so on.

The scripts that are directly accessed by the user are listed on Quiz user interface overview, and I will not list them again here. It would probably also help to look at the Quiz database structure before trying to understand the back-end code.

The back-end code is organised thus:

lib.php
All the functions that are called by the Moodle core. For performance reasons it is important that this does not include any other files.
locallib.php
This contains all the other quiz library functions that do not have a more specific home. Including this file also includes all the other quiz libraries that you might need.
mod_form.php
The module settings form, as you would expect for any Moodle activity module.
editlib.php
This defines the functions that are used when at teacher edits the quiz. Thus, they are mostly called from edit.php.
attemptlib.php
This defines the quiz and quiz_attempt classes. These are all about what happens when a student (or other user) is looking at the quiz, so they provide a personalised view of the quiz data from the point of view of that user. Thus these classes are mainly used by the view.php, startattempt.php, attempt.php, processattempt.php, summary.php and review.php scripts.
accessmanager.php
This provides the interface point that the rest of the quiz code uses to access the quiz access rule sub-plugins.
renderer.php
the quiz uses the Moodle 2.x renderer system to generate the HTML for all the student-visible parts of the UI. This means that theme designer have a lot of freedom as to how the quiz is displayed to students. Perhaps one day the editing UI will also be re-factored. As is standard for a Moodle plug-in, this file defines the renderer class.
settings.php & settingslib.php
define the admin settings for the quiz.
accessmanager_form.php, addrandomform.php & override_form.php
Used by accessmanager.php, addrandom.php and overridedit.php respectively.
module.js
JavaScript used by attempt.php and to a lesser extent view.php, summary.php and review.php.
edit.js
JavaScript used by edit.php.


Quiz navigation fake block

During the quiz attempt (so, on attempt.php, summary,php (from Moodle 2.2 onwards) and review.php) we display a navigation UI that looks like a block. This uses the block_manager::add_fake_block feature.

The block contents is produce by classes in attemptlib.php working with methods in the renderer. The navigation relies on some JavaScript in module.js because it is vitally important that every time the user moves from one page of the quiz attempt to another, we save their answers. The javascript turns click on the navigation links into form submissions.


Quiz layout

Information about the structure of a quiz is stored in the quiz.qestions column and the quiz_question_instances table. The layout is a comma-separated list of question ids, with 0s to represent page-breaks.

There are methods in locallib.php and editlib.php to manipulate this structure.

When a quiz attempt is stared, the questions are added to a question_usage_by_activity object ($quba) that is managed by the question bank. The $quba indexes things by 'slot' rather than question.id, so the quiz layout gets rewritten and stored in quiz_attempts.layout. This is done in startattempt.php.

One of the options is to randomise the order of the question in the quiz. This is done by shuffling quiz_attempts.layout.

startattempt.php also handles selecting a real question to go in each place where the teacher has added a 'Random question from category X' to the quiz.


See also