Note: You are currently viewing documentation for Moodle 2.9. Up-to-date documentation for the latest stable version of Moodle may be available here: Quiz code methods.

Development:Quiz code methods

From MoodleDocs
(Redirected from Quiz conversion)

Guidelines for converting existing questiontypes

In order to work in the rewritten quiz module in Moodle 1.5, question types have to provide a range of standard methods. Many of them are new, others are modifications of those that existed in the old quiz module. Unfortunately, converting an existing question type to work under Moodle 1.5 does require a lot of work.

In the new model of the quiz module, there are a number of key objects, mainly the question and state objects. As described in the document describing the question and state objects, these objects have some fields that directly correspond to the database and others which are only added at runtime. This page describes the standard methods for handling these objects.

Essential methods

In order to guarantee the presence of the runtime fields, there are a number of questiontype specific functions that likely need to be overwritten. Therefore converting these methods is the first step in the conversion of a questiontype. Note that generally the arguments are passed by reference in the new code. This means that all changes made to the objects inside the methods will directly affect the passed arguments and return values usually indicate success or failure.

  • get_question_options(): This method is responsible for loading any questiontype specific data into the runtime question object. It is recommended that the additional data is stored in the options field of the question object. This method also replaces the get_answers() method. Most questiontypes need to overwrite this method.
  • save_question_options(): Every questiontype needs to define this method in order to save specific data to the type specific tables. Generally it is not necessary to modify this method when converting question types.
  • print_question_formulation_and_controls(): The arguments for this method have changed, so it needs to be adapted to the new set of arguments.
  • grade_responses(): Formerly this method was called grade_response(). The arguments have changed,so this method needs to be adapted to the new set of arguments. Also the return value has changed.

Methods to handle question states (aka question session), including previous responses

These methods need to be overwritten by questiontypes that store their responses and other state data in their own tables or that serialize their answers into the $state->answer field.

  • create_session_and_responses(): Initializes the necessary fields in the $state object, using default values.
  • restore_session_and_responses(): Reads the data from the database and maps it to the type specific fields in the state object.
  • save_session_and_responses(): Reads the fields from the state object and writes it to the appropriate database fields/table.

Other interesting methods

  • apply_penalty(): Allows to give penalties depending on the previously graded state.
  • compare_responses(): Allows to specify a custom comparison of two state objects. The default implementation does a simle comparison between the responses fields of each state object. It is possible, however to determine equivalence in entirely different ways and even to take the last graded state into account, which may be desirable for adaptive items.

Detailed description of data handling methods

1. get_question_options(&$question)

The get_question_options method gets data from any questiontype specific tables. Such tables can be, but are not limited to, the existing questiontype specific tables.

Relevant database queries should be executed and the relevant data stored in the options field of the question object. The structure of the data within the options field can be arbitrarily chosen, as it is only used by other questiontype specific functions, which of course need to be made aware of it.

Questiontypes that used to overwrite the get_answers() method will wish to add a ->options->answers field, to contain that data.

A simple example is the relevant implementation for the multiple choice questiontype:

    function get_question_options(&$question) {
        // Get additional information from database
        // and attach it to the question object
        if (!$question->options = get_record('quiz_multichoice', 'question', $question->id)) {
            notify('Error: Missing question options!');
            return false;
        }
        if (!$question->options->answers = get_records('quiz_answers', 'question',
         $question->id)) {
           notify('Error: Missing question answers!');
           return false;
        }
        return true;
    }

2. save_question_options($questions)

Fortunately the save_options method should generally not need to be altered.

3. create_session_and_responses(&$question, &$state, $quiz, $attempt) (optional)

Strictly speaking, this method does not get any type specific data, however, it sets the responses and other type specific fields to their default values. This method is called, when there is no previous state in the database.

4. restore_session_and_responses(&$question, &$state) (optional)

The responsibility of this method is to set up the states object, i.e. assign a value to the responses field and any other questiontype specific state information (e.g. for adaptive items).

By default $states->responses contains the value of the answer field in the quiz_states table (formerly quiz_responses). In the case where that field is used for the one and only response, nothing needs to be done (which is the default). In the case where data is stored in this field in a serialised form, this is the place to deserialise it and attach it in an appropriate format to the responses field. If a response has multiple components, it is recommended that each component is a named element in the responses array. E.g. if there was a questiontype that asks to specify coordinates in a two-dimensional grid, this response could be represented as an associative array with indices x and y.

More complex question types can also store their own state information in a dedicated table, referenced with the state id of the generic quiz_question_states table, thus allowing maximum flexibility. They can also store the responses in their own table in which case they should ignore the original value of $states->responses and create an associative array using the data from their table.

5. save_session_and_responses(&$question, &$state) (optional)

This method complements the restore_session_and_responses method. I.e. it does the opposite: takes the data structure from the responses field and stores it in the database in a type specific way. It also saves any type specific information from the options field.

Question types which use the old answer field of the quiz_states table and do not create an array of values from it in restore_session_and_responses do not need to do anything; the value of the responses field is automatically saved into the legacy answer field in that case.

Question types which use the old answer field but deserialize it into an array should serialize the responses (and optionally any other data) into a string and use set_field to write it to the database.

Question types which do not use the legacy field should write the data from the array of responses into their own table.

Methods to do questiontype specific processing

Finally there are some methods, which do questiontype specific processing. Firstly there is the method that does the actual printing of most of the question, print_question_formulation_and_controls. This method already existed before the changes, however, the argument list has changed. While the changes are relatively straight forward, care needs to be taken that all occurrences of the old arguments are replaced with their new equivalents.

1. print_question_formulation_and_controls(&$question, &$state, $index, $quiz, $options)

This function has changed its arguments. It used to be print_question_formulation_and_controls($question, $quiz, $readonly, $answers, $correctanswers, $nameprefix).

To simplify the process of conversion it is possible to re-initialize some of the original arguments at the beginning of the method.

  • $readonly = empty($options->readonly) ?  : 'disabled="disabled"';
  • $answers = $question->options->answers;
  • $correctanswers = get_correct_responses($question, $state);


  • $nameprefix = $question->name_prefix

If the input elements printed are only named with the nameprefix, the submitted value will later be contained in the $state->responses field as a string. If there are identifiers after the nameprefix (e.g. the answer id), the $state->responses field will contain an array indexed by these identifiers.

Note that the question type MUST use the correct names for the storage model chosen when writing restore_session_and_responses and friends. In particular it is not allowed to use a suffix for some element(s) and not for another (the code assumes one system or the other and overwrites the responses field without regard to whether it received other HTTP POST variables for the same question).

Note also that the name prefix is automatically removed so the responses array is indexed by the suffixes only; the question type should never need to worry about the name prefix except in this method.

Like with the old code this method is called by the print_question method (which has also changed its arguments). Very few question types will override print_question but the assignments above will also apply for this method.

2. grade_responses(&$question, &$state, $quiz)

The method grade_response was renamed to grade_responses and its argument list has changed. Therefore it needs to be adapted to the new code. While the logic for grading is likely to stay more or less the same, it is necessary to re-implement this method entirely. If the recommended compare_responses() method is used, the bullets below should help:

  • Find out which part of the logic is used to determine equivalence of different responses and move it to compare_responses(). Change it so that it returns true for equivalent answers and false otherwise.
  • Rewrite the remaining logic (usually a loop to test against different pre-defined answers). To do this the $state object is cloned into a $teststate and then the responses are overwritten with the responses (or answers) from the database before comparing them with the compare_responses() method.
  • grade_responses() does not assign the ->grade field any more. Instead it assigns ->raw_grade and ->penalty, which are later used by the function quiz_apply_penalty() to calculate the ->grade.

A questiontype is free, however, to deviate from the outlined structure. This may be appropriate, if an external system or some clever algorithm is used for marking responses.

3.apply_penalty(&$question, &$state, $quiz) (optional)

The penalty is a value between 0 and 1, where 0 is no penalty and 1 is 100% penalty. It is applied by the function quiz_apply_penalty(). There are three differnet quiz options:

  • No penalty: does what it says, it ignores any penalties.
  • Subtractive penalty: subtracts marks from the raw grade according to the formula: grade = raw_grade - (maxgrade * penalty)
  • Multiplicative penalty: scales the raw grade down according to the formula: grade = raw_grade * (1 - penalty)

The default implementation sets the penalty to 1 after marking the question for the first time, i.e. it only takes the first response into account and assigns no marks to any subsequent attempt at a question.

To overwrite this method the $state->penalty field needs to be set to a value between 0 and 1 and return true for success, false for failure.

4. compare_responses(&$question, $state, $teststate)

In order to determine whether two responses are equivalent it may not be appropriate only to check for equality. This method allows for more sophisticated checks to be included into a questiontype. It is called from the default implementation of grade_responses to find out whether to award marks or not. The return values are true for equivalence and false otherwise.

See also

Quiz plugins