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 "

"; + print_single_button("multiple.php", $options, get_string("randomcreate", "quiz")); + echo "
"; + + return true; + } + +} + +?> diff --git a/mod/quiz/format/blackboard/format.php b/mod/quiz/format/blackboard/format.php new file mode 100644 index 00000000000..66f72efb2f2 --- /dev/null +++ b/mod/quiz/format/blackboard/format.php @@ -0,0 +1,252 @@ +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"]; + + if (strcmp($id, $correct_answer) == 0) { // true is correct + $question->answer = 1; + $question->feedbacktrue = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"])); + $question->feedbackfalse = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"])); + } else { // false is correct + $question->answer = 0; + $question->feedbacktrue = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"])); + $question->feedbackfalse = addslashes(trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][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/coursetestmanager/format.php b/mod/quiz/format/coursetestmanager/format.php new file mode 100755 index 00000000000..4ba52ef9e73 --- /dev/null +++ b/mod/quiz/format/coursetestmanager/format.php @@ -0,0 +1,507 @@ +category = $category; // Important + return true; + } + function importprocess($filename) { + global $CFG,$QUIZ_FILE_FORMAT,$strimportquestions,$form,$question_category,$category,$course,$THEME, + $hostname, $mdapath, $mdbpath; + if ((PHP_OS == "Linux") and isset($hostname)) { + $hostname = trim($hostname); + // test the ODBC socket server connection + // if failure, unset hostname and set hostname_access_error + $question_categories = $this->getquestioncategories($mdbpath, $mdapath, $hostname); + if (!$question_categories) { + $hostname_access_error = $hostname . " "; + unset($hostname); + } else { + $hostname_access_error = 0; + } + } + + if ((PHP_OS == "Linux") and !isset($hostname)) { + // copy the file to a semi-permanent location + if (! $basedir = make_upload_directory("$course->id")) { + error("The site administrator needs to fix the file permissions for the data directory"); + } + if (!isset($hostname_access_error)) { + $bname=basename($filename); + $cleanfilename = clean_filename($bname); + if ($cleanfilename) { + $newfile = "$basedir/$cleanfilename"; + if (move_uploaded_file($filename, $newfile)) { + chmod($newfile, 0666); + } else { + notify(get_string("uploadproblem", "", $filename)); + } + } + $filename = $newfile; + } + print_heading_with_help($strimportquestions, "import", "quiz"); + print_simple_box_start("center", "", "$THEME->cellheading"); + if ($hostname_access_error) { notify("couldn't connect to ODBC Socket Server on " . $hostname_access_error); } + echo "
"; + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + echo "
"; + echo "What is the hostname or IP address of the ODBC Socket Server:"; + echo " "; + echo " "; + echo " id."\">"; + echo " format."\">"; + echo " 
"; + echo "What is the location of the database (.mdb file) on the Socket Server:"; + echo " "; + echo " 
"; + echo "What is the location of the system database (System.mda file) on the Socket Server:"; + echo " "; + echo " 
 "; + echo " "; + echo "
"; + echo "
"; + print_simple_box_end(); + print_footer($course); + exit; + } + + // we get here if running windows or after connect to ODBC socket server on linux +// +// this generates the page to choose categories of questions to import +// + if (!isset($question_category)) { + + if (PHP_OS == "WINNT") { + // copy the file to a semi-permanent location + if (! $basedir = make_upload_directory("$course->id")) { + error("The site administrator needs to fix the file permissions for the data directory"); + } + $bname=basename($filename); + $cleanfilename = clean_filename($bname); + if ($cleanfilename) { + $newfile = "$basedir/$cleanfilename"; + if (move_uploaded_file($filename, $newfile)) { + chmod($newfile, 0666); + } else { + notify(get_string("uploadproblem", "", $filename)); + } + } + $filename = $newfile; + } + // end of file copy + + // don't have to do this on linux, since it's alreay been done in the test above + if (PHP_OS == "WINNT") { $question_categories = $this->getquestioncategories($filename); } + // print the intermediary form + if (!$categories = quiz_get_category_menu($course->id, true)) { + error("No categories!"); + } + print_heading_with_help($strimportquestions, "import", "quiz"); + print_simple_box_start("center", "", "$THEME->cellheading"); + echo "
"; + echo ""; + echo ""; + echo ""; + echo "
"; + echo "Choose a category of questions to import:"; + asort($question_categories); + choose_from_menu($question_categories, "question_category","All Categories","All Categories", "", "allcategories"); + echo " "; + echo " id."\">"; + echo " format."\">"; + if (PHP_OS == "Linux") { + echo " "; + echo " "; + echo " "; + } + echo " 
 "; + echo " "; + echo "
"; + echo "
"; + print_simple_box_end(); + print_footer($course); + exit; + } +// +// this is the main import section +// + notify("Importing questions"); + if (PHP_OS == "Linux") { + $hostname = trim($hostname); + $records = $this->getquestions($mdbpath,$question_category,$mdapath, $hostname); + } else { + $records = $this->getquestions($filename,$question_category); + } + foreach ($records as $qrec) + { + $question = NULL; + $question->image = ""; // No images with this format + if ($qrec[9] != "") { + $question->image = $qrec[9]; + } + $question->defaultgrade = 1; +// 0 Selected +// 1 PracticeTestOK? +// 2 QuestionText +// 3 QuestionType +// 4 Option1Text +// 5 Option2Text +// 6 Option3Text +// 7 Option4Text +// 8 CorrectAnswer +// 9 Graphic +// 10 Module +// 11 ChapterNumber +// 12 PageNumber + $ref = "Answer can be found in chapter ". $qrec[11] . ", page " . $qrec[12] . "."; + switch ($qrec[3]) { + case 1: + $question->qtype = MULTICHOICE; // MULTICHOICE, SHORTANSWER, TRUEFALSE + // echo "
";echo htmlspecialchars($qrec[2]); echo "
"; + $question->questiontext = addslashes(trim($qrec[2])); + // echo "
";echo $question->questiontext; echo "
"; + $question->name = preg_replace("/
/", "", $question->questiontext); + $question->single = 1; // Only one answer is allowed -- used for multiple choicers + $fractionset = 0; + for ($i=4;$i<=7;$i++) { + if ($qrec[$i] != "") { + $question->answer[$i-3]=addslashes($qrec[$i]); + if ($qrec[8] == $i-3) { // if this is the index of CorrectAnswer + $question->fraction[$i-3] = 1; + $fractionset = 1; + } else { + $question->fraction[$i-3] = 0; + } + $question->feedback[$i-3] = (($qrec[8] == $i-3)?"Correct. ":"Incorrect. ") . $ref; + } + } + if ($fractionset == 0) { $question->fraction[1] = 1; } + break; + case 2: // TRUE FALSE + $question->qtype = TRUEFALSE; + $question->questiontext = addslashes(trim($qrec[2])); + $question->name = preg_replace("/
/", "", $question->questiontext); + // for TF, $question->answer should be 1 for true, 0 for false + if ($qrec[8] == "T") { $question->answer =1;} else { $question->answer = 0; } + // for TF, use $question->feedbacktrue and feedbackfalse + $question->feedbacktrue = (($qrec[8] =="T")?"Correct. ":"Incorrect. ") . $ref; + $question->feedbackfalse = (($qrec[8] =="F")?"Correct. ":"Incorrect. ") . $ref; + break; + case 3: + $question->qtype = SHORTANSWER; + $question->questiontext = addslashes(trim($qrec[2])); + // echo "
";echo $question->questiontext; echo "
"; + $question->name = preg_replace("/
/", "", $question->questiontext); + $question->usecase=0; // Ignore case -- for SHORT ANSWER questions + $answers = explode("~", $qrec[8]); + $question->answer[0]=" "; + $question->fraction[0]=1; + for ($i=0;$ianswer[$i] = addslashes(trim($answers[$i])); + $question->feedback[$i] = $ref; + $question->fraction[$i] = 1; // 1 for 100%, 0 for none or somewhere in between + } + break; + case 4: + $question = 0; + notify("Cannot use essay questions - skipping question ". $qrec[2] . " " . $ref); + break; + default: + $question = 0; + notify("Misformatted Record. Question Skipped."); + break; + } + if ($question) { $questions[] = $question; } + } + $count = 0; + // process all the questions + if (PHP_OS == "WINNT") { + $filename = str_replace("\\\\","\\",$filename); + $filename = str_replace("/","\\",$filename); + } + foreach ($questions as $question) { // Process and store each question + $count++; + echo "

$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 "
Error: Could not delete: $filename"; + return false; + } + return true; + } + + function getquestions($filename, $category, $mdapath="", $hostname="") { + if (($category == "allcategories") or ($category == "")) { + $sql = "SELECT * FROM TBQuestions"; + } else { + $sql = "SELECT * FROM TBQuestions where module = '".$category."'"; + } + if (PHP_OS == "WINNT") { + $ldb =& $this->connect_win($filename); + $qset = $ldb->Execute("$sql"); + if ( $qset->RecordCount() > 0 ) { + $records = $qset->GetAssoc(true); + } else { + $this->err("There were no records in the database.",$dsn); + $ldb->Close(); + return false; + } + $ldb->Close(); + } else { // if PHP_OS == WINNT + // we have a linux installation + $result = $this->query_linux($sql,$filename, $mdapath,$hostname); + if ( count($result) > 0 ) { + // get rid of the ID field in the first column. + for($i=0;$ierr("There were no records in the database.",$dsn); + $ldb->Close(); + return false; + } + // xml test and connect + } // PHP_OS TEST + return $records; + } + + function getquestioncategories($filename, $mdapath="", $hostname="") { + global $CFG, $result; + $sql = "SELECT Distinct module FROM TBQuestions"; + if (PHP_OS == "WINNT") { + $ldb =& $this->connect_win($filename); + $qset = $ldb->Execute("$sql"); + if ( $qset->RecordCount() > 0 ) { + $records = $qset->GetArray(true); + foreach ($records as $record) { + $categories[$record[0]] = $record[0]; + } + } else { // if recordcount + $this->err("There were no records in the database.",$dsn); + $ldb->Close(); + return false; + } + $ldb->Close(); + } else { // if PHP_OS == WINNT + // we have a linux installation + $result = $this->query_linux($sql, $filename, $mdapath, $hostname); + for($i=0;$isHostName = $hostname; + $oTest->nPort = 9628; +// $oTest->sConnectionString="DRIVER=Microsoft Access Driver (*.mdb);SystemDB=C:\CTM\System.mda;DBQ=C:\CTM\of2K3\ctm.mdb;UID=Assess;PWD=VBMango;"; + $oTest->sConnectionString="DRIVER=Microsoft Access Driver (*.mdb);SystemDB=". + $mdapath.";DBQ=".$mdbpath.";UID=Assess;PWD=VBMango;"; + // send and receive XML communication + $qResult = $oTest->ExecSQL($sql); + // set up XML parser to read the results + $xml_parser = xml_parser_create("US-ASCII"); + xml_set_element_handler($xml_parser, "quiz_xmlstart", "quiz_xmlend"); + xml_set_character_data_handler($xml_parser, "quiz_xmldata"); + // parse the XML and get back the result set array + if (!xml_parse($xml_parser, $qResult)) { + $this->err("XML error: ".xml_error_string(xml_get_error_code($xml_parser)) + ." at line ".xml_get_current_line_number($xml_parser),$oTest->sConnectionString); + return false; + } else { +// echo("Successful XML parse. "); + // prepare the array for use in the pull-down +/* echo "
count of rows is ". count ($result); + 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 "
"; + } + + function fulldelete($location) { + if (is_dir($location)) { + $currdir = opendir($location); + while ($file = readdir($currdir)) { + if ($file <> ".." && $file <> ".") { + $fullfile = $location."/".$file; + if (is_dir($fullfile)) { + if (!fulldelete($fullfile)) { + return false; + } + } else { + if (!unlink($fullfile)) { + return false; + } + } + } + } + closedir($currdir); + if (! rmdir($location)) { + return false; + } + + } else { + if (!unlink($location)) { + return false; + } + } + return true; + } + + + function findfile($filename) { + global $CFG; + $dirs = $this->getcoursedirs(); + $dirs[] = $CFG->dirroot."\mod\quiz\format"; + foreach ($dirs as $dir) { + $file = $dir . "\System.mda"; + // look for System.mda + if (is_file($file)) return $file; + } + return false; + } + + function getcoursedirs() { + global $CFG; + // for every course in the system, find the root of the data directory + $courses = get_records_sql("select distinct id,fullname from ".$CFG->prefix."course"); + $dirs = array(); + if ($courses) { + foreach ($courses as $course) { + $dir = $CFG->dataroot . "/" . $course->id; + if (is_dir($dir)) { $dirs[] = $dir; } + } + } + return $dirs; + } + +} // END OF CLASS + + //Handler for starting elements + function quiz_xmlstart($parser, $name, $attribs) { + global $result,$row, $col, $incolumn; + $name = strtolower($name); + switch ($name) { + case "row": + $col=0;break; + case "column": + $incolumn = 1;break; + case "error": + break; + case "result": + $row = 0; break; + } // switch + } + + //handler for the end of elements + function quiz_xmlend($parser, $name) { + global $result, $row, $col, $incolumn; + $name = strtolower($name); + switch ($name) { + case "row": + $row++;break; + case "column": + $incolumn = 0; + $col++; + break; + case "error": + break; + case "result": + break; + } // switch + } // function + + //handler for character data + function quiz_xmldata($parser, $data) { + global $result, $row, $col, $incolumn; + if ($incolumn) { $result[$row][$col] = $result[$row][$col] . $data; + } + } + +?> diff --git a/mod/quiz/format/custom.php b/mod/quiz/format/custom.php deleted file mode 100644 index 05ed991b153..00000000000 --- a/mod/quiz/format/custom.php +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/mod/quiz/format/gift/format.php b/mod/quiz/format/gift/format.php new file mode 100755 index 00000000000..3a5065fb29e --- /dev/null +++ b/mod/quiz/format/gift/format.php @@ -0,0 +1,274 @@ + 0 and $answer_weight <> 1){ + /// $question->single = 0; // ok many good answers + /// } + return $answer_weight; + } + + + function commentparser(&$answer) { + //Answer Comment parser + if (strpos($answer,"#") > 0){ + $hashpos = strpos($answer,"#"); + $comment = addslashes(substr($answer, $hashpos+1)); + $answer = substr($answer, 0, $hashpos); + } else { + $comment = " "; + } + return $comment; + } + + + 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; + $comment = NULL; + $answer_weight_regex = "^%\-*([0-9]{1,2})\.?([0-9]*)%"; + $text = implode(" ", $lines); + + /// QUESTION NAME parser + $text = trim($text); + if (substr($text, 0, 2) == "::") { + $text = substr($text, 2); + + $namefinish = strpos($text, "::"); + if ($namefinish === false) { + $question->name = false; + // name will be assigned after processing question text below + } else { + $question->name = addslashes(trim(substr($text, 0, $namefinish))); + $text = substr($text, $namefinish+2); // Remove name from text + } + } else { + $question->name = false; + } + + + /// FIND ANSWER section + $answerstart = strpos($text, "{"); + if ($answerstart === false) { + if ($this->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 = 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 ""; print_string("category", "quiz"); echo ":"; - asort($QUIZ_FILE_FORMAT); choose_from_menu($categories, "category", "$category->id", ""); echo ""; echo ""; print_string("fileformat", "quiz"); echo ":"; - choose_from_menu($QUIZ_FILE_FORMAT, "format", "custom", ""); + choose_from_menu($fileformatnames, "format", "gift", ""); helpbutton("import", $strimportquestions, "quiz"); echo ""; diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index dc45f5462c5..3238ac65324 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -34,14 +34,6 @@ $QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz") MULTIANSWER => get_string("multianswer", "quiz") ); -$QUIZ_FILE_FORMAT = array ( "custom" => get_string("custom", "quiz"), - "missingword" => get_string("missingword", "quiz"), - "blackboard" => get_string("blackboard", "quiz"), - "aon" => "AON", - "aiken" => "Aiken", - "multianswer" => get_string("multianswer", "quiz"), - "coursetestmanager" => "Course Test Manager" - ); define("QUIZ_PICTURE_MAX_HEIGHT", "600"); // Not currently implemented define("QUIZ_PICTURE_MAX_WIDTH", "600"); // Not currently implemented