c599ca8bbf9179f3f2b3426a44cb61cd27052500
[moodle.git] / question / format / webct / 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  * Web CT question importer.
19  *
20  * @package    qformat
21  * @subpackage webct
22  * @copyright  2004 ASP Consulting http://www.asp-consulting.net
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
29 /**
30  * Manipulate HTML editites in a string. Used by WebCT import.
31  * @param string $string
32  * @return string
33  */
34 function unhtmlentities($string){
35     $search = array ("'<script[?>]*?>.*?</script>'si",  // remove javascript
36                  "'<[\/\!]*?[^<?>]*?>'si",  // remove HTML tags
37                  "'([\r\n])[\s]+'",  // remove spaces
38                  "'&(quot|#34);'i",  // remove HTML entites
39                  "'&(amp|#38);'i",
40                  "'&(lt|#60);'i",
41                  "'&(gt|#62);'i",
42                  "'&(nbsp|#160);'i",
43                  "'&(iexcl|#161);'i",
44                  "'&(cent|#162);'i",
45                  "'&(pound|#163);'i",
46                  "'&(copy|#169);'i",
47                  "'&#(\d+);'e");  // Evaluate like PHP
48     $replace = array ("",
49                   "",
50                   "\\1",
51                   "\"",
52                   "&",
53                   "<",
54                   "?>",
55                   " ",
56                   chr(161),
57                   chr(162),
58                   chr(163),
59                   chr(169),
60                   "chr(\\1)");
61     return preg_replace ($search, $replace, $string);
62 }
64 /**
65  * Helper function for WebCT import.
66  * @param unknown_type $formula
67  */
68 function qformat_webct_convert_formula($formula) {
70     // Remove empty space, as it would cause problems otherwise:
71     $formula = str_replace(' ', '', $formula);
73     // Remove paranthesis after e,E and *10**:
74     while (preg_match('~[0-9.](e|E|\\*10\\*\\*)\\([+-]?[0-9]+\\)~', $formula, $regs)) {
75         $formula = str_replace(
76                 $regs[0], preg_replace('/[)(]/', '', $regs[0]), $formula);
77     }
79     // Replace *10** with e where possible
80     while (preg_match('~(^[+-]?|[^eE][+-]|[^0-9eE+-])[0-9.]+\\*10\\*\\*[+-]?[0-9]+([^0-9.eE]|$)~',
81             $formula, $regs)) {
82         $formula = str_replace(
83                 $regs[0], str_replace('*10**', 'e', $regs[0]), $formula);
84     }
86     // Replace other 10** with 1e where possible
87     while (preg_match('~(^|[^0-9.eE])10\\*\\*[+-]?[0-9]+([^0-9.eE]|$)~', $formula, $regs)) {
88         $formula = str_replace(
89                 $regs[0], str_replace('10**', '1e', $regs[0]), $formula);
90     }
92     // Replace all other base**exp with the PHP equivalent function pow(base,exp)
93     // (Pretty tricky to exchange an operator with a function)
94     while (2 == count($splits = explode('**', $formula, 2))) {
96         // Find $base
97         if (preg_match('~^(.*[^0-9.eE])?(([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][+-]?[0-9]+)?|\\{[^}]*\\})$~',
98                 $splits[0], $regs)) {
99             // The simple cases
100             $base = $regs[2];
101             $splits[0] = $regs[1];
103         } else if (preg_match('~\\)$~', $splits[0])) {
104             // Find the start of this parenthesis
105             $deep = 1;
106             for ($i = 1 ; $deep ; ++$i) {
107                 if (!preg_match('~^(.*[^[:alnum:]_])?([[:alnum:]_]*([)(])([^)(]*[)(]){'.$i.'})$~',
108                         $splits[0], $regs)) {
109                     print_error("parenthesisinproperstart", 'question', '', $splits[0]);
110                 }
111                 if ('(' == $regs[3]) {
112                     --$deep;
113                 } else if (')' == $regs[3]) {
114                     ++$deep;
115                 } else {
116                     print_error('impossiblechar', 'question', '', $regs[3]);
117                 }
118             }
119             $base = $regs[2];
120             $splits[0] = $regs[1];
122         } else {
123             print_error('badbase', 'question', '', $splits[0]);
124         }
126         // Find $exp (similar to above but a little easier)
127         if (preg_match('~^([+-]?(\\{[^}]\\}|([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][+-]?[0-9]+)?))(.*)~',
128                 $splits[1], $regs)) {
129             // The simple case
130             $exp = $regs[1];
131             $splits[1] = $regs[6];
133         } else if (preg_match('~^[+-]?[[:alnum:]_]*\\(~', $splits[1])) {
134             // Find the end of the parenthesis
135             $deep = 1;
136             for ($i = 1 ; $deep ; ++$i) {
137                 if (!preg_match('~^([+-]?[[:alnum:]_]*([)(][^)(]*){'.$i.'}([)(]))(.*)~',
138                         $splits[1], $regs)) {
139                     print_error("parenthesisinproperclose", 'question', '', $splits[1]);
140                 }
141                 if (')' == $regs[3]) {
142                     --$deep;
143                 } else if ('(' == $regs[3]) {
144                     ++$deep;
145                 } else {
146                     print_error("impossiblechar", 'question');
147                 }
148             }
149             $exp = $regs[1];
150             $splits[1] = $regs[4];
151         }
153         // Replace it!
154         $formula = "$splits[0]pow($base,$exp)$splits[1]";
155     }
157     // Nothing more is known to need to be converted
159     return $formula;
163 /**
164  * Web CT question importer.
165  *
166  * @copyright  2004 ASP Consulting http://www.asp-consulting.net
167  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
168  */
169 class qformat_webct extends qformat_default {
171     function provide_import() {
172       return true;
173     }
175     protected function readquestions($lines) {
176         $webctnumberregex =
177                 '[+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)((e|E|\\*10\\*\\*)([+-]?[0-9]+|\\([+-]?[0-9]+\\)))?';
179         $questions = array();
180         $errors = array();
181         $warnings = array();
182         $webct_options = array();
184         $ignore_rest_of_question = FALSE;
186         $nLineCounter = 0;
187         $nQuestionStartLine = 0;
188         $bIsHTMLText = FALSE;
189         $lines[] = ":EOF:";    // for an easiest processing of the last line
190     //    $question = $this->defaultquestion();
192         foreach ($lines as $line) {
193             $nLineCounter++;
194             $line = iconv("Windows-1252","UTF-8",$line);
195             // Processing multiples lines strings
197             if (isset($questiontext) and is_string($questiontext)) {
198                 if (preg_match("~^:~",$line)) {
199                     $question->questiontext = trim($questiontext);
200                     unset($questiontext);
201                 }
202                  else {
203                     $questiontext .= str_replace('\:', ':', $line);
204                     continue;
205                 }
206             }
208             if (isset($answertext) and is_string($answertext)) {
209                 if (preg_match("~^:~",$line)) {
210                     $answertext = trim($answertext);
211                     $question->answer[$currentchoice] = $answertext;
212                     $question->subanswers[$currentchoice] = $answertext;
213                     unset($answertext);
214                 }
215                  else {
216                     $answertext .= str_replace('\:', ':', $line);
217                     continue;
218                 }
219             }
221             if (isset($responsetext) and is_string($responsetext)) {
222                 if (preg_match("~^:~",$line)) {
223                     $question->subquestions[$currentchoice] = trim($responsetext);
224                     unset($responsetext);
225                 }
226                  else {
227                     $responsetext .= str_replace('\:', ':', $line);
228                     continue;
229                 }
230             }
232             if (isset($feedbacktext) and is_string($feedbacktext)) {
233                 if (preg_match("~^:~",$line)) {
234                    $question->feedback[$currentchoice] = trim($feedbacktext);
235                     unset($feedbacktext);
236                 }
237                  else {
238                     $feedbacktext .= str_replace('\:', ':', $line);
239                     continue;
240                 }
241             }
243             if (isset($generalfeedbacktext) and is_string($generalfeedbacktext)) {
244                 if (preg_match("~^:~",$line)) {
245                    $question->tempgeneralfeedback= trim($generalfeedbacktext);
246                     unset($generalfeedbacktext);
247                 }
248                  else {
249                     $generalfeedbacktext .= str_replace('\:', ':', $line);
250                     continue;
251                 }
252             }
254             $line = trim($line);
256             if (preg_match("~^:(TYPE|EOF):~i",$line)) {
257                 // New Question or End of File
258                 if (isset($question)) {            // if previous question exists, complete, check and save it
260                     // Setup default value of missing fields
261                     if (!isset($question->name)) {
262                         $question->name = $question->questiontext;
263                     }
264                     if (strlen($question->name) > 255) {
265                         $question->name = substr($question->name,0,250)."...";
266                         $warnings[] = get_string("questionnametoolong", "qformat_webct", $nQuestionStartLine);
267                     }
268                     if (!isset($question->defaultmark)) {
269                         $question->defaultmark = 1;
270                     }
271                     if (!isset($question->image)) {
272                         $question->image = "";
273                     }
275                     // Perform sanity checks
276                     $QuestionOK = TRUE;
277                     if (strlen($question->questiontext) == 0) {
278                         $warnings[] = get_string("missingquestion", "qformat_webct", $nQuestionStartLine);
279                         $QuestionOK = FALSE;
280                     }
281                     if (sizeof($question->answer) < 1) {  // a question must have at least 1 answer
282                        $errors[] = get_string("missinganswer", "qformat_webct", $nQuestionStartLine);
283                        $QuestionOK = FALSE;
284                     }
285                     else {
286                         // Create empty feedback array
287                         foreach ($question->answer as $key => $dataanswer) {
288                             if(!isset( $question->feedback[$key])){
289                                 $question->feedback[$key] = '';
290                             }
291                         }
292                         // this tempgeneralfeedback allows the code to work with versions from 1.6 to 1.9
293                         // when question->generalfeedback is undefined, the webct feedback is added to each answer feedback
294                         if (isset($question->tempgeneralfeedback)){
295                             if (isset($question->generalfeedback)) {
296                                 $question->generalfeedback = $question->tempgeneralfeedback;
297                             } else {
298                                 foreach ($question->answer as $key => $dataanswer) {
299                                     if ($question->tempgeneralfeedback !=''){
300                                         $question->feedback[$key] = $question->tempgeneralfeedback.'<br/>'.$question->feedback[$key];
301                                     }
302                                 }
303                             }
304                             unset($question->tempgeneralfeedback);
305                         }
306                         $maxfraction = -1;
307                         $totalfraction = 0;
308                         foreach($question->fraction as $fraction) {
309                             if ($fraction > 0) {
310                                 $totalfraction += $fraction;
311                             }
312                             if ($fraction > $maxfraction) {
313                                 $maxfraction = $fraction;
314                             }
315                         }
316                         switch ($question->qtype) {
317                             case SHORTANSWER:
318                                 if ($maxfraction != 1) {
319                                     $maxfraction = $maxfraction * 100;
320                                     $errors[] = "'$question->name': ".get_string("wronggrade", "qformat_webct", $nLineCounter).' '.get_string("fractionsnomax", "question", $maxfraction);
321                                     $QuestionOK = FALSE;
322                                 }
323                                 break;
325                             case MULTICHOICE:
326                                 if ($question->single) {
327                                     if ($maxfraction != 1) {
328                                         $maxfraction = $maxfraction * 100;
329                                         $errors[] = "'$question->name': ".get_string("wronggrade", "qformat_webct", $nLineCounter).' '.get_string("fractionsnomax", "question", $maxfraction);
330                                         $QuestionOK = FALSE;
331                                     }
332                                 } else {
333                                     $totalfraction = round($totalfraction,2);
334                                     if ($totalfraction != 1) {
335                                         $totalfraction = $totalfraction * 100;
336                                         $errors[] = "'$question->name': ".get_string("wronggrade", "qformat_webct", $nLineCounter).' '.get_string("fractionsaddwrong", "question", $totalfraction);
337                                         $QuestionOK = FALSE;
338                                     }
339                                 }
340                                 break;
342                             case CALCULATED:
343                                 foreach ($question->answers as $answer) {
344                                     if ($formulaerror = qtype_calculated_find_formula_errors($answer)) {
345                                         $warnings[] = "'$question->name': ". $formulaerror;
346                                         $QuestionOK = FALSE;
347                                     }
348                                 }
349                                 foreach ($question->dataset as $dataset) {
350                                     $dataset->itemcount=count($dataset->datasetitem);
351                                 }
352                                 $question->import_process=TRUE ;
353                                 unset($question->answer); //not used in calculated question
354                                 break;
355                             case MATCH:
356                                 // MDL-10680:
357                                 // switch subquestions and subanswers
358                                 foreach ($question->subquestions as $id=>$subquestion) {
359                                     $temp = $question->subquestions[$id];
360                                     $question->subquestions[$id] = $question->subanswers[$id];
361                                     $question->subanswers[$id] = $temp;
362                                 }
363                                 if (count($question->answer) < 3){
364                                     // add a dummy missing question
365                                     $question->name = 'Dummy question added '.$question->name ;
366                                     $question->answer[] = 'dummy';
367                                     $question->subanswers[] = 'dummy';
368                                     $question->subquestions[] = 'dummy';
369                                     $question->fraction[] = '0.0';
370                                     $question->feedback[] = '';
371                                  }
372                                  break;
373                             default:
374                                 // No problemo
375                         }
376                     }
378                     if ($QuestionOK) {
379                        // echo "<pre>"; print_r ($question);
380                         $questions[] = $question;    // store it
381                         unset($question);            // and prepare a new one
382                         $question = $this->defaultquestion();
383                     }
384                 }
385                 $nQuestionStartLine = $nLineCounter;
386             }
388             // Processing Question Header
390             if (preg_match("~^:TYPE:MC:1(.*)~i",$line,$webct_options)) {
391                 // Multiple Choice Question with only one good answer
392                 $question = $this->defaultquestion();
393                 $question->feedback = array();
394                 $question->qtype = MULTICHOICE;
395                 $question->single = 1;        // Only one answer is allowed
396                 $ignore_rest_of_question = FALSE;
397                 continue;
398             }
400             if (preg_match("~^:TYPE:MC:N(.*)~i",$line,$webct_options)) {
401                 // Multiple Choice Question with several good answers
402                 $question = $this->defaultquestion();
403                 $question->feedback = array();
404                 $question->qtype = MULTICHOICE;
405                 $question->single = 0;        // Many answers allowed
406                 $ignore_rest_of_question = FALSE;
407                 continue;
408             }
410             if (preg_match("~^:TYPE:S~i",$line)) {
411                 // Short Answer Question
412                 $question = $this->defaultquestion();
413                 $question->feedback = array();
414                 $question->qtype = SHORTANSWER;
415                 $question->usecase = 0;       // Ignore case
416                 $ignore_rest_of_question = FALSE;
417                 continue;
418             }
420             if (preg_match("~^:TYPE:C~i",$line)) {
421                 // Calculated Question
422                 $question = $this->defaultquestion();
423                 $question->qtype = CALCULATED;
424                 $question->answers = array(); // No problem as they go as :FORMULA: from webct
425                 $question->units = array();
426                 $question->dataset = array();
428                 // To make us pass the end-of-question sanity checks
429                 $question->answer = array('dummy');
430                 $question->fraction = array('1.0');
431                 $question->feedback = array();
433                 $currentchoice = -1;
434                 $ignore_rest_of_question = FALSE;
435                 continue;
436             }
438             if (preg_match("~^:TYPE:M~i",$line)) {
439                 // Match Question
440                 $question = $this->defaultquestion();
441                 $question->qtype = MATCH;
442                 $question->feedback = array();
443                 $ignore_rest_of_question = FALSE;         // match question processing is not debugged
444                 continue;
445             }
447             if (preg_match("~^:TYPE:P~i",$line)) {
448                 // Paragraph Question
449                 $warnings[] = get_string("paragraphquestion", "qformat_webct", $nLineCounter);
450                 unset($question);
451                 $ignore_rest_of_question = TRUE;         // Question Type not handled by Moodle
452                 continue;
453             }
455             if (preg_match("~^:TYPE:~i",$line)) {
456                 // Unknow Question
457                 $warnings[] = get_string("unknowntype", "qformat_webct", $nLineCounter);
458                 unset($question);
459                 $ignore_rest_of_question = TRUE;         // Question Type not handled by Moodle
460                 continue;
461             }
463             if ($ignore_rest_of_question) {
464                 continue;
465             }
467             if (preg_match("~^:TITLE:(.*)~i",$line,$webct_options)) {
468                 $name = trim($webct_options[1]);
469                 if (strlen($name) > 255) {
470                     $name = substr($name,0,250)."...";
471                     $warnings[] = get_string("questionnametoolong", "qformat_webct", $nLineCounter);
472                 }
473                 $question->name = $name;
474                 continue;
475             }
477             if (preg_match("~^:IMAGE:(.*)~i",$line,$webct_options)) {
478                 $filename = trim($webct_options[1]);
479                 if (preg_match("~^http://~i",$filename)) {
480                     $question->image = $filename;
481                 }
482                 continue;
483             }
485             // Need to put the parsing of calculated items here to avoid ambitiuosness:
486             // if question isn't defined yet there is nothing to do here (avoid notices)
487             if (!isset($question)) {
488                 continue;
489             }
490             if (isset($question->qtype ) && CALCULATED == $question->qtype && preg_match(
491                     "~^:([[:lower:]].*|::.*)-(MIN|MAX|DEC|VAL([0-9]+))::?:?($webctnumberregex)~", $line, $webct_options)) {
492                 $datasetname = preg_replace('/^::/', '', $webct_options[1]);
493                 $datasetvalue = qformat_webct_convert_formula($webct_options[4]);
494                 switch ($webct_options[2]) {
495                     case 'MIN':
496                         $question->dataset[$datasetname]->min = $datasetvalue;
497                         break;
498                     case 'MAX':
499                         $question->dataset[$datasetname]->max = $datasetvalue;
500                         break;
501                     case 'DEC':
502                         $datasetvalue = floor($datasetvalue); // int only!
503                         $question->dataset[$datasetname]->length = max(0, $datasetvalue);
504                         break;
505                     default:
506                         // The VAL case:
507                         $question->dataset[$datasetname]->datasetitem[$webct_options[3]] = new stdClass();
508                         $question->dataset[$datasetname]->datasetitem[$webct_options[3]]->itemnumber = $webct_options[3];
509                         $question->dataset[$datasetname]->datasetitem[$webct_options[3]]->value  = $datasetvalue;
510                         break;
511                 }
512                 continue;
513             }
516             $bIsHTMLText = preg_match("~:H$~i",$line);  // True if next lines are coded in HTML
517             if (preg_match("~^:QUESTION~i",$line)) {
518                 $questiontext="";               // Start gathering next lines
519                 continue;
520             }
522             if (preg_match("~^:ANSWER([0-9]+):([^:]+):([0-9\.\-]+):(.*)~i",$line,$webct_options)) {      /// SHORTANSWER
523                 $currentchoice=$webct_options[1];
524                 $answertext=$webct_options[2];            // Start gathering next lines
525                 $question->fraction[$currentchoice]=($webct_options[3]/100);
526                 continue;
527             }
529             if (preg_match("~^:ANSWER([0-9]+):([0-9\.\-]+)~i",$line,$webct_options)) {
530                 $answertext="";                 // Start gathering next lines
531                 $currentchoice=$webct_options[1];
532                 $question->fraction[$currentchoice]=($webct_options[2]/100);
533                 continue;
534             }
536             if (preg_match('~^:FORMULA:(.*)~i', $line, $webct_options)) {
537                 // Answer for a CALCULATED question
538                 ++$currentchoice;
539                 $question->answers[$currentchoice] =
540                         qformat_webct_convert_formula($webct_options[1]);
542                 // Default settings:
543                 $question->fraction[$currentchoice] = 1.0;
544                 $question->tolerance[$currentchoice] = 0.0;
545                 $question->tolerancetype[$currentchoice] = 2; // nominal (units in webct)
546                 $question->feedback[$currentchoice] = '';
547                 $question->correctanswerlength[$currentchoice] = 4;
549                 $datasetnames = question_bank::get_qtype('calculated')->
550                         find_dataset_names($webct_options[1]);
551                 foreach ($datasetnames as $datasetname) {
552                     $question->dataset[$datasetname] = new stdClass();
553                     $question->dataset[$datasetname]->datasetitem = array();
554                     $question->dataset[$datasetname]->name = $datasetname ;
555                     $question->dataset[$datasetname]->distribution = 'uniform';
556                     $question->dataset[$datasetname]->status ='private';
557                 }
558                 continue;
559             }
561             if (preg_match("~^:L([0-9]+)~i",$line,$webct_options)) {
562                 $answertext="";                 // Start gathering next lines
563                 $currentchoice=$webct_options[1];
564                 $question->fraction[$currentchoice]=1;
565                 continue;
566             }
568             if (preg_match("~^:R([0-9]+)~i",$line,$webct_options)) {
569                 $responsetext="";                // Start gathering next lines
570                 $currentchoice=$webct_options[1];
571                 continue;
572             }
574             if (preg_match("~^:REASON([0-9]+):?~i",$line,$webct_options)) {
575                 $feedbacktext="";               // Start gathering next lines
576                 $currentchoice=$webct_options[1];
577                 continue;
578             }
579             if (preg_match("~^:FEEDBACK([0-9]+):?~i",$line,$webct_options)) {
580                 $generalfeedbacktext="";               // Start gathering next lines
581                 $currentchoice=$webct_options[1];
582                 continue;
583             }
584             if (preg_match('~^:FEEDBACK:(.*)~i',$line,$webct_options)) {
585                 $generalfeedbacktext="";               // Start gathering next lines
586                 continue;
587             }
588             if (preg_match('~^:LAYOUT:(.*)~i',$line,$webct_options)) {
589             //    ignore  since layout in question_multichoice  is no more used in moodle
590             //    $webct_options[1] contains either vertical or horizontal ;
591                 continue;
592             }
594             if (isset($question->qtype ) && CALCULATED == $question->qtype && preg_match('~^:ANS-DEC:([1-9][0-9]*)~i', $line, $webct_options)) {
595                 // We can but hope that this always appear before the ANSTYPE property
596                 $question->correctanswerlength[$currentchoice] = $webct_options[1];
597                 continue;
598             }
600             if (isset($question->qtype )&& CALCULATED == $question->qtype && preg_match("~^:TOL:($webctnumberregex)~i", $line, $webct_options)) {
601                 // We can but hope that this always appear before the TOL property
602                 $question->tolerance[$currentchoice] =
603                         qformat_webct_convert_formula($webct_options[1]);
604                 continue;
605             }
607             if (isset($question->qtype )&& CALCULATED == $question->qtype && preg_match('~^:TOLTYPE:percent~i', $line)) {
608                 // Percentage case is handled as relative in Moodle:
609                 $question->tolerance[$currentchoice]  /= 100;
610                 $question->tolerancetype[$currentchoice] = 1; // Relative
611                 continue;
612             }
614             if (preg_match('~^:UNITS:(.+)~i', $line, $webct_options)
615                     and $webctunits = trim($webct_options[1])) {
616                 // This is a guess - I really do not know how different webct units are separated...
617                 $webctunits = explode(':', $webctunits);
618                 $unitrec->multiplier = 1.0; // Webct does not seem to support this
619                 foreach ($webctunits as $webctunit) {
620                     $unitrec->unit = trim($webctunit);
621                     $question->units[] = $unitrec;
622                 }
623                 continue;
624             }
626             if (!empty($question->units) && preg_match('~^:UNITREQ:(.*)~i', $line, $webct_options)
627                     && !$webct_options[1]) {
628                 // There are units but units are not required so add the no unit alternative
629                 // We can but hope that the UNITS property always appear before this property
630                 $unitrec->unit = '';
631                 $unitrec->multiplier = 1.0;
632                 $question->units[] = $unitrec;
633                 continue;
634             }
636             if (!empty($question->units) && preg_match('~^:UNITCASE:~i', $line)) {
637                 // This could be important but I was not able to figure out how
638                 // it works so I ignore it for now
639                 continue;
640             }
642             if (isset($question->qtype )&& CALCULATED == $question->qtype && preg_match('~^:ANSTYPE:dec~i', $line)) {
643                 $question->correctanswerformat[$currentchoice]='1';
644                 continue;
645             }
646             if (isset($question->qtype )&& CALCULATED == $question->qtype && preg_match('~^:ANSTYPE:sig~i', $line)) {
647                 $question->correctanswerformat[$currentchoice]='2';
648                 continue;
649             }
650         }
652         if (sizeof($errors) > 0) {
653             echo "<p>".get_string("errorsdetected", "qformat_webct", sizeof($errors))."</p><ul>";
654             foreach($errors as $error) {
655                 echo "<li>$error</li>";
656             }
657             echo "</ul>";
658             unset($questions);     // no questions imported
659         }
661         if (sizeof($warnings) > 0) {
662             echo "<p>".get_string("warningsdetected", "qformat_webct", sizeof($warnings))."</p><ul>";
663             foreach($warnings as $warning) {
664                 echo "<li>$warning</li>";
665             }
666             echo "</ul>";
667         }
668         return $questions;
669     }
672 ?>