diff --git a/mod/quiz/format/default.php b/mod/quiz/format.php
similarity index 97%
rename from mod/quiz/format/default.php
rename to mod/quiz/format.php
index 0145c54c7d9..2af24e200ca 100644
--- a/mod/quiz/format/default.php
+++ b/mod/quiz/format.php
@@ -1,12 +1,12 @@
2 November 2003
+///
+
+////////////////////////////////////////////////////////////////////////////
+/// AIKEN FORMAT
+///
+/// This Moodle class provides all functions necessary to import and export
+/// one-correct-answer multiple choice questions in this format:
+///
+/// Question text
+/// A) Choice #1
+/// B) Choice #2
+/// C) Choice #3
+/// D) Choice #4
+/// ANSWER: B
+/// (blank line next not necessary since "AN" at the beginning of a line
+/// triggers the question input and causes input to start all over.
+///
+///Only ONE correct answer is allowed with no feedback responses.
+///
+///Be sure to reword "All of the above" type questions as "All of these" (etc.) so that choices can
+/// be randomized
+///
+///This should work on WIN, Mac and Unix although only tested on Mac
+///
+////////////////////////////////////////////////////////////////////////////
+
+// Based on format.php, included by ../../import.php
+
+class quiz_file_format extends quiz_default_format {
+
+//will this override default function?
+ function readquestions($lines){
+ $questions = array();
+ $endchar = chr(13);
+ foreach ($lines as $line) {
+ $stp = strpos($line,$endchar,0);
+ $newlines = explode($endchar,$line);
+ $foundQ = 0;
+ for ($i=0; $i < count($newlines);$i++){
+ $nowline = addslashes($newlines[$i]);
+ ///Go through the arrage and build an object called $question
+ ///When done, add $question to $questions
+ if (strlen($nowline)< 2) {
+ continue;
+ }
+ // This will show everyline when file is being processed
+ // print("$nowline
");
+ $leader = substr(ltrim($nowline),0,2);
+ if (strpos(".A)B)C)D)E)F)G)H)I)J)A.B.C.D.E.F.G.H.I.J.",$leader)>0){
+ //trim off the label and space
+ $question->answer[] = substr($nowline,3);
+ $question->fraction[] = 0;
+ continue;
+ }
+ if ($leader == "AN"){
+ $ans = trim(strstr($nowline,":"));
+ $ans = substr($ans,2,1);
+ //A becomes 0 since array starts from 0
+ $rightans = ord($ans) - 65;
+ $question->fraction[$rightans] = 1;
+ $questions[] = $question;
+ //clear array for next question set
+ $question = NULL;
+ continue;
+ } else {
+ //Must be the first line since no leader
+ $question->qtype = MULTICHOICE;
+ $question->name = substr($nowline,0,50);
+ $question->questiontext = $nowline;
+ $question->single = 1;
+ $question->feedback[] = "";
+ $question->usecase = 0; // Ignore case
+ $question->defaultgrade = 1;
+ $question->image = ""; // No images with this format
+ }
+ }
+ }
+ return $questions;
+ }
+
+ function readquestion($lines) {
+ //this is no longer needed but might still be called by default.php
+ return;
+ }
+}
+
+?>
diff --git a/mod/quiz/format/aon/format.php b/mod/quiz/format/aon/format.php
new file mode 100644
index 00000000000..c9c39af1371
--- /dev/null
+++ b/mod/quiz/format/aon/format.php
@@ -0,0 +1,224 @@
+displayerrors) {
+ echo "
$text
Could not find a {"; + } + return false; + } + + $answerfinish = strpos($text, "}"); + if ($answerfinish === false) { + if ($this->displayerrors) { + echo "
$text
Could not find a }"; + } + return false; + } + + $answerlength = $answerfinish - $answerstart; + $answertext = substr($text, $answerstart + 1, $answerlength - 1); + + /// Save the new question text + $question->questiontext = addslashes(substr_replace($text, "_____", $answerstart, $answerlength+1)); + $question->name = substr($question->questiontext, 0, 60)." ..."; + + + /// Parse the answers + $answers = explode("~", $answertext); + + $countanswers = count($answers); + + switch ($countanswers) { + case 0: // invalid question + if ($this->displayerrors) { + echo "
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->defaultgrade = 1; + $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->defaultgrade = 1; + $question->single = 1; // Only one answer is allowed + $question->image = ""; // No images with this format + return $question; + } + } + + function importpostprocess() { + /// Goes through the questionids, looking for shortanswer questions + /// and converting random groups of 4 into matching questions. + + /// Doesn't handle shortanswer questions with more than one answer + + global $CFG; + + print_heading(count($this->questionids)." ".get_string("questions", "quiz")); + + $questionids = implode(',', $this->questionids); + + if (!$shortanswers = get_records_select("quiz_questions", + "id IN ($questionids) AND qtype = ".SHORTANSWER, + "", "id,qtype")) { + return true; + } + + + $shortanswerids = array(); + foreach ($shortanswers as $key => $shortanswer) { + $shortanswerids[] = $key; + } + + $strmatch = get_string("match", "quiz")." (".$this->category->name.")"; + + $shortanswerids = swapshuffle($shortanswerids); + $count = $shortanswercount = count($shortanswerids); + $i = 1; + $matchcount = 0; + + $question->category = $this->category->id; + $question->qtype = MATCH; + $question->questiontext = get_string("randomsamatchintro", "quiz"); + $question->image = ""; + + while ($count > 4) { + $matchcount++; + $question->name = "$strmatch $i"; + $question->subquestions = array(); + $question->subanswers = array(); + + $extractids = implode(',', array_splice($shortanswerids, -4)); + $count = count($shortanswerids); + + $extracts = get_records_sql("SELECT q.questiontext, a.answer + FROM {$CFG->prefix}quiz_questions q, + {$CFG->prefix}quiz_shortanswer sa, + {$CFG->prefix}quiz_answers a + WHERE q.id in ($extractids) + AND sa.question = q.id + AND a.id = sa.answers"); + + if (count($extracts) != 4) { + print_object($extracts); + notify("Could not find exactly four shortanswer questions with ids: $extractids"); + continue; + } + + $question->stamp = make_unique_id_code(); // Set the unique code (not to be changed) + $question->version = 1; // Original version of this question + + if (!$question->id = insert_record("quiz_questions", $question)) { + error("Could not insert new question!"); + } + + foreach ($extracts as $shortanswer) { + $question->subquestions[] = addslashes($shortanswer->questiontext); + $question->subanswers[] = addslashes($shortanswer->answer); + } + + $result = quiz_save_question_options($question); + + if (!empty($result->error)) { + notify("Error: $result->error"); + } + + if (!empty($result->notice)) { + notify($result->notice); + } + + /// Delete the old short-answer questions + + execute_sql("DELETE FROM {$CFG->prefix}quiz_questions WHERE id IN ($extractids)", false); + execute_sql("DELETE FROM {$CFG->prefix}quiz_shortanswer WHERE question IN ($extractids)", false); + execute_sql("DELETE FROM {$CFG->prefix}quiz_answers WHERE question IN ($extractids)", false); + + } + + if ($count) { /// Delete the remaining ones + foreach ($shortanswerids as $shortanswerid) { + delete_records("quiz_questions", "id", $shortanswerid); + delete_records("quiz_shortanswer", "question", $shortanswerid); + delete_records("quiz_answers", "question", $shortanswerid); + } + } + $info = "$shortanswercount ".get_string("shortanswer", "quiz"). + " => $matchcount ".get_string("match", "quiz"); + + print_heading($info); + + $options['category'] = $this->category->id; + echo "
";echo htmlspecialchars($qrec[2]); echo ""; + $question->questiontext = addslashes(trim($qrec[2])); + // echo "
";echo $question->questiontext; echo ""; + $question->name = preg_replace("/
";echo $question->questiontext; echo ""; + $question->name = preg_replace("/
$count. ".stripslashes($question->questiontext)."
"; + $question->category = $this->category->id; + $question->stamp = make_unique_id_code(); // Set the unique code (not to be changed) + $question->version = 1; // Original version of this question + if (!$question->id = insert_record("quiz_questions", $question)) { + error("Could not insert new question!"); + } + $this->questionids[] = $question->id; + // Now to save all the answers and type-specific options + $result = quiz_save_question_options($question); + if (!empty($result->error)) { + notify($result->error); + $this->deletedatabase($filename); + return false; + } + if (!empty($result->notice)) { + notify($result->notice); + $this->deletedatabase($filename); + return true; + } + } + $this->deletedatabase($filename); + return true; + } + + function importpostprocess() { + return true; + } + + function deletedatabase($filename) { + if (! $this->fulldelete($filename)) { + echo "\n"; + $qResult = HtmlSpecialChars($qResult); + echo $qResult; + echo "\n"; +*/ + xml_parser_free($xml_parser); +// $sResult = HtmlSpecialChars($qResult); + //echo("
");
+// echo($sResult);
+// echo("");
+
+ return $result;
+ }
+ }
+
+ function connect_win($filename) {
+ global $CFG, $systemdb;
+ // first, verify the location of System.mda
+ if (!isset($systemdb)) {
+ $systemdb=$this->findfile("System.mda");
+ }
+ if (! $systemdb) {
+ $this->err("The system database System.mda cannot be found. Check that you've uploaded it to the course.",$dsn);
+ die;
+ }
+
+ $ldb = &ADONewConnection('access');
+ $dsn="DRIVER=Microsoft Access Driver (*.mdb);SystemDB=".$systemdb.";DBQ=".$filename.";UID=Assess;PWD=VBMango;";
+ $dbconnected = $ldb->Connect($dsn);
+ if (! $dbconnected) {
+ $this->err("Moodle could not connect to the database.",$dsn);
+ die;
+ }
+ return $ldb;
+ }
+
+ function err($message, $dsn) {
+ echo "";
+ echo "Error: $message
"; + echo "ODBC File DSN: $dsn
";
+ echo "
$text
Could not find a {"; + } + return false; + } + + $answerfinish = strpos($text, "}"); + if ($answerfinish === false) { + if ($this->displayerrors) { + echo "
$text
Could not find a }"; + } + return false; + } + + $answerlength = $answerfinish - $answerstart; + $answertext = trim(substr($text, $answerstart + 1, $answerlength - 1)); + + /// SAVE QUESTION TEXT without answer, inserting "_____" as necessary + if (substr($text, -1) == "}") { + /// no blank line if answers follow question, outside of closing punctuation + $question->questiontext = addslashes(substr_replace($text, "", $answerstart, $answerlength+1)); + } else { + /// inserts blank line for missing word format + $question->questiontext = addslashes(substr_replace($text, "_____", $answerstart, $answerlength+1)); + } + + /// set question name if not already set + if ($question->name === false) { + $question->name = $question->questiontext; + } + + + /// ANSWERS + // Only Multiple-choice questions contain tilde ~ + if (strstr($answertext, "~") !== FALSE) { // tilde conditional + + ///MULTIPLE CHOICE + $answertext = str_replace("=", "~=", $answertext); + $answers = explode("~", $answertext); + if (isset($answers[0])) { + $answers[0] = trim($answers[0]); + } + if (empty($answers[0])) { + array_shift($answers); + } + + $countanswers = count($answers); + if ($countanswers < 2) { // MC $countanswers conditional + if ($this->displayerrors) { + echo "
Found tilde, but " . $countanswers . " answers in $answertext"; + } + return false; + } else { // MC $countanswers conditional + + $question->qtype = MULTICHOICE; + $question->single = 1; // Only one answer allowed by default + + foreach ($answers as $key => $answer) { + $answer = trim($answer); + + // determine answer weight + if ($answer[0] == "=") { + $answer_weight = 1; + $answer = substr($answer, 1); + + } elseif (ereg($answer_weight_regex, $answer)) { // check for properly formatted answer weight + $answer_weight = $this->answerweightparser($answer); + + } else { //default, i.e., wrong anwer + $answer_weight = 0; + } + $question->fraction[$key] = $answer_weight; + + $comment = $this->commentparser($answer); // commentparser also cleans up $answer + $question->feedback[$key] = $comment; + + $question->answer[$key] = addslashes($answer); + } // end foreach answer + + $question->defaultgrade = 1; + $question->image = ""; // No images with this format + return $question; + } // end MC $countanswers conditional + + /// Otherwise, begin parsing other question-types + } else { // tilde conditional + + /// TRUEFALSE Question + $TF_check = $answertext; + if (strpos($answertext,"#") > 0){ + // strip comments to check for TrueFalse question + $TF_check = trim(substr($answertext, 0, strpos($answertext,"#"))); + } + + if (($TF_check == "T") // TrueFalse/ShortAnswer QuestionType conditional + OR ($TF_check == "TRUE") + OR ($TF_check == "F") + OR ($TF_check == "FALSE")) { + $answer = $answertext; + $question->qtype = TRUEFALSE; + $comment = $this->commentparser($answer); // commentparser also cleans up $answer + + if ($answer == "T" OR $answer == "TRUE") { + $question->answer = 1; + $question->feedbackfalse = $comment; //feedback if answer is wrong + } else { + $question->answer = 0; + $question->feedbacktrue = $comment; //feedback if answer is wrong + } + + + } else { // TrueFalse/ShortAnswer QuestionType conditional + + /// SHORTANSWER Question + $answers = explode("=", $answertext); + if (isset($answers[0])) { + $answers[0] = trim($answers[0]); + } + if (empty($answers[0])) { + array_shift($answers); + } + + if (count($answers) == 0) { + /// invalid question + if ($this->displayerrors) { + echo "
Found equals=, but no answers in $answertext"; + } + return false; + } else { + + $question->qtype = SHORTANSWER; + $question->usecase = 0; // Ignore case + + foreach ($answers as $key => $answer) { + $answer = trim($answer); + + // Answer Weight + if (ereg($answer_weight_regex, $answer)) { // check for properly formatted answer weight + $answer_weight = $this->answerweightparser($answer); + } else { //default, i.e., full-credit anwer + $answer_weight = 1; + } + $question->fraction[$key] = $answer_weight; + + $comment = $this->commentparser($answer); //commentparser also cleans up $answer + $question->feedback[$key] = $comment; + + $question->answer[$key] = addslashes($answer); + } // end foreach + } // end ount($answers) conditional + + } // end TrueFalse/ShortAnswer QuestionType conditional + + $question->defaultgrade = 1; + $question->image = ""; // No images with this format + return $question; + + } // end tilde conditional + + } // end function readquestion($lines) +} + +?> + diff --git a/mod/quiz/format/missingword/format.php b/mod/quiz/format/missingword/format.php new file mode 100644 index 00000000000..7014b72bf7d --- /dev/null +++ b/mod/quiz/format/missingword/format.php @@ -0,0 +1,156 @@ +displayerrors) { + echo "
$text
Could not find a {"; + } + return false; + } + + $answerfinish = strpos($text, "}"); + if ($answerfinish === false) { + if ($this->displayerrors) { + echo "
$text
Could not find a }"; + } + return false; + } + + $answerlength = $answerfinish - $answerstart; + $answertext = substr($text, $answerstart + 1, $answerlength - 1); + + /// Save the new question text + $question->questiontext = addslashes(substr_replace($text, "_____", $answerstart, $answerlength+1)); + $question->name = $question->questiontext; + + + /// Parse the answers + $answertext = str_replace("=", "~=", $answertext); + $answers = explode("~", $answertext); + if (isset($answers[0])) { + $answers[0] = trim($answers[0]); + } + if (empty($answers[0])) { + array_shift($answers); + } + + $countanswers = count($answers); + + switch ($countanswers) { + case 0: // invalid question + if ($this->displayerrors) { + echo "
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->defaultgrade = 1; + $question->image = ""; // No images with this format + return $question; + + default: + $question->qtype = MULTICHOICE; + + foreach ($answers as $key => $answer) { + $answer = trim($answer); + + // Tom's addition starts here + $answeight = 0; + if (strspn($answer,"1234567890%") > 0){ + //Make sure that the percent sign is the last in the span + if (strpos($answer,"%") == strspn($answer,"1234567890%") - 1) { + $answeight0 = substr($answer,0,strspn($answer,"1234567890%")); + $answeight = round(($answeight0/100),2); + $answer = substr($answer,(strspn($answer,"1234567890%"))); + } + } + if ($answer[0] == "="){ + $answeight = 1; + } + //remove the protective underscore for leading numbers in answers + if ($answer[0] == "_"){ + $answer = substr($answer, 1); + } + $answer = trim($answer); + + if (strpos($answer,"#") > 0){ + $hashpos = strpos($answer,"#"); + $comment = addslashes(substr(($answer),$hashpos+1)); + $answer = substr($answer,0,$hashpos); + } else { + $comment = " "; + } + // End of Tom's addition + + if ($answer[0] == "=") { +# $question->fraction[$key] = 1; + $question->fraction[$key] = $answeight; + $answer = substr($answer, 1); + } else { +# $question->fraction[$key] = 0; + $question->fraction[$key] = $answeight; + } + $question->answer[$key] = addslashes($answer); + $question->feedback[$key] = $comment; + } + + $question->defaultgrade = 1; + $question->single = 1; // Only one answer is allowed + $question->image = ""; // No images with this format + return $question; + } + } +} + +?> diff --git a/mod/quiz/format/multianswer/format.php b/mod/quiz/format/multianswer/format.php new file mode 100644 index 00000000000..a7fd72e1f9d --- /dev/null +++ b/mod/quiz/format/multianswer/format.php @@ -0,0 +1,162 @@ +qtype= MULTIANSWER; + $question->questiontext= $text; + $question->answers= array(); + $question->defaultgrade = 0; // Will be increased for each answer norm + + for ($positionkey=1 + ; ereg(ANSWER_REGEX, $question->questiontext, $answerregs) + ; ++$positionkey ) + { + unset($multianswer); + + $multianswer->positionkey = $positionkey; + $multianswer->norm = $answerregs[ANSWER_REGEX_NORM] + or $multianswer->norm = '1'; + if ($answerregs[ANSWER_REGEX_ANSWER_TYPE_NUMERICAL]) { + $multianswer->answertype = NUMERICAL; + } else if($answerregs[ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER]) { + $multianswer->answertype = SHORTANSWER; + } else if($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE]){ + $multianswer->answertype = MULTICHOICE; + } else { + error("Cannot identify answertype $answerregs[2]"); + return false; + } + + $multianswer->alternatives= array(); + $remainingalts = $answerregs[ANSWER_REGEX_ALTERNATIVES]; + while (ereg(ANSWER_ALTERNATIVE_REGEX, $remainingalts, $altregs)) { + unset($alternative); + + if ('=' == $altregs[ANSWER_ALTERNATIVE_REGEX_FRACTION]) { + $alternative->fraction = '1'; + } else { + $alternative->fraction = .01 * + $altregs[ANSWER_ALTERNATIVE_REGEX_PERCENTILE_FRACTION] + or $alternative->fraction = '0'; + } + $alternative->feedback = $altregs[ANSWER_ALTERNATIVE_REGEX_FEEDBACK]; + if ($answerregs[ANSWER_REGEX_ANSWER_TYPE_NUMERICAL] + && ereg(NUMERICAL_ALTERNATIVE_REGEX, + $altregs[ANSWER_ALTERNATIVE_REGEX_ANSWER], + $numregs) ) + { + $alternative->answer = $numregs[NUMERICAL_CORRECT_ANSWER]; + if ($numregs[NUMERICAL_ABS_ERROR_MARGIN]) { + $alternative->min = $numregs[NUMERICAL_CORRECT_ANSWER] + - $numregs[NUMERICAL_ABS_ERROR_MARGIN]; + $alternative->max = $numregs[NUMERICAL_CORRECT_ANSWER] + + $numregs[NUMERICAL_ABS_ERROR_MARGIN]; + } else { + $alternative->min = $numregs[NUMERICAL_CORRECT_ANSWER]; + $alternative->max = $numregs[NUMERICAL_CORRECT_ANSWER]; + } + } else { // Min and max must stay undefined... + $alternative->answer = + $altregs[ANSWER_ALTERNATIVE_REGEX_ANSWER]; + } + + $multianswer->alternatives[] = $alternative; + $tmp = explode($altregs[0], $remainingalts, 2); + $remainingalts = $tmp[1]; + } + + $question->defaultgrade += $multianswer->norm; + $question->answers[] = $multianswer; + $question->questiontext = implode("{#$positionkey}", + explode($answerregs[0], $question->questiontext, 2)); + } + return $question; +} + +class quiz_file_format extends quiz_default_format { + + function readquestions($lines) { + /// Parses an array of lines into an array of questions. + /// For this class the method has been simplified as + /// there can never be more than one question for a + /// multianswer import + + $questions= array(); + $thequestion= extractMultiAnswerQuestion(implode('',$lines)); + + if (!empty($thequestion)) { + $thequestion->name = $lines[0]; + + $questions[] = $thequestion; + } + + return $questions; + } +} + +?> diff --git a/mod/quiz/format/qti/format.php b/mod/quiz/format/qti/format.php new file mode 100644 index 00000000000..9c0bab3533c --- /dev/null +++ b/mod/quiz/format/qti/format.php @@ -0,0 +1,19 @@ +wwwroot/mod/quiz/import.php?category=$category->id"); + } + +} + +?> diff --git a/mod/quiz/format/webct/format.php b/mod/quiz/format/webct/format.php new file mode 100644 index 00000000000..1b8ded9dc1d --- /dev/null +++ b/mod/quiz/format/webct/format.php @@ -0,0 +1,19 @@ +wwwroot/mod/quiz/import.php?category=$category->id"); + } + +} + +?> diff --git a/mod/quiz/import.php b/mod/quiz/import.php index 8259a9c6449..9219963cd8d 100644 --- a/mod/quiz/import.php +++ b/mod/quiz/import.php @@ -49,12 +49,12 @@ } else { // Valid file is found - if (! is_readable("format/$form->format".".php")) { + if (! is_readable("format/$form->format/format.php")) { error("Format not known ($form->format)"); } - require("format/default.php"); // Parent class - require("format/$form->format".".php"); + require("format.php"); // Parent class + require("format/$form->format/format.php"); $format = new quiz_file_format(); @@ -83,6 +83,18 @@ error("No categories!"); } + $fileformats = get_list_of_plugins("mod/quiz/format"); + $fileformatname = array(); + foreach ($fileformats as $key => $fileformat) { + $formatname = get_string($fileformat, 'quiz'); + if ($formatname == "[[$fileformat]]") { + $formatname = $fileformat; // Just use the raw folder name + } + $fileformatnames[$fileformat] = $formatname; + } + natcasesort($fileformatnames); + + print_heading_with_help($strimportquestions, "import", "quiz"); print_simple_box_start("center", "", "$THEME->cellheading"); @@ -92,14 +104,13 @@ echo "