Merge branch 'MDL-37068_23' of git://github.com/timhunt/moodle into MOODLE_23_STABLE
[moodle.git] / question / format / learnwise / 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  * Examview question importer.
19  *
20  * @package    qformat
21  * @subpackage learnwise
22  * @copyright  2005 Alton College, Hampshire, UK
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
30 /**
31  * Examview question importer.
32  *
33  * Alton College, Hampshire, UK - Tom Flannaghan, Andrew Walker
34  *
35  * Imports learnwise multiple choice questions (single and multiple answers)
36  * currently ignores the deduct attribute for multiple answer questions
37  * deductions are currently simply found by dividing the award for the incorrect
38  * answer by the total number of options
39  *
40  * @copyright  2005 Alton College, Hampshire, UK
41  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42  */
43 class qformat_learnwise extends qformat_default {
45     public function provide_import() {
46         return true;
47     }
49     public function export_file_extension() {
50         return '.xml';
51     }
53     protected function readquestions($lines) {
54         $questions = array();
55         $currentquestion = array();
57         foreach($lines as $line) {
58             $line = trim($line);
59             $currentquestion[] = $line;
61             if ($question = $this->readquestion($currentquestion)) {
62                 $questions[] = $question;
63                 $currentquestion = array();
64             }
65         }
66         return $questions;
67     }
69     protected function readquestion($lines) {
70         $text = implode(' ', $lines);
71         $text = str_replace(array('\t','\n','\r'), array('','',''), $text);
73         $startpos = strpos($text, '<question type');
74         $endpos = strpos($text, '</question>');
75         if ($startpos === false || $endpos === false) {
76             return false;
77         }
79         preg_match("/<question type=[\"\']([^\"\']+)[\"\']>/i", $text, $matches);
80         $type = strtolower($matches[1]); // multichoice or multianswerchoice
82         $questiontext = textlib::entities_to_utf8($this->stringbetween($text, '<text>', '</text>'));
83         $questionhint = textlib::entities_to_utf8($this->stringbetween($text, '<hint>', '</hint>'));
84         $questionaward = $this->stringbetween($text, '<award>', '</award>');
85         $optionlist = $this->stringbetween($text, '<answer>', '</answer>');
87         $optionlist = explode('<option', $optionlist);
89         $n = 0;
91         $optionscorrect = array();
92         $optionstext = array();
94         if ($type == 'multichoice') {
95             foreach ($optionlist as $option) {
96                 if (trim($option) === '') {
97                     continue;
98                 }
99                 $correct = $this->stringbetween($option, ' correct="', '">');
100                 $answer = $this->stringbetween($option, '">', '</option>');
101                 $optionscorrect[$n] = $correct;
102                 $optionstext[$n] = textlib::entities_to_utf8($answer);
103                 ++$n;
104             }
105         } else if ($type == 'multianswerchoice') {
106             $numcorrect = 0;
107             $totalaward = 0;
109             $optionsaward = array();
111             foreach ($optionlist as $option) {
112                 if (trim($option) === '') {
113                     continue;
114                 }
115                 preg_match("/correct=\"([^\"]*)\"/i", $option, $correctmatch);
116                 preg_match("/award=\"([^\"]*)\"/i", $option, $awardmatch);
118                 $correct = $correctmatch[1];
119                 $award = $awardmatch[1];
120                 if ($correct == 'yes') {
121                     $totalaward += $award;
122                     ++$numcorrect;
123                 }
125                 $answer = $this->stringbetween($option, '">', '</option>');
127                 $optionscorrect[$n] = $correct;
128                 $optionstext[$n] = textlib::entities_to_utf8($answer);
129                 $optionsaward[$n] = $award;
130                 ++$n;
131             }
133         } else {
134             echo "<p>I don't understand this question type (type = <strong>$type</strong>).</p>\n";
135         }
137         $question = $this->defaultquestion();
138         $question->qtype = MULTICHOICE;
139         $question->name = $this->create_default_question_name($questiontext, get_string('questionname', 'question'));
140         $this->add_blank_combined_feedback($question);
142         $question->questiontext = $questiontext;
143         $question->questiontextformat = FORMAT_HTML;
144         $question->single = ($type == 'multichoice') ? 1 : 0;
146         $question->fraction = array();
147         $question->answer = array();
148         for ($n = 0; $n < count($optionstext); ++$n) {
149             if ($optionstext[$n]) {
150                 if (!isset($numcorrect)) {
151                     // Single answer.
152                     if ($optionscorrect[$n] == 'yes') {
153                         $fraction = (int) $questionaward;
154                     } else {
155                         $fraction = 0;
156                     }
157                 } else {
158                     // Multiple answers.
159                     if ($optionscorrect[$n] == 'yes') {
160                         $fraction = $optionsaward[$n] / $totalaward;
161                     } else {
162                         $fraction = -$optionsaward[$n] / count($optionstext);
163                     }
164                 }
165                 $question->fraction[] = $fraction;
166                 $question->answer[] = array('text' => $optionstext[$n], 'format' => FORMAT_HTML);
167                 $question->feedback[] = array('text' => '', 'format' => FORMAT_HTML); // No feedback in this type.
168             }
169         }
171         return $question;
172     }
174     /**
175      * Extract the substring of $text between $start and $end.
176      * @param string $text text to analyse.
177      * @param string $start opening delimiter.
178      * @param string $end closing delimiter.
179      * @return string the requested substring.
180      */
181     protected function stringbetween($text, $start, $end) {
182         $startpos = strpos($text, $start) + strlen($start);
183         $endpos = strpos($text, $end);
185         if ($startpos <= $endpos) {
186             return substr($text, $startpos, $endpos - $startpos);
187         }
188     }