Development:Cloze question type: Difference between revisions
Line 98: | Line 98: | ||
} | } | ||
The code flow of the question_edit_QTYPES_USEABLE_BY_MULTIANSWER[$qtype->name()]_form are described in | |||
[[Development:question_edit_QTYPES_USEABLE_BY_MULTIANSWER_form_functions]] | |||
to be continued [[User:Pierre Pichet|Pierre Pichet]] 17:13, 14 November 2007 (CST)for correct answer | to be continued [[User:Pierre Pichet|Pierre Pichet]] 17:13, 14 November 2007 (CST)for correct answer |
Revision as of 08:39, 15 November 2007
Template:Questiontype developer docs
Template:Multianswer (Cloze) editing interface
The code for the multianswer cloze question type is in the directory question/type/multianswer.
The name of the questiontype class is class embedded_cloze_qtype extends default_questiontype
.
Cloze question creation
A cloze question is a container for several subquestions. So when you create a simple question like
America was discovered in {1:NUMERICAL:=1492:0.1#Feedback} by {1:shortanswer;=Columbus#OK} who was {1:MULTICHOICE:=italian#OK~spanish#Wrong~portugese#Wrong}
Cloze question creation
on saving the question text is analyzed by function qtype_multianswer_extract_question($text)
which extract the three subquestions
for ($positionkey=1
; preg_match('/'.ANSWER_REGEX.'/', $question->questiontext, $answerregs)
; ++$positionkey ) {
$wrapped = new stdClass;
....
// stored them in
$question->options->questions[$positionkey] = clone($wrapped);
and replace the sub question i.e. {1:NUMERICAL:=1492:1#Feedback} by
{#$positionkey} i.e.{#1} {#2} {#3} successively.
the original definition i.e. {1:NUMERICAL:=1492:1#Feedback} being stored in
$wrapped->questiontext= = $answerregs[0];
In the multianswer->questiontext the question stored is
America was discovered in {#1}
by {#2}
who was {#3}
On saving the question the $question->options->questions are stored using the functions specific of their qtypes (short,numerical,multichoice).
$wrapped = $QTYPES[$wrapped->qtype]->save_question($wrapped,
$wrapped, $question->course);
Notice that the subquestions are identified in the database by setting their parent field with the main cloze question id.
Cloze question edition
When you edited a question the process is the reversed.
The get_question_options get the subanswers after retrieving them
// Get relevant data indexed by positionkey from the multianswers table
if (!$sequence = get_field('question_multianswer', 'sequence', 'question', $question->id)) {
notify('Error: Cloze question '.$question->id.' is missing question options!');
return false;
}
$wrappedquestions = get_records_list('question', 'id', $sequence, 'id ASC');
As the original text defining a given subquestion was stored in the subquestion->questiontext, it is put back in the main multianswer cloze question->questiontext using the {#1},{#2}etc that where left as place marker.
A new editing interface
The existing subquestion storage can be the main structure of a new interface where the user will put in the question text the the {#1},{#2}etc as place marker and fill the subquestions parameters ( answer, feedback etc.) using the same interface that are used when editing the various qtypes (shortanswer, numerical and multichoice)
.
Furthermore this could lead to an open structure allowing plug-ins to be included as long as they fullfill conditions to be defined
Main design strategy
Backward compatibility
The old question can be edited either in the the new one or old interface or
(as 14 November 2007 the first second is true and the last on the to-dolist.
However as the new one allow more parameters the conversion from old struture to new is a one way process.
The integration of other qtypes
THE FUNCTIONS SHOULD BE PUT IN THE edit_multianswer_form.php and should use functions in the specific edit_qtype_form.php. This means that edit_multianswer_form should be abe to create the desired edit_qtype_form without creating the corresponding active form.
A closer look at the code show that the edit_qtype_form classes are called by question.php with the following call
require_once($definition_file);
$classname = 'question_edit_'.$this->name().'_form';
if (!class_exists($classname)) {
return null;
}
return new $classname($submiturl, $question, $category, $contexts, $formeditable);
}
As there is no, the call is transfered to the parent question_edit_form() function
function question_edit_form($submiturl, $question, $category, $contexts, $formeditable = true){
So the solution is to stop the tranfer to question_edit_form by creating something like
function question_edit_shortanswer_form($submiturl, $question, $category, $contexts, $formeditable = true,$go=true){
if( $go) parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable );
}
The sixth new go parameter default to true will not interfered will the usual call, but allow us to obtain a valid question_edit_shortanswer_form with all the internal functions but with no parameter initialized
When this is done we can create $QTYPES_USEABLE_BY_MULTIANSWER to access these classes.
$QTYPES_USEABLE_BY_MULTIANSWER = array();
$displayoptions = new stdClass ;
foreach($QTYPES as $qtype) {
if( $qtype->is_usable_by_multianswer()){
$definition_file =
$CFG->dirroot.'/question/type/'.$qtype->name().'/edit_'.$qtype->name().'_form.php';
if (is_readable($definition_file) && is_file($definition_file)) {
require_once($definition_file);
$classname = 'question_edit_'.$qtype->name().'_form';
if (class_exists($classname)) {
$QTYPES_USEABLE_BY_MULTIANSWER[$qtype->name()]= new $classname(1,2,3,4,5,false);
//question_edit_shortanswer_form($submiturl, $question, $category, $contexts, $formeditable = true,$go=true) ;
}
}
}
}
The code flow of the question_edit_QTYPES_USEABLE_BY_MULTIANSWER[$qtype->name()]_form are described in
Development:question_edit_QTYPES_USEABLE_BY_MULTIANSWER_form_functions
to be continued Pierre Pichet 17:13, 14 November 2007 (CST)for correct answer
We call these subquestions 'wrapped questions'. Most of the methods of this questiontype simply call the corresponding methods of the wrapped questions. So mostly this questiontype class only has to do clever bookkeeping.
The way the questiontype it is implemented at the moment all wrapped questions must be of a type that use a single response field $state->responses['']
.
Data structure
quiz_multianswers database table
The quiz_multianswers table belongs to the multianswer questiontype and is an extension of the quiz_questions table. It merely stores a comma separated list of question ids in the sequence field, which is important, because that's the only way to know which sub question belongs to which position in the questiontext.
- id
- int(10) unsigned NOT NULL auto_increment,
- Primary key
- question
- int(10) unsigned NOT NULL default '0',
- Foreign key refering to the id field in the quiz_questions table
- sequence
- varchar(255) NOT NULL default ,
- A comma separated list of question ids in the order in which they appear in the questiontext.
Response storage model
The multianswer question uses a serialized format consisting of 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). After the hyphen is the raw response, that was submitted by the student. The serialized format could look like the following example:
1-34,2-Napoleon,3-4,4-correct response,5-1
All commas and hyphens that are part of the raw answer are HTML entity encoded to avoid problems.
The fact that the sequence number of the subquestion, rather than the id of the subquestion is used in the response storage is a pity. It could leads to problems when the teacher changes the layout of the cloze question.