Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

MDL-34738 qformat blackboard: blackboard format is broken

  • Loading branch information...
commit 8a8dd7d8c8a4330d306f6d34edd0c7b7b68a38b4 1 parent 7033316
Jean-Michel Vedrine authored
View
54 question/format.php
@@ -898,3 +898,57 @@ protected function format_question_text($question) {
$question->questiontextformat, $formatoptions), 0, false);
}
}
+
+class qformat_based_on_xml extends qformat_default {
+
+ /**
+ * Return the array moodle is expecting
+ * for an HTML text. No processing is done on $text.
+ * qformat classes that want to process $text
+ * for instance to import external images files
+ * and recode urls in $text must overwrite this method.
+ * @param array $text some HTML text string
+ * @return array with keys text, format and files.
+ */
+ public function text_field($text) {
+ return array(
+ 'text' => trim($text),
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ );
+ }
+
+ /**
+ * Return the value of a node, given a path to the node
+ * if it doesn't exist return the default value.
+ * @param array xml data to read
+ * @param array path path to node expressed as array
+ * @param mixed default
+ * @param bool istext process as text
+ * @param string error if set value must exist, return false and issue message if not
+ * @return mixed value
+ */
+ public function getpath($xml, $path, $default, $istext=false, $error='') {
+ foreach ($path as $index) {
+ if (!isset($xml[$index])) {
+ if (!empty($error)) {
+ $this->error($error);
+ return false;
+ } else {
+ return $default;
+ }
+ }
+
+ $xml = $xml[$index];
+ }
+
+ if ($istext) {
+ if (!is_string($xml)) {
+ $this->error(get_string('invalidxml', 'qformat_xml'));
+ }
+ $xml = trim($xml);
+ }
+
+ return $xml;
+ }
+}
View
641 question/format/blackboard/format.php
@@ -17,8 +17,7 @@
/**
* Blackboard question importer.
*
- * @package qformat
- * @subpackage blackboard
+ * @package qformat_blackboard
* @copyright 2003 Scott Elliott
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@@ -26,7 +25,7 @@
defined('MOODLE_INTERNAL') || die();
-require_once ($CFG->libdir . '/xmlize.php');
+require_once($CFG->libdir . '/xmlize.php');
/**
@@ -35,19 +34,67 @@
* @copyright 2003 Scott Elliott
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class qformat_blackboard extends qformat_default {
+class qformat_blackboard extends qformat_based_on_xml {
+ // Is the current question's question text escaped HTML (true for most if not all Blackboard files).
+ public $ishtml = true;
+
public function provide_import() {
return true;
}
- function readquestions($lines) {
- /// Parses an array of lines into an array of questions,
- /// where each item is a question object as defined by
- /// readquestion().
+ public function mime_type() {
+ return mimeinfo('type', '.dat');
+ }
+
+ /**
+ * Some softwares put entities in exported files.
+ * This method try to clean up known problems.
+ * @param string str string to correct
+ * @return string the corrected string
+ */
+ public function cleaninput($str) {
+ if (!$this->ishtml) {
+ return $str;
+ }
+ $html_code_list = array(
+ "'" => "'",
+ "’" => "'",
+ "[" => "[",
+ "“" => "\"",
+ "”" => "\"",
+ "]" => "]",
+ "'" => "'",
+ "–" => "-",
+ "—" => "-",
+ );
+ $str = strtr($str, $html_code_list);
+ // Use textlib entities_to_utf8 function to convert only numerical entities.
+ $str = textlib::entities_to_utf8($str, false);
+ return $str;
+ }
- $text = implode($lines, " ");
- $xml = xmlize($text, 0);
+ /**
+ * Parse the array of lines into an array of questions
+ * this *could* burn memory - but it won't happen that much
+ * so fingers crossed!
+ * @param array of lines from the input file.
+ * @param stdClass $context
+ * @return array (of objects) question objects.
+ */
+ protected function readquestions($lines) {
+
+ $text = implode($lines, ' ');
+ unset($lines);
+
+ // This converts xml to big nasty data structure,
+ // the 0 means keep white space as it is.
+ try {
+ $xml = xmlize($text, 0, 'UTF-8', true);
+ } catch (xml_format_exception $e) {
+ $this->error($e->getMessage(), '');
+ return false;
+ }
$questions = array();
@@ -61,341 +108,405 @@ function readquestions($lines) {
return $questions;
}
-//----------------------------------------
-// Process Essay Questions
-//----------------------------------------
- function process_essay($xml, &$questions ) {
-
- if (isset($xml["POOL"]["#"]["QUESTION_ESSAY"])) {
- $essayquestions = $xml["POOL"]["#"]["QUESTION_ESSAY"];
+ /**
+ * Do question import processing common to every qtype.
+ * @param array $questiondata the xml tree related to the current question
+ * @return object initialized question object.
+ */
+ public function process_common($questiondata) {
+ global $CFG;
+
+ // This routine initialises the question object.
+ $question = $this->defaultquestion();
+
+ // Determine if the question is already escaped html.
+ $this->ishtml = $this->getpath($questiondata,
+ array('#', 'BODY', 0, '#', 'FLAGS', 0, '#', 'ISHTML', 0, '@', 'value'),
+ false, false);
+
+ // Put questiontext in question object.
+ $text = $this->getpath($questiondata,
+ array('#', 'BODY', 0, '#', 'TEXT', 0, '#'),
+ '', true, get_string('importnotext', 'qformat_blackboard'));
+
+ if ($this->ishtml) {
+ $question->questiontext = $this->cleaninput($text);
+ $question->questiontextformat = FORMAT_HTML;
+ $question->questiontextfiles = array();
+
+ } else {
+ $question->questiontext = $text;
}
- else {
- return;
+ // Put name in question object. We must ensure it is not empty and it is less than 250 chars.
+ $question->name = shorten_text(strip_tags($question->questiontext), 200);
+ $question->name = substr($question->name, 0, 250);
+ if (!$question->name) {
+ $id = $this->getpath($questiondata,
+ array('@', 'id'), '', true);
+ $question->name = get_string('defaultname', 'qformat_blackboard' , $id);
}
- foreach ($essayquestions as $essayquestion) {
+ $question->generalfeedback = '';
+ $question->generalfeedbackformat = FORMAT_HTML;
+ $question->generalfeedbackfiles = array();
- $question = $this->defaultquestion();
+ // TODO : read the mark from the POOL TITLE QUESTIONLIST section.
+ $question->defaultmark = 1;
+ return $question;
+ }
+
+ /**
+ * Process Essay Questions
+ * @param array xml the xml tree
+ * @param array questions the questions already parsed
+ */
+ public function process_essay($xml, &$questions) {
+
+ if ($this->getpath($xml, array('POOL', '#', 'QUESTION_ESSAY'), false, false)) {
+ $essayquestions = $this->getpath($xml,
+ array('POOL', '#', 'QUESTION_ESSAY'), false, false);
+ } else {
+ return;
+ }
- $question->qtype = ESSAY;
+ foreach ($essayquestions as $thisquestion) {
- // determine if the question is already escaped html
- $ishtml = $essayquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
+ $question = $this->process_common($thisquestion);
- // put questiontext in question object
- if ($ishtml) {
- $question->questiontext = html_entity_decode(trim($essayquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]));
- }
+ $question->qtype = 'essay';
- // put name in question object
- $question->name = substr($question->questiontext, 0, 254);
$question->answer = '';
+ $answer = $this->getpath($thisquestion,
+ array('#', 'ANSWER', 0, '#', 'TEXT', 0, '#'), '', true);
+ $question->graderinfo = $this->text_field($this->cleaninput($answer));
$question->feedback = '';
+ $question->responseformat = 'editor';
+ $question->responsefieldlines = 15;
+ $question->attachments = 0;
$question->fraction = 0;
$questions[] = $question;
}
}
- //----------------------------------------
- // Process True / False Questions
- //----------------------------------------
- function process_tf($xml, &$questions) {
-
- if (isset($xml["POOL"]["#"]["QUESTION_TRUEFALSE"])) {
- $tfquestions = $xml["POOL"]["#"]["QUESTION_TRUEFALSE"];
- }
- else {
+ /**
+ * Process True / False Questions
+ * @param array xml the xml tree
+ * @param array questions the questions already parsed
+ */
+ public function process_tf($xml, &$questions) {
+
+ if ($this->getpath($xml, array('POOL', '#', 'QUESTION_TRUEFALSE'), false, false)) {
+ $tfquestions = $this->getpath($xml,
+ array('POOL', '#', 'QUESTION_TRUEFALSE'), false, false);
+ } else {
return;
}
- for ($i = 0; $i < sizeof ($tfquestions); $i++) {
-
- $question = $this->defaultquestion();
+ foreach ($tfquestions as $thisquestion) {
- $question->qtype = TRUEFALSE;
- $question->single = 1; // Only one answer is allowed
+ $question = $this->process_common($thisquestion);
- $thisquestion = $tfquestions[$i];
+ $question->qtype = 'truefalse';
+ $question->single = 1; // Only one answer is allowed.
- // determine if the question is already escaped html
- $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
+ $choices = $this->getpath($thisquestion, array('#', 'ANSWER'), array(), false);
- // put questiontext in question object
- if ($ishtml) {
- $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
- }
- // put name in question object
- $question->name = shorten_text($question->questiontext, 254);
+ $correct_answer = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'CORRECTANSWER', 0, '@', 'answer_id'),
+ '', true);
- $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
+ // First choice is true, second is false.
+ $id = $this->getpath($choices[0], array('@', 'id'), '', true);
+ $correctfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_CORRECT', 0, '#'),
+ '', true);
+ $incorrectfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_INCORRECT', 0, '#'),
+ '', true);
+ if (strcmp($id, $correct_answer) == 0) { // True is correct.
$question->answer = 1;
- $question->feedbacktrue = trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]);
- $question->feedbackfalse = trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]);
- } else { // false is correct
+ $question->feedbacktrue = $this->text_field($this->cleaninput($correctfeedback));
+ $question->feedbackfalse = $this->text_field($this->cleaninput($incorrectfeedback));
+ } else { // False is correct.
$question->answer = 0;
- $question->feedbacktrue = trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]);
- $question->feedbackfalse = trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]);
+ $question->feedbacktrue = $this->text_field($this->cleaninput($incorrectfeedback));
+ $question->feedbackfalse = $this->text_field($this->cleaninput($correctfeedback));
}
$question->correctanswer = $question->answer;
$questions[] = $question;
- }
+ }
}
- //----------------------------------------
- // Process Multiple Choice Questions
- //----------------------------------------
- function process_mc($xml, &$questions) {
-
- if (isset($xml["POOL"]["#"]["QUESTION_MULTIPLECHOICE"])) {
- $mcquestions = $xml["POOL"]["#"]["QUESTION_MULTIPLECHOICE"];
- }
- else {
+ /**
+ * Process Multiple Choice Questions with single answer
+ * @param array xml the xml tree
+ * @param array questions the questions already parsed
+ */
+ public function process_mc($xml, &$questions) {
+
+ if ($this->getpath($xml, array('POOL', '#', 'QUESTION_MULTIPLECHOICE'), false, false)) {
+ $mcquestions = $this->getpath($xml,
+ array('POOL', '#', 'QUESTION_MULTIPLECHOICE'), false, false);
+ } else {
return;
}
- for ($i = 0; $i < sizeof ($mcquestions); $i++) {
-
- $question = $this->defaultquestion();
-
- $question->qtype = MULTICHOICE;
- $question->single = 1; // Only one answer is allowed
-
- $thisquestion = $mcquestions[$i];
-
- // determine if the question is already escaped html
- $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
-
- // put questiontext in question object
- if ($ishtml) {
- $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
- }
-
- // put name of question in question object, careful of length
- $question->name = shorten_text($question->questiontext, 254);
-
- $choices = $thisquestion["#"]["ANSWER"];
- for ($j = 0; $j < sizeof ($choices); $j++) {
-
- $choice = trim($choices[$j]["#"]["TEXT"][0]["#"]);
- // put this choice in the question object.
- if ($ishtml) {
- $question->answer[$j] = html_entity_decode($choice,ENT_QUOTES,'UTF-8');
- }
- $question->answer[$j] = $question->answer[$j];
-
- $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;
- if ($ishtml) {
- $question->feedback[$j] = html_entity_decode(trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]),ENT_QUOTES,'UTF-8');
- }
- $question->feedback[$j] = $question->feedback[$j];
+ foreach ($mcquestions as $thisquestion) {
+
+ $question = $this->process_common($thisquestion);
+
+ $correctfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_CORRECT', 0, '#'),
+ '', true);
+ $incorrectfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_INCORRECT', 0, '#'),
+ '', true);
+ $question->correctfeedback = $this->text_field($this->cleaninput($correctfeedback));
+ $question->partiallycorrectfeedback = $this->text_field('');
+ $question->incorrectfeedback = $this->text_field($this->cleaninput($incorrectfeedback));
+
+ $question->qtype = 'multichoice';
+ $question->single = 1; // Only one answer is allowed.
+
+ $choices = $this->getpath($thisquestion, array('#', 'ANSWER'), false, false);
+ $correct_answer_id = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'CORRECTANSWER', 0, '@', 'answer_id'),
+ '', true);
+ foreach ($choices as $choice) {
+ $choicetext = $this->getpath($choice, array('#', 'TEXT', 0, '#'), '', true);
+ // Put this choice in the question object.
+ $question->answer[] = $this->text_field($this->cleaninput($choicetext));
+
+ $choice_id = $this->getpath($choice, array('@', 'id'), '', true);
+ // If choice is the right answer, give 100% mark, otherwise give 0%.
+ if (strcmp ($choice_id, $correct_answer_id) == 0) {
+ $question->fraction[] = 1;
} else {
- $question->fraction[$j] = 0;
- if ($ishtml) {
- $question->feedback[$j] = html_entity_decode(trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]),ENT_QUOTES,'UTF-8');
- }
- $question->feedback[$j] = $question->feedback[$j];
+ $question->fraction[] = 0;
}
+ // There is never feedback specific to each choice.
+ $question->feedback[] = $this->text_field('');
}
$questions[] = $question;
}
}
- //----------------------------------------
- // Process Multiple Choice Questions With Multiple Answers
- //----------------------------------------
- function process_ma($xml, &$questions) {
-
- if (isset($xml["POOL"]["#"]["QUESTION_MULTIPLEANSWER"])) {
- $maquestions = $xml["POOL"]["#"]["QUESTION_MULTIPLEANSWER"];
- }
- else {
+ /**
+ * Process Multiple Choice Questions With Multiple Answers
+ * @param array xml the xml tree
+ * @param array questions the questions already parsed
+ */
+ public function process_ma($xml, &$questions) {
+ if ($this->getpath($xml, array('POOL', '#', 'QUESTION_MULTIPLEANSWER'), false, false)) {
+ $maquestions = $this->getpath($xml,
+ array('POOL', '#', 'QUESTION_MULTIPLEANSWER'), false, false);
+ } else {
return;
}
- for ($i = 0; $i < sizeof ($maquestions); $i++) {
-
- $question = $this->defaultquestion();
-
- $question->qtype = MULTICHOICE;
+ foreach ($maquestions as $thisquestion) {
+ $question = $this->process_common($thisquestion);
+
+ $correctfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_CORRECT', 0, '#'),
+ '', true);
+ $incorrectfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_INCORRECT', 0, '#'),
+ '', true);
+ $question->correctfeedback = $this->text_field($this->cleaninput($correctfeedback));
+ // As there is no partially correct feedback we use incorrect one.
+ $question->partiallycorrectfeedback = $this->text_field($this->cleaninput($incorrectfeedback));
+ $question->incorrectfeedback = $this->text_field($this->cleaninput($incorrectfeedback));
+
+ $question->qtype = 'multichoice';
$question->defaultmark = 1;
- $question->single = 0; // More than one answers allowed
- $question->image = ""; // No images with this format
-
- $thisquestion = $maquestions[$i];
-
- // determine if the question is already escaped html
- $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
-
- // put questiontext in question object
- if ($ishtml) {
- $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
+ $question->single = 0; // More than one answers allowed.
+
+ $choices = $this->getpath($thisquestion, array('#', 'ANSWER'), false, false);
+ $correct_answer_ids = array();
+ foreach ($this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'CORRECTANSWER'), false, false) as $correctanswer) {
+ if ($correctanswer) {
+ $correct_answer_ids[] = $this->getpath($correctanswer,
+ array('@', 'answer_id'),
+ '', true);
+ }
}
- // put name of question in question object
- $question->name = shorten_text($question->questiontext, 254);
+ $fraction = 1/count($correct_answer_ids);
- $choices = $thisquestion["#"]["ANSWER"];
- $correctanswers = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"];
+ foreach ($choices as $choice) {
+ $choicetext = $this->getpath($choice, array('#', 'TEXT', 0, '#'), '', true);
+ // Put this choice in the question object.
+ $question->answer[] = $this->text_field($this->cleaninput($choicetext));
- for ($j = 0; $j < sizeof ($choices); $j++) {
+ $choice_id = $this->getpath($choice, array('@', 'id'), '', true);
- $choice = trim($choices[$j]["#"]["TEXT"][0]["#"]);
- // put this choice in the question object.
- $question->answer[$j] = $choice;
+ $iscorrect = in_array($choice_id, $correct_answer_ids);
- $correctanswercount = sizeof($correctanswers);
- $id = $choices[$j]["@"]["id"];
- $iscorrect = 0;
- for ($k = 0; $k < $correctanswercount; $k++) {
-
- $correct_answer_id = trim($correctanswers[$k]["@"]["answer_id"]);
- if (strcmp ($id, $correct_answer_id) == 0) {
- $iscorrect = 1;
- }
-
- }
if ($iscorrect) {
- $question->fraction[$j] = floor(100000/$correctanswercount)/100000; // strange behavior if we have more than 5 decimal places
- $question->feedback[$j] = trim($thisquestion["#"]["GRADABLE"][$j]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]);
+ $question->fraction[] = $fraction;
} else {
- $question->fraction[$j] = 0;
- $question->feedback[$j] = trim($thisquestion["#"]["GRADABLE"][$j]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]);
+ $question->fraction[] = 0;
}
+ // There is never feedback specific to each choice.
+ $question->feedback[] = $this->text_field('');
}
-
$questions[] = $question;
}
}
- //----------------------------------------
- // Process Fill in the Blank Questions
- //----------------------------------------
- function process_fib($xml, &$questions) {
-
- if (isset($xml["POOL"]["#"]["QUESTION_FILLINBLANK"])) {
- $fibquestions = $xml["POOL"]["#"]["QUESTION_FILLINBLANK"];
- }
- else {
+ /**
+ * Process Fill in the Blank Questions
+ * @param array xml the xml tree
+ * @param array questions the questions already parsed
+ */
+ public function process_fib($xml, &$questions) {
+ if ($this->getpath($xml, array('POOL', '#', 'QUESTION_FILLINBLANK'), false, false)) {
+ $fibquestions = $this->getpath($xml,
+ array('POOL', '#', 'QUESTION_FILLINBLANK'), false, false);
+ } else {
return;
}
- for ($i = 0; $i < sizeof ($fibquestions); $i++) {
- $question = $this->defaultquestion();
-
- $question->qtype = SHORTANSWER;
- $question->usecase = 0; // Ignore case
-
- $thisquestion = $fibquestions[$i];
-
- // determine if the question is already escaped html
- $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
-
- // put questiontext in question object
- if ($ishtml) {
- $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
- }
- // put name of question in question object
- $question->name = shorten_text($question->questiontext, 254);
-
- $answer = trim($thisquestion["#"]["ANSWER"][0]["#"]["TEXT"][0]["#"]);
-
- $question->answer[] = $answer;
- $question->fraction[] = 1;
- $question->feedback = array();
-
- if (is_array( $thisquestion['#']['GRADABLE'][0]['#'] )) {
- $question->feedback[0] = trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]);
- }
- else {
- $question->feedback[0] = '';
- }
- if (is_array( $thisquestion["#"]["GRADABLE"][0]["#"] )) {
- $question->feedback[1] = trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]);
- }
- else {
- $question->feedback[1] = '';
+ foreach ($fibquestions as $thisquestion) {
+
+ $question = $this->process_common($thisquestion);
+
+ $question->qtype = 'shortanswer';
+ $question->usecase = 0; // Ignore case.
+
+ $correctfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_CORRECT', 0, '#'),
+ '', true);
+ $incorrectfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_INCORRECT', 0, '#'),
+ '', true);
+ $answers = $this->getpath($thisquestion, array('#', 'ANSWER'), false, false);
+ foreach ($answers as $answer) {
+ $question->answer[] = $this->getpath($answer,
+ array('#', 'TEXT', 0, '#'), '', true);
+ $question->fraction[] = 1;
+ $question->feedback[] = $this->text_field($this->cleaninput($correctfeedback));
}
+ $question->answer[] = '*';
+ $question->fraction[] = 0;
+ $question->feedback[] = $this->text_field($this->cleaninput($incorrectfeedback));
$questions[] = $question;
}
}
- //----------------------------------------
- // Process Matching Questions
- //----------------------------------------
- function process_matching($xml, &$questions) {
-
- if (isset($xml["POOL"]["#"]["QUESTION_MATCH"])) {
- $matchquestions = $xml["POOL"]["#"]["QUESTION_MATCH"];
- }
- else {
+ /**
+ * Process Matching Questions
+ * @param array xml the xml tree
+ * @param array questions the questions already parsed
+ */
+ public function process_matching($xml, &$questions) {
+ if ($this->getpath($xml, array('POOL', '#', 'QUESTION_MATCH'), false, false)) {
+ $matchquestions = $this->getpath($xml,
+ array('POOL', '#', 'QUESTION_MATCH'), false, false);
+ } else {
return;
}
-
- for ($i = 0; $i < sizeof ($matchquestions); $i++) {
-
- $question = $this->defaultquestion();
-
- $question->qtype = MATCH;
-
- $thisquestion = $matchquestions[$i];
-
- // determine if the question is already escaped html
- $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
-
- // put questiontext in question object
- if ($ishtml) {
- $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
+ // Blackboard questions can't be imported in core Moodle without a loss in data,
+ // as core match question don't allow HTML in subanswers. The contributed ddmatch
+ // question type support HTML in subanswers.
+ // The ddmatch question type is not part of core, so we need to check if it is defined.
+ $ddmatch_is_installed = question_bank::is_qtype_installed('ddmatch');
+
+ foreach ($matchquestions as $thisquestion) {
+
+ $question = $this->process_common($thisquestion);
+ if ($ddmatch_is_installed) {
+ $question->qtype = 'ddmatch';
+ } else {
+ $question->qtype = 'match';
}
- // put name of question in question object
- $question->name = shorten_text($question->questiontext, 254);
-
- $choices = $thisquestion["#"]["CHOICE"];
- for ($j = 0; $j < sizeof ($choices); $j++) {
-
- $subquestion = NULL;
-
- $choice = $choices[$j]["#"]["TEXT"][0]["#"];
- $choice_id = $choices[$j]["@"]["id"];
-
- $question->subanswers[] = 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++) {
+ $correctfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_CORRECT', 0, '#'),
+ '', true);
+ $incorrectfeedback = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'FEEDBACK_WHEN_INCORRECT', 0, '#'),
+ '', true);
+ $question->correctfeedback = $this->text_field($this->cleaninput($correctfeedback));
+ // As there is no partially correct feedback we use incorrect one.
+ $question->partiallycorrectfeedback = $this->text_field($this->cleaninput($incorrectfeedback));
+ $question->incorrectfeedback = $this->text_field($this->cleaninput($incorrectfeedback));
+
+ $choices = $this->getpath($thisquestion,
+ array('#', 'CHOICE'), false, false); // Blackboard "choices" are Moodle subanswers.
+ $answers = $this->getpath($thisquestion,
+ array('#', 'ANSWER'), false, false); // Blackboard "answers" are Moodle subquestions.
+ $correctanswers = $this->getpath($thisquestion,
+ array('#', 'GRADABLE', 0, '#', 'CORRECTANSWER'), false, false); // Mapping between choices and answers.
+ $mappings = array();
+ foreach ($correctanswers as $correctanswer) {
+ if ($correctanswer) {
+ $correct_choice_id = $this->getpath($correctanswer,
+ array('@', 'choice_id'), '', true);
+ $correct_answer_id = $this->getpath($correctanswer,
+ array('@', 'answer_id'),
+ '', true);
+ $mappings[$correct_answer_id] = $correct_choice_id;
+ }
+ }
- $answer = $answers[$m];
- $current_ans_id = $answer["@"]["id"];
- if (strcmp ($current_ans_id, $answer_id) == 0) {
+ foreach ($choices as $choice) {
+ if ($ddmatch_is_installed) {
+ $choicetext = $this->text_field($this->cleaninput($this->getpath($choice,
+ array('#', 'TEXT', 0, '#'), '', true)));
+ } else {
+ $choicetext = trim(strip_tags($this->getpath($choice,
+ array('#', 'TEXT', 0, '#'), '', true)));
+ }
- $answer = $answer["#"]["TEXT"][0]["#"];
- $question->subquestions[] = trim($answer);
+ if ($choicetext != '') { // Only import non empty subanswers.
+ $subquestion = '';
+ $choice_id = $this->getpath($choice,
+ array('@', 'id'), '', true);
+ $fiber = array_search($choice_id, $mappings);
+ $fiber = array_keys ($mappings, $choice_id);
+ foreach ($fiber as $correct_answer_id) {
+ // We have found a correspondance for this choice so we need to take the associated answer.
+ foreach ($answers as $answer) {
+ $current_ans_id = $this->getpath($answer,
+ array('@', 'id'), '', true);
+ if (strcmp ($current_ans_id, $correct_answer_id) == 0) {
+ $subquestion = $this->getpath($answer,
+ array('#', 'TEXT', 0, '#'), '', true);
break;
}
}
- break;
+ $question->subquestions[] = $this->text_field($this->cleaninput($subquestion));
+ $question->subanswers[] = $choicetext;
+ }
+
+ if ($subquestion == '') { // Then in this case, $choice is a distractor.
+ $question->subquestions[] = $this->text_field('');
+ $question->subanswers[] = $choicetext;
}
}
}
- $questions[] = $question;
+ // Verify that this matching question has enough subquestions and subanswers.
+ $subquestioncount = 0;
+ $subanswercount = 0;
+ $subanswers = $question->subanswers;
+ foreach ($question->subquestions as $key => $subquestion) {
+ $subquestion = $subquestion['text'];
+ $subanswer = $subanswers[$key];
+ if ($subquestion != '') {
+ $subquestioncount++;
+ }
+ $subanswercount++;
+ }
+ if ($subquestioncount < 2 || $subanswercount < 3) {
+ $this->error(get_string('notenoughtsubans', 'qformat_blackboard', $question->questiontext));
+ } else {
+ $questions[] = $question;
+ }
}
}
View
6 question/format/blackboard/lang/en/qformat_blackboard.php
@@ -17,11 +17,13 @@
/**
* Strings for component 'qformat_blackboard', language 'en', branch 'MOODLE_20_STABLE'
*
- * @package qformat
- * @subpackage blackboard
+ * @package qformat_blackboard
* @copyright 2010 Helen Foster
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+$string['defaultname'] = 'Imported question {$a}';
+$string['importnotext'] = 'Missing question text in XML file';
+$string['notenoughtsubans'] = 'Unable to import matching question \'{$a}\' because a matching question must comprise at least two questions and three answers.';
$string['pluginname'] = 'Blackboard';
$string['pluginname_help'] = 'Blackboard format enables questions saved in the Blackboard version 5 "POOL" type export format to be imported.';
View
469 question/format/blackboard/tests/blackboardformat_test.php
@@ -0,0 +1,469 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Unit tests for the Moodle Blackboard format.
+ *
+ * @package qformat_blackboard
+ * @copyright 2012 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/questionlib.php');
+require_once($CFG->dirroot . '/question/format.php');
+require_once($CFG->dirroot . '/question/format/blackboard/format.php');
+require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
+
+
+/**
+ * Unit tests for the blackboard question import format.
+ *
+ * @copyright 2012 Jean-Michel Vedrine
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qformat_blackboard_test extends question_testcase {
+
+ public function make_test_xml() {
+ $xml = "<?xml version='1.0' encoding='utf-8'?>
+<POOL>
+ <TITLE value='exam 3 2008-9'/>
+ <QUESTIONLIST>
+ <QUESTION id='q1' class='QUESTION_TRUEFALSE' points='1'/>
+ <QUESTION id='q7' class='QUESTION_MULTIPLECHOICE' points='1'/>
+ <QUESTION id='q8' class='QUESTION_MULTIPLEANSWER' points='1'/>
+ <QUESTION id='q39-44' class='QUESTION_MATCH' points='2'/>
+ <QUESTION id='q9' class='QUESTION_ESSAY' points='1'/>
+ <QUESTION id='q27' class='QUESTION_FILLINBLANK' points='1'/>
+ </QUESTIONLIST>
+ <QUESTION_TRUEFALSE id='q1'>
+ <BODY>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">42 is the Absolute Answer to everything.</span>]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q1_a1'>
+ <TEXT>False</TEXT>
+ </ANSWER>
+ <ANSWER id='q1_a2'>
+ <TEXT>True</TEXT>
+ </ANSWER>
+ <GRADABLE>
+ <CORRECTANSWER answer_id='q1_a2'/>
+ <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
+ <FEEDBACK_WHEN_INCORRECT><![CDATA[42 is the Ultimate Answer.]]></FEEDBACK_WHEN_INCORRECT>
+ </GRADABLE>
+ </QUESTION_TRUEFALSE>
+ <QUESTION_MULTIPLECHOICE id='q7'>
+ <BODY>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">What's between orange and green in the spectrum?</span>]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q7_a1' position='1'>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">red</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q7_a2' position='2'>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">yellow</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q7_a3' position='3'>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">blue</span>]]></TEXT>
+ </ANSWER>
+ <GRADABLE>
+ <CORRECTANSWER answer_id='q7_a2'/>
+ <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
+ <FEEDBACK_WHEN_INCORRECT><![CDATA[Only yellow is between orange and green in the spectrum.]]></FEEDBACK_WHEN_INCORRECT>
+ </GRADABLE>
+ </QUESTION_MULTIPLECHOICE>
+ <QUESTION_MULTIPLEANSWER id='q8'>
+ <BODY>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">What's between orange and green in the spectrum?</span>]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q8_a1' position='1'>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">yellow</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q8_a2' position='2'>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">red</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q8_a3' position='3'>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">off-beige</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q8_a4' position='4'>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">blue</span>]]></TEXT>
+ </ANSWER>
+ <GRADABLE>
+ <CORRECTANSWER answer_id='q8_a1'/>
+ <CORRECTANSWER answer_id='q8_a3'/>
+ <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
+ <FEEDBACK_WHEN_INCORRECT>
+ <![CDATA[Only yellow and off-beige are between orange and green in the spectrum.]]>
+ </FEEDBACK_WHEN_INCORRECT>
+ </GRADABLE>
+ </QUESTION_MULTIPLEANSWER>
+ <QUESTION_MATCH id='q39-44'>
+ <BODY>
+ <TEXT><![CDATA[<i>Classify the animals.</i>]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q39-44_a1' position='1'>
+ <TEXT><![CDATA[frog]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q39-44_a2' position='2'>
+ <TEXT><![CDATA[cat]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q39-44_a3' position='3'>
+ <TEXT><![CDATA[newt]]></TEXT>
+ </ANSWER>
+ <CHOICE id='q39-44_c1' position='1'>
+ <TEXT><![CDATA[mammal]]></TEXT>
+ </CHOICE>
+ <CHOICE id='q39-44_c2' position='2'>
+ <TEXT><![CDATA[insect]]></TEXT>
+ </CHOICE>
+ <CHOICE id='q39-44_c3' position='3'>
+ <TEXT><![CDATA[amphibian]]></TEXT>
+ </CHOICE>
+ <GRADABLE>
+ <CORRECTANSWER answer_id='q39-44_a1' choice_id='q39-44_c3'/>
+ <CORRECTANSWER answer_id='q39-44_a2' choice_id='q39-44_c1'/>
+ <CORRECTANSWER answer_id='q39-44_a3' choice_id='q39-44_c3'/>
+ </GRADABLE>
+ </QUESTION_MATCH>
+ <QUESTION_ESSAY id='q9'>
+ <BODY>
+ <TEXT><![CDATA[How are you?]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q9_a1'>
+ <TEXT><![CDATA[Blackboard answer for essay questions will be imported as informations for graders.]]></TEXT>
+ </ANSWER>
+ <GRADABLE>
+ </GRADABLE>
+ </QUESTION_ESSAY>
+ <QUESTION_FILLINBLANK id='q27'>
+ <BODY>
+ <TEXT><![CDATA[<span style=\"font-size:12pt\">Name an amphibian: __________.</span>]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q27_a1' position='1'>
+ <TEXT>frog</TEXT>
+ </ANSWER>
+ <GRADABLE>
+ </GRADABLE>
+ </QUESTION_FILLINBLANK></POOL>";
+ return $xml;
+ }
+ public function test_import_match() {
+
+ $xmldata = xmlize($this->make_test_xml());
+ $questions = array();
+
+ $importer = new qformat_blackboard();
+ $importer->process_matching($xmldata, $questions);
+ $q = $questions[0];
+ $expectedq = new stdClass();
+ $expectedq->qtype = 'match';
+ $expectedq->name = 'Classify the animals.';
+ $expectedq->questiontext = '<i>Classify the animals.</i>';
+ $expectedq->questiontextformat = FORMAT_HTML;
+ $expectedq->correctfeedback = array('text' => '',
+ 'format' => FORMAT_HTML, 'files' => array());
+ $expectedq->partiallycorrectfeedback = array('text' => '',
+ 'format' => FORMAT_HTML, 'files' => array());
+ $expectedq->incorrectfeedback = array('text' => '',
+ 'format' => FORMAT_HTML, 'files' => array());
+ $expectedq->generalfeedback = '';
+ $expectedq->generalfeedbackformat = FORMAT_HTML;
+ $expectedq->defaultmark = 1;
+ $expectedq->length = 1;
+ $expectedq->penalty = 0.3333333;
+ $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers');
+ $expectedq->subquestions = array(
+ array('text' => 'cat', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => 'frog', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => 'newt', 'format' => FORMAT_HTML, 'files' => array()));
+ $expectedq->subanswers = array('mammal', 'insect', 'amphibian', 'amphibian');
+
+ $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
+ }
+
+ public function test_import_multichoice_single() {
+
+ $xmldata = xmlize($this->make_test_xml());
+ $questions = array();
+
+ $importer = new qformat_blackboard();
+ $importer->process_mc($xmldata, $questions);
+ $q = $questions[0];
+
+ $expectedq = new stdClass();
+ $expectedq->qtype = 'multichoice';
+ $expectedq->single = 1;
+ $expectedq->name = 'What\'s between orange and green in the spectrum?';
+ $expectedq->questiontext = '<span style="font-size:12pt">What\'s between orange and green in the spectrum?</span>';
+ $expectedq->questiontextformat = FORMAT_HTML;
+ $expectedq->correctfeedback = array('text' => 'You gave the right answer.',
+ 'format' => FORMAT_HTML, 'files' => array());
+ $expectedq->partiallycorrectfeedback = array('text' => '',
+ 'format' => FORMAT_HTML, 'files' => array());
+ $expectedq->incorrectfeedback = array('text' => 'Only yellow is between orange and green in the spectrum.',
+ 'format' => FORMAT_HTML, 'files' => array());
+ $expectedq->generalfeedback = '';
+ $expectedq->generalfeedbackformat = FORMAT_HTML;
+ $expectedq->defaultmark = 1;
+ $expectedq->length = 1;
+ $expectedq->penalty = 0.3333333;
+ $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers');
+ $expectedq->answer = array(
+ 0 => array(
+ 'text' => '<span style="font-size:12pt">red</span>',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 1 => array(
+ 'text' => '<span style="font-size:12pt">yellow</span>',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 2 => array(
+ 'text' => '<span style="font-size:12pt">blue</span>',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ )
+ );
+ $expectedq->fraction = array(0, 1, 0);
+ $expectedq->feedback = array(
+ 0 => array(
+ 'text' => '',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 1 => array(
+ 'text' => '',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 2 => array(
+ 'text' => '',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ )
+ );
+
+ $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
+ }
+
+ public function test_import_multichoice_multi() {
+
+ $xmldata = xmlize($this->make_test_xml());
+ $questions = array();
+
+ $importer = new qformat_blackboard();
+ $importer->process_ma($xmldata, $questions);
+ $q = $questions[0];
+
+ $expectedq = new stdClass();
+ $expectedq->qtype = 'multichoice';
+ $expectedq->single = 0;
+ $expectedq->name = 'What\'s between orange and green in the spectrum?';
+ $expectedq->questiontext = '<span style="font-size:12pt">What\'s between orange and green in the spectrum?</span>';
+ $expectedq->questiontextformat = FORMAT_HTML;
+ $expectedq->correctfeedback = array(
+ 'text' => 'You gave the right answer.',
+ 'format' => FORMAT_HTML,
+ 'files' => array());
+ $expectedq->partiallycorrectfeedback = array(
+ 'text' => 'Only yellow and off-beige are between orange and green in the spectrum.',
+ 'format' => FORMAT_HTML,
+ 'files' => array());
+ $expectedq->incorrectfeedback = array(
+ 'text' => 'Only yellow and off-beige are between orange and green in the spectrum.',
+ 'format' => FORMAT_HTML,
+ 'files' => array());
+ $expectedq->generalfeedback = '';
+ $expectedq->generalfeedbackformat = FORMAT_HTML;
+ $expectedq->defaultmark = 1;
+ $expectedq->length = 1;
+ $expectedq->penalty = 0.3333333;
+ $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers');
+ $expectedq->answer = array(
+ 0 => array(
+ 'text' => '<span style="font-size:12pt">yellow</span>',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 1 => array(
+ 'text' => '<span style="font-size:12pt">red</span>',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 2 => array(
+ 'text' => '<span style="font-size:12pt">off-beige</span>',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 3 => array(
+ 'text' => '<span style="font-size:12pt">blue</span>',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ )
+ );
+ $expectedq->fraction = array(0.5, 0, 0.5, 0);
+ $expectedq->feedback = array(
+ 0 => array(
+ 'text' => '',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 1 => array(
+ 'text' => '',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 2 => array(
+ 'text' => '',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 3 => array(
+ 'text' => '',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ )
+ );
+
+ $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
+ }
+
+ public function test_import_truefalse() {
+
+ $xmldata = xmlize($this->make_test_xml());
+ $questions = array();
+
+ $importer = new qformat_blackboard();
+ $importer->process_tf($xmldata, $questions);
+ $q = $questions[0];
+
+ $expectedq = new stdClass();
+ $expectedq->qtype = 'truefalse';
+ $expectedq->name = '42 is the Absolute Answer to everything.';
+ $expectedq->questiontext = '<span style="font-size:12pt">42 is the Absolute Answer to everything.</span>';
+ $expectedq->questiontextformat = FORMAT_HTML;
+ $expectedq->generalfeedback = '';
+ $expectedq->generalfeedbackformat = FORMAT_HTML;
+ $expectedq->defaultmark = 1;
+ $expectedq->length = 1;
+ $expectedq->correctanswer = 0;
+ $expectedq->feedbacktrue = array(
+ 'text' => '42 is the Ultimate Answer.',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ );
+ $expectedq->feedbackfalse = array(
+ 'text' => 'You gave the right answer.',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ );
+ $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
+ }
+
+ public function test_import_fill_in_the_blank() {
+
+ $xmldata = xmlize($this->make_test_xml());
+ $questions = array();
+
+ $importer = new qformat_blackboard();
+ $importer->process_fib($xmldata, $questions);
+ $q = $questions[0];
+
+ $expectedq = new stdClass();
+ $expectedq->qtype = 'shortanswer';
+ $expectedq->name = 'Name an amphibian: __________.';
+ $expectedq->questiontext = '<span style="font-size:12pt">Name an amphibian: __________.</span>';
+ $expectedq->questiontextformat = FORMAT_HTML;
+ $expectedq->generalfeedback = '';
+ $expectedq->generalfeedbackformat = FORMAT_HTML;
+ $expectedq->defaultmark = 1;
+ $expectedq->length = 1;
+ $expectedq->usecase = 0;
+ $expectedq->answer = array('frog', '*');
+ $expectedq->fraction = array(1, 0);
+ $expectedq->feedback = array(
+ 0 => array(
+ 'text' => '',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ ),
+ 1 => array(
+ 'text' => '',
+ 'format' => FORMAT_HTML,
+ 'files' => array(),
+ )
+ );
+
+ $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
+ }
+
+ public function test_import_essay() {
+
+ $xmldata = xmlize($this->make_test_xml());
+ $questions = array();
+
+ $importer = new qformat_blackboard();
+ $importer->process_essay($xmldata, $questions);
+ $q = $questions[0];
+
+ $expectedq = new stdClass();
+ $expectedq->qtype = 'essay';
+ $expectedq->name = 'How are you?';
+ $expectedq->questiontext = 'How are you?';
+ $expectedq->questiontextformat = FORMAT_HTML;
+ $expectedq->generalfeedback = '';
+ $expectedq->generalfeedbackformat = FORMAT_HTML;
+ $expectedq->defaultmark = 1;
+ $expectedq->length = 1;
+ $expectedq->responseformat = 'editor';
+ $expectedq->responsefieldlines = 15;
+ $expectedq->attachments = 0;
+ $expectedq->graderinfo = array(
+ 'text' => 'Blackboard answer for essay questions will be imported as informations for graders.',
+ 'format' => FORMAT_HTML,
+ 'files' => array());
+
+ $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
+ }
+}
View
142 question/format/blackboard/tests/fixtures/sample_blackboard.dat
@@ -0,0 +1,142 @@
+<?xml version='1.0' encoding='utf-8'?>
+<POOL>
+ <TITLE value='exam 3 2008-9'/>
+ <QUESTIONLIST>
+ <QUESTION id='q1' class='QUESTION_TRUEFALSE' points='1'/>
+ <QUESTION id='q7' class='QUESTION_MULTIPLECHOICE' points='1'/>
+ <QUESTION id='q8' class='QUESTION_MULTIPLEANSWER' points='1'/>
+ <QUESTION id='q39-44' class='QUESTION_MATCH' points='1'/>
+ <QUESTION id='q9' class='QUESTION_ESSAY' points='1'/>
+ <QUESTION id='q27' class='QUESTION_FILLINBLANK' points='1'/>
+ </QUESTIONLIST>
+ <QUESTION_TRUEFALSE id='q1'>
+ <BODY>
+ <TEXT><![CDATA[<span style="font-size:12pt">42 is the Absolute Answer to everything.</span>]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q1_a1'>
+ <TEXT>False</TEXT>
+ </ANSWER>
+ <ANSWER id='q1_a2'>
+ <TEXT>True</TEXT>
+ </ANSWER>
+ <GRADABLE>
+ <CORRECTANSWER answer_id='q1_a2'/>
+ <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
+ <FEEDBACK_WHEN_INCORRECT><![CDATA[42 is the Ultimate Answer.]]></FEEDBACK_WHEN_INCORRECT>
+ </GRADABLE>
+ </QUESTION_TRUEFALSE>
+ <QUESTION_MULTIPLECHOICE id='q7'>
+ <BODY>
+ <TEXT><![CDATA[<span style="font-size:12pt">What's between orange and green in the spectrum?</span>]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q7_a1' position='1'>
+ <TEXT><![CDATA[<span style="font-size:12pt">red</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q7_a2' position='2'>
+ <TEXT><![CDATA[<span style="font-size:12pt">yellow</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q7_a3' position='3'>
+ <TEXT><![CDATA[<span style="font-size:12pt">blue</span>]]></TEXT>
+ </ANSWER>
+ <GRADABLE>
+ <CORRECTANSWER answer_id='q7_a2'/>
+ <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
+ <FEEDBACK_WHEN_INCORRECT><![CDATA[Only yellow is between orange and green in the spectrum.]]></FEEDBACK_WHEN_INCORRECT>
+ </GRADABLE>
+ </QUESTION_MULTIPLECHOICE>
+ <QUESTION_MULTIPLEANSWER id='q8'>
+ <BODY>
+ <TEXT><![CDATA[<span style="font-size:12pt">What's between orange and green in the spectrum?</span>]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q8_a1' position='1'>
+ <TEXT><![CDATA[<span style="font-size:12pt">yellow</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q8_a2' position='2'>
+ <TEXT><![CDATA[<span style="font-size:12pt">red</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q8_a3' position='3'>
+ <TEXT><![CDATA[<span style="font-size:12pt">off-beige</span>]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q8_a4' position='4'>
+ <TEXT><![CDATA[<span style="font-size:12pt">blue</span>]]></TEXT>
+ </ANSWER>
+ <GRADABLE>
+ <CORRECTANSWER answer_id='q8_a1'/>
+ <CORRECTANSWER answer_id='q8_a3'/>
+ <FEEDBACK_WHEN_CORRECT><![CDATA[You gave the right answer.]]></FEEDBACK_WHEN_CORRECT>
+ <FEEDBACK_WHEN_INCORRECT><![CDATA[Only yellow and off-beige are between orange and green in the spectrum.]]></FEEDBACK_WHEN_INCORRECT>
+ </GRADABLE>
+ </QUESTION_MULTIPLEANSWER>
+ <QUESTION_MATCH id='q39-44'>
+ <BODY>
+ <TEXT><![CDATA[<i>Classify the animals.</i>]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q39-44_a1' position='1'>
+ <TEXT><![CDATA[frog]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q39-44_a2' position='2'>
+ <TEXT><![CDATA[cat]]></TEXT>
+ </ANSWER>
+ <ANSWER id='q39-44_a3' position='3'>
+ <TEXT><![CDATA[newt]]></TEXT>
+ </ANSWER>
+ <CHOICE id='q39-44_c1' position='1'>
+ <TEXT><![CDATA[mammal]]></TEXT>
+ </CHOICE>
+ <CHOICE id='q39-44_c2' position='2'>
+ <TEXT><![CDATA[insect]]></TEXT>
+ </CHOICE>
+ <CHOICE id='q39-44_c3' position='3'>
+ <TEXT><![CDATA[amphibian]]></TEXT>
+ </CHOICE>
+ <GRADABLE>
+ <CORRECTANSWER answer_id='q39-44_a1' choice_id='q39-44_c3'/>
+ <CORRECTANSWER answer_id='q39-44_a2' choice_id='q39-44_c1'/>
+ <CORRECTANSWER answer_id='q39-44_a3' choice_id='q39-44_c3'/>
+ </GRADABLE>
+ </QUESTION_MATCH>
+ <QUESTION_ESSAY id='q9'>
+ <BODY>
+ <TEXT><![CDATA[How are you?]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q9_a1'>
+ <TEXT><![CDATA[Blackboard answer for essay questions will be imported as informations for graders.]]></TEXT>
+ </ANSWER>
+ <GRADABLE>
+ </GRADABLE>
+ </QUESTION_ESSAY>
+ <QUESTION_FILLINBLANK id='q27'>
+ <BODY>
+ <TEXT><![CDATA[Name an amphibian: __________.]]></TEXT>
+ <FLAGS>
+ <ISHTML value='true'/>
+ <ISNEWLINELITERAL value='false'/>
+ </FLAGS>
+ </BODY>
+ <ANSWER id='q27_a1' position='1'>
+ <TEXT>frog</TEXT>
+ </ANSWER>
+ <GRADABLE>
+ </GRADABLE>
+ </QUESTION_FILLINBLANK>
+</POOL>
View
3  question/format/blackboard/version.php
@@ -17,8 +17,7 @@
/**
* Version information for the calculated question type.
*
- * @package qformat
- * @subpackage blackboard
+ * @package qformat_blackboard
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
Please sign in to comment.
Something went wrong with that request. Please try again.