From c6bcd06abac61a6d15a8f9b4d4bb12090eb93bbb Mon Sep 17 00:00:00 2001 From: moodler Date: Fri, 2 May 2003 15:31:24 +0000 Subject: [PATCH] Tidied up the quiz imports a bit, and added AON as a format choice Also added Scott Elliott's Blackboard module! --- lang/en/help/quiz/import.html | 31 ++-- lang/en/quiz.php | 1 + mod/quiz/format/aon.php | 18 +- mod/quiz/format/blackboard.php | 249 ++++++++++++++++++++++++++++ mod/quiz/format/custom-template.php | 63 ------- mod/quiz/format/custom.php | 42 +---- mod/quiz/format/default.php | 56 ++++++- mod/quiz/format/missingword.php | 8 - mod/quiz/format/qti.php | 27 --- mod/quiz/format/webct.php | 27 --- mod/quiz/import.php | 85 +++------- mod/quiz/lib.php | 7 +- 12 files changed, 364 insertions(+), 250 deletions(-) create mode 100644 mod/quiz/format/blackboard.php delete mode 100644 mod/quiz/format/custom-template.php diff --git a/lang/en/help/quiz/import.html b/lang/en/help/quiz/import.html index 0ce15388ec..d99a847beb 100644 --- a/lang/en/help/quiz/import.html +++ b/lang/en/help/quiz/import.html @@ -1,11 +1,11 @@

Importing new questions

This function allows you to import questions from - external text files. + external text files, uploaded through a form. -

Several common file formats are supported: +

A number of file formats are supported: -

Missing word format

+

Missing Word

-

IMS QTI format

+

AON

-

WebCT format

+

Blackboard

-

Custom format

+

Custom

+ + +

More formats are yet to come, including WebCT, IMS QTI and whatever else + Moodle users can contribute!

diff --git a/lang/en/quiz.php b/lang/en/quiz.php index 806d9e8019..614ad3dd92 100644 --- a/lang/en/quiz.php +++ b/lang/en/quiz.php @@ -24,6 +24,7 @@ $string['attemptsallowed'] = "Attempts allowed"; $string['attemptsunlimited'] = "Unlimited attempts"; $string['backtoquiz'] = "Back to quiz editing"; $string['bestgrade'] = "Best grade"; +$string['blackboard'] = "Blackboard"; $string['casesensitive'] = "Case sensitivity"; $string['caseyes'] = "Yes, case must match"; $string['caseno'] = "No, case is unimportant"; diff --git a/mod/quiz/format/aon.php b/mod/quiz/format/aon.php index e07fcadd97..b6aa66018c 100644 --- a/mod/quiz/format/aon.php +++ b/mod/quiz/format/aon.php @@ -110,17 +110,17 @@ class quiz_file_format extends quiz_default_format { } } - function postprocess($category, $questionids) { + function postprocess() { /// 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 $db, $CFG; + global $CFG; - print_heading(count($questionids)." ".get_string("questions", "quiz")); + print_heading(count($this->questionids)." ".get_string("questions", "quiz")); - $questionids = implode(',', $questionids); + $questionids = implode(',', $this->questionids); if (!$shortanswers = get_records_select("quiz_questions", "id IN ($questionids) AND qtype = ".SHORTANSWER, @@ -134,14 +134,14 @@ class quiz_file_format extends quiz_default_format { $shortanswerids[] = $key; } - $strmatch = "$category->name - ".get_string("match", "quiz"); + $strmatch = get_string("match", "quiz")." (".$this->category->name.")"; $shortanswerids = swapshuffle($shortanswerids); $count = $shortanswercount = count($shortanswerids); $i = 1; $matchcount = 0; - $question->category = $category->id; + $question->category = $this->category->id; $question->qtype = MATCH; $question->questiontext = get_string("randomsamatchintro", "quiz"); $question->image = ""; @@ -208,10 +208,10 @@ class quiz_file_format extends quiz_default_format { print_heading($info); - $options['category'] = $category->id; - echo "
"; + $options['category'] = $this->category->id; + echo "
"; print_single_button("multiple.php", $options, get_string("randomcreate", "quiz")); - echo "
"; + echo "
"; return true; } diff --git a/mod/quiz/format/blackboard.php b/mod/quiz/format/blackboard.php new file mode 100644 index 0000000000..66019c57ac --- /dev/null +++ b/mod/quiz/format/blackboard.php @@ -0,0 +1,249 @@ +libdir/xmlize.php"); + +class quiz_file_format extends quiz_default_format { + +/******************************** + Need to re-compile php with zip support before testing this + + function readdata($filename) { + /// Returns complete file with an array, one item per line + + if (is_readable($filename)) { + + $zip = zip_open($filename); + $zip_entry = $zip_read($zip); + if (strstr($zip_entry_name($zip_entry), "imsmanifest") == 0) + $zip_entry = $zip_read($zip); // skip past manifest file + + if (zip_entry_open($zip, $zip_entry, "r")) { + + $strbuf = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry)); + $buf = explode("\n", $strbuf); + zip_entry_close($zip_entry); + zip_close($zip); + return $buf; + + } else { + + zip_close($zip); + return false; + + } + + } + + return false; + } + +********************************/ + + function readquestions ($lines) { + /// Parses an array of lines into an array of questions, + /// where each item is a question object as defined by + /// readquestion(). + + $text = implode($lines, " "); + $xml = xmlize($text); + + $questions = array(); + + process_tf($xml, $questions); + process_mc($xml, $questions); + process_fib($xml, $questions); + process_matching($xml, $questions); + + return $questions; + } +} + +//---------------------------------------- +// Process True / False Questions +//---------------------------------------- +function process_tf($xml, &$questions) { + + $tfquestions = $xml["POOL"]["#"]["QUESTION_TRUEFALSE"]; + + for ($i = 0; $i < sizeof ($tfquestions); $i++) { + + $question = NULL; + + $question->qtype = TRUEFALSE; + $question->defaultgrade = 1; + $question->single = 1; // Only one answer is allowed + $question->image = ""; // No images with this format + + $thisquestion = $tfquestions[$i]; + // put questiontext in question object + $question->questiontext = addslashes(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"])); + // put name in question object + $question->name = $question->questiontext; + + $choices = $thisquestion["#"]["ANSWER"]; + + $correct_answer = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"][0]["@"]["answer_id"]; + + // first choice is true, second is false. + $id = $choices[0]["@"]["id"]; + + $question->feedbacktrue = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"])); + $question->feedbackfalse = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"])); + + if (strcmp($id, $correct_answer) == 0) // true is correct + $question->answer = 1; + else // false is correct + $question->answer = 0; + + $questions[] = $question; + } +} + +//---------------------------------------- +// Process Multiple Choice Questions +//---------------------------------------- +function process_mc($xml, &$questions) { + + $mcquestions = $xml["POOL"]["#"]["QUESTION_MULTIPLECHOICE"]; + + for ($i = 0; $i < sizeof ($mcquestions); $i++) { + + $question = NULL; + + $question->qtype = MULTICHOICE; + $question->defaultgrade = 1; + $question->single = 1; // Only one answer is allowed + $question->image = ""; // No images with this format + + $thisquestion = $mcquestions[$i]; + // put questiontext in question object + $question->questiontext = addslashes(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"])); + // put name of question in question object + $question->name = $question->questiontext; + + $choices = $thisquestion["#"]["ANSWER"]; + for ($j = 0; $j < sizeof ($choices); $j++) { + + $choice = trim($choices[$j]["#"]["TEXT"][0]["#"]); + // put this choice in the question object. + $question->answer[$j] = addslashes($choice); + + $id = $choices[$j]["@"]["id"]; + $correct_answer_id = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"][0]["@"]["answer_id"]; + // if choice is the answer, give 100%, otherwise give 0% + if (strcmp ($id, $correct_answer_id) == 0) { + $question->fraction[$j] = 1; + $question->feedback[$j] = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"])); + } else { + $question->fraction[$j] = 0; + $question->feedback[$j] = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"])); + } + } + $questions[] = $question; + } +} + +//---------------------------------------- +// Process Fill in the Blank Questions +//---------------------------------------- +function process_fib($xml, &$questions) { + + $fibquestions = $xml["POOL"]["#"]["QUESTION_FILLINBLANK"]; + for ($i = 0; $i < sizeof ($fibquestions); $i++) { + + $question = NULL; + + $question->qtype = SHORTANSWER; + $question->defaultgrade = 1; + $question->usecase = 0; // Ignore case + $question->image = ""; // No images with this format + + $thisquestion = $fibquestions[$i]; + // put questiontext in question object + $question->questiontext = addslashes(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"])); + // put name of question in question object + $question->name = $question->questiontext; + + $answer = trim($thisquestion["#"]["ANSWER"][0]["#"]["TEXT"][0]["#"]); + + $question->answer[] = addslashes($answer); + $question->fraction[] = 1; + $question->feedback[0] = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"])); + $question->feedback[1] = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"])); + + $questions[] = $question; + } +} + +//---------------------------------------- +// Process Matching Questions +//---------------------------------------- +function process_matching($xml, &$questions) { + + $matchquestions = $xml["POOL"]["#"]["QUESTION_MATCH"]; + for ($i = 0; $i < sizeof ($matchquestions); $i++) { + + $question = NULL; + + $question->qtype = MATCH; + $question->defaultgrade = 1; + $question->image = ""; // No images with this format + + $thisquestion = $matchquestions[$i]; + // put questiontext in question object + $question->questiontext = addslashes(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"])); + // put name of question in question object + $question->name = $question->questiontext; + + $choices = $thisquestion["#"]["CHOICE"]; + for ($j = 0; $j < sizeof ($choices); $j++) { + + $subquestion = NULL; + + $choice = $choices[$j]["#"]["TEXT"][0]["#"]; + $choice_id = $choices[$j]["@"]["id"]; + + $question->subanswers[] = addslashes(trim($choice)); + + $correctanswers = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"]; + for ($k = 0; $k < sizeof ($correctanswers); $k++) { + + if (strcmp($choice_id, $correctanswers[$k]["@"]["choice_id"]) == 0) { + + $answer_id = $correctanswers[$k]["@"]["answer_id"]; + + $answers = $thisquestion["#"]["ANSWER"]; + for ($m = 0; $m < sizeof ($answers); $m++) { + + $answer = $answers[$m]; + $current_ans_id = $answer["@"]["id"]; + if (strcmp ($current_ans_id, $answer_id) == 0) { + + $answer = $answer["#"]["TEXT"][0]["#"]; + $question->subquestions[] = addslashes(trim($answer)); + break; + + } + + } + + break; + + } + + } + + } + + $questions[] = $question; + + } +} +?> diff --git a/mod/quiz/format/custom-template.php b/mod/quiz/format/custom-template.php deleted file mode 100644 index dacb12784f..0000000000 --- a/mod/quiz/format/custom-template.php +++ /dev/null @@ -1,63 +0,0 @@ -readquestion($currentquestion)) { - $questions[] = $question; - } - $currentquestion = array(); - } - } else { - $currentquestion[] = $line; - } - } - - return $questions; - } - - - - function readquestion($lines) { - /// Given an array of lines known to define a question in - /// this format, this function converts it into a question - /// object suitable for processing and insertion into Moodle. - - $question = NULL; - - return $question; - } -} - -?> diff --git a/mod/quiz/format/custom.php b/mod/quiz/format/custom.php index dacb12784f..a8994465f8 100644 --- a/mod/quiz/format/custom.php +++ b/mod/quiz/format/custom.php @@ -6,11 +6,8 @@ /// This format provides a starting point for creating your own /// import format. /// -/// If your questions are separated by blank lines, then you will -/// only need to modify the readquestion() function to parse the -/// lines into each question. See default.php for the other -/// functions that you may need to override if you have different -/// needs. +/// See default.php for the functions that you may need to override +/// if you have different needs. /// /// See missingword.php for an example of how it's done, and see /// the top of ../lib.php for some constants you might need. @@ -21,43 +18,8 @@ require("default.php"); class quiz_file_format extends quiz_default_format { - function readquestions($lines) { - /// Parses an array of lines into an array of questions, - /// where each item is a question object as defined by - /// readquestion(). Questions are defined as anything - /// between blank lines. - - $questions = array(); - $currentquestion = array(); - foreach ($lines as $line) { - $line = trim($line); - if (empty($line)) { - if (!empty($currentquestion)) { - if ($question = $this->readquestion($currentquestion)) { - $questions[] = $question; - } - $currentquestion = array(); - } - } else { - $currentquestion[] = $line; - } - } - return $questions; - } - - - - function readquestion($lines) { - /// Given an array of lines known to define a question in - /// this format, this function converts it into a question - /// object suitable for processing and insertion into Moodle. - - $question = NULL; - - return $question; - } } ?> diff --git a/mod/quiz/format/default.php b/mod/quiz/format/default.php index 91056ff697..357e345f65 100644 --- a/mod/quiz/format/default.php +++ b/mod/quiz/format/default.php @@ -10,15 +10,67 @@ class quiz_default_format { var $displayerrors = true; + var $category = NULL; + var $questionids = array(); /// Importing functions function preprocess($category) { /// Does any pre-processing that may be desired + $this->category = $category; // Important + return true; } + function process($filename) { + /// Processes a given file. There's probably little need to change this + + if (! $lines = $this->readdata($filename)) { + notify("File could not be read, or was empty"); + return false; + } + + if (! $questions = $this->readquestions($lines)) { // Extract all the questions + notify("There are no questions in this file!"); + return false; + } + + notify("Importing ".count($questions)." questions"); + + $count = 0; + + foreach ($questions as $question) { // Process and store each question + $count++; + + echo "

$count. ".stripslashes($question->questiontext)."

"; + + $question->category = $this->category->id; + + 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); + return false; + } + + if (!empty($result->notice)) { + notify($result->notice); + return true; + } + } + return true; + } + + function readdata($filename) { /// Returns complete file with an array, one item per line @@ -61,13 +113,13 @@ class quiz_default_format { /// this format, this function converts it into a question /// object suitable for processing and insertion into Moodle. - echo "

You need to override the readquestion() function"; + echo "

This quiz format has not yet been completed!

"; return NULL; } - function postprocess($questionids) { + function postprocess() { /// Does any post-processing that may be desired /// Argument is a simple array of question ids that /// have just been added. diff --git a/mod/quiz/format/missingword.php b/mod/quiz/format/missingword.php index fad018598f..a7a7868460 100644 --- a/mod/quiz/format/missingword.php +++ b/mod/quiz/format/missingword.php @@ -105,14 +105,6 @@ class quiz_file_format extends quiz_default_format { } } - function postprocess($questionids) { - /// Does any post-processing that may be desired - /// Argument is a simple array of question ids that - /// have just been added. - - return true; - } - } ?> diff --git a/mod/quiz/format/qti.php b/mod/quiz/format/qti.php index ca996eef52..a069297595 100644 --- a/mod/quiz/format/qti.php +++ b/mod/quiz/format/qti.php @@ -2,40 +2,13 @@ //////////////////////////////////////////////////////////////////////////// /// IMS QTI FORMAT -/// -/// This Moodle class provides all functions necessary to import and export -/// QTI-formatted XML question files. -/// //////////////////////////////////////////////////////////////////////////// require("default.php"); class quiz_file_format extends quiz_default_format { - function readquestions($lines) { - /// Parses an array of lines into an array of questions, - /// where each item is a question object as defined by - /// readquestion(). - - $questions = array(); - /// FIXME - - return $questions; - } - - - function readquestion($lines) { - /// Given an array of lines known to define a question in - /// this format, this function converts it into a question - /// object suitable for processing and insertion into Moodle. - - $question = NULL; - - /// FIXME - - return $question; - } } diff --git a/mod/quiz/format/webct.php b/mod/quiz/format/webct.php index 3d7bfea60d..c19999e1fe 100644 --- a/mod/quiz/format/webct.php +++ b/mod/quiz/format/webct.php @@ -2,40 +2,13 @@ //////////////////////////////////////////////////////////////////////////// /// WEBCT FORMAT -/// -/// This Moodle class provides all functions necessary to import and export -/// WebCT-formatted question files. -/// //////////////////////////////////////////////////////////////////////////// require("default.php"); class quiz_file_format extends quiz_default_format { - function readquestions($lines) { - /// Parses an array of lines into an array of questions, - /// where each item is a question object as defined by - /// readquestion(). - - $questions = array(); - /// FIXME - - return $questions; - } - - - function readquestion($lines) { - /// Given an array of lines known to define a question in - /// this format, this function converts it into a question - /// object suitable for processing and insertion into Moodle. - - $question = NULL; - - /// FIXME - - return $question; - } } diff --git a/mod/quiz/import.php b/mod/quiz/import.php index 02406da469..fad020fba3 100644 --- a/mod/quiz/import.php +++ b/mod/quiz/import.php @@ -51,51 +51,16 @@ $format = new quiz_file_format(); - if (! $format->preprocess($category)) { - error("Error occurred during pre-processing."); + if (! $format->preprocess($category)) { // Do anything before that we need to + error("Error occurred during pre-processing!"); } - if (! $lines = $format->readdata($newfile['tmp_name'])) { - error("File could not be read, or was empty"); + if (! $format->process($newfile['tmp_name'])) { // Process the uploaded file + error("Error occurred during processing!"); } - if (! $questions = $format->readquestions($lines)) { - error("There are no questions in this file!"); - } - - notify("Importing ".count($questions)." questions"); - - $count = 0; - $questionids = array(); - - foreach ($questions as $question) { - $count++; - - echo "

$count. ".stripslashes($question->questiontext)."

"; - - $question->category = $category->id; - - if (!$question->id = insert_record("quiz_questions", $question)) { - error("Could not insert new question!"); - } - - $questionids[] = $question->id; - - // Now to save all the answers and type-specific options - - $result = quiz_save_question_options($question); - - if (!empty($result->error)) { - error($result->error); - } - - if (!empty($result->notice)) { - notice($result->notice); - } - } - - if (! $format->postprocess($category, $questionids)) { - error("Error occurred during post-processing."); + if (! $format->postprocess()) { // In case anything needs to be done after + error("Error occurred during post-processing!"); } echo "
"; @@ -114,32 +79,36 @@ print_heading_with_help($strimportquestions, "import", "quiz"); print_simple_box_start("center", "", "$THEME->cellheading"); - echo "
"; - echo ""; - echo ""; + + echo "
"; + echo ""; + echo ""; + + echo ""; + echo ""; - echo ""; + + echo ""; - echo "
"; print_string("category", "quiz"); - echo ":"; + echo ":"; + asort($QUIZ_FILE_FORMAT); choose_from_menu($categories, "category", "$category->id", ""); - echo "
"; + echo "
"; print_string("fileformat", "quiz"); - echo ":"; + echo ":"; choose_from_menu($QUIZ_FILE_FORMAT, "format", "custom", ""); helpbutton("import", $strimportquestions, "quiz"); - echo "
"; + echo "
"; print_string("upload"); - echo ":"; - echo " "; - echo "
 "; - echo " id\">"; - echo " "; - echo "
"; - echo ""; + echo ":
"; + echo " "; + echo "
 "; + echo " id\">"; + echo " "; + echo "
"; + echo ""; print_simple_box_end(); print_footer($course); - ?> diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index a436b2ba38..69b9fb6776 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -28,9 +28,10 @@ $QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz") RANDOMSAMATCH => get_string("randomsamatch", "quiz") ); $QUIZ_FILE_FORMAT = array ( "custom" => get_string("custom", "quiz"), - "webct" => get_string("webct", "quiz"), - "qti" => get_string("qti", "quiz"), - "missingword" => get_string("missingword", "quiz") ); + "missingword" => get_string("missingword", "quiz"), + "blackboard" => get_string("blackboard", "quiz"), + "aon" => "AON" + ); define("QUIZ_PICTURE_DEFAULT_HEIGHT", "200"); -- 2.39.5