--- /dev/null
+<p align=center><b>Random Matching questions</b></p>
+
+<p>In response to a question (that may include a image) the respondent
+ is presented with several questions and several answers. There is one
+ correct answer for each question.
+
+<p>The respondent selects the answers that match each question.
+
+<p>The questions and answers are randomly drawn from the pool of
+ "Short Answer" questions in the current category.
$string['editingshortanswer'] = "Editing a short answer question";
$string['editingtruefalse'] = "Editing a true/false question";
$string['editingmultichoice'] = "Editing a multiple choice question";
+$string['editingrandommatch'] = "Editing a random matching question";
$string['false'] = "False";
$string['feedback'] = "Feedback";
$string['filloutoneanswer'] = "You must fill out at least one possible answer. Answers left blank will not be used.";
$string['quizopen'] = "Open the quiz";
$string['quiznotavailable'] = "The quiz will not be available until: \$a";
$string['random'] = "Random set";
+$string['randommatch'] = "Random Match";
+$string['randommatchintro'] = "For each of the following questions, select the matching answer from the menu.";
+$string['randommatchnumber'] = "Number of questions to select";
$string['readytosend'] = "You are about to send your whole quiz to be graded. Are you sure you want to continue?";
$string['regrade'] = "Regrade all attempts";
$string['regradecomplete'] = "All attempts have been regraded";
error("No questions found!");
}
- foreach ($rawanswers as $key => $value) { // Parse input for question -> answers
+ foreach ($rawanswers as $key => $value) { // Parse input for question -> answers
if (substr($key, 0, 1) == "q") {
$key = substr($key,1);
- if (!isset($questions[$key])) {
- if (substr_count($key, "a")) { // checkbox style multiple answers
- $check = explode("a", $key);
- $key = $check[0];
- $value = $check[1];
- } else {
- error("Answer received for non-existent question ($key)!");
- }
+ if (isset($questions[$key])) { // It's a real question number, not a coded one
+ $questions[$key]->answer[] = trim($value);
+
+ } else if (substr_count($key, "a")) { // Checkbox style multiple answers
+ $check = explode("a", $key);
+ $key = $check[0]; // The main question number
+ $value = $check[1]; // The actual answer
+ $questions[$key]->answer[] = trim($value);
+
+ } else if (substr_count($key, "r")) { // Random-style questions
+ $check = explode("r", $key);
+ $key = $check[0]; // The main question
+ $rand = $check[1]; // The random sub-question
+ $questions[$key]->answer[] = "$rand-$value";
+
+ } else {
+ error("Answer received for non-existent question ($key)!");
}
- $questions[$key]->answer[] = trim($value); // Store answers in array (whitespace trimmed)
}
}
table_column("quiz_questions", "type", "qtype", "INTEGER", "10", "UNSIGNED", "0", "NOT NULL", "");
}
+ if ($oldversion < 2003022303) {
+ modify_database ("", "CREATE TABLE `prefix_quiz_randommatch` (
+ `id` int(10) unsigned NOT NULL auto_increment,
+ `question` int(10) unsigned NOT NULL default '0',
+ `choose` INT UNSIGNED DEFAULT '4' NOT NULL,
+ PRIMARY KEY ( `id` )
+ );");
+ }
+
return true;
}
) TYPE=MyISAM COMMENT='The grade for a question in a quiz';
# --------------------------------------------------------
+#
+# Table structure for table `quiz_randommatch`
+#
+
+CREATE TABLE `prefix_quiz_randommatch` (
+ `id` int(10) unsigned NOT NULL auto_increment,
+ `question` int(10) unsigned NOT NULL default '0',
+ `choose` INT UNSIGNED DEFAULT '4' NOT NULL,
+ PRIMARY KEY ( `id` )
+) TYPE=MyISAM COMMENT='Info about a random matching question';
+
#
# Table structure for table `quiz_questions`
#
foreach ($rawquestions as $key => $value) { // Parse input for question ids
if (substr($key, 0, 1) == "q") {
$key = substr($key,1);
- if ($questions) {
+ if (!empty($questions)) {
foreach ($questions as $question) {
if ($question == $key) {
continue 2;
}
- function swapshuffle($array) {
- /// Given a simple array, shuffles it up just like shuffle()
- /// Unlike PHP's shuffle() ihis function works on any machine.
-
- srand ((double) microtime() * 10000000);
- $last = count($array) - 1;
- for ($i=0;$i<=$last;$i++) {
- $from = rand(0,$last);
- $curr = $array[$i];
- $array[$i] = $array[$from];
- $array[$from] = $curr;
- }
- return $array;
- }
-
}
?>
/// Parse the answers
$answers = explode("~", $answertext);
- if (! count($answers)) {
- if ($this->displayerrors) {
- echo "<P>No answers found in $answertext";
- }
- return false;
+ $countanswers = count($answers);
+
+ switch ($countanswers) {
+ case 0: // invalid question
+ if ($this->displayerrors) {
+ echo "<P>No answers found in $answertext";
+ }
+ return false;
+
+ case 1:
+ $question->qtype = SHORTANSWER;
+
+ $answer = trim($answers[0]);
+ if ($answer[0] == "=") {
+ $answer = substr($answer, 1);
+ }
+ $question->answer[] = addslashes($answer);
+ $question->fraction[] = 1;
+ $question->feedback[] = "";
+
+ $question->usecase = 0; // Ignore case
+ $question->image = ""; // No images with this format
+ return $question;
+
+ default:
+ $question->qtype = MULTICHOICE;
+
+ $answers = swapshuffle($answers);
+ foreach ($answers as $key => $answer) {
+ $answer = trim($answer);
+ if ($answer[0] == "=") {
+ $question->fraction[$key] = 1;
+ $answer = substr($answer, 1);
+ } else {
+ $question->fraction[$key] = 0;
+ }
+ $question->answer[$key] = addslashes($answer);
+ $question->feedback[$key] = "";
+ }
+
+ $question->single = 1; // Only one answer is allowed
+ $question->image = ""; // No images with this format
+ return $question;
}
-
- if (count($answers) == 1) {
- return false;
- }
-
- $answers = $this->swapshuffle($answers);
-
- foreach ($answers as $key => $answer) {
- $answer = trim($answer);
- if ($answer[0] == "=") {
- $question->fraction[$key] = 1;
- $answer = substr($answer, 1);
- } else {
- $question->fraction[$key] = 0;
- }
- $question->answer[$key] = addslashes($answer);
- $question->feedback[$key] = "";
- }
-
- $question->qtype = MULTICHOICE;
- $question->single = 1; // Only one answer is allowed
- $question->image = ""; // No images with this format
-
- return $question;
}
-
}
?>
define("TRUEFALSE", "2");
define("MULTICHOICE", "3");
define("RANDOM", "4");
+define("MATCH", "5");
+define("RANDOMMATCH", "6");
+
$QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz"),
TRUEFALSE => get_string("truefalse", "quiz"),
- SHORTANSWER => get_string("shortanswer", "quiz") );
+ SHORTANSWER => get_string("shortanswer", "quiz"),
+ RANDOMMATCH => get_string("randommatch", "quiz") );
$QUIZ_FILE_FORMAT = array ( "custom" => get_string("custom", "quiz"),
"webct" => get_string("webct", "quiz"),
function quiz_get_answers($question) {
// Given a question, returns the correct answers and grades
global $CFG;
+
switch ($question->qtype) {
- case SHORTANSWER; // Could be multiple answers
+ case SHORTANSWER: // Could be multiple answers
return get_records_sql("SELECT a.*, sa.usecase, g.grade
FROM {$CFG->prefix}quiz_shortanswer sa,
{$CFG->prefix}quiz_answers a,
AND sa.question = g.question");
break;
- case TRUEFALSE; // Should be always two answers
+ case TRUEFALSE: // Should be always two answers
return get_records_sql("SELECT a.*, g.grade
FROM {$CFG->prefix}quiz_answers a,
{$CFG->prefix}quiz_question_grades g
AND a.question = g.question");
break;
- case MULTICHOICE; // Should be multiple answers
+ case MULTICHOICE: // Should be multiple answers
return get_records_sql("SELECT a.*, mc.single, g.grade
FROM {$CFG->prefix}quiz_multichoice mc,
{$CFG->prefix}quiz_answers a,
AND mc.question = g.question");
break;
- case RANDOM:
+ case RANDOM:
return false; // Not done yet
break;
+ case RANDOMMATCH: // Could be any of many answers, return them all
+ return get_records_sql("SELECT a.*, g.grade
+ FROM {$CFG->prefix}quiz_questions q,
+ {$CFG->prefix}quiz_answers a,
+ {$CFG->prefix}quiz_question_grades g
+ WHERE q.category = '$question->category'
+ AND q.qtype = ".SHORTANSWER."
+ AND q.id = a.question
+ AND g.question = '$question->id'");
+ break;
+
default:
return false;
}
// for regrading using quiz_grade_attempt_results()
global $CFG;
- if (!$responses = get_records_sql("SELECT q.id, q.qtype, r.answer
+ if (!$responses = get_records_sql("SELECT q.id, q.qtype, q.category, r.answer
FROM {$CFG->prefix}quiz_responses r,
{$CFG->prefix}quiz_questions q
WHERE r.attempt = '$attempt->id'
case RANDOM:
echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/rs.gif\">";
break;
+ case RANDOMMATCH:
+ echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/rm.gif\">";
+ break;
}
echo "</A>\n";
}
echo "<P>Random questions not supported yet</P>";
break;
+ case RANDOMMATCH:
+ if (!$options = get_record("quiz_randommatch", "question", $question->id)) {
+ notify("Error: Missing question options!");
+ }
+ echo text_to_html($question->questiontext);
+ if ($question->image) {
+ print_file_picture($question->image, $courseid, QUIZ_PICTURE_DEFAULT_HEIGHT);
+ }
+
+ /// First, get all the questions available
+
+ $allquestions = get_records_select("quiz_questions",
+ "category = $question->category AND qtype = ".SHORTANSWER);
+ if (count($allquestions) < $options->choose) {
+ notify("Error: could not find enough Short Answer questions in the database!");
+ notify("Found ".count($allquestions).", need $options->choose.");
+ break;
+ }
+
+ if (empty($response)) { // Randomly pick the questions
+ if (!$randomquestions = draw_rand_array($allquestions, $options->choose)) {
+ notify("Error choosing $options->choose random questions");
+ break;
+ }
+ } else { // Use existing questions
+ $randomquestions = array();
+ foreach ($response as $key => $rrr) {
+ $rrr = explode("-", $rrr);
+ $randomquestions[$key] = $allquestions[$key];
+ $responseanswer[$key] = $rrr[1];
+ }
+ }
+
+ /// For each selected, find the best matching answers
+
+ foreach ($randomquestions as $randomquestion) {
+ $shortanswerquestion = get_record("quiz_shortanswer", "question", $randomquestion->id);
+ $questionanswers = get_records_list("quiz_answers", "id", $shortanswerquestion->answers);
+ $bestfraction = 0;
+ $bestanswer = NULL;
+ foreach ($questionanswers as $questionanswer) {
+ if ($questionanswer->fraction > $bestfraction) {
+ $bestanswer = $questionanswer;
+ }
+ }
+ if (empty($bestanswer)) {
+ notify("Error: Could not find the best answer for question: ".$randomquestions->name);
+ break;
+ }
+ $randomanswers[$bestanswer->id] = trim($bestanswer->answer);
+ }
+
+ if (!$randomanswers = draw_rand_array($randomanswers, $options->choose)) { // Mix them up
+ notify("Error randomising answers!");
+ break;
+ }
+
+ echo "<table border=0 cellpadding=10>";
+ foreach ($randomquestions as $key => $randomquestion) {
+ echo "<tr><td align=left valign=top>";
+ echo $randomquestion->questiontext;
+ echo "</td>";
+ echo "<td align=right valign=top>";
+ if (empty($response)) {
+ choose_from_menu($randomanswers, "q$question->id"."r$randomquestion->id");
+ } else {
+ if (!empty($correct[$key])) {
+ if ($randomanswers[$responseanswer[$key]] == $correct[$key]) {
+ echo "<span=highlight>";
+ choose_from_menu($randomanswers, "q$question->id"."r$randomquestion->id", $responseanswer[$key]);
+ echo "</span><br \>";
+ } else {
+ choose_from_menu($randomanswers, "q$question->id"."r$randomquestion->id", $responseanswer[$key]);
+ quiz_print_correctanswer($correct[$key]);
+ }
+ } else {
+ choose_from_menu($randomanswers, "q$question->id"."r$randomquestion->id", $responseanswer[$key]);
+ }
+ if (!empty($feedback[$key])) {
+ quiz_print_comment($feedback[$key]);
+ }
+ }
+ echo "</td></tr>";
+ }
+ echo "</table>";
+ break;
+
default:
notify("Error: Unknown question type!");
echo "<TABLE BORDER=0 CELLPADDING=5 CELLSPACING=2 WIDTH=\"100%\">";
echo "<TR><TH WIDTH=\"*\" COLSPAN=3 NOWRAP>$strorder</TH><TH align=left WIDTH=\"100%\" NOWRAP>$strquestionname</TH><TH width=\"*\" NOWRAP>$strtype</TH><TH WIDTH=\"*\" NOWRAP>$strgrade</TH><TH WIDTH=\"*\" NOWRAP>$stredit</TH></TR>";
foreach ($order as $qnum) {
+ if (empty($questions[$qnum])) {
+ continue;
+ }
$count++;
echo "<TR BGCOLOR=\"$THEME->cellcontent\">";
echo "<TD>$count</TD>";
if (!empty($question->answer)) {
foreach ($question->answer as $questionanswer) {
if ($questionanswer == $answer->id) {
+ $response[$answer->id] = true;
if ($answer->single) {
$grade = (float)$answer->fraction * $answer->grade;
- $response[$answer->id] = true;
continue;
} else {
$grade += (float)$answer->fraction * $answer->grade;
- $response[$answer->id] = true;
}
}
}
}
}
break;
- case RANDOM:
- // Not done yet
+
+ case RANDOMMATCH:
+ $bestanswer = array();
+ foreach ($answers as $answer) { // Loop through them all looking for correct answers
+ if (empty($bestanswer[$answer->question])) {
+ $bestanswer[$answer->question] = 0;
+ $correct[$answer->question] = "";
+ }
+ if ($answer->fraction > $bestanswer[$answer->question]) {
+ $bestanswer[$answer->question] = $answer->fraction;
+ $correct[$answer->question] = $answer->answer;
+ }
+ }
+ $answerfraction = 1.0 / (float) count($question->answer);
+ foreach ($question->answer as $questionanswer) { // For each random answered question
+ $rqarr = explode('-', $questionanswer); // Extract question/answer.
+ $rquestion = $rqarr[0];
+ $ranswer = $rqarr[1];
+ $response[$rquestion] = $questionanswer;
+ if (isset($answers[$ranswer])) { // If the answer exists in the list
+ $answer = $answers[$ranswer];
+ $feedback[$rquestion] = $answer->feedback;
+ if ($answer->question == $rquestion) { // Check that this answer matches the question
+ $grade += (float)$answer->fraction * $answer->grade * $answerfraction;
+ }
+ }
+ }
break;
-
}
if ($grade < 0.0) { // No negative grades
$grade = 0.0;
}
}
break;
+
+ case RANDOMMATCH:
+ $options->question = $question->id;
+ $options->choose = $question->choose;
+ if ($existing = get_record("quiz_randommatch", "question", $options->question)) {
+ $options->id = $existing->id;
+ if (!update_record("quiz_randommatch", $options)) {
+ $result->error = "Could not update quiz randommatch options!";
+ return $result;
+ }
+ } else {
+ if (!insert_record("quiz_randommatch", $options)) {
+ $result->error = "Could not insert quiz randommatch options!";
+ return $result;
+ }
+ }
+ break;
+
default:
$result->error = "Unsupported question type ($question->qtype)!";
return $result;
}
+
+
?>
-<FORM name="theform" method="post" action="question.php">\r
+<FORM name="theform" method="post" <?=$onsubmit ?> action="question.php">\r
<CENTER>\r
<TABLE cellpadding=5>\r
<TR valign=top>\r
formerr($err["questiontext"]); \r
echo "<BR \>";\r
}\r
+ print_textarea($usehtmleditor, 15, 60, 595, 300, "questiontext", $question->questiontext);\r
+ if ($usehtmleditor) {\r
+ helpbutton("richtext", get_string("helprichtext"), "moodle");\r
+ } else {\r
+ helpbutton("text", get_string("helptext"), "moodle");\r
+ }\r
?>\r
- <textarea name="questiontext" rows=5 cols=50 wrap="virtual"><? p($question->questiontext)?></textarea>\r
- <? helpbutton("text", get_string("helptext")); ?>\r
</TD>\r
</TR>\r
<TR valign=top>\r
\r
</CENTER>\r
</FORM>\r
+<? \r
+ if ($usehtmleditor) { \r
+ print_richedit_javascript("theform", "questiontext", "no");\r
+ }\r
+?>\r
$question->image = "";
}
+ // Set up some Richtext editing if necessary
+ if ($usehtmleditor = can_use_richtext_editor()) {
+ $defaultformat = FORMAT_HTML;
+ $onsubmit = "onsubmit=\"copyrichtext(theform.questiontext);\"";
+ } else {
+ $defaultformat = FORMAT_MOODLE;
+ $onsubmit = "";
+ }
switch ($qtype) {
case SHORTANSWER:
print_continue("edit.php");
break;
+ case RANDOMMATCH:
+ if (!empty($question->id)) {
+ $options = get_record("quiz_randommatch", "question", $question->id);
+ } else {
+ $options->choose = "";
+ }
+ $numberavailable = count_records("quiz_questions", "category", $category->id, "qtype", SHORTANSWER);
+ print_heading_with_help(get_string("editingrandommatch", "quiz"), "randommatch", "quiz");
+ require("randommatch.html");
+ break;
+
default:
error("Invalid question type");
break;
--- /dev/null
+<FORM name="theform" method="post" <?=$onsubmit ?> action="question.php">\r
+<CENTER>\r
+<TABLE cellpadding=5>\r
+<TR valign=top>\r
+ <TD align=right><P><B><? print_string("category", "quiz") ?>:</B></P></TD>\r
+ <TD>\r
+ <?PHP echo $categories[$question->category]; ?>\r
+ <input type="hidden" name="category" value="<?PHP echo "$question->category"; ?>">\r
+ </TD>\r
+</TR>\r
+<TR valign=top>\r
+ <TD align=right><P><B><? print_string("questionname", "quiz") ?>:</B></P></TD>\r
+ <TD>\r
+ <?PHP\r
+ if (empty($question->name)) {\r
+ $question->name = get_string("randommatch", "quiz");\r
+ }\r
+ ?>\r
+ <INPUT type="text" name="name" size=40 value="<? p($question->name) ?>">\r
+ <? if (isset($err["name"])) formerr($err["name"]); ?>\r
+ </TD>\r
+</TR>\r
+<TR valign=top>\r
+ <TD align=right><P><B><? print_string("introduction", "quiz") ?>:</B></P></TD>\r
+ <TD>\r
+ <? if (isset($err["questiontext"])) {\r
+ formerr($err["questiontext"]); \r
+ echo "<BR \>";\r
+ }\r
+ if (empty($question->questiontext)) {\r
+ $question->questiontext = get_string("randommatchintro", "quiz");\r
+ }\r
+ print_textarea($usehtmleditor, 15, 60, 595, 300, "questiontext", $question->questiontext);\r
+ if ($usehtmleditor) {\r
+ helpbutton("richtext", get_string("helprichtext"), "moodle");\r
+ } else {\r
+ helpbutton("text", get_string("helptext"), "moodle");\r
+ }\r
+ ?>\r
+ </TD>\r
+</TR>\r
+<TR valign=top>\r
+ <TD align=right><P><B><? print_string("randommatchnumber", "quiz") ?>:</B></P></TD>\r
+ <TD>\r
+ <?\r
+ if ($numberavailable < 2) {\r
+ echo "ERROR";\r
+ } else if ($numberavailable < 6) {\r
+ $maxrandom = $numberavailable;\r
+ } else {\r
+ $maxrandom = 6;\r
+ }\r
+\r
+ for ($i=2;$i<=$maxrandom;$i++) {\r
+ $menu[$i] = $i;\r
+ }\r
+ choose_from_menu($menu, "choose", "$options->choose", "");\r
+ unset($menu);\r
+ ?>\r
+ </TD>\r
+</TR>\r
+</TABLE>\r
+\r
+<INPUT type="hidden" name=id value="<? p($question->id) ?>">\r
+<INPUT type="hidden" name=qtype value="<? p($question->qtype) ?>">\r
+<INPUT type="submit" value="<? print_string("savechanges") ?>">\r
+\r
+</CENTER>\r
+</FORM>\r
+<? \r
+ if ($usehtmleditor) { \r
+ print_richedit_javascript("theform", "questiontext", "no");\r
+ }\r
+?>\r
-<FORM name="theform" method="post" action="question.php">\r
+<FORM name="theform" method="post" <?=$onsubmit ?> action="question.php">\r
<CENTER>\r
<TABLE cellpadding=5>\r
<TR valign=top>\r
formerr($err["questiontext"]); \r
echo "<BR \>";\r
}\r
+ print_textarea($usehtmleditor, 15, 60, 595, 300, "questiontext", $question->questiontext);\r
+ if ($usehtmleditor) {\r
+ helpbutton("richtext", get_string("helprichtext"), "moodle");\r
+ } else {\r
+ helpbutton("text", get_string("helptext"), "moodle");\r
+ }\r
?>\r
- <textarea name="questiontext" rows=6 cols=50 wrap="virtual"><? p($question->questiontext)?></textarea>\r
- <? helpbutton("text", get_string("helptext")); ?>\r
</TD>\r
</TR>\r
<TR valign=top>\r
\r
</CENTER>\r
</FORM>\r
+<? \r
+ if ($usehtmleditor) { \r
+ print_richedit_javascript("theform", "questiontext", "no");\r
+ }\r
+?>\r
-<FORM name="theform" method="post" action="question.php">\r
+<FORM name="theform" method="post" <?=$onsubmit ?> action="question.php">\r
<CENTER>\r
<TABLE cellpadding=5>\r
<TR valign=top>\r
formerr($err["questiontext"]); \r
echo "<BR \>";\r
}\r
+ print_textarea($usehtmleditor, 15, 60, 595, 300, "questiontext", $question->questiontext);\r
+ if ($usehtmleditor) {\r
+ helpbutton("richtext", get_string("helprichtext"), "moodle");\r
+ } else {\r
+ helpbutton("text", get_string("helptext"), "moodle");\r
+ }\r
?>\r
- <textarea name="questiontext" rows=6 cols=50 wrap="virtual"><? p($question->questiontext)?></textarea>\r
- <? helpbutton("text", get_string("helptext")); ?>\r
</TD>\r
</TR>\r
<TR valign=top>\r
\r
</CENTER>\r
</FORM>\r
+<? \r
+ if ($usehtmleditor) { \r
+ print_richedit_javascript("theform", "questiontext", "no");\r
+ }\r
+?>\r
// This fragment is called by moodle_needs_upgrading() and /admin/index.php
////////////////////////////////////////////////////////////////////////////////
-$module->version = 2003021600; // The (date) version of this module
+$module->version = 2003022303; // The (date) version of this module
$module->cron = 0; // How often should cron check this module (seconds)?
?>