Bug 5968 - Improvements to the numerical question type. You can now display feedback for wrong answers. You can also award partial credit for different levels of accuracy.
This commit is contained in:
@@ -393,6 +393,14 @@ class qformat_gift extends qformat_default {
|
||||
// Note similarities to ShortAnswer
|
||||
$answertext = substr($answertext, 1); // remove leading "#"
|
||||
|
||||
// If there is feedback for a wrong answer, store it for now.
|
||||
if (($pos = strpos($answertext, '~')) !== false) {
|
||||
$wrongfeedback = substr($answertext, $pos);
|
||||
$answertext = substr($answertext, 0, $pos);
|
||||
} else {
|
||||
$wrongfeedback = '';
|
||||
}
|
||||
|
||||
$answers = explode("=", $answertext);
|
||||
if (isset($answers[0])) {
|
||||
$answers[0] = trim($answers[0]);
|
||||
@@ -454,6 +462,14 @@ class qformat_gift extends qformat_default {
|
||||
$question->tolerance[$key] = $tol;
|
||||
} // end foreach
|
||||
|
||||
if ($wrongfeedback) {
|
||||
$key += 1;
|
||||
$question->fraction[$key] = 0;
|
||||
$question->feedback[$key] = $this->commentparser($wrongfeedback);
|
||||
$question->answer[$key] = '';
|
||||
$question->tolerance[$key] = '';
|
||||
}
|
||||
|
||||
//$question->defaultgrade = 1;
|
||||
//$question->image = ""; // No images with this format
|
||||
//$question->multiplier = array(); // no numeric multipliers with GIFT
|
||||
@@ -571,12 +587,19 @@ function writequestion( $question ) {
|
||||
$expout .= "}\n";
|
||||
break;
|
||||
case NUMERICAL:
|
||||
$answer = array_pop( $question->options->answers );
|
||||
$tolerance = $answer->tolerance;
|
||||
$min = $answer->answer - $tolerance;
|
||||
$max = $answer->answer + $tolerance;
|
||||
$expout .= "::".$question->name."::".$tfname.$this->repchar( $question->questiontext, $textformat )."{\n";
|
||||
$expout .= "\t#".$min."..".$max."#".$this->repchar( $answer->feedback )."\n";
|
||||
$expout .= "::".$question->name."::".$tfname.$this->repchar( $question->questiontext, $textformat )."{#\n";
|
||||
foreach ($question->options->answers as $answer) {
|
||||
// DONOTCOMMIT
|
||||
echo '<pre>';
|
||||
var_export($answer);
|
||||
echo '</pre>';
|
||||
|
||||
if ($answer->answer != '') {
|
||||
$expout .= "\t=".$answer->answer.":".(float)$answer->tolerance."#".$this->repchar( $answer->feedback )."\n";
|
||||
} else {
|
||||
$expout .= "\t~#".$this->repchar( $answer->feedback )."\n";
|
||||
}
|
||||
}
|
||||
$expout .= "}\n";
|
||||
break;
|
||||
case MATCH:
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
<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 />
|
||||
@@ -30,7 +30,7 @@
|
||||
</font>
|
||||
</td>
|
||||
<td align="left">
|
||||
<?php if (isset($err["questiontext"])) {
|
||||
<?php if (isset($err["questiontext"])) {
|
||||
formerr($err["questiontext"]);
|
||||
echo "<br />";
|
||||
}
|
||||
@@ -54,9 +54,10 @@
|
||||
</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"),"","");
|
||||
@@ -65,42 +66,65 @@
|
||||
</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;
|
||||
@@ -111,12 +135,12 @@
|
||||
?>
|
||||
<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
|
||||
@@ -132,11 +156,11 @@ for ($i = 1; $i < 6; $i++) {
|
||||
<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
|
||||
|
||||
@@ -2,26 +2,29 @@
|
||||
|
||||
// Get all the extra information if we're editing
|
||||
if (!empty($question->id) && isset($question->qtype) &&
|
||||
$QTYPES[$question->qtype]->get_question_options($question)) {
|
||||
$answer = array_values($question->options->answers);
|
||||
usort($answer, create_function('$a, $b',
|
||||
'if ($a->fraction == $a->fraction) { return 0; }' .
|
||||
'else { return $a->fraction < $b->fraction ? -1 : 1; }'));
|
||||
$answer = $answer[0]; // Get the answer with the highest fraction (i.e. 1)
|
||||
$QTYPES[$question->qtype]->get_question_options($question)) {
|
||||
|
||||
$answers = array_values($question->options->answers);
|
||||
$units = array_values($question->options->units);
|
||||
usort($units, 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; }'));
|
||||
$tolerance = $answer->tolerance;
|
||||
'if (1.0 === (float)$a->multiplier) { return -1; } else '.
|
||||
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
|
||||
} else {
|
||||
$answer = new stdClass;
|
||||
$answer->answer = '';
|
||||
$answer->feedback = '';
|
||||
$answers = array();
|
||||
$units = array();
|
||||
$tolerance = '';
|
||||
}
|
||||
|
||||
// Add blank answers to make the number up to QUESTION_NUMANS
|
||||
// or one more than current, if there are already lots.
|
||||
$emptyanswer = new stdClass;
|
||||
$emptyanswer->answer = '';
|
||||
$i = count($answers);
|
||||
$limit = QUESTION_NUMANS;
|
||||
$limit = $limit <= $i ? $i+1 : $limit;
|
||||
for (; $i < $limit; $i++) {
|
||||
$answers[] = $emptyanswer;
|
||||
}
|
||||
|
||||
print_heading_with_help(get_string("editingnumerical", "quiz"), "numerical", "quiz");
|
||||
require("$CFG->dirroot/question/type/numerical/editquestion.html");
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
<?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() {
|
||||
@@ -45,9 +46,9 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,11 +89,11 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
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();
|
||||
@@ -102,6 +103,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
$oldoptions = array();
|
||||
}
|
||||
|
||||
// Save the units.
|
||||
$result = $this->save_numerical_units($question);
|
||||
if (isset($result->error)) {
|
||||
return $result;
|
||||
@@ -111,10 +113,17 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
|
||||
// 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]);
|
||||
|
||||
@@ -124,7 +133,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
$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;
|
||||
@@ -137,8 +146,15 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
}
|
||||
$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)) {
|
||||
@@ -151,23 +167,28 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
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($oldoptions)) {
|
||||
foreach($oldoptions as $oo) {
|
||||
delete_records('question_numerical', 'id', $oo->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);
|
||||
}
|
||||
}
|
||||
|
||||
// Report any problems.
|
||||
if (!empty($result->notice)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_numerical_units($question) {
|
||||
@@ -180,8 +201,8 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
$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);
|
||||
}
|
||||
@@ -190,8 +211,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
// 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]);
|
||||
@@ -200,6 +220,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
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);
|
||||
@@ -223,11 +244,11 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@@ -241,25 +262,25 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
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
|
||||
@@ -274,18 +295,14 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -308,11 +325,12 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
|
||||
// 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);
|
||||
@@ -326,8 +344,6 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
}
|
||||
$answers[$aid] = $r;
|
||||
}
|
||||
} else {
|
||||
$answers[]="error"; // just for debugging, eliminate
|
||||
}
|
||||
$result->id = $question->id;
|
||||
$result->responses = $answers;
|
||||
@@ -337,8 +353,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
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)
|
||||
@@ -376,40 +391,51 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -437,9 +463,9 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
return $status;
|
||||
}
|
||||
|
||||
/// RESTORE FUNCTIONS /////////////////
|
||||
/// RESTORE FUNCTIONS /////////////////
|
||||
|
||||
/*
|
||||
/**
|
||||
* Restores the data in the question
|
||||
*
|
||||
* This is used in question/restorelib.php
|
||||
@@ -456,6 +482,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
$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']['#']);
|
||||
@@ -467,7 +494,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
}
|
||||
|
||||
//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) {
|
||||
@@ -492,11 +519,8 @@ class question_numerical_qtype extends question_shortanswer_qtype {
|
||||
}
|
||||
|
||||
}
|
||||
//// 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");
|
||||
|
||||
Reference in New Issue
Block a user