diff --git a/backup/moodle2/backup_qtype_plugin.class.php b/backup/moodle2/backup_qtype_plugin.class.php
index c0d48436f42..c5cc3d36f7c 100644
--- a/backup/moodle2/backup_qtype_plugin.class.php
+++ b/backup/moodle2/backup_qtype_plugin.class.php
@@ -101,8 +101,7 @@ abstract class backup_qtype_plugin extends backup_plugin {
// Define the elements
$options = new backup_nested_element('numerical_options');
$option = new backup_nested_element('numerical_option', array('id'), array(
- 'instructions', 'instructionsformat', 'showunits', 'unitsleft',
- 'unitgradingtype', 'unitpenalty'));
+ 'showunits', 'unitsleft', 'unitgradingtype', 'unitpenalty'));
// Build the tree
$element->add_child($options);
@@ -192,15 +191,4 @@ abstract class backup_qtype_plugin extends backup_plugin {
}
return $components;
}
-
- /**
- * Returns one array with filearea => mappingname elements for the qtype
- *
- * Used by {@link get_components_and_fileareas} to know about all the qtype
- * files to be processed both in backup and restore.
- */
- public static function get_qtype_fileareas() {
- // By default, return empty array, only qtypes having own fileareas will override this
- return array();
- }
}
diff --git a/question/behaviour/deferredfeedback/behaviour.php b/question/behaviour/deferredfeedback/behaviour.php
index 579c0076e8a..c739795714d 100644
--- a/question/behaviour/deferredfeedback/behaviour.php
+++ b/question/behaviour/deferredfeedback/behaviour.php
@@ -68,6 +68,33 @@ class qbehaviour_deferredfeedback extends question_behaviour_with_save {
}
}
+ /*
+ * Like the parent method, except that when a respones is gradable, but not
+ * completely, we move it to the invalid state.
+ *
+ * TODO refactor, to remove the duplication.
+ */
+ public function process_save(question_attempt_pending_step $pendingstep) {
+ if ($this->qa->get_state()->is_finished()) {
+ return question_attempt::DISCARD;
+ } else if (!$this->qa->get_state()->is_active()) {
+ throw new coding_exception('Question is not active, cannot process_actions.');
+ }
+
+ if ($this->is_same_response($pendingstep)) {
+ return question_attempt::DISCARD;
+ }
+
+ if ($this->is_complete_response($pendingstep)) {
+ $pendingstep->set_state(question_state::$complete);
+ } else if ($this->question->is_gradable_response($pendingstep->get_qt_data())) {
+ $pendingstep->set_state(question_state::$invalid);
+ } else {
+ $pendingstep->set_state(question_state::$todo);
+ }
+ return question_attempt::KEEP;
+ }
+
public function summarise_action(question_attempt_step $step) {
if ($step->has_behaviour_var('comment')) {
return $this->summarise_manual_comment($step);
diff --git a/question/format/gift/simpletest/testgiftformat.php b/question/format/gift/simpletest/testgiftformat.php
index 14171bbc3fc..54bc5720eed 100644
--- a/question/format/gift/simpletest/testgiftformat.php
+++ b/question/format/gift/simpletest/testgiftformat.php
@@ -563,8 +563,6 @@ class qformat_gift_test extends UnitTestCase {
'options' => (object) array(
'id' => 123,
'question' => 666,
- 'instructions' => '',
- 'instructionsformat' => FORMAT_MOODLE,
'showunits' => 0,
'unitsleft' => 0,
'showunits' => 2,
diff --git a/question/type/ddwtos/simpletest/testwalkthrough.php b/question/type/ddwtos/simpletest/testwalkthrough.php
index 11adf6388b0..1435dd1e0f5 100644
--- a/question/type/ddwtos/simpletest/testwalkthrough.php
+++ b/question/type/ddwtos/simpletest/testwalkthrough.php
@@ -198,7 +198,7 @@ class qtype_ddwtos_walkthrough_test extends qbehaviour_walkthrough_test_base {
$this->process_submission(array('p1' => '1', 'p2' => '2'));
// Verify.
- $this->check_current_state(question_state::$todo);
+ $this->check_current_state(question_state::$invalid);
$this->check_current_mark(null);
$this->check_current_output(
$this->get_contains_drop_box_expectation('p1', 1, false),
@@ -338,7 +338,7 @@ class qtype_ddwtos_walkthrough_test extends qbehaviour_walkthrough_test_base {
$this->process_submission(array('p1' => '1', 'p2' => '0', 'p3' => '0'));
// Verify.
- $this->check_current_state(question_state::$todo);
+ $this->check_current_state(question_state::$invalid);
$this->check_current_mark(null);
$this->check_current_output(
$this->get_contains_drop_box_expectation('p1', 1, false),
diff --git a/question/type/match/simpletest/testwalkthrough.php b/question/type/match/simpletest/testwalkthrough.php
index ea884decf37..1fc86401d03 100644
--- a/question/type/match/simpletest/testwalkthrough.php
+++ b/question/type/match/simpletest/testwalkthrough.php
@@ -124,7 +124,7 @@ class qtype_match_walkthrough_test extends qbehaviour_walkthrough_test_base {
'sub1' => $orderforchoice[2], 'sub2' => '0', 'sub3' => '0'));
// Verify.
- $this->check_current_state(question_state::$todo);
+ $this->check_current_state(question_state::$invalid);
$this->check_current_mark(null);
$this->check_current_output(
$this->get_contains_select_expectation('sub0', $choices, 1, true),
diff --git a/question/type/numerical/backup/moodle2/backup_qtype_numerical_plugin.class.php b/question/type/numerical/backup/moodle2/backup_qtype_numerical_plugin.class.php
index 9c91ff0b70d..a0693955bd3 100644
--- a/question/type/numerical/backup/moodle2/backup_qtype_numerical_plugin.class.php
+++ b/question/type/numerical/backup/moodle2/backup_qtype_numerical_plugin.class.php
@@ -83,6 +83,6 @@ class backup_qtype_numerical_plugin extends backup_qtype_plugin {
* files to be processed both in backup and restore.
*/
public static function get_qtype_fileareas() {
- return array('instruction' => 'question_created');
+ return array();
}
}
diff --git a/question/type/numerical/db/install.xml b/question/type/numerical/db/install.xml
index f5cc856bf0b..21b76313ecf 100644
--- a/question/type/numerical/db/install.xml
+++ b/question/type/numerical/db/install.xml
@@ -22,10 +22,8 @@
-
-
-
-
+
+
diff --git a/question/type/numerical/db/upgrade.php b/question/type/numerical/db/upgrade.php
index 447454b8c8a..84f2047f2e6 100644
--- a/question/type/numerical/db/upgrade.php
+++ b/question/type/numerical/db/upgrade.php
@@ -59,7 +59,7 @@ function xmldb_qtype_numerical_upgrade($oldversion) {
if (!$dbman->table_exists($table)) {
// $dbman->create_table doesnt return a result, we just have to trust it
$dbman->create_table($table);
- }//else
+ }
upgrade_plugin_savepoint(true, 2009100100, 'qtype', 'numerical');
}
@@ -96,5 +96,176 @@ function xmldb_qtype_numerical_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2009100101, 'qtype', 'numerical');
}
+ if ($oldversion < 2011042600) {
+ // Get rid of the instructions field by adding it to the qestion
+ // text. Also, if the unit was set to be displayed beside the input,
+ // deal with that within the question text too.
+
+ // The hard-coded constants used here are:
+ // 2 = the old qtype_numerical::UNITDISPLAY for ->showunits
+ // 3 = qtype_numerical::UNITNONE
+
+ $fs = get_file_storage();
+
+ $rs = $DB->get_recordset_sql('
+ SELECT q.id AS questionid,
+ q.questiontext,
+ q.questiontextformat,
+ qc.contextid,
+ qno.id AS qnoid,
+ qno.instructions,
+ qno.instructionsformat,
+ qno.showunits,
+ qno.unitsleft,
+ qnu.unit AS defaultunit
+
+ FROM {question} q
+ FROM {question_categories} qc ON qc.id = q.category
+ JOIN {question_numerical_options} qno ON qno.question = q.id
+ JOIN {question_numerical_units} qnu ON qnu.id = (
+ SELECT min(id)
+ FROM {question_numerical_units}
+ WHERE question = q.id AND ABS(multiplier - 1) < 0.0000000001)');
+ foreach ($rs as $numericaloptions) {
+ if ($numericaloptions->showunits != 2 && empty($numericaloptions->instructions)) {
+ // Nothing to do for this question.
+ continue;
+ }
+
+ $ishtml = qtype_numerical_convert_text_format($numericaloptions);
+
+ $response = '_______________';
+ if ($numericaloptions->showunits == 2) {
+ if ($numericaloptions->unitsleft) {
+ $response = $numericaloptions->defaultunit . ' _______________';
+ } else {
+ $response = '_______________ ' . $numericaloptions->defaultunit;
+ }
+
+ $DB->set_field('question_numerical_options', 'showunits', 3,
+ array('id' => $numericaloptions->qnoid));
+ }
+
+ if ($ishtml) {
+ $numericaloptions->questiontext .= '' . $response . '
';
+ } else {
+ $numericaloptions->questiontext .= "\n\n" . $response;
+ }
+
+ if (!empty($numericaloptions->instructions)) {
+ if ($ishtml) {
+ $numericaloptions->questiontext .= $numericaloptions->instructions;
+ } else {
+ $numericaloptions->questiontext .= "\n\n" . $numericaloptions->instructions;
+ }
+
+ $oldfiles = $fs->get_area_files($numericaloptions->contextid,
+ 'qtype_numerical', 'instruction', $numericaloptions->questionid, 'id', false);
+ foreach ($oldfiles as $oldfile) {
+ $filerecord = new stdClass();
+ $filerecord->component = 'question';
+ $filerecord->filearea = 'questiontext';
+ $fs->create_file_from_storedfile($filerecord, $oldfile);
+ }
+
+ if ($oldfiles) {
+ $fs->delete_area_files($numericaloptions->contextid,
+ 'qtype_numerical', 'instruction', $numericaloptions->questionid);
+ }
+ }
+
+ $updaterecord = new stdClass();
+ $updaterecord->id = $numericaloptions->questionid;
+ $updaterecord->questiontext = $numericaloptions->questiontext;
+ $updaterecord->questiontextformat = $numericaloptions->questiontextformat;
+ $DB->update_record('question', $updaterecord);
+ }
+ $rs->close();
+ }
+
+ if ($oldversion < 2011042601) {
+ // Define field instructions to be dropped from question_numerical_options
+ $table = new xmldb_table('question_numerical_options');
+ $field = new xmldb_field('instructions');
+
+ // Conditionally launch drop field instructions
+ if ($dbman->field_exists($table, $field)) {
+ $dbman->drop_field($table, $field);
+ }
+
+ // numerical savepoint reached
+ upgrade_plugin_savepoint(true, 2011042601, 'qtype', 'numerical');
+ }
+
+ if ($oldversion < 2011042602) {
+ // Define field instructionsformat to be dropped from question_numerical_options
+ $table = new xmldb_table('question_numerical_options');
+ $field = new xmldb_field('instructionsformat');
+
+ // Conditionally launch drop field instructionsformat
+ if ($dbman->field_exists($table, $field)) {
+ $dbman->drop_field($table, $field);
+ }
+
+ // numerical savepoint reached
+ upgrade_plugin_savepoint(true, 2011042602, 'qtype', 'numerical');
+ }
+
return true;
}
+
+
+/**
+ * Convert the ->questiontext and ->instructions fields to have the same text format.
+ * If they are already the same, do nothing. Otherwise, this method works by
+ * converting both to HTML.
+ * @param $numericaloptions the data to convert.
+ * @return bool true if the resulting fields are in HTML, as opposed to one of
+ * the text-based formats.
+ */
+function qtype_numerical_convert_text_format($numericaloptions) {
+ if ($numericaloptions->questiontextformat == $numericaloptions->instructionsformat) {
+ // Nothing to do:
+ return $numericaloptions->questiontextformat == FORMAT_HTML;
+ }
+
+ if ($numericaloptions->questiontextformat != FORMAT_HTML) {
+ $numericaloptions->questiontext = qtype_numerical_convert_to_html(
+ $numericaloptions->questiontext, $numericaloptions->questiontextformat);
+ $numericaloptions->questiontextformat = FORMAT_HTML;
+ }
+
+ if ($numericaloptions->instructionsformat != FORMAT_HTML) {
+ $numericaloptions->instructions = qtype_numerical_convert_to_html(
+ $numericaloptions->instructions, $numericaloptions->instructionsformat);
+ $numericaloptions->instructionsformat = FORMAT_HTML;
+ }
+
+ return true;
+}
+
+/**
+ * Convert some content to HTML.
+ * @param string $text the content to convert to HTML
+ * @param int $oldformat One of the FORMAT_... constants.
+ */
+function qtype_numerical_convert_to_html($text, $oldformat) {
+ switch ($oldformat) {
+ // Similar to format_text.
+
+ case FORMAT_PLAIN:
+ $text = s($text);
+ $text = str_replace(' ', ' ', $text);
+ $text = nl2br($text);
+ return $text;
+
+ case FORMAT_MARKDOWN:
+ return markdown_to_html($text);
+
+ case FORMAT_MOODLE:
+ return text_to_html($text, null, $options['para'], $options['newlines']);
+
+ default:
+ throw new coding_exception('Unexpected text format when upgrading numerical questions.');
+ }
+}
diff --git a/question/type/numerical/edit_numerical_form.php b/question/type/numerical/edit_numerical_form.php
index e4165316868..aeff0251fbb 100644
--- a/question/type/numerical/edit_numerical_form.php
+++ b/question/type/numerical/edit_numerical_form.php
@@ -71,7 +71,6 @@ class qtype_numerical_edit_form extends question_edit_form {
$unitoptions = array(
qtype_numerical::UNITNONE => get_string('onlynumerical', 'qtype_numerical'),
- qtype_numerical::UNITDISPLAY => get_string('oneunitshown', 'qtype_numerical'),
qtype_numerical::UNITOPTIONAL => get_string('manynumerical', 'qtype_numerical'),
qtype_numerical::UNITGRADED => get_string('unitgraded', 'qtype_numerical'),
);
@@ -112,19 +111,12 @@ class qtype_numerical_edit_form extends question_edit_form {
get_string('unitposition', 'qtype_numerical'), $unitsleftoptions);
$mform->setDefault('unitsleft', 0);
- $mform->addElement('editor', 'instructions',
- get_string('instructions', 'qtype_numerical'), null, $this->editoroptions);
- $mform->setType('instructions', PARAM_RAW);
- $mform->addHelpButton('instructions', 'numericalinstructions', 'qtype_numerical');
-
$mform->disabledIf('penaltygrp', 'unitrole', 'eq', qtype_numerical::UNITNONE);
- $mform->disabledIf('penaltygrp', 'unitrole', 'eq', qtype_numerical::UNITDISPLAY);
$mform->disabledIf('penaltygrp', 'unitrole', 'eq', qtype_numerical::UNITOPTIONAL);
$mform->disabledIf('unitsleft', 'unitrole', 'eq', qtype_numerical::UNITNONE);
$mform->disabledIf('multichoicedisplay', 'unitrole', 'eq', qtype_numerical::UNITNONE);
- $mform->disabledIf('multichoicedisplay', 'unitrole', 'eq', qtype_numerical::UNITDISPLAY);
$mform->disabledIf('multichoicedisplay', 'unitrole', 'eq', qtype_numerical::UNITOPTIONAL);
}
@@ -234,20 +226,6 @@ class qtype_numerical_edit_form extends question_edit_form {
$question->unitrole = $question->options->showunits;
}
- // Instructions field.
- $draftitemid = file_get_submitted_draft_itemid('instruction');
- $question->instructions['text'] = file_prepare_draft_area(
- $draftitemid, // draftid
- $this->context->id, // context
- 'qtype_' . $this->qtype(), // component
- 'instruction', // filarea
- !empty($question->id) ? (int) $question->id : null, // itemid
- $this->fileoptions, // options
- $question->options->instructions // text
- );
- $question->instructions['itemid'] = $draftitemid ;
- $question->instructions['format'] = $question->options->instructionsformat;
-
return $question;
}
diff --git a/question/type/numerical/lang/en/qtype_numerical.php b/question/type/numerical/lang/en/qtype_numerical.php
index 91cb9276122..abe1c0600b9 100644
--- a/question/type/numerical/lang/en/qtype_numerical.php
+++ b/question/type/numerical/lang/en/qtype_numerical.php
@@ -33,12 +33,13 @@ $string['answerno'] = 'Answer {$a}';
$string['decfractionofquestiongrade'] = 'as a fraction (0-1) of the question grade';
$string['decfractionofresponsegrade'] = 'as a fraction (0-1) of the response grade';
$string['decimalformat'] = 'decimals';
-$string['editableunittext'] = 'a text input element';
+$string['editableunittext'] = 'the text input element';
$string['editingnumerical'] = 'Editing a Numerical question';
$string['errornomultiplier'] = 'You must specify a multiplier for this unit.';
$string['errorrepeatedunit'] = 'You cannot have two units with the same name.';
$string['geometric'] = 'Geometric';
-$string['instructions'] = 'Instructions ';
+$string['invalidnumber'] = 'You must enter a valid number.';
+$string['invalidnumbernounit'] = 'You must enter a valid number. Do not include a unit in your response.';
$string['invalidnumericanswer'] = 'One of the answers you entered was not a valid number.';
$string['invalidnumerictolerance'] = 'One of the tolerances you entered was not a valid number.';
$string['leftexample'] = 'on the left, for example $1.00 or £1.00';
@@ -51,8 +52,6 @@ $string['numerical'] = 'Numerical';
$string['numerical_help'] = 'From the student perspective, a numerical question looks just like a short-answer question. The difference is that numerical answers are allowed to have an accepted error. This allows a fixed range of answers to be evaluated as one answer. For example, if the answer is 10 with an accepted error of 2, then any number between 8 and 12 will be accepted as correct. ';
$string['numerical_link'] = 'question/type/numerical';
$string['numericalsummary'] = 'Allows a numerical response, possibly with units, that is graded by comparing against various model answers, possibly with tolerances.';
-$string['numericalinstructions'] = 'Instructions';
-$string['numericalinstructions_help'] = 'Enter any instructions you would like to give the student about how to complete their response.';
$string['numericalmultiplier'] = 'Multiplier';
$string['numericalmultiplier_help'] = 'The multiplier is the factor by which the correct numerical response will be multiplied.
@@ -100,7 +99,7 @@ $string['unitpenalty_help'] = 'The penalty is applied if
* the wrong unit name is entered into the unit input, or
* a unit is entered into the value input box';
$string['unitappliedpenalty'] = 'These marks include a penalty of {$a} for bad unit.';
-$string['unitposition'] = 'Units are displayed';
+$string['unitposition'] = 'Units go';
$string['unitnotselected'] = 'No unit selected';
$string['unithandling'] = 'Unit handling';
$string['validnumberformats'] = 'Valid number formats';
diff --git a/question/type/numerical/question.php b/question/type/numerical/question.php
index a879977cbf7..60453ea6102 100644
--- a/question/type/numerical/question.php
+++ b/question/type/numerical/question.php
@@ -40,7 +40,7 @@ class qtype_numerical_question extends question_graded_automatically {
/** @var array of question_answer. */
public $answers = array();
- /** @var int one of the constants UNITNONE, UNITDISPLAY, UNITSELECT or UNITINPUT. */
+ /** @var int one of the constants UNITNONE, UNITSELECT or UNITINPUT. */
public $unitdisplay;
/** @var int one of the constants UNITGRADEDOUTOFMARK or UNITGRADEDOUTOFMAX. */
public $unitgradingtype;
@@ -76,16 +76,43 @@ class qtype_numerical_question extends question_graded_automatically {
}
}
- public function is_complete_response(array $response) {
+ public function is_gradable_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 '';
+ public function is_complete_response(array $response) {
+ if (!$this->is_gradable_response($response)) {
+ return false;
}
- return get_string('pleaseenterananswer', 'qtype_numerical');
+
+ list($value, $unit) = $this->ap->apply_units($response['answer']);
+ if (is_null($value)) {
+ return false;
+ }
+
+ if ($this->unitdisplay == qtype_numerical::UNITNONE && $unit) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function get_validation_error(array $response) {
+ if (!$this->is_gradable_response($response)) {
+ return get_string('pleaseenterananswer', 'qtype_numerical');
+ }
+
+ list($value, $unit) = $this->ap->apply_units($response['answer']);
+ if (is_null($value)) {
+ return get_string('invalidnumber', 'qtype_numerical');
+ }
+
+ if ($this->unitdisplay == qtype_numerical::UNITNONE && $unit) {
+ return get_string('invalidnumbernounit', 'qtype_numerical');
+ }
+
+ return '';
}
public function is_same_response(array $prevresponse, array $newresponse) {
@@ -129,7 +156,7 @@ class qtype_numerical_question extends question_graded_automatically {
}
protected function apply_unit_penalty($fraction, $unit) {
- if (!empty($unit)) {
+ if (!empty($unit) && $this->ap->is_known_unit($unit)) {
return $fraction;
}
@@ -178,9 +205,6 @@ class qtype_numerical_question extends question_graded_automatically {
} else if ($component == 'question' && $filearea == 'hint') {
return $this->check_hint_file_access($qa, $options, $args);
- } else if ($component == 'qtype_numerical' && $filearea == 'instruction') {
- return true;
-
} else {
return parent::check_file_access($qa, $options, $component, $filearea, $args, $forcedownload);
}
diff --git a/question/type/numerical/questiontype.php b/question/type/numerical/questiontype.php
index caa62be0f18..a3cf6f8426e 100644
--- a/question/type/numerical/questiontype.php
+++ b/question/type/numerical/questiontype.php
@@ -45,7 +45,6 @@ class qtype_numerical extends question_type {
const UNITSELECT = 1;
const UNITNONE = 3;
- const UNITDISPLAY = 2;
const UNITGRADED = 1;
const UNITOPTIONAL = 0;
@@ -79,7 +78,7 @@ class qtype_numerical extends question_type {
array('questionid' => $question->id), 'id ASC');
$this->get_numerical_units($question);
- //get_numerical_options() need to know if there are units
+ // get_numerical_options() need to know if there are units
// to set correctly default values
$this->get_numerical_options($question);
@@ -139,16 +138,12 @@ class qtype_numerical extends question_type {
$question->options->showunits = self::UNITNONE ;
}
$question->options->unitsleft = 0;
- $question->options->instructions = '';
- $question->options->instructionsformat = editors_get_preferred_format();
} else {
$question->options->unitgradingtype = $options->unitgradingtype;
$question->options->unitpenalty = $options->unitpenalty;
$question->options->showunits = $options->showunits;
$question->options->unitsleft = $options->unitsleft;
- $question->options->instructions = $options->instructions;
- $question->options->instructionsformat = $options->instructionsformat;
}
return true;
@@ -267,7 +262,6 @@ class qtype_numerical extends question_type {
if (!$options) {
$options = new stdClass();
$options->question = $question->id;
- $options->instructions = '';
$options->id = $DB->insert_record('question_numerical_options', $options);
}
@@ -302,10 +296,6 @@ class qtype_numerical extends question_type {
$options->unitsleft = !empty($question->unitsleft);
- $options->instructions = $this->import_or_save_files($question->instructions,
- $question->context, 'qtype_'.$question->qtype , 'instruction', $question->id);
- $options->instructionsformat = $question->instructions['format'];
-
$DB->update_record('question_numerical_options', $options);
// Report any problems.
@@ -407,11 +397,6 @@ class qtype_numerical extends question_type {
$formatoptions->noclean = true;
$formatoptions->para = false;
$nameprefix = $question->name_prefix;
- $component = 'qtype_' . $question->qtype;
- // rewrite instructions text
- $question->options->instructions = quiz_rewrite_question_urls(
- $question->options->instructions, 'pluginfile.php', $context->id, $component,
- 'instruction', array($state->attempt, $state->question), $question->id);
/// Print question text and media
$questiontext = format_text($question->questiontext,
@@ -672,9 +657,6 @@ class qtype_numerical extends question_type {
parent::move_files($questionid, $oldcontextid, $newcontextid);
$this->move_files_in_answers($questionid, $oldcontextid, $newcontextid);
-
- $fs->move_area_files_to_new_context($oldcontextid,
- $newcontextid, 'qtype_numerical', 'instruction', $questionid);
}
protected function delete_files($questionid, $contextid) {
@@ -682,7 +664,6 @@ class qtype_numerical extends question_type {
parent::delete_files($questionid, $contextid);
$this->delete_files_in_answers($questionid, $contextid);
- $fs->delete_area_files($contextid, 'qtype_numerical', 'instruction', $questionid);
}
}
@@ -797,9 +778,6 @@ class qtype_numerical_answer_processor {
$unit = substr($response, strlen($matchedpart));
}
$unit = trim($unit);
- if ($unit && !array_key_exists($unit, $this->units)) {
- $unit = '';
- }
return array($beforepoint, $decimals, $exponent, $unit);
}
@@ -826,7 +804,7 @@ class qtype_numerical_answer_processor {
$numberstring .= 'e' . $exponent;
}
- if ($unit) {
+ if ($unit && $this->is_known_unit($unit)) {
$value = $numberstring * $this->units[$unit];
} else {
$value = $numberstring * 1;
@@ -855,4 +833,13 @@ class qtype_numerical_answer_processor {
return $answer . ' ' . $unit;
}
}
+
+ /**
+ * Is this unit recognised.
+ * @param string $unit the unit
+ * @return bool whether this is a unit we recognise.
+ */
+ public function is_known_unit($unit) {
+ return array_key_exists($unit, $this->units);
+ }
}
diff --git a/question/type/numerical/simpletest/testanswerprocessor.php b/question/type/numerical/simpletest/testanswerprocessor.php
index d1986aa8c06..5ef9620de1a 100644
--- a/question/type/numerical/simpletest/testanswerprocessor.php
+++ b/question/type/numerical/simpletest/testanswerprocessor.php
@@ -77,7 +77,7 @@ class qtype_numerical_answer_processor_test extends UnitTestCase {
$ap->parse_response('1,000.00 m'));
$this->assertEqual(array(null, null, null, null), $ap->parse_response('frog'));
- $this->assertEqual(array('3', '', '', ''), $ap->parse_response('3 frogs'));
+ $this->assertEqual(array('3', '', '', 'frogs'), $ap->parse_response('3 frogs'));
$this->assertEqual(array(null, null, null, null), $ap->parse_response('. m'));
$this->assertEqual(array(null, null, null, null), $ap->parse_response('.e8 m'));
$this->assertEqual(array(null, null, null, null), $ap->parse_response(','));
@@ -92,7 +92,7 @@ class qtype_numerical_answer_processor_test extends UnitTestCase {
$this->assertEqual(array(299792458, 'c'), $ap->apply_units('1c'));
$this->assertEqual(array(0.44704, 'mph'), $ap->apply_units('0001.000 mph'));
- $this->assertEqual(array(1, ''), $ap->apply_units('1 frogs'));
+ $this->assertEqual(array(1, 'frogs'), $ap->apply_units('1 frogs'));
$this->assertEqual(array(null, null), $ap->apply_units('. m/s'));
}
@@ -120,6 +120,6 @@ class qtype_numerical_answer_processor_test extends UnitTestCase {
$this->assertEqual(array('100', '$'), $ap->apply_units('$100.'));
$this->assertEqual(array('100.00', '$'), $ap->apply_units('$100.00'));
$this->assertEqual(array('100', ''), $ap->apply_units('100'));
- $this->assertEqual(array('100', ''), $ap->apply_units('frog 100'));
+ $this->assertEqual(array('100', 'frog'), $ap->apply_units('frog 100'));
}
}
diff --git a/question/type/numerical/simpletest/testquestion.php b/question/type/numerical/simpletest/testquestion.php
index d788abddc54..7d61a58f27e 100644
--- a/question/type/numerical/simpletest/testquestion.php
+++ b/question/type/numerical/simpletest/testquestion.php
@@ -39,7 +39,7 @@ class qtype_numerical_question_test extends UnitTestCase {
$this->assertFalse($question->is_complete_response(array()));
$this->assertTrue($question->is_complete_response(array('answer' => '0')));
$this->assertTrue($question->is_complete_response(array('answer' => 0)));
- $this->assertTrue($question->is_complete_response(array('answer' => 'test')));
+ $this->assertFalse($question->is_complete_response(array('answer' => 'test')));
}
public function test_is_gradable_response() {
diff --git a/question/type/numerical/simpletest/testwalkthrough.php b/question/type/numerical/simpletest/testwalkthrough.php
new file mode 100644
index 00000000000..0240f17cbf1
--- /dev/null
+++ b/question/type/numerical/simpletest/testwalkthrough.php
@@ -0,0 +1,181 @@
+.
+
+/**
+ * This file contains overall tests of numerical questions.
+ *
+ * @package qtype
+ * @subpackage numerical
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php');
+require_once($CFG->dirroot . '/question/type/numerical/simpletest/helper.php');
+
+
+/**
+ * Unit tests for the numerical question type.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_numerical_walkthrough_test extends qbehaviour_walkthrough_test_base {
+ public function test_interactive_currency() {
+
+ // Create a gapselect question.
+ $q = test_question_maker::make_question('numerical', 'currency');
+ $q->hints = array(
+ new question_hint(1, 'This is the first hint.', FORMAT_HTML),
+ new question_hint(2, 'This is the second hint.', FORMAT_HTML),
+ );
+ $this->start_attempt_at_question($q, 'interactive', 3);
+
+ // Check the initial state.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_does_not_contain_feedback_expectation(),
+ $this->get_does_not_contain_validation_error_expectation(),
+ $this->get_does_not_contain_try_again_button_expectation(),
+ $this->get_no_hint_visible_expectation());
+
+ // Submit blank.
+ $this->process_submission(array('-submit' => 1, 'answer' => ''));
+
+ // Verify.
+ $this->check_current_state(question_state::$invalid);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_does_not_contain_feedback_expectation(),
+ $this->get_contains_validation_error_expectation(),
+ $this->get_does_not_contain_try_again_button_expectation(),
+ $this->get_no_hint_visible_expectation());
+
+ // Sumit something that does not look liek a number.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'newt'));
+
+ // Verify.
+ $this->check_current_state(question_state::$invalid);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_does_not_contain_feedback_expectation(),
+ $this->get_contains_validation_error_expectation(),
+ new PatternExpectation('/' .
+ preg_quote(get_string('invalidnumber', 'qtype_numerical') . '/')),
+ $this->get_does_not_contain_try_again_button_expectation(),
+ $this->get_no_hint_visible_expectation());
+
+ // Now get it right.
+ $this->process_submission(array('-submit' => 1, 'answer' => '$1332'));
+
+ // Verify.
+ $this->check_current_state(question_state::$gradedright);
+ $this->check_current_mark(3);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(3),
+ $this->get_contains_submit_button_expectation(false),
+ $this->get_contains_correct_expectation(),
+ $this->get_does_not_contain_validation_error_expectation(),
+ $this->get_no_hint_visible_expectation());
+ $this->assertEqual('$1332',
+ $this->quba->get_response_summary($this->slot));
+ }
+
+ public function test_deferredfeedback_currency() {
+
+ // Create a gapselect question.
+ $q = test_question_maker::make_question('numerical', 'currency');
+ $this->start_attempt_at_question($q, 'deferredfeedback', 3);
+
+ // Check the initial state.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_does_not_contain_feedback_expectation(),
+ $this->get_does_not_contain_validation_error_expectation(),
+ $this->get_does_not_contain_try_again_button_expectation(),
+ $this->get_no_hint_visible_expectation());
+
+ // Submit blank.
+ $this->process_submission(array('answer' => ''));
+
+ // Verify.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_does_not_contain_feedback_expectation(),
+ $this->get_does_not_contain_validation_error_expectation(),
+ $this->get_does_not_contain_try_again_button_expectation(),
+ $this->get_no_hint_visible_expectation());
+
+ // Sumit something that does not look like a number.
+ $this->process_submission(array('answer' => '$newt'));
+
+ // Verify.
+ $this->check_current_state(question_state::$invalid);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_does_not_contain_feedback_expectation(),
+ $this->get_contains_validation_error_expectation(),
+ new PatternExpectation('/' .
+ preg_quote(get_string('invalidnumber', 'qtype_numerical') . '/')),
+ $this->get_does_not_contain_try_again_button_expectation(),
+ $this->get_no_hint_visible_expectation());
+
+ // Now put in the right answer but without a unit.
+ $this->process_submission(array('answer' => '1332'));
+
+ $this->check_current_state(question_state::$complete);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_does_not_contain_feedback_expectation(),
+ $this->get_does_not_contain_validation_error_expectation(),
+ $this->get_does_not_contain_try_again_button_expectation(),
+ $this->get_no_hint_visible_expectation());
+
+ // Submit all and finish.
+ $this->process_submission(array('-finish' => '1'));
+
+ // Verify.
+ $this->check_current_state(question_state::$gradedpartial);
+ $this->check_current_mark(2.4);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(2.4),
+ $this->get_contains_partcorrect_expectation(),
+ $this->get_does_not_contain_validation_error_expectation(),
+ $this->get_no_hint_visible_expectation());
+ $this->assertEqual('1332',
+ $this->quba->get_response_summary($this->slot));
+ }
+
+ // Todo. Test validation if you try to type a unit into a question that does
+ // not expect one.
+}
diff --git a/question/type/numerical/version.php b/question/type/numerical/version.php
index 9001eb36044..46f8ef80420 100644
--- a/question/type/numerical/version.php
+++ b/question/type/numerical/version.php
@@ -26,5 +26,5 @@
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2010090501;
+$plugin->version = 2011042602;
$plugin->requires = 2010090501;