Mostly coded by Henrik Kaipe - I changed a few things on the way in.
<li><a href="help.php?module=quiz&file=categories.html">Categories</a>\r
<li><a href="help.php?module=quiz&file=correctanswers.html">Correct answers</a>\r
<li><a href="help.php?module=quiz&file=createmultiple.html">Creating multiple quizzes</a>\r
+ <li><a href="help.php?module=quiz&file=description.html">Descriptions</a>\r
<li><a href="help.php?module=quiz&file=grademethod.html">Grading method</a>\r
<li><a href="help.php?module=quiz&file=import.html">Importing questions</a>\r
<li><a href="help.php?module=quiz&file=match.html">Matching questions</a>\r
<li><a href="help.php?module=quiz&file=maxgrade.html">Maximum grade</a>\r
<li><a href="help.php?module=quiz&file=multichoice.html">Multiple choice questions</a>\r
+ <li><a href="help.php?module=quiz&file=numerical.html">Numerical questions</a>\r
<li><a href="help.php?module=quiz&file=timeopen.html">Opening and closing the quiz</a>\r
<li><a href="help.php?module=quiz&file=feedback.html">Question Feedback</a>\r
<li><a href="help.php?module=quiz&file=questiontypes.html">Question types- creating a new question</a>\r
--- /dev/null
+<P ALIGN=CENTER><B>Numerical questions</B></P>
+
+<p>In response to a question (that may include a image) the respondent
+ must type a number.</p>
+
+<p>The required answer may have an accepted error. This allows a range of
+ answers to be set.</p>
+
+<p>For example, if the answer is 30 with an error of 5, then any number
+ between 25 and 35 will be accepted as correct.</p>
$string['modulenameplural'] = "Quizzes";
#------------------------------------------------------------
+$string['acceptederror'] = "Accepted error";
$string['addquestions'] = "Add questions";
$string['addingquestions'] = "This side of the page is where you manage your database of questions. Questions are stored in categories to help you keep them organised, and can be used by any quiz in your course or even other courses if you choose to 'publish' them. <br /><br />After you select or create a question category you will be able to create or edit questions. You can select any of these questions to add to your quiz over on the other side of this page.";
$string['addquestionstoquiz'] = "Add questions to current quiz";
$string['answerhowmany'] = "One or multiple answers?";
$string['answersingleyes'] = "One answer only";
$string['answersingleno'] = "Multiple answers allowed";
+$string['answerswithacceptederrormarginmustbenumeric'] = "Answers with accepted error must be numeric";
$string['attempt'] = "Attempt \$a";
$string['attemptfirst'] = "First attempt";
$string['attemptlast'] = "Last attempt";
$string['editingdescription'] = "Editing a Description";
$string['editingmatch'] = "Editing a Matching Question";
$string['editingmultichoice'] = "Editing a Multiple Choice question";
+$string['editingnumerical'] = "Editing a numerical question";
$string['editingquiz'] = "Editing quiz";
$string['editingquestion'] = "Editing a question";
$string['editingrandom'] = "Editing a Random Question";
$string['marks'] = "Marks";
$string['match'] = "Matching";
$string['matchanswer'] = "Matching answer";
+$string['missingcorrectanswer'] = "Correct answer must be specified";
$string['missingname'] = "Missing question name";
$string['missingquestiontext'] = "Missing question text";
$string['missingword'] = "Missing word format";
$string['noreviewuntil'] = "You are not allowed to review this quiz until \$a";
$string['notenoughsubquestions'] = "Not enough sub-questions have been defined!<br>
Do you want to go back and fix this question?";
+$string['numerical'] = "Numerical";
$string['publish'] = "Publish";
$string['qti'] = "IMS QTI format";
$string['question'] = "Question";
table_column("quiz", "", "shuffleanswers", "INTEGER", "4", "UNSIGNED", "0", "NOT NULL", "shufflequestions");
}
+ if ($oldversion < 2003071000) {
+
+ modify_database ("", " CREATE TABLE `prefix_quiz_numerical` (
+ `id` int(10) unsigned NOT NULL auto_increment,
+ `answer` int(10) unsigned NOT NULL default '0',
+ `min` varchar(255) NOT NULL default '',
+ `max` varchar(255) NOT NULL default '',
+ PRIMARY KEY (`id`),
+ KEY `answer` (`answer`)
+ ) TYPE=MyISAM COMMENT='Options for numerical questions'; ");
+ }
+
return true;
}
) TYPE=MyISAM COMMENT='Options for short answer questions';
# --------------------------------------------------------
+#
+# Table structure for table `quiz_numerical`
+#
+
+CREATE TABLE `prefix_quiz_numerical` (
+ `id` int(10) unsigned NOT NULL auto_increment,
+ `answer` int(10) unsigned NOT NULL default '0',
+ `min` varchar(255) NOT NULL default '',
+ `max` varchar(255) NOT NULL default '',
+ PRIMARY KEY (`id`),
+ KEY `answer` (`answer`)
+) TYPE=MyISAM COMMENT='Options for numerical questions';
+# --------------------------------------------------------
+
#
# Table structure for table `quiz_truefalse`
#
define("MATCH", "5");
define("RANDOMSAMATCH", "6");
define("DESCRIPTION", "7");
+define("NUMERICAL", "8");
$QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz"),
TRUEFALSE => get_string("truefalse", "quiz"),
SHORTANSWER => get_string("shortanswer", "quiz"),
+ NUMERICAL => get_string("numerical", "quiz"),
MATCH => get_string("match", "quiz"),
+ DESCRIPTION => get_string("description", "quiz"),
RANDOM => get_string("random", "quiz"),
- RANDOMSAMATCH => get_string("randomsamatch", "quiz"),
- DESCRIPTION => get_string("description", "quiz") );
+ RANDOMSAMATCH => get_string("randomsamatch", "quiz")
+ );
$QUIZ_FILE_FORMAT = array ( "custom" => get_string("custom", "quiz"),
"missingword" => get_string("missingword", "quiz"),
AND q.id = a.question ");
break;
+ case NUMERICAL: // Logical support for multiple answers
+ return get_records_sql("SELECT a.*, n.min, n.max
+ FROM {$CFG->prefix}quiz_numerical n,
+ {$CFG->prefix}quiz_answers a
+ WHERE a.question = '$question->id'
+ AND n.answer = a.id ");
+ break;
+
+
default:
return false;
}
function quiz_print_comment($text) {
global $THEME;
- echo "<SPAN CLASS=feedbacktext>".text_to_html($text, true, false)."</SPAN>";
+ echo "<span class=feedbacktext>".text_to_html($text, true, false)."</span>";
}
function quiz_print_correctanswer($text) {
global $THEME;
- echo "<P ALIGN=RIGHT><SPAN CLASS=highlight>$text</SPAN></P>";
+ echo "<p align=right><span class=highlight>$text</span></p>";
}
function quiz_print_question_icon($question, $editlink=true) {
global $QUIZ_QUESTION_TYPE;
if ($editlink) {
- echo "<A HREF=\"question.php?id=$question->id\" TITLE=\"".$QUIZ_QUESTION_TYPE[$question->qtype]."\">";
+ echo "<a href=\"question.php?id=$question->id\" title=\"".$QUIZ_QUESTION_TYPE[$question->qtype]."\">";
}
switch ($question->qtype) {
case SHORTANSWER:
- echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/sa.gif\">";
+ echo '<img border=0 height=16 width=16 src="pix/sa.gif">';
break;
case TRUEFALSE:
- echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/tf.gif\">";
+ echo '<img border=0 height=16 width=16 src="pix/tf.gif">';
break;
case MULTICHOICE:
- echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/mc.gif\">";
+ echo '<img border=0 height=16 width=16 src="pix/mc.gif">';
break;
case RANDOM:
- echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/rs.gif\">";
+ echo '<img border=0 height=16 width=16 src="pix/rs.gif">';
break;
case MATCH:
- echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/ma.gif\">";
+ echo '<img border=0 height=16 width=16 src="pix/ma.gif">';
break;
case RANDOMSAMATCH:
- echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/rm.gif\">";
+ echo '<img border=0 height=16 width=16 src="pix/rm.gif">';
break;
case DESCRIPTION:
- echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/de.gif\">";
+ echo '<img border=0 height=16 width=16 src="pix/de.gif">';
+ break;
+ case NUMERICAL:
+ echo '<img border=0 height=16 width=16 src="pix/nu.gif">';
break;
}
if ($editlink) {
- echo "</A>\n";
+ echo "</a>\n";
}
}
$stranswer = get_string("answer", "quiz");
$strmarks = get_string("marks", "quiz");
- echo "<TABLE WIDTH=100% CELLSPACING=10><TR><TD NOWRAP WIDTH=100 VALIGN=top>";
- echo "<P ALIGN=CENTER><B>$number</B></P>";
+ echo "<table width=100% cellspacing=10><tr><td nowrap width=100 valign=top>";
+ echo "<p align=center><b>$number</b></p>";
if ($feedback or $response) {
- echo "<P ALIGN=CENTER><FONT SIZE=1>$strmarks: $actualgrade/$grade</FONT></P>";
+ echo "<p align=center><font size=1>$strmarks: $actualgrade/$grade</font></p>";
} else {
- echo "<P ALIGN=CENTER><FONT SIZE=1>$grade $strmarks</FONT></P>";
+ echo "<p align=center><font size=1>$grade $strmarks</font></p>";
}
print_spacer(1,100);
- echo "</TD><TD VALIGN=TOP>";
+ echo "</td><td valign=top>";
if (empty($realquestion)) {
$realquestion->id = $question->id;
switch ($question->qtype) {
case SHORTANSWER:
- if (!$options = get_record("quiz_shortanswer", "question", $question->id)) {
- notify("Error: Missing question options!");
- }
+ case NUMERICAL:
echo text_to_html($question->questiontext);
if ($question->image) {
print_file_picture($question->image, $courseid);
}
$response[0] = $question->answer;
$bestshortanswer = 0;
- foreach($answers as $answer) { // There might be multiple right answers
+ foreach ($answers as $answer) { // There might be multiple right answers
if ($answer->fraction > $bestshortanswer) {
$correct[$answer->id] = $answer->answer;
$bestshortanswer = $answer->fraction;
}
break;
+ case NUMERICAL:
+ if ($question->answer) {
+ $question->answer = trim(stripslashes($question->answer[0]));
+ } else {
+ $question->answer = "";
+ }
+ $response[0] = $question->answer;
+ $bestshortanswer = 0;
+ foreach ($answers as $answer) { // There might be multiple right answers
+ if ($answer->fraction > $bestshortanswer) {
+ $correct[$answer->id] = $answer->answer;
+ $bestshortanswer = $answer->fraction;
+ }
+ if ( ((float)$question->answer >= (float)$answer->min) and
+ ((float)$question->answer <= (float)$answer->max) ) {
+ $feedback[0] = $answer->feedback;
+ $grade = (float)$answer->fraction * $question->grade;
+ }
+ }
+ break;
case TRUEFALSE:
if ($question->answer) {
}
break;
+ case NUMERICAL: // Note similarities to SHORTANSWER
+
+ if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
+ $oldanswers = array();
+ }
+
+ $answers = array();
+ $maxfraction = -1;
+
+ // Insert all the new answers
+ foreach ($question->answer as $key => $dataanswer) {
+ if ($dataanswer != "") {
+ if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
+ $answer = $oldanswer;
+ $answer->answer = $dataanswer;
+ $answer->fraction = $question->fraction[$key];
+ $answer->feedback = $question->feedback[$key];
+ if (!update_record("quiz_answers", $answer)) {
+ $result->error = "Could not update quiz answer! (id=$answer->id)";
+ return $result;
+ }
+ } else { // This is a completely new answer
+ unset($answer);
+ $answer->answer = $dataanswer;
+ $answer->question = $question->id;
+ $answer->fraction = $question->fraction[$key];
+ $answer->feedback = $question->feedback[$key];
+ if (!$answer->id = insert_record("quiz_answers", $answer)) {
+ $result->error = "Could not insert quiz answer!";
+ return $result;
+ }
+ }
+ $answers[] = $answer->id;
+ if ($question->fraction[$key] > $maxfraction) {
+ $maxfraction = $question->fraction[$key];
+ }
+
+ if ($options = get_record("quiz_numerical", "answer", $answer->id)) {
+ $options->min= $question->min[$key];
+ $options->max= $question->max[$key];
+ if (!update_record("quiz_numerical", $options)) {
+ $result->error = "Could not update quiz numerical options! (id=$options->id)";
+ return $result;
+ }
+ } else { // completely new answer
+ unset($options);
+ $options->min= $question->min[$key];
+ $options->max= $question->max[$key];
+ $options->answer= $answer->id;
+ if (!insert_record("quiz_numerical", $options)) {
+ $result->error = "Could not insert quiz numerical options!";
+ return $result;
+ }
+ }
+ }
+ }
+
+ /// Perform sanity checks on fractional grades
+ if ($maxfraction != 1) {
+ $maxfraction = $maxfraction * 100;
+ $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
+ return $result;
+ }
+ break;
case TRUEFALSE:
--- /dev/null
+<FORM name="theform" method="post" <?=$onsubmit ?> action="question.php">
+<CENTER>
+<TABLE cellpadding=5>
+<TR valign=top>
+ <TD align=right><P><B><? print_string("category", "quiz") ?>:</B></P></TD>
+ <TD>
+ <? choose_from_menu($categories, "category", "$question->category", ""); ?>
+ </TD>
+</TR>
+<TR valign=top>
+ <TD align=right><P><B><? print_string("questionname", "quiz") ?>:</B></P></TD>
+ <TD>
+ <INPUT type="text" name="name" size=50 value="<? p($question->name) ?>">
+ <? if (isset($err["name"])) formerr($err["name"]); ?>
+ </TD>
+</TR>
+<TR valign=top>
+ <TD align=right><P><B><? print_string("question", "quiz") ?>:</B></P></TD>
+ <TD>
+ <? if (isset($err["questiontext"])) {
+ formerr($err["questiontext"]);
+ echo "<BR \>";
+ }
+ print_textarea($usehtmleditor, 15, 60, 630, 300, "questiontext", $question->questiontext);
+ if ($usehtmleditor) {
+ helpbutton("richtext", get_string("helprichtext"), "moodle");
+ } else {
+ helpbutton("text", get_string("helptext"), "moodle");
+ }
+ ?>
+ </TD>
+</TR>
+<TR valign=top>
+ <TD align=right><P><B><? print_string("imagedisplay", "quiz") ?>:</B></P></TD>
+ <TD>
+ <? 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><P><B><? print_string("correctanswer", "quiz") ?>:</B></P></TD>
+ <?
+ // Even thou the rest of the module can handle up to six numerical answers,
+ // this form will limit the number of numerical answers to one only.
+ if (is_numeric($answers[0]->min) && is_numeric($answers[0]->answer)) {
+ $acceptederror = (float)($answers[0]->answer)
+ - (float)($answers[0]->min);
+ } else {
+ $acceptederror = "";
+ }
+ ?>
+ <TD>
+ <INPUT align="RIGHT" type="text" id="correct0" name="answer[]" size="20" value="<? p($answers[0]->answer) ?>"/>
+ <B><? print_string("acceptederror", "quiz"); ?>:</B>
+ <INPUT align="LEFT" type="text" id="acceptederror0" name="acceptederror[]" size="15" value="<? p($acceptederror) ?>" />
+ <!-- Values max and min will be determined when the form is submitted -->
+ <INPUT type="HIDDEN" id="min0" name="min[]" value=""/>
+ <INPUT type="HIDDEN" id="max0" name="max[]" value=""/>
+ <INPUT type="HIDDEN" name="fraction[]" value="1"/>
+ <BR/>
+ </TD>
+</TR>
+<TR valign=top>
+ <TD align=right><P><B><? print_string("feedback", "quiz") ?>:</B></P></TD>
+ <TD>
+ <textarea name="feedback[]" rows=2 cols=50 wrap="virtual"><? p($answers[0]->feedback) ?></textarea>
+ </TD>
+</TR>
+</TABLE>
+
+<INPUT type="hidden" name=id value="<? p($question->id) ?>">
+<INPUT type="hidden" name=qtype value="<? p($question->qtype) ?>">
+<INPUT type="submit" onClick="return determineMinAndMax();" value="<? print_string("savechanges") ?>">
+</CENTER>
+</FORM>
+<SCRIPT language="JAVASCRIPT">
+function determineMinAndMax() {
+ // This client-side script will determine the values for min and max
+ // based on the input for answer and acceptederror.
+ with(document.theform) {
+ if (correct0.value=='') {
+ alert('<? print_string("missingcorrectanswer","quiz") ?>');
+ return false;
+ } else if (acceptederror0.value=='') {
+ var correct= parseFloat(correct0.value);
+ if (!isNaN(correct)) {
+ min0.value= correct;
+ max0.value= correct;
+ }
+ return true;
+ } else if (isNaN(acceptederror0.value) || isNaN(correct0.value)) {
+ alert('<? print_string("answerswithacceptederrormarginmustbenumeric", "quiz") ?>');
+ return false;
+ } else {
+ var correct= parseFloat(correct0.value);
+ var error= Math.abs(acceptederror0.value);
+ min0.value= correct-error;
+ max0.value= correct+error;
+ return true;
+ }
+ }
+}
+</SCRIPT>
+<?
+ if ($usehtmleditor) {
+ print_richedit_javascript("theform", "questiontext", "no");
+ }
+?>
+
break;
case DESCRIPTION:
- print_heading(get_string("editingdescription", "quiz"));
+ print_heading_with_help(get_string("editingdescription", "quiz"), "description", "quiz");
require("description.html");
break;
+ case NUMERICAL:
+ // This will only support one answer of the type NUMERICAL
+ // However, lib.php has support for multiple answers
+ if (!empty($question->id)) {
+ $answersraw= quiz_get_answers($question);
+ }
+ $answers= array();
+ for ($i=0; $i<6; $i++) {
+ $answers[$i]->answer = ""; // Make answer slots, default as blank...
+ $answers[$i]->min = "";
+ $answers[$i]->max = "";
+ $answers[$i]->feedback = "";
+ }
+ if (!empty($answersraw)) {
+ $i=0;
+ foreach ($answersraw as $answer) {
+ $answers[$i] = $answer;
+ $i++;
+ }
+ }
+ print_heading_with_help(get_string("editingnumerical", "quiz"), "numerical", "quiz");
+ require("numerical.html");
+ break;
+
+
default:
error("Invalid question type");
break;
// This fragment is called by moodle_needs_upgrading() and /admin/index.php
////////////////////////////////////////////////////////////////////////////////
-$module->version = 2003042702; // The (date) version of this module
+$module->version = 2003071000; // The (date) version of this module
$module->cron = 0; // How often should cron check this module (seconds)?
?>