<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>http://wiki.mipt.ru/index.php?action=history&amp;feed=atom&amp;title=Development%3AUsing_the_question_engine_from_module</id>
	<title>Development:Using the question engine from module - История изменений</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.mipt.ru/index.php?action=history&amp;feed=atom&amp;title=Development%3AUsing_the_question_engine_from_module"/>
	<link rel="alternate" type="text/html" href="http://wiki.mipt.ru/index.php?title=Development:Using_the_question_engine_from_module&amp;action=history"/>
	<updated>2026-05-06T20:44:02Z</updated>
	<subtitle>История изменений этой страницы в вики</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>http://wiki.mipt.ru/index.php?title=Development:Using_the_question_engine_from_module&amp;diff=12098&amp;oldid=prev</id>
		<title>Олег Давидович: 1 версия импортирована</title>
		<link rel="alternate" type="text/html" href="http://wiki.mipt.ru/index.php?title=Development:Using_the_question_engine_from_module&amp;diff=12098&amp;oldid=prev"/>
		<updated>2024-10-21T08:53:24Z</updated>

		<summary type="html">&lt;p&gt;1 версия импортирована&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ru&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Предыдущая версия&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Версия от 08:53, 21 октября 2024&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;ru&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(нет различий)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Олег Давидович</name></author>
	</entry>
	<entry>
		<id>http://wiki.mipt.ru/index.php?title=Development:Using_the_question_engine_from_module&amp;diff=12097&amp;oldid=prev</id>
		<title>1&gt;TimHunt в 06:58, 1 мая 2011</title>
		<link rel="alternate" type="text/html" href="http://wiki.mipt.ru/index.php?title=Development:Using_the_question_engine_from_module&amp;diff=12097&amp;oldid=prev"/>
		<updated>2011-05-01T06:58:07Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Template:Question_engine_2}}&lt;br /&gt;
This page explains how to use the new Moodle [[Development:Question Engine 2|question engine]] in an activity module you are developing.&lt;br /&gt;
&lt;br /&gt;
Previous section: [[Development:Developing_a_Question_Type|Developing a Question Type]]&lt;br /&gt;
&lt;br /&gt;
The first example of a module that uses the question engine is the quiz module. Looking at how the quiz code works will let you see a real working example of what explained on this page. Another, much simpler example to look at is &amp;lt;tt&amp;gt;question/preview.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Note that all the question engine code has extensive PHP documentor comments that should explain the purpose of every class and method. This document is supposed to provide an overview of the key points to get you started. It does not attempt to duplicate all the details in the PHPdocs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The key class you will be working with is &amp;lt;tt&amp;gt;question_usage_by_activity&amp;lt;/tt&amp;gt;. This tracks an attempt at some questions, for example a quiz attempt. The usage should provide all the interface methods you need to do things with the question engine. You should rarely need to dive deeper into the inner workings of the question engine than this.&lt;br /&gt;
&lt;br /&gt;
A usage is a collection of &amp;lt;tt&amp;gt;question_attempt&amp;lt;/tt&amp;gt; objects. A question attempt represents the state of the user&amp;#039;s attempt at one question within the usage. When you add a question to the usage, you get back a &amp;#039;&amp;#039;&amp;#039;slot&amp;#039;&amp;#039;&amp;#039; number. This serves to index that question within the attempt. Using slot numbers like this means that the same question can be added more than once to the same usage. A lot of the usage API calls take this slot number to indicate which question attempt to operate on. Other API methods let you perform batch actions on all the question attempts within the usage.&lt;br /&gt;
&lt;br /&gt;
==Starting an attempt at some questions==&lt;br /&gt;
&lt;br /&gt;
The example code in this section all came from &amp;lt;tt&amp;gt;mod/quiz/startattempt.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Creating the usage===&lt;br /&gt;
&lt;br /&gt;
To create a new usage, ask the question engine:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$quba = question_engine::make_questions_usage_by_activity(&amp;#039;mod_quiz&amp;#039;, $quizobj-&amp;gt;get_context());&lt;br /&gt;
$quba-&amp;gt;set_preferred_behaviour($quiz-&amp;gt;preferredbehaviour);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You will see that the constructor takes the plugin name (using the Moodle 2.0 &amp;#039;frankenstyle&amp;#039; naming convention) and the context that owns the usage. Usages should be cleaned up automatically when a context is deleted or a plugin un-installed. You also have to tell the usage which behaviour should be used when creating the attempt at each question.&lt;br /&gt;
&lt;br /&gt;
===Adding questions===&lt;br /&gt;
&lt;br /&gt;
Having got a usage, you will probably then want to add some questions to it. The question data you loaded from the database (probably using the &amp;lt;tt&amp;gt;get_question_options&amp;lt;/tt&amp;gt; function) come in a form suitable for data handling like import/export, backup/restore, and so on. We need to convert that to an &amp;lt;tt&amp;gt;question_definition&amp;lt;/tt&amp;gt; object. That can be done using the &amp;lt;tt&amp;gt;question_bank::make_question&amp;lt;/tt&amp;gt; method. Alternatively, you could just use the &amp;lt;tt&amp;gt;question_bank::load_question&amp;lt;/tt&amp;gt; method, but that can only load one question at a time.&lt;br /&gt;
&lt;br /&gt;
Once you have a &amp;lt;tt&amp;gt;question_definition&amp;lt;/tt&amp;gt; object, you add it to the usage by calling the &amp;lt;tt&amp;gt;add_question&amp;lt;/tt&amp;gt; method passing the question definition, and also the score you want this question marked out of. You get back the slot number that has been assigned to this question. The slot numbers are allocated in order, starting from 1. You can rely on that numbering convention. For example, the quiz module uses the slot numbers to make sure the grade for each question lines up properly in the reports.&lt;br /&gt;
&lt;br /&gt;
Here is the applicable code from the quiz module&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
foreach ($quizobj-&amp;gt;get_questions() as $i =&amp;gt; $questiondata) {&lt;br /&gt;
    $question = question_bank::make_question($questiondata);&lt;br /&gt;
    $idstoslots[$i] = $quba-&amp;gt;add_question($question, $questiondata-&amp;gt;maxmark);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Starting the question attempts===&lt;br /&gt;
&lt;br /&gt;
The usage does not do much when the question is added. Before the user can start interacting with the question, you have to start it. You can either do that in bulk:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$quba-&amp;gt;start_all_questions();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
or you can start just one particular question using the &amp;lt;tt&amp;gt;start_question($slot)&amp;lt;/tt&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
===Saving the usage===&lt;br /&gt;
&lt;br /&gt;
So far, all these method calls have just built up a data structure in memory. If you want to keep it, you have to save it in the database. Fortunately, that is easy:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
question_engine::save_questions_usage_by_activity($quba);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The usage is stored as a row in the &amp;lt;tt&amp;gt;question_usages&amp;lt;/tt&amp;gt; table (plus rows in other tables). To get the id of the usage, call &amp;lt;tt&amp;gt;$quba-&amp;gt;get_id()&amp;lt;/tt&amp;gt; after you have saved it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Displaying questions==&lt;br /&gt;
&lt;br /&gt;
Suppose that you wish to display a page that contains several questions from the usage. The questions to display will be the ones whose slot numbers are in an array called &amp;lt;tt&amp;gt;$slotsonpage&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The example code in this section is derived from code used by &amp;lt;tt&amp;gt;mod/quiz/attempt.php&amp;lt;/tt&amp;gt;. However, the quiz code is split between &amp;lt;tt&amp;gt;mod/quiz/attempt.php&amp;lt;/tt&amp;gt; and the &amp;lt;tt&amp;gt;quiz_attempt&amp;lt;/tt&amp;gt; class in &amp;lt;tt&amp;gt;mod/quiz/attemptlib.php&amp;lt;/tt&amp;gt;. That separation means that if I used the real quiz code in this tutorial, it would be unnecessarily hard to understand. Instead, I have simplified the examples below. If you are interested, you should be able to match up what you see below with the real quiz code.&lt;br /&gt;
&lt;br /&gt;
===Loading the usage===&lt;br /&gt;
&lt;br /&gt;
At the end of the last section, you had just saved the usage to the database. To display the questions as part of a different script, they must be loaded from the database again. Once again you ask the question engine to do this:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$quba = question_engine::load_questions_usage_by_activity($usageid);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The id that you pass to this method is the id you got from &amp;lt;tt&amp;gt;$quba-&amp;gt;get_id()&amp;lt;/tt&amp;gt; after saving the usage.&lt;br /&gt;
&lt;br /&gt;
===Before print_header===&lt;br /&gt;
&lt;br /&gt;
One subtlety is that different question types may rely on JavaScript and CSS to work. In order for this, particularly the links to the CSS, to be included in the page you are outputting, you must call the &amp;lt;tt&amp;gt;render_question_head_html&amp;lt;/tt&amp;gt; method for each question you will be outputting on this page. You need to collect all the HTML returned, and also the return value from &amp;lt;tt&amp;gt;question_engine::initialise_js()&amp;lt;/tt&amp;gt; method, and pass all that as the $meta parameter to print_header.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$meta = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach ($slotsonpage as $slot) {&lt;br /&gt;
    $meta .= $quba-&amp;gt;render_question_head_html($slot);&lt;br /&gt;
}&lt;br /&gt;
$meta .= question_engine::initialise_js();&lt;br /&gt;
print_header(/* ... lots of args ... */, $meta);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===The question form===&lt;br /&gt;
&lt;br /&gt;
Within the page, all the questions have to be wrapped in a HTML &amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;form&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/tt&amp;gt; tag. This form needs to POST to a scripts that processes the submitted data ([[#Processing_responses|see below]]).&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
echo &amp;#039;&amp;lt;form id=&amp;quot;responseform&amp;quot; method=&amp;quot;post&amp;quot; action=&amp;quot;&amp;#039; . $processurl .&lt;br /&gt;
        &amp;#039;&amp;quot; enctype=&amp;quot;multipart/form-data&amp;quot; accept-charset=&amp;quot;utf-8&amp;quot;&amp;gt;&amp;#039;, &amp;quot;\n&amp;lt;div&amp;gt;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
$PAGE-&amp;gt;requires-&amp;gt;js_init_call(&amp;#039;M.core_question_engine.init_form&amp;#039;,&lt;br /&gt;
        array(&amp;#039;#responseform&amp;#039;), false, &amp;#039;core_question_engine&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
echo &amp;#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;slots&amp;quot; value=&amp;quot;&amp;#039; . implode(&amp;#039;,&amp;#039;, $slotsonpage) . &amp;quot;\&amp;quot; /&amp;gt;\n&amp;quot;;&lt;br /&gt;
echo &amp;#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;scrollpos&amp;quot; value=&amp;quot;&amp;quot; /&amp;gt;&amp;#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;enctype&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;accept-charset&amp;lt;/tt&amp;gt; parameters are important. Please copy them. The form must have an id, and you must then write some JavaScript that passes that id to to the &amp;lt;tt&amp;gt;question_init_form&amp;lt;/tt&amp;gt; function. That function is defined in &amp;lt;tt&amp;gt;question/qengine.js&amp;lt;/tt&amp;gt; if you want to find out what it does and why it is important.&lt;br /&gt;
&lt;br /&gt;
You should output a hidden form field &amp;lt;tt&amp;gt;slots&amp;lt;/tt&amp;gt; that contains a comma-separated list of the slot numbers that appear on this page. That is not absolutely necessary, but it will make processing the form submission much more efficient.&lt;br /&gt;
&lt;br /&gt;
Another nicety is the &amp;lt;tt&amp;gt;scrollpos&amp;lt;/tt&amp;gt; hidden field. Some question behaviours, for example the interactive behaviour, have submit buttons that do something to the question. When the user clicks this button, it is nice if when the page re-displays, it is scrolled to exactly the same place where it was. The question engine has JavaScript which, if it finds a &amp;lt;tt&amp;gt;scrollpos&amp;lt;/tt&amp;gt; hidden field, will automatically save the scroll position to it when a button is clicked, and cause the page to scroll to that position when it reloads.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Outputting the questions===&lt;br /&gt;
&lt;br /&gt;
Finally, you can actually print the questions. Well, there is one more thing to take care of first. To control how the question appears, you must prepare a &amp;lt;tt&amp;gt;question_display_options&amp;lt;/tt&amp;gt; object that describes what should be visible. For example:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$options = new question_display_options();&lt;br /&gt;
$options-&amp;gt;marks = question_display_options::MAX_ONLY;&lt;br /&gt;
$options-&amp;gt;markdp = 2; // Display marks to 2 decimal places.&lt;br /&gt;
$options-&amp;gt;feedback = question_display_options::VISIBLE;&lt;br /&gt;
$options-&amp;gt;generalfeedback = question_display_options::HIDDEN;&lt;br /&gt;
// etc.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(In the quiz code, the display options actually come from the quiz settings.) Then you really are ready to output the questions&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
foreach ($slotsonpage as $displaynumber =&amp;gt; $slot) {&lt;br /&gt;
    echo $quba-&amp;gt;render_question($slot, $options, $displaynumber);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here &amp;lt;tt&amp;gt;$displaynumber&amp;lt;/tt&amp;gt; is the &amp;#039;question number&amp;#039; you want printed next to each question. This is not necessarily the same as the slot number. For example, in the quiz, Description &amp;#039;questions&amp;#039; are not numbered, although they take up a slot. If you don&amp;#039;t pass in a number to display, then the question is just printed without one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Processing responses==&lt;br /&gt;
&lt;br /&gt;
Once again, the example here are inspired by real code from the quiz module, in this case &amp;lt;tt&amp;gt;mod/quiz/processattempt.php&amp;lt;/tt&amp;gt;, but simplified to remove mention of the &amp;lt;tt&amp;gt;quiz_attempt&amp;lt;/tt&amp;gt; class.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===The simple case===&lt;br /&gt;
&lt;br /&gt;
Normally, you just need to do something like:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$timenow = time();&lt;br /&gt;
$transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
&lt;br /&gt;
$quba = question_engine::load_questions_usage_by_activity($usageid);&lt;br /&gt;
$quba-&amp;gt;process_all_actions($timenow);&lt;br /&gt;
question_engine::save_questions_usage_by_activity($quba);&lt;br /&gt;
&lt;br /&gt;
$transaction-&amp;gt;allow_commit();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Behind the scenes there is, of course, a lot going on, including the use of the &amp;lt;tt&amp;gt;slots&amp;lt;/tt&amp;gt; parameter to control which questions are processed.&lt;br /&gt;
&lt;br /&gt;
There may also be additional work you need to do. For example, in the quiz (before the &amp;lt;tt&amp;gt;commit_sql()&amp;lt;/tt&amp;gt; line) there is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($attempt-&amp;gt;timefinish) {&lt;br /&gt;
    $attempt-&amp;gt;sumgrades = $quba-&amp;gt;get_total_mark();&lt;br /&gt;
}&lt;br /&gt;
$attempt-&amp;gt;timemodified = $timenow;&lt;br /&gt;
$DB-&amp;gt;update_record(&amp;#039;quiz_attempts&amp;#039;, $attempt);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===More selective processing===&lt;br /&gt;
&lt;br /&gt;
If you want something other than the default processing, you can use code like &amp;lt;tt&amp;gt;$submitteddata = $quba-&amp;gt;extract_responses($slot); $quba-&amp;gt;process_action($slot, $submitteddata);&amp;lt;/tt&amp;gt; or something similar.&lt;br /&gt;
&lt;br /&gt;
===Handling scrollpos===&lt;br /&gt;
&lt;br /&gt;
You will remember that when we printed the questions, we created a &amp;lt;tt&amp;gt;scrollpos&amp;lt;/tt&amp;gt; hidden field. Presumably, after you have processed the submission, you will redirect the user back to a page to display the questions again. In that case, you need to get the &amp;lt;tt&amp;gt;scrollpos&amp;lt;/tt&amp;gt; parameter, and add it on to the URL you redirect to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// With the other required/optional_param calls at the start of processattempt.php.&lt;br /&gt;
$scrollpos = optional_param(&amp;#039;scrollpos&amp;#039;, &amp;#039;&amp;#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
// ... a bit later in the file&lt;br /&gt;
$nexturl = /* something */;&lt;br /&gt;
if ($scrollpos !== &amp;#039;&amp;#039;) {&lt;br /&gt;
    $nexturl .= &amp;#039;&amp;amp;scrollpos=&amp;#039; . ((int) $scrollpos);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... after all the processing is done.&lt;br /&gt;
redirect($nexturl);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Reports==&lt;br /&gt;
&lt;br /&gt;
Places like the quiz reports, we need to get data about a lot of usages at the same time. There are methods on the &amp;lt;tt&amp;gt;question_engine_data_mapper&amp;lt;/tt&amp;gt; to do this. You should never access the data in the question engine tables directly.&lt;br /&gt;
&lt;br /&gt;
For example from the quiz grades report. (Again the example code has been simplified.)&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// $qubaids is an array of integer ids.&lt;br /&gt;
$qubaidscondition = new qubaid_list($qubaids);&lt;br /&gt;
&lt;br /&gt;
$dm = new question_engine_data_mapper();&lt;br /&gt;
$latesstepdata = $dm-&amp;gt;load_questions_usages_latest_steps(&lt;br /&gt;
        $qubaidscondition, $slots);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;qubaid_list&amp;lt;/tt&amp;gt; is a subclass of the &amp;lt;tt&amp;gt;qubaid_condition&amp;lt;/tt&amp;gt; class. This is an efficient way to represent a set of usage ids in a way that can be used in database queries. The other main subclass is &amp;lt;tt&amp;gt;qubaid_join&amp;lt;/tt&amp;gt;. That could be used like this:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$qubaidscondition = new qubaid_join(&amp;#039;{quiz_attempts} quiza&amp;#039;, &amp;#039;quiza.uniqueid&amp;#039;,&lt;br /&gt;
        &amp;#039;quiza.quiz = :quizaquiz&amp;#039;, array(&amp;#039;quizaquiz&amp;#039; =&amp;gt; $quizid))&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another example, from the quiz manual grading report is&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$dm = new question_engine_data_mapper();&lt;br /&gt;
$statesummary = $dm-&amp;gt;load_questions_usages_question_state_summary(&lt;br /&gt;
        $qubaidscondition, $slots);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Perhaps the use of the class &amp;lt;tt&amp;gt;question_engine_data_mapper&amp;lt;/tt&amp;gt; should be hidden inside a helper class &amp;lt;tt&amp;gt;question_engine_reporter&amp;lt;/tt&amp;gt;?)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Other methods==&lt;br /&gt;
&lt;br /&gt;
===Methods to help with settings UI===&lt;br /&gt;
&lt;br /&gt;
When you want to present users with a choice of question behaviour (for example, the quiz How questions behave option) use&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$options = question_engine::get_behaviour_options();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Question marks are stored in the database to a fixed number of decimal places. To get a list of possible decimal place display options, use&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$options = question_engine::get_dp_options();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Clean-up methods===&lt;br /&gt;
&lt;br /&gt;
To delete usages you no longer need:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// A single usage ...&lt;br /&gt;
question_engine::delete_questions_usage_by_activity($qubaid);&lt;br /&gt;
&lt;br /&gt;
// ... or many.&lt;br /&gt;
question_engine::delete_questions_usage_by_activity(&lt;br /&gt;
        new qubaid_join(&amp;#039;{quiz_attempts} quiza&amp;#039;, &amp;#039;quiza.uniqueid&amp;#039;,&lt;br /&gt;
        &amp;#039;quiza.quiz = :quizaquiz&amp;#039;, array(&amp;#039;quizaquiz&amp;#039; =&amp;gt; $quizid)));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Changing the score of a question in all attempts===&lt;br /&gt;
&lt;br /&gt;
Suppose you change the number of marks awarded to question 2 in the quiz, you need to change that information in every attempt at that quiz, you can do that with a call like&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
question_engine::set_max_mark_in_attempts($qubaids, $slot, $newmaxmark);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Re-grading a question===&lt;br /&gt;
&lt;br /&gt;
You only need to re-grade a question when the question definition id edited, for example to change the scoring rules. If you are just changing the maximum possible score for a question, see the previous sub-section.&lt;br /&gt;
&lt;br /&gt;
This code example is adapted from &amp;lt;tt&amp;gt;mod/quiz/report/overview/report.php&amp;lt;/tt&amp;gt;. It re-grades selected slots within a quiz attempt.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
$quba = question_engine::load_questions_usage_by_activity($attempt-&amp;gt;uniqueid);&lt;br /&gt;
&lt;br /&gt;
foreach ($slots as $slot) {&lt;br /&gt;
    $oldfraction[$slot] = $quba-&amp;gt;get_question_fraction($slot);&lt;br /&gt;
    $quba-&amp;gt;regrade_question($slot);&lt;br /&gt;
    $newfraction[$slot] = $quba-&amp;gt;get_question_fraction($slot);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
question_engine::save_questions_usage_by_activity($quba);&lt;br /&gt;
$attempt-&amp;gt;sumgrades = $quba-&amp;gt;get_total_mark();&lt;br /&gt;
update_record(&amp;#039;quiz_attempts&amp;#039;, $attempt);&lt;br /&gt;
$transaction-&amp;gt;allow_commit();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
In the next section, [[Development:Question Engine 2:Implementation plan|Implementation plan]] outlines how I will implement this proposal.&lt;br /&gt;
&lt;br /&gt;
* The PHP documenter comments that explain the purposes of every method in the question engine code.&lt;br /&gt;
* Back to [[Development:Question_Engine_2|Question Engine 2]]&lt;/div&gt;</summary>
		<author><name>1&gt;TimHunt</name></author>
	</entry>
</feed>