<center>
<table cellpadding="5">
<tr valign="top">
- <td align="right"><b><?php print_string("category", "quiz") ?>:</b></td>
+ <td align="right"><b><?php print_string("category", "quiz") ?>:</b></td>
<td align="left">
- <?php question_category_select_menu($course->id, true, true, $question->category); ?>
+ <?php question_category_select_menu($course->id, true, true, $question->category); ?>
</td>
</tr>
<tr valign="top">
- <td align="right"><b><?php print_string("questionname", "quiz") ?>:</b></td>
+ <td align="right"><b><?php print_string("questionname", "quiz") ?>:</b></td>
<td align="left">
- <input type="text" name="name" size="50" value="<?php p($question->name) ?>" alt="<?php print_string("questionname", "quiz") ?>" />
- <?php if (isset($err["name"])) formerr($err["name"]); ?>
+ <input type="text" name="name" size="50" value="<?php p($question->name) ?>" alt="<?php print_string("questionname", "quiz") ?>" />
+ <?php if (isset($err["name"])) formerr($err["name"]); ?>
</td>
</tr>
<tr valign="top">
- <td align="right"><b><?php print_string("question", "quiz") ?>:</b>
+ <td align="right"><b><?php print_string("question", "quiz") ?>:</b>
<br />
<br />
<br />
</font>
</td>
<td align="left">
- <?php if (isset($err["questiontext"])) {
+ <?php if (isset($err["questiontext"])) {
formerr($err["questiontext"]);
echo "<br />";
}
</td>
</tr>
<tr valign="top">
- <td align="right"><b><?php print_string("imagedisplay", "quiz") ?>:</b></td>
+ <td align="right"><b><?php print_string("imagedisplay", "quiz") ?>:</b></td>
<td align="left">
- <?php if (empty($images)) {
+ <?php
+ if (empty($images)) {
print_string("noimagesyet");
} else {
choose_from_menu($images, "image", "$question->image", get_string("none"),"","");
</td>
</tr>
<tr valign="top">
- <td align="right"><b><?php print_string("defaultgrade", "quiz") ?>:</b></td>
+ <td align="right"><b><?php print_string("defaultgrade", "quiz") ?>:</b></td>
<td align="left">
- <input type="text" name="defaultgrade" size="3" value="<?php p($question->defaultgrade) ?>" alt="<?php print_string("defaultgrade", "quiz") ?>" />
+ <input type="text" name="defaultgrade" size="3" value="<?php p($question->defaultgrade) ?>" alt="<?php print_string("defaultgrade", "quiz") ?>" />
- <?php if (isset($err["defaultgrade"])) formerr($err["defaultgrade"]); ?>
+ <?php if (isset($err["defaultgrade"])) formerr($err["defaultgrade"]); ?>
</td>
</tr>
<tr valign="top">
- <td align="right"><b><?php print_string("penaltyfactor", "quiz") ?>:</b></td>
+ <td align="right"><b><?php print_string("penaltyfactor", "quiz") ?>:</b></td>
<td align="left">
- <input type="text" name="penalty" size="3" value="<?php p($question->penalty) ?>" alt="<?php print_string("penaltyfactor", "quiz") ?>" />
+ <input type="text" name="penalty" size="3" value="<?php p($question->penalty) ?>" alt="<?php print_string("penaltyfactor", "quiz") ?>" />
<?php helpbutton('penalty', get_string('penalty', 'quiz'), 'quiz'); ?>
- <?php if (isset($err["penalty"])) formerr($err["penalty"]); ?>
+ <?php if (isset($err["penalty"])) formerr($err["penalty"]); ?>
</td>
</tr>
+<!-- Answers. -->
+<?php
+ for ($i=1; $i<=count($answers); $i++) {
+ $answer = $answers[$i-1];
+?>
<tr valign="top">
- <td align="right"><b><?php print_string("correctanswer", "quiz") ?>:</b></td>
+ <td align="right"><b><?php echo get_string("answer", "quiz")." $i"; ?>:</b></td>
<td align="left">
- <input align="left" type="text" id="correct0" name="answer[]" size="10" value="<?php p($answer->answer) ?>" alt="<?php print_string("correctanswer", "quiz") ?>" />
+ <?php
+ if ($answer->answer === '' && !isset($answer->tolerance) && !isset($answer->fraction)) {
+ $answervalue = '';
+ $answertolerance = '';
+ $fractionval = 0;
+ $feedbacktext = '';
+ } else {
+ $answervalue = $answer->answer;
+ $answertolerance = $answer->tolerance;
+ $fractionval = $answer->fraction;
+ $feedbacktext = $answer->feedback;
+ }
+ ?>
+ <input type="text" name="answer[]" size="25" value="<?php p($answervalue); ?>" />
+ <?php echo get_string("acceptederror", "quiz"); ?> <input type="text" name="tolerance[]" size="15" value="<?php p($answertolerance) ?>" />±
+ <?php print_string("grade");
+ echo ": ";
+ choose_from_menu($gradeoptions, "fraction[]", $fractionval,""); ?>
+ <br />
</td>
</tr>
<tr valign="top">
- <td align="right"><b><?php print_string("acceptederror", "quiz"); ?>:</b></td>
+ <td align="right"><b><?php print_string("feedback", "quiz") ?>:</b></td>
<td align="left">
- <input align="left" type="text" id="acceptederror0" name="tolerance[]" size="10" value="<?php p($tolerance); ?>" alt="<?php print_string("acceptederror", "quiz"); ?>" />±
- <input type="hidden" name="fraction[]" value="1" />
+ <textarea name="feedback[]" rows="2" cols="50"><?php p($feedbacktext) ?></textarea>
</td>
</tr>
<tr valign="top">
- <td align="right"><b><?php print_string("feedback", "quiz") ?>:</b></td>
- <td align="left">
- <textarea name="feedback[]" rows="2" cols="50"><?php p($answer->feedback); ?></textarea>
- </td>
+ <td colspan="2"> </td>
</tr>
+<?php
+ }
+?>
+<!-- Units. -->
<tr valign="top">
-<td align="right"><b><?php print_string("unit", "quiz") ?>:</b></td>
+<td align="right"><b><?php print_string("unit", "quiz") ?>:</b></td>
<td align="left"><?php
if (isset($units[0]) && 1.0 === (float)$units[0]->multiplier) {
$unit = $units[0]->unit;
?>
<input type="hidden" name="multiplier[]" value="<?php p($multiplier) ?>" />
<input align="left" type="text" id="defaultunit" name="unit[]" size="10" value="<?php p($unit) ?>" alt="<?php print_string("unit", "quiz") ?>" />
- <b>(<?php print_string("optional", "quiz") ?>)</b>
+ <b>(<?php print_string("optional", "quiz") ?>)</b>
</td>
</tr>
<tr valign="top">
<td></td>
-<td align="left"><b><?php print_string("alternativeunits", "quiz") ?>:</b></td>
+<td align="left"><b><?php print_string("alternativeunits", "quiz") ?>:</b></td>
<td></td>
</tr>
<?php
<tr valign="top">
<td></td>
<td align="left">
- <b><?php print_string("multiplier", "quiz") ?>:</b>
- <input type="text" size="10" name="multiplier[]" value="<?php p($multiplier) ?>"
+ <b><?php print_string("multiplier", "quiz") ?>:</b>
+ <input type="text" size="10" name="multiplier[]" value="<?php p($multiplier) ?>"
alt="<?php print_string("multiplier", "quiz") ?>" />
<b> <?php print_string("unit", "quiz") ?>:</b>
- <input type="text" name="unit[]" size="5" value="<?php p($unit) ?>" />
+ <input type="text" name="unit[]" size="5" value="<?php p($unit) ?>" />
</td>
</tr>
<?php
-<?php // $Id$
-
-/////////////////
-/// NUMERICAL ///
-/////////////////
-
-/// QUESTION TYPE CLASS //////////////////
-
-///
-/// This class contains some special features in order to make the
-/// question type embeddable within a multianswer (cloze) question
-///
-
-/// This question type behaves like shortanswer in most cases.
-/// Therefore, it extends the shortanswer question type...
+<?php
+/**
+ * @version $Id$
+ * @author Martin Dougiamas and many others. Tim Hunt.
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package question
+ *//** */
require_once("$CFG->dirroot/question/type/shortanswer/questiontype.php");
+/**
+ * NUMERICAL QUESTION TYPE CLASS
+ *
+ * This class contains some special features in order to make the
+ * question type embeddable within a multianswer (cloze) question
+ *
+ * This question type behaves like shortanswer in most cases.
+ * Therefore, it extends the shortanswer question type...
+ */
class question_numerical_qtype extends question_shortanswer_qtype {
function name() {
foreach($question->options->answers as $key => $val) {
$answer = trim($val->answer);
$length = strlen($defaultunit->unit);
- if (substr($answer, -$length) == $defaultunit->unit) {
+ if ($length && substr($answer, -$length) == $defaultunit->unit) {
$question->options->answers[$key]->answer =
- substr($answer, 0, strlen($answer)-$length);
+ substr($answer, 0, strlen($answer)-$length);
}
}
}
return $unit;
}
+ /**
+ * Save the units and the answers associated with this question.
+ */
function save_question_options($question) {
- // save_question_options supports the definition of multiple answers
- // for numerical questions. This is not currently used by the editing
- // interface, but the GIFT format supports it. The multianswer qtype,
- // for example can make use of this feature.
+
// Get old versions of the objects
if (!$oldanswers = get_records("question_answers", "question", $question->id)) {
$oldanswers = array();
$oldoptions = array();
}
+ // Save the units.
$result = $this->save_numerical_units($question);
if (isset($result->error)) {
return $result;
// Insert all the new answers
foreach ($question->answer as $key => $dataanswer) {
- if ($dataanswer != "") {
+ if ($dataanswer != '' || trim($question->feedback[$key])) {
$answer = new stdClass;
$answer->question = $question->id;
- $answer->answer = trim($dataanswer);
+ if (trim($dataanswer) == '') {
+ $answer->answer = '';
+ } else {
+ $answer->answer = $this->apply_unit($dataanswer, $units);
+ if ($answer->answer === false) {
+ $result->notice = get_string('invalidnumericanswer', 'quiz');
+ }
+ }
$answer->fraction = $question->fraction[$key];
$answer->feedback = trim($question->feedback[$key]);
$result->error = "Could not update quiz answer! (id=$answer->id)";
return $result;
}
- } else { // This is a completely new answer
+ } else { // This is a completely new answer
if (! $answer->id = insert_record("question_answers", $answer)) {
$result->error = "Could not insert quiz answer!";
return $result;
}
$options->question = $question->id;
$options->answer = $answer->id;
- $options->tolerance = $this->apply_unit($question->tolerance[$key], $units);
-
+ if (trim($question->tolerance[$key]) == '') {
+ $options->tolerance = '';
+ } else {
+ $options->tolerance = $this->apply_unit($question->tolerance[$key], $units);
+ if ($options->tolerance === false) {
+ $result->notice = get_string('invalidnumerictolerance', 'quiz');
+ }
+ }
+
// Save options
if (isset($options->id)) { // reusing existing record
if (! update_record('question_numerical', $options)) {
return $result;
}
}
+ }
+ }
+ // delete old answer records
+ if (!empty($oldanswers)) {
+ foreach($oldanswers as $oa) {
+ delete_records('question_answers', 'id', $oa->id);
+ }
+ }
- // delete old answer records
- if (!empty($oldanswers)) {
- foreach($oldanswers as $oa) {
- delete_records('question_answers', 'id', $oa->id);
- }
- }
-
- // delete old answer records
- if (!empty($oldoptions)) {
- foreach($oldoptions as $oo) {
- delete_records('question_numerical', 'id', $oo->id);
- }
- }
-
+ // delete old answer records
+ if (!empty($oldoptions)) {
+ foreach($oldoptions as $oo) {
+ delete_records('question_numerical', 'id', $oo->id);
}
}
+
+ // Report any problems.
+ if (!empty($result->notice)) {
+ return $result;
+ }
+
+ return true;
}
function save_numerical_units($question) {
$keys = array();
$oldunits = array_values($oldunits);
usort($oldunits, create_function('$a, $b', // make sure the default unit is at index 0
- 'if (1.0 === (float)$a->multiplier) { return -1; } else '.
- 'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
+ 'if (1.0 === (float)$a->multiplier) { return -1; } else '.
+ 'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
foreach ($oldunits as $unit) {
$units[] = clone($unit);
}
// Discard any unit which doesn't specify the unit or the multiplier
if (!empty($question->multiplier[$i]) && !empty($question->unit[$i])) {
$units[$i]->question = $question->id;
- $units[$i]->multiplier =
- $this->apply_unit($question->multiplier[$i], array());
+ $units[$i]->multiplier = $this->apply_unit($question->multiplier[$i], array());
$units[$i]->unit = $question->unit[$i];
} else {
unset($units[$i]);
unset($question->multiplier, $question->unit);
/// Save units
+ $result = new stdClass;
for ($i = 0; $i < $n; $i++) {
if (!isset($units[$i]) && isset($oldunits[$i])) { // Delete if it hasn't been resubmitted
delete_records('question_numerical_units', 'id', $oldunits[$i]->id);
}
/**
- * Deletes question from the question-type specific tables
- *
- * @return boolean Success/Failure
- * @param object $question The question being deleted
- */
+ * Deletes question from the question-type specific tables
+ *
+ * @return boolean Success/Failure
+ * @param object $question The question being deleted
+ */
function delete_question($questionid) {
delete_records("question_numerical", "question", $questionid);
delete_records("question_numerical_units", "question", $questionid);
return ($response == $testresponse);
}
-
-
- // Checks whether a response matches a given answer, taking the tolerance
- // into account. Returns a true for if a response matches the answer, false
- // if it doesn't.
+ /**
+ * Checks whether a response matches a given answer, taking the tolerance
+ * and units into account. Returns a true for if a response matches the
+ * answer, false if it doesn't.
+ */
function test_response(&$question, &$state, $answer) {
- if (isset($state->responses[''])) {
- $response = $this->apply_unit(stripslashes($state->responses['']),
- $question->options->units);
- } else {
- $response = '';
+ if ($answer->answer == '') {
+ return true; // Blank answer matches anything.
}
- if (is_numeric($response) && is_numeric($answer->answer)) {
- $this->get_tolerance_interval($answer);
- return ($answer->min <= $response && $answer->max >= $response);
- } else {
- return ($response == $answer->answer);
+ $response = $this->apply_unit(stripslashes($state->responses['']), $question->options->units);
+
+ if ($response === false) {
+ return false; // The student did not type a number.
}
+
+ // The student did type a number, so check it with tolerances.
+ $this->get_tolerance_interval($answer);
+ return ($answer->min <= $response && $response <= $answer->max);
}
// ULPGC ecastro
}
function grade_responses(&$question, &$state, $cmoptions) {
- $answers = &$question->options->answers;
+ $answers = &$question->options->answers;
$state->raw_grade = 0;
foreach($answers as $answer) {
if($this->test_response($question, $state, $answer)) {
- if ($state->raw_grade < $answer->fraction) {
- $state->raw_grade = $answer->fraction;
- }
+ $state->raw_grade = $answer->fraction;
+ break;
}
}
- if (empty($state->raw_grade)) {
- $state->raw_grade = 0;
- }
// Make sure we don't assign negative or too high marks
$state->raw_grade = min(max((float) $state->raw_grade,
// ULPGC ecastro
function get_all_responses(&$question, &$state) {
- unset($answers);
+ $result = new stdClass;
+ $answers = array();
$unit = $this->get_default_numerical_unit($question);
if (is_array($question->options->answers)) {
foreach ($question->options->answers as $aid=>$answer) {
- unset ($r);
+ $r = new stdClass;
$r->answer = $answer->answer;
$r->credit = $answer->fraction;
$this->get_tolerance_interval($answer);
}
$answers[$aid] = $r;
}
- } else {
- $answers[]="error"; // just for debugging, eliminate
}
$result->id = $question->id;
$result->responses = $answers;
function get_tolerance_interval(&$answer) {
// No tolerance
if (empty($answer->tolerance)) {
- $answer->min = $answer->max = $answer->answer;
- return true;
+ $answer->tolerance = 0;
}
// Calculate the interval of correct responses (min/max)
}
/**
- * Checks if the $rawresponse has a unit and applys it if appropriate.
- *
- * @param string $rawresponse The response string to be converted to a float.
- * @param array $units An array with the defined units, where the
- * unit is the key and the multiplier the value.
- * @return float The rawresponse with the unit taken into
- * account as a float.
- */
+ * Checks if the $rawresponse has a unit and applys it if appropriate.
+ *
+ * @param string $rawresponse The response string to be converted to a float.
+ * @param array $units An array with the defined units, where the
+ * unit is the key and the multiplier the value.
+ * @return float The rawresponse with the unit taken into
+ * account as a float.
+ */
function apply_unit($rawresponse, $units) {
// Make units more useful
$tmpunits = array();
foreach ($units as $unit) {
$tmpunits[$unit->unit] = $unit->multiplier;
}
-
+ // remove spaces and normalise decimal places.
$search = array(' ', ',');
$replace = array('', '.');
- $rawresponse = str_replace($search, $replace, $rawresponse); // remove spaces
- if (ereg(
- '^([+-]?([0-9]+(\\.[0-9]*)?|[.][0-9]+)([eE][-+]?[0-9]+)?)([^0-9].*)?$',
- $rawresponse, $responseparts)) {
- $responsenum = (float)$responseparts[1];
- if (isset($tmpunits[$responseparts[5]])) {
- return (float)$responseparts[1] / $tmpunits[$responseparts[5]];
+ $rawresponse = str_replace($search, $replace, trim($rawresponse));
+
+ // Apply any unit that is present.
+ if (ereg('^([+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][-+]?[0-9]+)?)([^0-9].*)?$',
+ $rawresponse, $responseparts)) {
+
+ if (!empty($responseparts[5])) {
+
+ if (isset($tmpunits[$responseparts[5]])) {
+ // Valid number with unit.
+ return (float)$responseparts[1] / $tmpunits[$responseparts[5]];
+ } else {
+ // Valid number with invalid unit. Must be wrong.
+ return false;
+ }
+
} else {
+ // Valid number without unit.
return (float)$responseparts[1];
}
}
- return $rawresponse;
+ // Invalid number. Must be wrong.
+ return false;
}
-/// BACKUP FUNCTIONS ////////////////////////////
+ /// BACKUP FUNCTIONS ////////////////////////////
- /*
+ /**
* Backup the data in the question
*
* This is used in question/backuplib.php
return $status;
}
-/// RESTORE FUNCTIONS /////////////////
+ /// RESTORE FUNCTIONS /////////////////
- /*
+ /**
* Restores the data in the question
*
* This is used in question/restorelib.php
$num_info = $numericals[$i];
//Now, build the question_numerical record structure
+ $numerical = new stdClass;
$numerical->question = $new_question_id;
$numerical->answer = backup_todb($num_info['#']['ANSWER']['0']['#']);
$numerical->tolerance = backup_todb($num_info['#']['TOLERANCE']['0']['#']);
}
//The structure is equal to the db, so insert the question_numerical
- $newid = insert_record ("question_numerical",$numerical);
+ $newid = insert_record ("question_numerical", $numerical);
//Do some output
if (($i+1) % 50 == 0) {
}
}
-//// END OF CLASS ////
-//////////////////////////////////////////////////////////////////////////
-//// INITIATION - Without this line the question type is not in use... ///
-//////////////////////////////////////////////////////////////////////////
+// INITIATION - Without this line the question type is not in use.
$QTYPES['numerical']= new question_numerical_qtype();
// The following adds the questiontype to the menu of types shown to teachers
$QTYPE_MENU['numerical'] = get_string("numerical", "quiz");