9ce23d0e9a3d1db51d6eeb4187fdbf60e918edc2
[moodle.git] / question / format / blackboard / format.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Blackboard question importer.
19  *
20  * @package    qformat
21  * @subpackage blackboard
22  * @copyright  2003 Scott Elliott
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
29 require_once ($CFG->libdir . '/xmlize.php');
32 /**
33  * Blackboard question importer.
34  *
35  * @copyright  2003 Scott Elliott
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class qformat_blackboard extends qformat_default {
40     public function provide_import() {
41         return true;
42     }
44     function readquestions($lines) {
45         /// Parses an array of lines into an array of questions,
46         /// where each item is a question object as defined by
47         /// readquestion().
49         $text = implode($lines, " ");
50         $xml = xmlize($text, 0);
52         $questions = array();
54         $this->process_tf($xml, $questions);
55         $this->process_mc($xml, $questions);
56         $this->process_ma($xml, $questions);
57         $this->process_fib($xml, $questions);
58         $this->process_matching($xml, $questions);
59         $this->process_essay($xml, $questions);
61         return $questions;
62     }
64 //----------------------------------------
65 // Process Essay Questions
66 //----------------------------------------
67     function process_essay($xml, &$questions ) {
69         if (isset($xml["POOL"]["#"]["QUESTION_ESSAY"])) {
70             $essayquestions = $xml["POOL"]["#"]["QUESTION_ESSAY"];
71         }
72         else {
73             return;
74         }
76         foreach ($essayquestions as $essayquestion) {
78             $question = $this->defaultquestion();
80             $question->qtype = ESSAY;
82             // determine if the question is already escaped html
83             $ishtml = $essayquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
85             // put questiontext in question object
86             if ($ishtml) {
87                 $question->questiontext = html_entity_decode(trim($essayquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]));
88             }
90             // put name in question object
91             $question->name = substr($question->questiontext, 0, 254);
92             $question->answer = '';
93             $question->feedback = '';
94             $question->fraction = 0;
96             $questions[] = $question;
97         }
98     }
100     //----------------------------------------
101     // Process True / False Questions
102     //----------------------------------------
103     function process_tf($xml, &$questions) {
105         if (isset($xml["POOL"]["#"]["QUESTION_TRUEFALSE"])) {
106             $tfquestions = $xml["POOL"]["#"]["QUESTION_TRUEFALSE"];
107         }
108         else {
109             return;
110         }
112         for ($i = 0; $i < sizeof ($tfquestions); $i++) {
114             $question = $this->defaultquestion();
116             $question->qtype = TRUEFALSE;
117             $question->single = 1; // Only one answer is allowed
119             $thisquestion = $tfquestions[$i];
121             // determine if the question is already escaped html
122             $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
124             // put questiontext in question object
125             if ($ishtml) {
126                 $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
127             }
128             // put name in question object
129             $question->name = shorten_text($question->questiontext, 254);
131             $choices = $thisquestion["#"]["ANSWER"];
133             $correct_answer = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"][0]["@"]["answer_id"];
135             // first choice is true, second is false.
136             $id = $choices[0]["@"]["id"];
138             if (strcmp($id, $correct_answer) == 0) {  // true is correct
139                 $question->answer = 1;
140                 $question->feedbacktrue = trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]);
141                 $question->feedbackfalse = trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]);
142             } else {  // false is correct
143                 $question->answer = 0;
144                 $question->feedbacktrue = trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]);
145                 $question->feedbackfalse = trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]);
146             }
147             $question->correctanswer = $question->answer;
148             $questions[] = $question;
149           }
150     }
152     //----------------------------------------
153     // Process Multiple Choice Questions
154     //----------------------------------------
155     function process_mc($xml, &$questions) {
157         if (isset($xml["POOL"]["#"]["QUESTION_MULTIPLECHOICE"])) {
158             $mcquestions = $xml["POOL"]["#"]["QUESTION_MULTIPLECHOICE"];
159         }
160         else {
161             return;
162         }
164         for ($i = 0; $i < sizeof ($mcquestions); $i++) {
166             $question = $this->defaultquestion();
168             $question->qtype = MULTICHOICE;
169             $question->single = 1; // Only one answer is allowed
171             $thisquestion = $mcquestions[$i];
173             // determine if the question is already escaped html
174             $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
176             // put questiontext in question object
177             if ($ishtml) {
178                 $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
179             }
181             // put name of question in question object, careful of length
182             $question->name = shorten_text($question->questiontext, 254);
184             $choices = $thisquestion["#"]["ANSWER"];
185             for ($j = 0; $j < sizeof ($choices); $j++) {
187                 $choice = trim($choices[$j]["#"]["TEXT"][0]["#"]);
188                 // put this choice in the question object.
189                 if ($ishtml) {
190                     $question->answer[$j] = html_entity_decode($choice,ENT_QUOTES,'UTF-8');
191                 }
192                 $question->answer[$j] = $question->answer[$j];
194                 $id = $choices[$j]["@"]["id"];
195                 $correct_answer_id = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"][0]["@"]["answer_id"];
196                 // if choice is the answer, give 100%, otherwise give 0%
197                 if (strcmp ($id, $correct_answer_id) == 0) {
198                     $question->fraction[$j] = 1;
199                     if ($ishtml) {
200                         $question->feedback[$j] = html_entity_decode(trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]),ENT_QUOTES,'UTF-8');
201                     }
202                     $question->feedback[$j] = $question->feedback[$j];
203                 } else {
204                     $question->fraction[$j] = 0;
205                     if ($ishtml) {
206                         $question->feedback[$j] = html_entity_decode(trim(@$thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]),ENT_QUOTES,'UTF-8');
207                     }
208                     $question->feedback[$j] = $question->feedback[$j];
209                 }
210             }
211             $questions[] = $question;
212         }
213     }
215     //----------------------------------------
216     // Process Multiple Choice Questions With Multiple Answers
217     //----------------------------------------
218     function process_ma($xml, &$questions) {
220         if (isset($xml["POOL"]["#"]["QUESTION_MULTIPLEANSWER"])) {
221             $maquestions = $xml["POOL"]["#"]["QUESTION_MULTIPLEANSWER"];
222         }
223         else {
224             return;
225         }
227         for ($i = 0; $i < sizeof ($maquestions); $i++) {
229             $question = $this->defaultquestion();
231             $question->qtype = MULTICHOICE;
232             $question->defaultmark = 1;
233             $question->single = 0; // More than one answers allowed
234             $question->image = ""; // No images with this format
236             $thisquestion = $maquestions[$i];
238             // determine if the question is already escaped html
239             $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
241             // put questiontext in question object
242             if ($ishtml) {
243                 $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
244             }
245             // put name of question in question object
246             $question->name = shorten_text($question->questiontext, 254);
248             $choices = $thisquestion["#"]["ANSWER"];
249             $correctanswers = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"];
251             for ($j = 0; $j < sizeof ($choices); $j++) {
253                 $choice = trim($choices[$j]["#"]["TEXT"][0]["#"]);
254                 // put this choice in the question object.
255                 $question->answer[$j] = $choice;
257                 $correctanswercount = sizeof($correctanswers);
258                 $id = $choices[$j]["@"]["id"];
259                 $iscorrect = 0;
260                 for ($k = 0; $k < $correctanswercount; $k++) {
262                     $correct_answer_id = trim($correctanswers[$k]["@"]["answer_id"]);
263                     if (strcmp ($id, $correct_answer_id) == 0) {
264                         $iscorrect = 1;
265                     }
267                 }
268                 if ($iscorrect) {
269                     $question->fraction[$j] = floor(100000/$correctanswercount)/100000; // strange behavior if we have more than 5 decimal places
270                     $question->feedback[$j] = trim($thisquestion["#"]["GRADABLE"][$j]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]);
271                 } else {
272                     $question->fraction[$j] = 0;
273                     $question->feedback[$j] = trim($thisquestion["#"]["GRADABLE"][$j]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]);
274                 }
275             }
277             $questions[] = $question;
278         }
279     }
281     //----------------------------------------
282     // Process Fill in the Blank Questions
283     //----------------------------------------
284     function process_fib($xml, &$questions) {
286         if (isset($xml["POOL"]["#"]["QUESTION_FILLINBLANK"])) {
287             $fibquestions = $xml["POOL"]["#"]["QUESTION_FILLINBLANK"];
288         }
289         else {
290             return;
291         }
293         for ($i = 0; $i < sizeof ($fibquestions); $i++) {
294             $question = $this->defaultquestion();
296             $question->qtype = SHORTANSWER;
297             $question->usecase = 0; // Ignore case
299             $thisquestion = $fibquestions[$i];
301             // determine if the question is already escaped html
302             $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
304             // put questiontext in question object
305             if ($ishtml) {
306                 $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
307             }
308             // put name of question in question object
309             $question->name = shorten_text($question->questiontext, 254);
311             $answer = trim($thisquestion["#"]["ANSWER"][0]["#"]["TEXT"][0]["#"]);
313             $question->answer[] = $answer;
314             $question->fraction[] = 1;
315             $question->feedback = array();
317             if (is_array( $thisquestion['#']['GRADABLE'][0]['#'] )) {
318                 $question->feedback[0] = trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_CORRECT"][0]["#"]);
319             }
320             else {
321                 $question->feedback[0] = '';
322             }
323             if (is_array( $thisquestion["#"]["GRADABLE"][0]["#"] )) {
324                 $question->feedback[1] = trim($thisquestion["#"]["GRADABLE"][0]["#"]["FEEDBACK_WHEN_INCORRECT"][0]["#"]);
325             }
326             else {
327                 $question->feedback[1] = '';
328             }
330             $questions[] = $question;
331         }
332     }
334     //----------------------------------------
335     // Process Matching Questions
336     //----------------------------------------
337     function process_matching($xml, &$questions) {
339         if (isset($xml["POOL"]["#"]["QUESTION_MATCH"])) {
340             $matchquestions = $xml["POOL"]["#"]["QUESTION_MATCH"];
341         }
342         else {
343             return;
344         }
346         for ($i = 0; $i < sizeof ($matchquestions); $i++) {
348             $question = $this->defaultquestion();
350             $question->qtype = MATCH;
352             $thisquestion = $matchquestions[$i];
354             // determine if the question is already escaped html
355             $ishtml = $thisquestion["#"]["BODY"][0]["#"]["FLAGS"][0]["#"]["ISHTML"][0]["@"]["value"];
357             // put questiontext in question object
358             if ($ishtml) {
359                 $question->questiontext = html_entity_decode(trim($thisquestion["#"]["BODY"][0]["#"]["TEXT"][0]["#"]),ENT_QUOTES,'UTF-8');
360             }
361             // put name of question in question object
362             $question->name = shorten_text($question->questiontext, 254);
364             $choices = $thisquestion["#"]["CHOICE"];
365             for ($j = 0; $j < sizeof ($choices); $j++) {
367                 $subquestion = NULL;
369                 $choice = $choices[$j]["#"]["TEXT"][0]["#"];
370                 $choice_id = $choices[$j]["@"]["id"];
372                 $question->subanswers[] = trim($choice);
374                 $correctanswers = $thisquestion["#"]["GRADABLE"][0]["#"]["CORRECTANSWER"];
375                 for ($k = 0; $k < sizeof ($correctanswers); $k++) {
377                     if (strcmp($choice_id, $correctanswers[$k]["@"]["choice_id"]) == 0) {
379                         $answer_id = $correctanswers[$k]["@"]["answer_id"];
381                         $answers = $thisquestion["#"]["ANSWER"];
382                         for ($m = 0; $m < sizeof ($answers); $m++) {
384                             $answer = $answers[$m];
385                             $current_ans_id = $answer["@"]["id"];
386                             if (strcmp ($current_ans_id, $answer_id) == 0) {
388                                 $answer = $answer["#"]["TEXT"][0]["#"];
389                                 $question->subquestions[] = trim($answer);
390                                 break;
391                             }
392                         }
393                         break;
394                     }
395                 }
396             }
398             $questions[] = $question;
400         }
401     }