mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 00:12:56 +02:00
Merge branch 'MDL-29644' of git://github.com/timhunt/moodle
This commit is contained in:
commit
cec6a15b99
@ -115,6 +115,40 @@ class test_question_maker {
|
||||
$q->modifiedby = $USER->id;
|
||||
}
|
||||
|
||||
public static function initialise_question_data($qdata) {
|
||||
global $USER;
|
||||
|
||||
$qdata->id = 0;
|
||||
$qdata->category = 0;
|
||||
$qdata->contextid = 0;
|
||||
$qdata->parent = 0;
|
||||
$qdata->questiontextformat = FORMAT_HTML;
|
||||
$qdata->generalfeedbackformat = FORMAT_HTML;
|
||||
$qdata->defaultmark = 1;
|
||||
$qdata->penalty = 0.3333333;
|
||||
$qdata->length = 1;
|
||||
$qdata->stamp = make_unique_id_code();
|
||||
$qdata->version = make_unique_id_code();
|
||||
$qdata->hidden = 0;
|
||||
$qdata->timecreated = time();
|
||||
$qdata->timemodified = time();
|
||||
$qdata->createdby = $USER->id;
|
||||
$qdata->modifiedby = $USER->id;
|
||||
$qdata->hints = array();
|
||||
}
|
||||
|
||||
public static function initialise_question_form_data($qdata) {
|
||||
$formdata = new stdClass();
|
||||
$formdata->id = 0;
|
||||
$formdata->category = '0,0';
|
||||
$formdata->usecurrentcat = 1;
|
||||
$formdata->categorymoveto = '0,0';
|
||||
$formdata->tags = array();
|
||||
$formdata->penalty = 0.3333333;
|
||||
$formdata->questiontextformat = FORMAT_HTML;
|
||||
$formdata->generalfeedbackformat = FORMAT_HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test helper class for a particular question type.
|
||||
* @param $qtype the question type name, e.g. 'multichoice'.
|
||||
@ -143,7 +177,16 @@ class test_question_maker {
|
||||
return self::$testhelpers[$qtype];
|
||||
}
|
||||
|
||||
public static function make_question($qtype, $which = null) {
|
||||
/**
|
||||
* Call a method on a qtype_{$qtype}_test_helper class and return the result.
|
||||
*
|
||||
* @param string $methodtemplate e.g. 'make_{qtype}_question_{which}';
|
||||
* @param string $qtype the question type to get a test question for.
|
||||
* @param string $which one of the names returned by the get_test_questions
|
||||
* method of the relevant qtype_{$qtype}_test_helper class.
|
||||
* @param unknown_type $which
|
||||
*/
|
||||
protected static function call_question_helper_method($methodtemplate, $qtype, $which = null) {
|
||||
$helper = self::get_test_helper($qtype);
|
||||
|
||||
$available = $helper->get_test_questions();
|
||||
@ -152,18 +195,64 @@ class test_question_maker {
|
||||
$which = reset($available);
|
||||
} else if (!in_array($which, $available)) {
|
||||
throw new coding_exception('Example question ' . $which . ' of type ' .
|
||||
$qtype . ' does not exist.');
|
||||
$qtype . ' does not exist.');
|
||||
}
|
||||
|
||||
$method = "make_{$qtype}_question_{$which}";
|
||||
$method = str_replace(array('{qtype}', '{which}'),
|
||||
array($qtype, $which), $methodtemplate);
|
||||
|
||||
if (!method_exists($helper, $method)) {
|
||||
throw new coding_exception('Method ' . $method . ' does not exist on the' .
|
||||
$qtype . ' question type test helper class.');
|
||||
$qtype . ' question type test helper class.');
|
||||
}
|
||||
|
||||
return $helper->$method();
|
||||
}
|
||||
|
||||
/**
|
||||
* Question types can provide a number of test question defintions.
|
||||
* They do this by creating a qtype_{$qtype}_test_helper class that extends
|
||||
* question_test_helper. The get_test_questions method returns the list of
|
||||
* test questions available for this question type.
|
||||
*
|
||||
* @param string $qtype the question type to get a test question for.
|
||||
* @param string $which one of the names returned by the get_test_questions
|
||||
* method of the relevant qtype_{$qtype}_test_helper class.
|
||||
* @return question_definition the requested question object.
|
||||
*/
|
||||
public static function make_question($qtype, $which = null) {
|
||||
return self::call_question_helper_method('make_{qtype}_question_{which}',
|
||||
$qtype, $which);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link make_question()} but returns the datastructure from
|
||||
* get_question_options instead of the question_definition object.
|
||||
*
|
||||
* @param string $qtype the question type to get a test question for.
|
||||
* @param string $which one of the names returned by the get_test_questions
|
||||
* method of the relevant qtype_{$qtype}_test_helper class.
|
||||
* @return stdClass the requested question object.
|
||||
*/
|
||||
public static function get_question_data($qtype, $which = null) {
|
||||
return self::call_question_helper_method('get_{qtype}_question_data_{which}',
|
||||
$qtype, $which);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link make_question()} but returns the data what would be saved from
|
||||
* the question editing form instead of the question_definition object.
|
||||
*
|
||||
* @param string $qtype the question type to get a test question for.
|
||||
* @param string $which one of the names returned by the get_test_questions
|
||||
* method of the relevant qtype_{$qtype}_test_helper class.
|
||||
* @return stdClass the requested question object.
|
||||
*/
|
||||
public static function get_question_form_data($qtype, $which = null) {
|
||||
return self::call_question_helper_method('get_{qtype}_question_form_data_{which}',
|
||||
$qtype, $which);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a multichoice question with choices 'A', 'B' and 'C' shuffled. 'A'
|
||||
* is correct, defaultmark 1.
|
||||
|
@ -408,34 +408,38 @@ class qformat_xml extends qformat_default {
|
||||
* @param array question question array from xml tree
|
||||
* @return object question object
|
||||
*/
|
||||
public function import_multianswer($questions) {
|
||||
public function import_multianswer($question) {
|
||||
question_bank::get_qtype('multianswer');
|
||||
|
||||
$questiontext = array();
|
||||
$questiontext['text'] = $this->import_text($questions['#']['questiontext'][0]['#']['text']);
|
||||
$questiontext['text'] = $this->import_text($question['#']['questiontext'][0]['#']['text']);
|
||||
$questiontext['format'] = '1';
|
||||
$questiontext['itemid'] = '';
|
||||
$qo = qtype_multianswer_extract_question($questiontext);
|
||||
|
||||
// 'header' parts particular to multianswer
|
||||
$qo->qtype = MULTIANSWER;
|
||||
$qo->qtype = 'multianswer';
|
||||
$qo->course = $this->course;
|
||||
$qo->generalfeedback = '';
|
||||
// restore files in generalfeedback
|
||||
$qo->generalfeedback = $this->getpath($questions,
|
||||
$qo->generalfeedback = $this->getpath($question,
|
||||
array('#', 'generalfeedback', 0, '#', 'text', 0, '#'), $qo->generalfeedback, true);
|
||||
$qo->generalfeedbackformat = $this->trans_format($this->getpath($questions,
|
||||
$qo->generalfeedbackformat = $this->trans_format($this->getpath($question,
|
||||
array('#', 'generalfeedback', 0, '@', 'format'), 'moodle_auto_format'));
|
||||
$qo->generalfeedbackfiles = $this->import_files($this->getpath($questions,
|
||||
$qo->generalfeedbackfiles = $this->import_files($this->getpath($question,
|
||||
array('#', 'generalfeedback', 0, '#', 'file'), array(), false));
|
||||
|
||||
if (!empty($questions)) {
|
||||
$qo->name = $this->import_text($questions['#']['name'][0]['#']['text']);
|
||||
}
|
||||
$qo->questiontext = $qo->questiontext['text'];
|
||||
$qo->name = $this->import_text($question['#']['name'][0]['#']['text']);
|
||||
$qo->questiontext = $qo->questiontext['text'];
|
||||
$qo->questiontextformat = '';
|
||||
|
||||
$this->import_hints($qo, $questions, true);
|
||||
$qo->penalty = $this->getpath($question,
|
||||
array('#', 'penalty', 0, '#'), $this->defaultquestion()->penalty);
|
||||
// Fix problematic rounding from old files:
|
||||
if (abs($qo->penalty - 0.3333333) < 0.005) {
|
||||
$qo->penalty = 0.3333333;
|
||||
}
|
||||
|
||||
$this->import_hints($qo, $question);
|
||||
|
||||
return $qo;
|
||||
}
|
||||
@ -633,12 +637,12 @@ class qformat_xml extends qformat_default {
|
||||
* @param array question question array from xml tree
|
||||
* @return object question object
|
||||
*/
|
||||
public function import_matching($question) {
|
||||
public function import_match($question) {
|
||||
// get common parts
|
||||
$qo = $this->import_headers($question);
|
||||
|
||||
// header parts particular to matching
|
||||
$qo->qtype = MATCH;
|
||||
$qo->qtype = 'match';
|
||||
$qo->shuffleanswers = $this->trans_single($this->getpath($question,
|
||||
array('#', 'shuffleanswers', 0, '#'), 1));
|
||||
|
||||
@ -900,9 +904,9 @@ class qformat_xml extends qformat_default {
|
||||
$qo = $this->import_numerical($question);
|
||||
} else if ($questiontype == 'description') {
|
||||
$qo = $this->import_description($question);
|
||||
} else if ($questiontype == 'matching') {
|
||||
$qo = $this->import_matching($question);
|
||||
} else if ($questiontype == 'cloze') {
|
||||
} else if ($questiontype == 'matching' || $questiontype == 'match') {
|
||||
$qo = $this->import_match($question);
|
||||
} else if ($questiontype == 'cloze' || $questiontype == 'multianswer') {
|
||||
$qo = $this->import_multianswer($question);
|
||||
} else if ($questiontype == 'essay') {
|
||||
$qo = $this->import_essay($question);
|
||||
@ -942,34 +946,21 @@ class qformat_xml extends qformat_default {
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the internal question code into a human readable form
|
||||
* (The code used to be numeric, but this remains as some of
|
||||
* the names don't match the new internal format)
|
||||
* @param mixed $typeid Internal code
|
||||
* @return string question type string
|
||||
* Turn the internal question type name into a human readable form.
|
||||
* (In the past, the code used to use integers internally. Now, it uses
|
||||
* strings, so there is less need for this, but to maintain
|
||||
* backwards-compatibility we change two of the type names.)
|
||||
* @param string $qtype question type plugin name.
|
||||
* @return string $qtype string to use in the file.
|
||||
*/
|
||||
protected function get_qtype($typeid) {
|
||||
switch($typeid) {
|
||||
case TRUEFALSE:
|
||||
return 'truefalse';
|
||||
case MULTICHOICE:
|
||||
return 'multichoice';
|
||||
case SHORTANSWER:
|
||||
return 'shortanswer';
|
||||
case NUMERICAL:
|
||||
return 'numerical';
|
||||
case MATCH:
|
||||
protected function get_qtype($qtype) {
|
||||
switch($qtype) {
|
||||
case 'match':
|
||||
return 'matching';
|
||||
case DESCRIPTION:
|
||||
return 'description';
|
||||
case MULTIANSWER:
|
||||
case 'multianswer':
|
||||
return 'cloze';
|
||||
case ESSAY:
|
||||
return 'essay';
|
||||
case CALCULATED:
|
||||
return 'calculated';
|
||||
default:
|
||||
return false;
|
||||
return $qtype;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1085,13 +1076,9 @@ class qformat_xml extends qformat_default {
|
||||
$expout .= "<!-- question: $question->id -->\n";
|
||||
|
||||
// Check question type
|
||||
if (!$questiontype = $this->get_qtype($question->qtype)) {
|
||||
// must be a plugin then, so just accept the name supplied
|
||||
$questiontype = $question->qtype;
|
||||
}
|
||||
$questiontype = $this->get_qtype($question->qtype);
|
||||
|
||||
// add opening tag
|
||||
// generates specific header for Cloze and category type question
|
||||
// Categories are a special case.
|
||||
if ($question->qtype == 'category') {
|
||||
$categorypath = $this->writetext($question->category);
|
||||
$expout .= " <question type=\"category\">\n";
|
||||
@ -1100,47 +1087,29 @@ class qformat_xml extends qformat_default {
|
||||
$expout .= " </category>\n";
|
||||
$expout .= " </question>\n";
|
||||
return $expout;
|
||||
|
||||
} else if ($question->qtype != MULTIANSWER) {
|
||||
// for all question types except Close
|
||||
$name_text = $this->writetext($question->name, 3);
|
||||
|
||||
$expout .= " <question type=\"$questiontype\">\n";
|
||||
$expout .= " <name>\n";
|
||||
$expout .= $name_text;
|
||||
$expout .= " </name>\n";
|
||||
$expout .= " <questiontext {$this->format($question->questiontextformat)}>\n";
|
||||
$expout .= $this->writetext($question->questiontext, 3);
|
||||
$expout .= $this->writefiles($question->questiontextfiles);
|
||||
$expout .= " </questiontext>\n";
|
||||
$expout .= " <generalfeedback {$this->format($question->generalfeedbackformat)}>\n";
|
||||
$expout .= $this->writetext($question->generalfeedback, 3);
|
||||
$expout .= $this->writefiles($question->generalfeedbackfiles);
|
||||
$expout .= " </generalfeedback>\n";
|
||||
$expout .= " <defaultgrade>{$question->defaultmark}</defaultgrade>\n";
|
||||
$expout .= " <penalty>{$question->penalty}</penalty>\n";
|
||||
$expout .= " <hidden>{$question->hidden}</hidden>\n";
|
||||
|
||||
} else {
|
||||
// for Cloze type only
|
||||
$name_text = $this->writetext($question->name);
|
||||
$question_text = $this->writetext($question->questiontext);
|
||||
$generalfeedback = $this->writetext($question->generalfeedback);
|
||||
$expout .= " <question type=\"$questiontype\">\n";
|
||||
$expout .= " <name>\n";
|
||||
$expout .= $name_text;
|
||||
$expout .= " </name>\n";
|
||||
$expout .= " <questiontext>\n";
|
||||
$expout .= $this->writetext($question->questiontext, 3);
|
||||
$expout .= $this->writefiles($question->questiontextfiles);
|
||||
$expout .= " </questiontext>\n";
|
||||
$expout .= " <generalfeedback>\n";
|
||||
$expout .= $this->writetext($question->generalfeedback, 3);
|
||||
$expout .= $this->writefiles($question->generalfeedbackfiles);
|
||||
$expout .= " </generalfeedback>\n";
|
||||
}
|
||||
|
||||
// output depends on question type
|
||||
// Now we know we are are handing a real question.
|
||||
// Output the generic information.
|
||||
$expout .= " <question type=\"$questiontype\">\n";
|
||||
$expout .= " <name>\n";
|
||||
$expout .= $this->writetext($question->name, 3);
|
||||
$expout .= " </name>\n";
|
||||
$expout .= " <questiontext {$this->format($question->questiontextformat)}>\n";
|
||||
$expout .= $this->writetext($question->questiontext, 3);
|
||||
$expout .= $this->writefiles($question->questiontextfiles);
|
||||
$expout .= " </questiontext>\n";
|
||||
$expout .= " <generalfeedback {$this->format($question->generalfeedbackformat)}>\n";
|
||||
$expout .= $this->writetext($question->generalfeedback, 3);
|
||||
$expout .= $this->writefiles($question->generalfeedbackfiles);
|
||||
$expout .= " </generalfeedback>\n";
|
||||
if ($question->qtype != 'multianswer') {
|
||||
$expout .= " <defaultgrade>{$question->defaultmark}</defaultgrade>\n";
|
||||
}
|
||||
$expout .= " <penalty>{$question->penalty}</penalty>\n";
|
||||
$expout .= " <hidden>{$question->hidden}</hidden>\n";
|
||||
|
||||
// The rest of the output depends on question type.
|
||||
switch($question->qtype) {
|
||||
case 'category':
|
||||
// not a qtype really - dummy used for category switching
|
||||
@ -1238,12 +1207,8 @@ class qformat_xml extends qformat_default {
|
||||
break;
|
||||
|
||||
case 'multianswer':
|
||||
$acount = 1;
|
||||
foreach ($question->options->questions as $question) {
|
||||
$thispattern = "{#".$acount."}";
|
||||
$thisreplace = $question->questiontext;
|
||||
$expout = preg_replace("~$thispattern~", $thisreplace, $expout);
|
||||
$acount++;
|
||||
foreach ($question->options->questions as $index => $subq) {
|
||||
$expout = preg_replace('~{#' . $index . '}~', $subq->questiontext, $expout);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
require_once($CFG->dirroot . '/question/format/xml/format.php');
|
||||
require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php');
|
||||
|
||||
|
||||
/**
|
||||
@ -64,6 +65,52 @@ class qformat_xml_test extends UnitTestCase {
|
||||
return $q;
|
||||
}
|
||||
|
||||
/**
|
||||
* The data the XML import format sends to save_question is not exactly
|
||||
* the same as the data returned from the editing form, so this method
|
||||
* makes necessary changes to the return value of
|
||||
* test_question_maker::get_question_form_data so that the tests can work.
|
||||
* @param object $expectedq as returned by get_question_form_data.
|
||||
* @return object one more likely to match the return value of import_...().
|
||||
*/
|
||||
public function remove_irrelevant_form_data_fields($expectedq) {
|
||||
return $this->itemid_to_files($expectedq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Becuase XML import uses a files array instead of an itemid integer to
|
||||
* handle saving files with a question, we need to covert the output of
|
||||
* test_question_maker::get_question_form_data to match. This method recursively
|
||||
* replaces all array elements with key itemid with an array entry with
|
||||
* key files and value an empty array.
|
||||
*
|
||||
* @param mixed $var any data structure.
|
||||
* @return mixed an equivalent structure with the relacements made.
|
||||
*/
|
||||
protected function itemid_to_files($var) {
|
||||
if (is_object($var)) {
|
||||
$newvar = new stdClass();
|
||||
foreach(get_object_vars($var) as $field => $value) {
|
||||
$newvar->$field = $this->itemid_to_files($value);
|
||||
}
|
||||
|
||||
} else if (is_array($var)) {
|
||||
$newvar = array();
|
||||
foreach ($var as $index => $value) {
|
||||
if ($index === 'itemid') {
|
||||
$newvar['files'] = array();
|
||||
} else {
|
||||
$newvar[$index] = $this->itemid_to_files($value);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$newvar = $var;
|
||||
}
|
||||
|
||||
return $newvar;
|
||||
}
|
||||
|
||||
public function test_write_hint_basic() {
|
||||
$q = $this->make_test_question();
|
||||
$q->name = 'Short answer question';
|
||||
@ -468,7 +515,7 @@ END;
|
||||
$xmldata = xmlize($xml);
|
||||
|
||||
$importer = new qformat_xml();
|
||||
$q = $importer->import_matching($xmldata['question']);
|
||||
$q = $importer->import_match($xmldata['question']);
|
||||
|
||||
$expectedq = new stdClass();
|
||||
$expectedq->qtype = 'match';
|
||||
@ -1216,4 +1263,136 @@ END;
|
||||
|
||||
$this->assert_same_xml($expectedxml, $xml);
|
||||
}
|
||||
|
||||
public function test_import_multianswer() {
|
||||
$xml = ' <question type="cloze">
|
||||
<name>
|
||||
<text>Simple multianswer</text>
|
||||
</name>
|
||||
<questiontext format="html">
|
||||
<text><![CDATA[Complete this opening line of verse: "The {1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer} and the {1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!} went to sea".]]></text>
|
||||
</questiontext>
|
||||
<generalfeedback format="html">
|
||||
<text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".]]></text>
|
||||
</generalfeedback>
|
||||
<penalty>0.5</penalty>
|
||||
<hidden>0</hidden>
|
||||
<hint format="html">
|
||||
<text>Hint 1</text>
|
||||
</hint>
|
||||
<hint format="html">
|
||||
<text>Hint 2</text>
|
||||
</hint>
|
||||
</question>
|
||||
';
|
||||
$xmldata = xmlize($xml);
|
||||
|
||||
$importer = new qformat_xml();
|
||||
$q = $importer->import_multianswer($xmldata['question']);
|
||||
|
||||
// Annoyingly, import works in a weird way (it duplicates code, rather
|
||||
// than just calling save_question) so we cannot use
|
||||
// test_question_maker::get_question_form_data('multianswer', 'twosubq').
|
||||
$expectedqa = new stdClass();
|
||||
$expectedqa->name = 'Simple multianswer';
|
||||
$expectedqa->qtype = 'multianswer';
|
||||
$expectedqa->questiontext = 'Complete this opening line of verse: "The {#1} and the {#2} went to sea".';
|
||||
$expectedqa->generalfeedback = 'General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea".';
|
||||
$expectedqa->defaultmark = 2;
|
||||
$expectedqa->penalty = 0.5;
|
||||
|
||||
$expectedqa->hint = array(
|
||||
array('text' => 'Hint 1', 'format' => FORMAT_HTML, 'files' => array()),
|
||||
array('text' => 'Hint 2', 'format' => FORMAT_HTML, 'files' => array()),
|
||||
);
|
||||
|
||||
$sa = new stdClass();
|
||||
|
||||
$sa->questiontext = array('text' => '{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer}',
|
||||
'format' => FORMAT_HTML, 'itemid' => null);
|
||||
$sa->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
|
||||
$sa->defaultmark = 1.0;
|
||||
$sa->qtype = 'shortanswer';
|
||||
$sa->usecase = 0;
|
||||
|
||||
$sa->answer = array('Dog', 'Owl', '*');
|
||||
$sa->fraction = array(0, 1, 0);
|
||||
$sa->feedback = array(
|
||||
array('text' => 'Wrong, silly!', 'format' => FORMAT_HTML, 'itemid' => null),
|
||||
array('text' => 'Well done!', 'format' => FORMAT_HTML, 'itemid' => null),
|
||||
array('text' => 'Wrong answer', 'format' => FORMAT_HTML, 'itemid' => null),
|
||||
);
|
||||
|
||||
$mc = new stdClass();
|
||||
|
||||
$mc->generalfeedback = '';
|
||||
$mc->questiontext = array('text' => '{1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~' .
|
||||
'Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!}',
|
||||
'format' => FORMAT_HTML, 'itemid' => null);
|
||||
$mc->generalfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
|
||||
$mc->defaultmark = 1.0;
|
||||
$mc->qtype = 'multichoice';
|
||||
|
||||
$mc->layout = 0;
|
||||
$mc->single = 1;
|
||||
$mc->shuffleanswers = 1;
|
||||
$mc->correctfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
|
||||
$mc->partiallycorrectfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
|
||||
$mc->incorrectfeedback = array('text' => '', 'format' => FORMAT_HTML, 'itemid' => null);
|
||||
$mc->answernumbering = 0;
|
||||
|
||||
$mc->answer = array(
|
||||
array('text' => 'Bow-wow', 'format' => FORMAT_HTML, 'itemid' => null),
|
||||
array('text' => 'Wiggly worm', 'format' => FORMAT_HTML, 'itemid' => null),
|
||||
array('text' => 'Pussy-cat', 'format' => FORMAT_HTML, 'itemid' => null),
|
||||
);
|
||||
$mc->fraction = array(0, 0, 1);
|
||||
$mc->feedback = array(
|
||||
array('text' => 'You seem to have a dog obsessions!', 'format' => FORMAT_HTML, 'itemid' => null),
|
||||
array('text' => 'Now you are just being ridiculous!', 'format' => FORMAT_HTML, 'itemid' => null),
|
||||
array('text' => 'Well done!', 'format' => FORMAT_HTML, 'itemid' => null),
|
||||
);
|
||||
|
||||
$expectedqa->options = new stdClass();
|
||||
$expectedqa->options->questions = array(
|
||||
1 => $sa,
|
||||
2 => $mc,
|
||||
);
|
||||
|
||||
$this->assertEqual($expectedqa->hint, $q->hint);
|
||||
$this->assertEqual($expectedqa->options->questions[1], $q->options->questions[1]);
|
||||
$this->assertEqual($expectedqa->options->questions[2], $q->options->questions[2]);
|
||||
$this->assert(new CheckSpecifiedFieldsExpectation($expectedqa), $q);
|
||||
}
|
||||
|
||||
public function test_export_multianswer() {
|
||||
$qdata = test_question_maker::get_question_data('multianswer', 'twosubq');
|
||||
|
||||
$exporter = new qformat_xml();
|
||||
$xml = $exporter->writequestion($qdata);
|
||||
|
||||
$expectedxml = '<!-- question: 0 -->
|
||||
<question type="cloze">
|
||||
<name>
|
||||
<text>Simple multianswer</text>
|
||||
</name>
|
||||
<questiontext format="html">
|
||||
<text><![CDATA[Complete this opening line of verse: "The {1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer} and the {1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!} went to sea".]]></text>
|
||||
</questiontext>
|
||||
<generalfeedback format="html">
|
||||
<text><![CDATA[General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: "The owl and the pussycat went to sea]]></text>
|
||||
</generalfeedback>
|
||||
<penalty>0.3333333</penalty>
|
||||
<hidden>0</hidden>
|
||||
<hint format="html">
|
||||
<text>Hint 1</text>
|
||||
</hint>
|
||||
<hint format="html">
|
||||
<text>Hint 2</text>
|
||||
</hint>
|
||||
</question>
|
||||
';
|
||||
|
||||
$this->assert_same_xml($expectedxml, $xml);
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class qtype_multianswer_test_helper extends question_test_helper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a multianswer question about summing two numbers.
|
||||
* Makes a multianswer question about completing two blanks in some text.
|
||||
* @return qtype_multianswer_question
|
||||
*/
|
||||
public function make_multianswer_question_twosubq() {
|
||||
@ -51,10 +51,8 @@ class qtype_multianswer_test_helper extends question_test_helper {
|
||||
$q->name = 'Simple multianswer';
|
||||
$q->questiontext =
|
||||
'Complete this opening line of verse: "The {#1} and the {#2} went to sea".';
|
||||
$q->generalfeedback = 'Generalfeedback: It\'s from "The Owl and the Pussy-cat" by Lear: ' .
|
||||
'"The owl and the pussycat went to see';
|
||||
$q->questiontextformat = FORMAT_HTML;
|
||||
$q->generalfeedbackformat = FORMAT_HTML;
|
||||
$q->generalfeedback = 'General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: ' .
|
||||
'"The owl and the pussycat went to sea';
|
||||
|
||||
// Shortanswer subquestion.
|
||||
question_bank::load_question_definition_classes('shortanswer');
|
||||
@ -107,4 +105,112 @@ class qtype_multianswer_test_helper extends question_test_helper {
|
||||
|
||||
return $q;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a multianswer question about completing two blanks in some text.
|
||||
* @return object the question definition data, as it might be returned from
|
||||
* get_question_options.
|
||||
*/
|
||||
public function get_multianswer_question_data_twosubq() {
|
||||
$qdata = new stdClass();
|
||||
test_question_maker::initialise_question_data($qdata);
|
||||
|
||||
$qdata->name = 'Simple multianswer';
|
||||
$qdata->questiontext =
|
||||
'Complete this opening line of verse: "The {#1} and the {#2} went to sea".';
|
||||
$qdata->generalfeedback = 'General feedback: It\'s from "The Owl and the Pussy-cat" by Lear: ' .
|
||||
'"The owl and the pussycat went to sea';
|
||||
|
||||
$qdata->defaultmark = 2.0;
|
||||
$qdata->qtype = 'multianswer';
|
||||
|
||||
$sa = new stdClass();
|
||||
test_question_maker::initialise_question_data($sa);
|
||||
|
||||
$sa->name = 'Simple multianswer';
|
||||
$sa->questiontext = '{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer}';
|
||||
$sa->generalfeedback = '';
|
||||
$sa->penalty = 0.0;
|
||||
$sa->qtype = 'shortanswer';
|
||||
|
||||
$sa->options = new stdClass();
|
||||
$sa->options->usecase = 0;
|
||||
|
||||
$sa->options->answers = array(
|
||||
13 => new question_answer(13, 'Dog', 0, 'Wrong, silly!', FORMAT_HTML),
|
||||
14 => new question_answer(14, 'Owl', 1, 'Well done!', FORMAT_HTML),
|
||||
15 => new question_answer(15, '*', 0, 'Wrong answer', FORMAT_HTML),
|
||||
);
|
||||
|
||||
$mc = new stdClass();
|
||||
test_question_maker::initialise_question_data($mc);
|
||||
|
||||
$mc->name = 'Simple multianswer';
|
||||
$mc->questiontext = '{1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!~' .
|
||||
'Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!}';
|
||||
$mc->generalfeedback = '';
|
||||
$mc->penalty = 0.0;
|
||||
$mc->qtype = 'multichoice';
|
||||
|
||||
$mc->options = new stdClass();
|
||||
$mc->options->layout = 0;
|
||||
$mc->options->single = 1;
|
||||
$mc->options->shuffleanswers = 1;
|
||||
$mc->options->correctfeedback = '';
|
||||
$mc->options->correctfeedbackformat = 1;
|
||||
$mc->options->partiallycorrectfeedback = '';
|
||||
$mc->options->partiallycorrectfeedbackformat = 1;
|
||||
$mc->options->incorrectfeedback = '';
|
||||
$mc->options->incorrectfeedbackformat = 1;
|
||||
$mc->options->answernumbering = '';
|
||||
$mc->options->shownumcorrect = 0;
|
||||
|
||||
$mc->options->answers = array(
|
||||
23 => new question_answer(23, 'Bow-wow', 0,
|
||||
'You seem to have a dog obsessions!', FORMAT_HTML),
|
||||
24 => new question_answer(24, 'Wiggly worm', 0,
|
||||
'Now you are just being ridiculous!', FORMAT_HTML),
|
||||
25 => new question_answer(25, 'Pussy-cat', 1,
|
||||
'Well done!', FORMAT_HTML),
|
||||
);
|
||||
|
||||
$qdata->options = new stdClass();
|
||||
$qdata->options->questions = array(
|
||||
1 => $sa,
|
||||
2 => $mc,
|
||||
);
|
||||
|
||||
$qdata->hints = array(
|
||||
new question_hint(0, 'Hint 1', FORMAT_HTML),
|
||||
new question_hint(0, 'Hint 2', FORMAT_HTML),
|
||||
);
|
||||
|
||||
return $qdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a multianswer question about completing two blanks in some text.
|
||||
* @return object the question definition data, as it might be returned from
|
||||
* the question editing form.
|
||||
*/
|
||||
public function get_multianswer_question_form_data_twosubq() {
|
||||
$formdata = new stdClass();
|
||||
test_question_maker::initialise_question_form_data($formdata);
|
||||
|
||||
$formdata->name = 'Simple multianswer';
|
||||
$formdata->questiontext = 'Complete this opening line of verse: "The ' .
|
||||
'{1:SHORTANSWER:Dog#Wrong, silly!~=Owl#Well done!~*#Wrong answer} ' .
|
||||
'and the {1:MULTICHOICE:Bow-wow#You seem to have a dog obsessions!' .
|
||||
'~Wiggly worm#Now you are just being ridiculous!~=Pussy-cat#Well done!}' .
|
||||
' went to sea".';
|
||||
$formdata->generalfeedback = 'General feedback: It\'s from "The Owl and the Pussy-cat" ' .
|
||||
'by Lear: "The owl and the pussycat went to sea';
|
||||
|
||||
$formdata->hint = array(
|
||||
0 => array('text' => 'Hint 1', 'format' => FORMAT_HTML, 'itemid' => 0),
|
||||
1 => array('text' => 'Hint 2', 'format' => FORMAT_HTML, 'itemid' => 0),
|
||||
);
|
||||
|
||||
return $formdata;
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ class qtype_multianswer_test extends UnitTestCase {
|
||||
}
|
||||
|
||||
public function test_get_random_guess_score() {
|
||||
$q = $this->get_test_question_data();
|
||||
$q = test_question_maker::get_question_data('multianswer', 'twosubq');
|
||||
$this->assertWithinMargin(0.1666667, $this->qtype->get_random_guess_score($q), 0.0000001);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user