diff --git a/question/behaviour/interactive/simpletest/testwalkthrough.php b/question/behaviour/interactive/simpletest/testwalkthrough.php index c761dfc12a1..570ad9a0fc3 100644 --- a/question/behaviour/interactive/simpletest/testwalkthrough.php +++ b/question/behaviour/interactive/simpletest/testwalkthrough.php @@ -422,8 +422,8 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes $this->check_current_mark(3); // Now change the quiestion so that answer is only partially right, and regrade. - $q->answers[0]->fraction = 0.6666667; - $q->answers[1]->fraction = 1; + $q->answers[13]->fraction = 0.6666667; + $q->answers[14]->fraction = 1; $this->quba->regrade_all_questions(false); @@ -455,8 +455,8 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes $this->check_current_mark(3); // Now change the quiestion so that answer is only partially right, and regrade. - $q->answers[0]->fraction = 0.6666667; - $q->answers[1]->fraction = 1; + $q->answers[13]->fraction = 0.6666667; + $q->answers[14]->fraction = 1; $this->quba->regrade_all_questions(true); diff --git a/question/engine/simpletest/helpers.php b/question/engine/simpletest/helpers.php index ba0189088d6..445941de307 100644 --- a/question/engine/simpletest/helpers.php +++ b/question/engine/simpletest/helpers.php @@ -237,11 +237,11 @@ class test_question_maker { $num->questiontext = 'What is pi to two d.p.?'; $num->generalfeedback = 'Generalfeedback: 3.14 is the right answer.'; $num->answers = array( - new qtype_numerical_answer('3.14', 1.0, 'Very good.', 0), - new qtype_numerical_answer('3.142', 0.0, 'Too accurate.', 0.005), - new qtype_numerical_answer('3.1', 0.0, 'Not accurate enough.', 0.05), - new qtype_numerical_answer('3', 0.0, 'Not accurate enough.', 0.5), - new qtype_numerical_answer('*', 0.0, 'Completely wrong.', 0), + 13 => new qtype_numerical_answer(13, '3.14', 1.0, 'Very good.', FORMAT_HTML, 0), + 14 => new qtype_numerical_answer(14, '3.142', 0.0, 'Too accurate.', FORMAT_HTML, 0.005), + 15 => new qtype_numerical_answer(15, '3.1', 0.0, 'Not accurate enough.', FORMAT_HTML, 0.05), + 16 => new qtype_numerical_answer(16, '3', 0.0, 'Not accurate enough.', FORMAT_HTML, 0.5), + 17 => new qtype_numerical_answer(17, '*', 0.0, 'Completely wrong.', FORMAT_HTML, 0), ); $num->qtype = question_bank::get_qtype('numerical'); $num->ap = new qtype_numerical_answer_processor(array()); diff --git a/question/format/xml/format.php b/question/format/xml/format.php index adc7e8ed1db..1555b865563 100644 --- a/question/format/xml/format.php +++ b/question/format/xml/format.php @@ -282,12 +282,25 @@ class qformat_xml extends qformat_default { * @param boolean $withshownumpartscorrect include the shownumcorrect field. */ public function import_combined_feedback($qo, $questionxml, $withshownumpartscorrect = false) { - $qo->correctfeedback = $this->getpath($questionxml, - array('#', 'correctfeedback', 0, '#', 'text', 0, '#'), '', true); - $qo->partiallycorrectfeedback = $this->getpath($questionxml, - array('#', 'partiallycorrectfeedback', 0, '#', 'text', 0, '#'), '', true); - $qo->incorrectfeedback = $this->getpath($questionxml, - array('#', 'incorrectfeedback', 0, '#', 'text', 0, '#'), '', true); + foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $field) { + $text = array(); + $text['text'] = $this->getpath($questionxml, + array('#', $field, 0, '#', 'text', 0, '#'), '', true); + $text['format'] = $this->trans_format($this->getpath($questionxml, + array('#', $field, 0, '@', 'format'), 'moodle_auto_format')); + + $text['files'] = array(); + $files = $this->getpath($questionxml, array('#', $field, 0, '#','file'), array(), false); + foreach ($files as $file) { + $data = new stdClass(); + $data->content = $file['#']; + $data->encoding = $file['@']['encoding']; + $data->name = $file['@']['name']; + $text['files'][] = $data; + } + + $qo->$field = $text; + } if ($withshownumpartscorrect) { $qo->shownumcorrect = array_key_exists('shownumcorrect', $questionxml['#']); @@ -341,7 +354,8 @@ class qformat_xml extends qformat_default { if (!isset($questionxml['#']['hint'])) { return; } -// TODO files in hints. + + // TODO Handle files in hints. foreach ($questionxml['#']['hint'] as $hintxml) { $hint = $this->import_hint($hintxml); $qo->hint[] = $hint->hint; @@ -374,52 +388,6 @@ class qformat_xml extends qformat_default { $qo->answernumbering = $this->getpath($question, array('#', 'answernumbering', 0, '#'), 'abc'); $qo->shuffleanswers = $this->trans_single($shuffleanswers); - // TODO move into import_combined_feedback - $qo->correctfeedback = array(); - $qo->correctfeedback['text'] = $this->getpath($question, array('#', 'correctfeedback', 0, '#', 'text', 0, '#'), '', true); - $qo->correctfeedback['format'] = $this->trans_format( - $this->getpath($question, array('#', 'correctfeedback', 0, '@', 'format'), 'moodle_auto_format')); - $qo->correctfeedback['files'] = array(); - // restore files in correctfeedback - $files = $this->getpath($question, array('#', 'correctfeedback', 0, '#','file'), array(), false); - foreach ($files as $file) { - $data = new stdclass; - $data->content = $file['#']; - $data->encoding = $file['@']['encoding']; - $data->name = $file['@']['name']; - $qo->correctfeedback['files'][] = $data; - } - - $qo->partiallycorrectfeedback = array(); - $qo->partiallycorrectfeedback['text'] = $this->getpath( $question, array('#','partiallycorrectfeedback',0,'#','text',0,'#'), '', true ); - $qo->partiallycorrectfeedback['format'] = $this->trans_format( - $this->getpath($question, array('#', 'partiallycorrectfeedback', 0, '@', 'format'), 'moodle_auto_format')); - $qo->partiallycorrectfeedback['files'] = array(); - // restore files in partiallycorrectfeedback - $files = $this->getpath($question, array('#', 'partiallycorrectfeedback', 0, '#','file'), array(), false); - foreach ($files as $file) { - $data = new stdclass; - $data->content = $file['#']; - $data->encoding = $file['@']['encoding']; - $data->name = $file['@']['name']; - $qo->partiallycorrectfeedback['files'][] = $data; - } - - $qo->incorrectfeedback = array(); - $qo->incorrectfeedback['text'] = $this->getpath( $question, array('#','incorrectfeedback',0,'#','text',0,'#'), '', true ); - $qo->incorrectfeedback['format'] = $this->trans_format( - $this->getpath($question, array('#', 'incorrectfeedback', 0, '@', 'format'), 'moodle_auto_format')); - $qo->incorrectfeedback['files'] = array(); - // restore files in incorrectfeedback - $files = $this->getpath($question, array('#', 'incorrectfeedback', 0, '#','file'), array(), false); - foreach ($files as $file) { - $data = new stdclass; - $data->content = $file['#']; - $data->encoding = $file['@']['encoding']; - $data->name = $file['@']['name']; - $qo->incorrectfeedback['files'][] = $data; - } - // There was a time on the 1.8 branch when it could output an empty answernumbering tag, so fix up any found. if (empty($qo->answernumbering)) { $qo->answernumbering = 'abc'; @@ -530,16 +498,14 @@ class qformat_xml extends qformat_default { $qo->feedbacktrue = array(); $qo->feedbacktrue['text'] = $feedback; $qo->feedbacktrue['format'] = $this->trans_format($feedbackformat); - $qo->feedbacktrue['itemid'] = null; - $qo->feedbacktruefiles = $files; + $qo->feedbacktrue['files'] = $files; } else { $qo->answer = ($answer['@']['fraction'] != 100); $qo->correctanswer = $qo->answer; $qo->feedbackfalse = array(); $qo->feedbackfalse['text'] = $feedback; $qo->feedbackfalse['format'] = $this->trans_format($feedbackformat); - $qo->feedbackfalse['itemid'] = null; - $qo->feedbackfalsefiles = $files; + $qo->feedbackfalse['files'] = $files; } $first = false; } @@ -576,7 +542,7 @@ class qformat_xml extends qformat_default { $acount = 0; foreach ($answers as $answer) { $ans = $this->import_answer($answer); - $qo->answer[$acount] = $ans->answer; + $qo->answer[$acount] = $ans->answer['text']; $qo->fraction[$acount] = $ans->fraction; $qo->feedback[$acount] = $ans->feedback; ++$acount; @@ -688,30 +654,27 @@ class qformat_xml extends qformat_default { $qo->shuffleanswers = $this->trans_single($this->getpath($question, array('#', 'shuffleanswers', 0, '#'), 1)); - // get subquestions - $subquestions = $question['#']['subquestion']; + // run through subquestions $qo->subquestions = array(); $qo->subanswers = array(); + foreach ($question['#']['subquestion'] as $subqxml) { + $subquestion = array(); + $subquestion['text'] = $this->getpath($subqxml, array('#', 'text', 0, '#'), '', true); + $subquestion['format'] = $this->trans_format( + $this->getpath($subqxml, array('@', 'format'), 'moodle_auto_format')); + $subquestion['files'] = array(); - // run through subquestions - foreach ($subquestions as $subquestion) { - $question = array(); - $question['text'] = $this->getpath($subquestion, array('#', 'text', 0, '#'), '', true); - $question['format'] = $this->trans_format( - $this->getpath($subquestion, array('@', 'format'), 'moodle_auto_format')); - $question['files'] = array(); - - $files = $this->getpath($subquestion, array('#', 'file'), array()); + $files = $this->getpath($subqxml, array('#', 'file'), array()); foreach ($files as $file) { $data = new stdclass(); $data->content = $file['#']; $data->encoding = $file['@']['encoding']; $data->name = $file['@']['name']; - $question['files'][] = $data; + $subquestion['files'][] = $data; } - $qo->subquestions[] = $question; - $answers = $this->getpath($subquestion, array('#', 'answer'), array()); - $qo->subanswers[] = $this->getpath($subquestion, array('#','answer',0,'#','text',0,'#'), '', true); + $qo->subquestions[] = $subquestion; + $answers = $this->getpath($subqxml, array('#', 'answer'), array()); + $qo->subanswers[] = $this->getpath($subqxml, array('#','answer',0,'#','text',0,'#'), '', true); } $this->import_combined_feedback($qo, $question, true); @@ -1122,16 +1085,16 @@ class qformat_xml extends qformat_default { $fs = get_file_storage(); $contextid = $question->contextid; - // files used by questiontext - $files = $fs->get_area_files($contextid, 'question', 'questiontext', $question->id); - $question->questiontextfiles = $files; - // files used by generalfeedback - $files = $fs->get_area_files($contextid, 'question', 'generalfeedback', $question->id); - $question->generalfeedbackfiles = $files; + // Get files used by the questiontext. + $question->questiontextfiles = $fs->get_area_files( + $contextid, 'question', 'questiontext', $question->id); + // Get files used by the generalfeedback. + $question->generalfeedbackfiles = $fs->get_area_files( + $contextid, 'question', 'generalfeedback', $question->id); if (!empty($question->options->answers)) { foreach ($question->options->answers as $answer) { - $files = $fs->get_area_files($contextid, 'question', 'answerfeedback', $answer->id); - $answer->feedbackfiles = $files; + $answer->feedbackfiles = $fs->get_area_files( + $contextid, 'question', 'answerfeedback', $answer->id); } } diff --git a/question/format/xml/simpletest/testxmlformat.php b/question/format/xml/simpletest/testxmlformat.php index 9ae3cea39b8..43de0c73ce1 100644 --- a/question/format/xml/simpletest/testxmlformat.php +++ b/question/format/xml/simpletest/testxmlformat.php @@ -330,7 +330,7 @@ END; $this->assert_same_xml($expectedxml, $xml); } - public function test_import_match() { + public function test_import_match_19() { $xml = ' Matching question @@ -398,16 +398,21 @@ END; $expectedq->name = 'Matching question'; $expectedq->questiontext = 'Match the upper and lower case letters.'; $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->correctfeedback = 'Well done.'; - $expectedq->partiallycorrectfeedback = 'Not entirely.'; + $expectedq->correctfeedback = array('text' => 'Well done.', 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.', 'format' => FORMAT_MOODLE, 'files' => array()); $expectedq->shownumcorrect = false; - $expectedq->incorrectfeedback = 'Completely wrong!'; + $expectedq->incorrectfeedback = array('text' => 'Completely wrong!', 'format' => FORMAT_MOODLE, 'files' => array()); $expectedq->generalfeedback = 'The answer is A -> a, B -> b and C -> c.'; + $expectedq->generalfeedbackformat = FORMAT_MOODLE; $expectedq->defaultmark = 1; $expectedq->length = 1; $expectedq->penalty = 0.3333333; $expectedq->shuffleanswers = 0; - $expectedq->subquestions = array('A', 'B', 'C', ''); + $expectedq->subquestions = array( + array('text' => 'A', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'B', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'C', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array())); $expectedq->subanswers = array('a', 'b', 'c', 'd'); $expectedq->hint = array('Hint 1', ''); $expectedq->hintshownumcorrect = array(true, true); @@ -539,7 +544,7 @@ END; $this->assert_same_xml($expectedxml, $xml); } - public function test_import_multichoice() { + public function test_import_multichoice_19() { $xml = ' Multiple choice question @@ -607,10 +612,11 @@ END; $expectedq->name = 'Multiple choice question'; $expectedq->questiontext = 'Which are the even numbers?'; $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->correctfeedback = '

Your answer is correct.

'; - $expectedq->partiallycorrectfeedback = '

Your answer is partially correct.

'; + $expectedq->correctfeedback = array('text' => '

Your answer is correct.

', 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->shownumcorrect = false; + $expectedq->partiallycorrectfeedback = array('text' => '

Your answer is partially correct.

', 'format' => FORMAT_MOODLE, 'files' => array()); $expectedq->shownumcorrect = true; - $expectedq->incorrectfeedback = '

Your answer is incorrect.

'; + $expectedq->incorrectfeedback = array('text' => '

Your answer is incorrect.

', 'format' => FORMAT_MOODLE, 'files' => array()); $expectedq->generalfeedback = 'The even numbers are 2 and 4.'; $expectedq->defaultmark = 2; $expectedq->length = 1; @@ -618,9 +624,17 @@ END; $expectedq->shuffleanswers = 0; $expectedq->single = false; - $expectedq->answer = array('1', '2', '3', '4'); + $expectedq->answer = array( + array('text' => '1', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => '2', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => '3', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => '4', 'format' => FORMAT_MOODLE, 'files' => array())); $expectedq->fraction = array(0, 1, 0, 1); - $expectedq->feedback = array('', '', '', ''); + $expectedq->feedback = array( + array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array())); $expectedq->hint = array('Hint 1.', 'Hint 2.'); $expectedq->hintshownumcorrect = array(false, false); @@ -734,7 +748,7 @@ END; $this->assert_same_xml($expectedxml, $xml); } - public function test_import_numerical() { + public function test_import_numerical_19() { $xml = ' Numerical question @@ -781,13 +795,17 @@ END; $expectedq->questiontext = 'What is the answer?'; $expectedq->questiontextformat = FORMAT_HTML; $expectedq->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.'; + $expectedq->generalfeedbackformat = FORMAT_MOODLE; $expectedq->defaultmark = 1; $expectedq->length = 1; $expectedq->penalty = 0.1; $expectedq->answer = array('42', '13', '*'); $expectedq->fraction = array(1, 0, 0); - $expectedq->feedback = array('Well done!', 'What were you thinking?!', 'Completely wrong.'); + $expectedq->feedback = array( + array('text' => 'Well done!', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'What were you thinking?!', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'Completely wrong.', 'format' => FORMAT_MOODLE, 'files' => array())); $expectedq->tolerance = array(0.001, 1, 0); $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); @@ -798,20 +816,22 @@ END; $qdata = new stdClass; $qdata->id = 123; + $qdata->contextid = 0; $qdata->qtype = 'numerical'; $qdata->name = 'Numerical question'; $qdata->questiontext = 'What is the answer?'; $qdata->questiontextformat = FORMAT_HTML; $qdata->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.'; + $qdata->generalfeedbackformat = FORMAT_HTML; $qdata->defaultmark = 1; $qdata->length = 1; $qdata->penalty = 0.1; $qdata->hidden = 0; $qdata->options->answers = array( - new qtype_numerical_answer('42', 1, 'Well done!', 0.001), - new qtype_numerical_answer('13', 0, 'What were you thinking?!', 1), - new qtype_numerical_answer('*', 0, 'Completely wrong.', ''), + 13 => new qtype_numerical_answer(13, '42', 1, 'Well done!', FORMAT_HTML, 0.001), + 14 => new qtype_numerical_answer(14, '13', 0, 'What were you thinking?!', FORMAT_HTML, 1), + 15 => new qtype_numerical_answer(15, '*', 0, 'Completely wrong.', FORMAT_HTML, ''), ); $qdata->options->units = array(); @@ -827,7 +847,7 @@ END; What is the answer? - + General feedback: Think Hitch-hikers guide to the Galaxy. 1 @@ -835,21 +855,21 @@ END; 0 42 - + Well done! 0.001 13 - + What were you thinking?! 1 * - + Completely wrong. 0 @@ -860,7 +880,7 @@ END; $this->assert_same_xml($expectedxml, $xml); } - public function test_import_shortanswer() { + public function test_import_shortanswer_19() { $xml = ' Short answer question @@ -912,7 +932,9 @@ END; $expectedq->answer = array('Beta', '*'); $expectedq->fraction = array(1, 0); - $expectedq->feedback = array('Well done!', 'Doh!'); + $expectedq->feedback = array( + array('text' => 'Well done!', 'format' => FORMAT_MOODLE, 'files' => array()), + array('text' => 'Doh!', 'format' => FORMAT_MOODLE, 'files' => array())); $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); } @@ -955,7 +977,7 @@ END; Fill in the gap in this sequence: Alpha, ________, Gamma. - + The answer is Beta. 1 @@ -964,20 +986,20 @@ END; 0 Beta - + Well done! * - + Doh! - + Hint 1 - + Hint 2 @@ -986,7 +1008,7 @@ END; $this->assert_same_xml($expectedxml, $xml); } - public function test_import_truefalse() { + public function test_import_truefalse_19() { $xml = ' True false question @@ -1028,8 +1050,8 @@ END; $expectedq->length = 1; $expectedq->penalty = 1; - $expectedq->feedbacktrue = 'Well done!'; - $expectedq->feedbackfalse = 'Doh!'; + $expectedq->feedbacktrue = array('text' => 'Well done!', 'format' => FORMAT_MOODLE, 'files' => array()); + $expectedq->feedbackfalse = array('text' => 'Doh!', 'format' => FORMAT_MOODLE, 'files' => array()); $expectedq->correctanswer = true; $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q); diff --git a/question/todo/diffstat.txt b/question/todo/diffstat.txt index e8c12f2580f..c517a44c318 100644 --- a/question/todo/diffstat.txt +++ b/question/todo/diffstat.txt @@ -160,9 +160,13 @@ DONE mod/quiz/protect_js.php | 56 - mod/quiz/report/statistics/statistics_table.php | 335 +++ mod/quiz/report/statistics/version.php | 26 + - pix/i/flagged.png | Bin 0 -> 193 bytes - pix/i/ne_red_mark.png | Bin 0 -> 121 bytes - pix/i/unflagged.png | Bin 0 -> 197 bytes + theme/standard/styles_color.css | 72 +- + theme/standard/styles_fonts.css | 20 +- + theme/standard/styles_layout.css | 265 ++- + +DONE pix/i/flagged.png | Bin 0 -> 193 bytes +DONE pix/i/ne_red_mark.png | Bin 0 -> 121 bytes +DONE pix/i/unflagged.png | Bin 0 -> 197 bytes question/backuplib.php | 26 +- DONE question/comment.html | 25 - @@ -246,10 +250,10 @@ DONE question/behaviour/informationitem/lang/en_utf8/qbehaviour_informationitem. DONE question/behaviour/informationitem/renderer.php | 42 + DONE question/behaviour/informationitem/simpletest/testwalkthrough.php | 78 + -DONE question/behaviour/interactive/behaviour.php | 232 ++ + question/behaviour/interactive/behaviour.php | 232 ++ DONE question/behaviour/interactive/lang/en_utf8/qbehaviour_interactive.php | 6 + DONE question/behaviour/interactive/renderer.php | 62 + -DONE question/behaviour/interactive/interactive/simpletest/testwalkthrough.php | 470 ++++ + question/behaviour/interactive/interactive/simpletest/testwalkthrough.php | 470 ++++ DONE question/behaviour/interactivecountback/behaviour.php | 91 + DONE question/behaviour/interactivecountback/en_utf8/qbehaviour_interactivecountback.php | 3 + @@ -276,8 +280,8 @@ DONE question/format/blackboard/format.php | 2 +- DONE question/format/gift/format.php | 6 +- DONE question/format/hotpot/format.php | 10 +- DONE question/format/webct/format.php | 4 +- - question/format/xml/format.php | 985 ++++--- - question/format/xml/simpletest/testxmlformat.php | 1064 ++++++++ +DONE question/format/xml/format.php | 985 ++++--- +DONE question/format/xml/simpletest/testxmlformat.php | 1064 ++++++++ question/type/calculated/db/upgrade.php | 2 - question/type/calculated/edit_calculated_form.php | 5 +- @@ -441,8 +445,3 @@ DONE question/type/truefalse/renderer.php | 142 + DONE question/type/truefalse/simpletest/testquestion.php | 99 + DONE question/type/truefalse/simpletest/testquestiontype.php | 73 + DONE question/type/truefalse/version.php | 4 +- - - theme/standard/styles_color.css | 72 +- - theme/standard/styles_fonts.css | 20 +- - theme/standard/styles_layout.css | 265 ++- - diff --git a/question/type/numerical/question.php b/question/type/numerical/question.php new file mode 100644 index 00000000000..7eba5a77365 --- /dev/null +++ b/question/type/numerical/question.php @@ -0,0 +1,151 @@ +. + + +/** + * Numerical question definition class. + * + * @package qtype_numerical + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once($CFG->dirroot . '/question/type/numerical/questiontype.php'); + + +/** + * Represents a numerical question. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_numerical_question extends question_graded_by_strategy + implements question_response_answer_comparer { + /** @var array of question_answer. */ + public $answers = array(); + /** @var qtype_numerical_answer_processor */ + public $ap; + + public function __construct() { + parent::__construct(new question_first_matching_answer_grading_strategy($this)); + } + + public function get_expected_data() { + return array('answer' => PARAM_TRIM); + } + + public function init_first_step(question_attempt_step $step) { + if ($step->has_qt_var('_separators')) { + list($point, $separator) = explode('$', $step->get_qt_var('_separators')); + $this->ap->set_characters($point, $separator); + } else { + $step->set_qt_var('_separators', + $this->ap->get_point() . '$' . $this->ap->get_separator()); + } + } + + public function summarise_response(array $response) { + if (isset($response['answer'])) { + return $response['answer']; + } else { + return null; + } + } + + public function is_complete_response(array $response) { + return array_key_exists('answer', $response) && + ($response['answer'] || $response['answer'] === '0' || $response['answer'] === 0); + } + + public function get_validation_error(array $response) { + if ($this->is_gradable_response($response)) { + return ''; + } + return get_string('pleaseenterananswer', 'qtype_numerical'); + } + + public function is_same_response(array $prevresponse, array $newresponse) { + return question_utils::arrays_same_at_key_missing_is_blank( + $prevresponse, $newresponse, 'answer'); + } + + public function get_answers() { + return $this->answers; + } + + public function compare_response_with_answer(array $response, question_answer $answer) { + list($value, $unit) = $this->ap->apply_units($response['answer']); + return $answer->within_tolerance($value); + } +} + + +/** + * Subclass of {@link question_answer} with the extra information required by + * the numerical question type. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_numerical_answer extends question_answer { + /** @var float allowable margin of error. */ + public $tolerance; + /** @var integer|string see {@link get_tolerance_interval()} for the meaning of this value. */ + public $tolerancetype = 2; + + public function __construct($id, $answer, $fraction, $feedback, $feedbackformat, $tolerance) { + parent::__construct($id, $answer, $fraction, $feedback, $feedbackformat); + $this->tolerance = abs($tolerance); + } + + public function get_tolerance_interval() { + if ($this->answer === '*') { + throw new Exception('Cannot work out tolerance interval for answer *.'); + } + + // We need to add a tiny fraction depending on the set precision to make + // the comparison work correctly, otherwise seemingly equal values can + // yield false. See MDL-3225. + $tolerance = (float) $this->tolerance + pow(10, -1 * ini_get('precision')); + + switch ($this->tolerancetype) { + case 1: case 'relative': + $range = abs($this->answer) * $tolerance; + return array($this->answer - $range, $this->answer + $range); + + case 2: case 'nominal': + $tolerance = $this->tolerance + pow(10, -1 * ini_get('precision')) * + max(1, abs($this->answer)); + return array($this->answer - $tolerance, $this->answer + $tolerance); + + case 3: case 'geometric': + $quotient = 1 + abs($tolerance); + return array($this->answer / $quotient, $this->answer * $quotient); + + default: + throw new Exception('Unknown tolerance type ' . $this->tolerancetype); + } + } + + public function within_tolerance($value) { + if ($this->answer === '*') { + return true; + } + list($min, $max) = $this->get_tolerance_interval(); + return $min <= $value && $value <= $max; + } +} diff --git a/question/type/shortanswer/simpletest/testquestion.php b/question/type/shortanswer/simpletest/testquestion.php index be906bde46d..022f546e84e 100644 --- a/question/type/shortanswer/simpletest/testquestion.php +++ b/question/type/shortanswer/simpletest/testquestion.php @@ -131,13 +131,13 @@ class qtype_shortanswer_question_test extends UnitTestCase { $sa->init_first_step(new question_attempt_step()); $this->assertEqual(array( - new question_classified_response(0, 'frog', 1.0)), + new question_classified_response(13, 'frog', 1.0)), $sa->classify_response(array('answer' => 'frog'))); $this->assertEqual(array( - new question_classified_response(1, 'toad', 0.8)), + new question_classified_response(14, 'toad', 0.8)), $sa->classify_response(array('answer' => 'toad'))); $this->assertEqual(array( - new question_classified_response(2, 'cat', 0.0)), + new question_classified_response(15, 'cat', 0.0)), $sa->classify_response(array('answer' => 'cat'))); $this->assertEqual(array( question_classified_response::no_response()), diff --git a/question/type/simpletest/testquestionbase.php b/question/type/simpletest/testquestionbase.php index 06b9c0b1949..a0286a2f211 100644 --- a/question/type/simpletest/testquestionbase.php +++ b/question/type/simpletest/testquestionbase.php @@ -72,7 +72,7 @@ class question_first_matching_answer_grading_strategy_test extends UnitTestCase public function test_matching_answer_returned2() { $answer = new question_answer(0, 'frog', 1, '', FORMAT_HTML); - $answer2 = new question_answer(0, 'frog', 0.5, ''); + $answer2 = new question_answer(0, 'frog', 0.5, '', FORMAT_HTML); $question = new test_response_answer_comparer(array($answer, $answer2)); $strategy = new question_first_matching_answer_grading_strategy($question); $this->assertIdentical($answer, $strategy->grade(array('answer' => 'frog')));