Tidied up the quiz imports a bit, and added AON as a format choice

Also added Scott Elliott's Blackboard module!
This commit is contained in:
moodler
2003-05-02 15:31:24 +00:00
parent 02fee14e28
commit c6bcd06aba
12 changed files with 366 additions and 252 deletions
+18 -13
View File
@@ -1,11 +1,11 @@
<P ALIGN=CENTER><B>Importing new questions</B></P>
<P>This function allows you to import questions from
external text files.
external text files, uploaded through a form.
<P>Several common file formats are supported:
<P>A number of file formats are supported:
<P><B>Missing word format</B></P>
<P><B>Missing Word</B></P>
<UL>
<P>This format only supports multiple choice questions.
Each answer is separated with a tilde (~), and the correct answer is
@@ -20,24 +20,25 @@ we become students of {=anatomy and physiology ~reflexology
</UL>
<P><B>IMS QTI format</B></P>
<P><B>AON</B></P>
<UL>
<P>The standard format for Question and Test Interoperability, from IMS.
<P>Not yet implemented
<P>More info: <? helpbutton("formatqti", "", "quiz") ?></P>
<P>This is the same as Missing Word Format, except that after importing
the questions all Short-Answer questions are converted four at a time
into Matching Questions.</P>
<p>It's named after an organisation that sponsored the development of many
quiz features</p>
</UL>
<P><B>WebCT format</B></P>
<P><B>Blackboard</B></P>
<UL>
<P>The format of files exported from WebCT.
<P>Not yet implemented
<P>This module can import questions saved in Blackboard's export
format. It relies on XML functions being compiled into your PHP.</P>
<P>More info: <? helpbutton("formatwebct", "", "quiz") ?></P>
<P>More info: <? helpbutton("formatblackboard", "", "quiz") ?></P>
</UL>
<P><B>Custom format</B></P>
<P><B>Custom</B></P>
<UL>
<P>If you have your own format that you need to import, you can
implement it yourself by editing mod/quiz/format/custom.php
@@ -47,3 +48,7 @@ we become students of {=anatomy and physiology ~reflexology
<P>More info: <? helpbutton("formatcustom", "", "quiz") ?></P>
</UL>
<P>More formats are yet to come, including WebCT, IMS QTI and whatever else
Moodle users can contribute! </p>
+1
View File
@@ -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";
+9 -9
View File
@@ -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 "<CENTER>";
$options['category'] = $this->category->id;
echo "<center>";
print_single_button("multiple.php", $options, get_string("randomcreate", "quiz"));
echo "</CENTER>";
echo "</center>";
return true;
}
+249
View File
@@ -0,0 +1,249 @@
<?PHP // $Id$
////////////////////////////////////////////////////////////////////////////
/// Blackboard 6.0 Format
///
/// This Moodle class provides all functions necessary to import and export
///
///
////////////////////////////////////////////////////////////////////////////
require ("default.php");
require_once ("$CFG->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;
}
}
?>
-63
View File
@@ -1,63 +0,0 @@
<?PHP // $Id$
////////////////////////////////////////////////////////////////////////////
/// CUSTOM FORMAT
///
/// 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 missingword.php for an example of how it's done, and see
/// the top of ../lib.php for some constants you might need.
///
////////////////////////////////////////////////////////////////////////////
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;
}
}
?>
+2 -40
View File
@@ -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;
}
}
?>
+54 -2
View File
@@ -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 "<hr><p><b>$count</b>. ".stripslashes($question->questiontext)."</p>";
$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 "<p>You need to override the readquestion() function";
echo "<p>This quiz format has not yet been completed!</p>";
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.
-8
View File
@@ -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;
}
}
?>
-27
View File
@@ -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;
}
}
-27
View File
@@ -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;
}
}
+29 -60
View File
@@ -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 "<hr><p><b>$count</b>. ".stripslashes($question->questiontext)."</p>";
$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 "<hr>";
@@ -114,32 +79,36 @@
print_heading_with_help($strimportquestions, "import", "quiz");
print_simple_box_start("center", "", "$THEME->cellheading");
echo "<FORM ENCTYPE=\"multipart/form-data\" METHOD=\"POST\" ACTION=import.php>";
echo "<TABLE cellpadding=5>";
echo "<TR><TD align=right>";
print_string("category", "quiz");
echo ":</TD><TD>";
choose_from_menu($categories, "category", "$category->id", "");
echo "</TR>";
echo "<form enctype=\"multipart/form-data\" method=\"post\" action=import.php>";
echo "<table cellpadding=5>";
echo "<TR><TD align=right>";
echo "<tr><td align=right>";
print_string("category", "quiz");
echo ":</td><td>";
asort($QUIZ_FILE_FORMAT);
choose_from_menu($categories, "category", "$category->id", "");
echo "</tr>";
echo "<tr><td align=right>";
print_string("fileformat", "quiz");
echo ":</TD><TD>";
echo ":</td><td>";
choose_from_menu($QUIZ_FILE_FORMAT, "format", "custom", "");
helpbutton("import", $strimportquestions, "quiz");
echo "</TR><TR><TD align=right>";
echo "</tr>";
echo "<tr><td align=right>";
print_string("upload");
echo ":</TD><TD>";
echo " <INPUT NAME=\"newfile\" TYPE=\"file\" size=\"50\">";
echo "</TR><TR><TD>&nbsp;</TD><TD>";
echo " <INPUT TYPE=hidden NAME=category VALUE=\"$category->id\">";
echo " <INPUT TYPE=submit NAME=save VALUE=\"".get_string("uploadthisfile")."\">";
echo "</TD></TR>";
echo "</TABLE>";
echo "</FORM>";
echo ":</td><td>";
echo " <input name=\"newfile\" type=\"file\" size=\"50\">";
echo "</tr><tr><td>&nbsp;</td><td>";
echo " <input type=hidden name=category value=\"$category->id\">";
echo " <input type=submit name=save value=\"".get_string("uploadthisfile")."\">";
echo "</td></tr>";
echo "</table>";
echo "</form>";
print_simple_box_end();
print_footer($course);
?>
+4 -3
View File
@@ -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");