MDL-34841 error importing questions with long names.
[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 = $this->create_default_question_name(
263                                 $question->questiontext, get_string('questionname', 'question'));
264                     }
265                     if (!isset($question->defaultmark)) {
266                         $question->defaultmark = 1;
267                     }
268                     if (!isset($question->image)) {
269                         $question->image = "";
270                     }
272                     // Perform sanity checks
273                     $QuestionOK = TRUE;
274                     if (strlen($question->questiontext) == 0) {
275                         $warnings[] = get_string("missingquestion", "qformat_webct", $nQuestionStartLine);
276                         $QuestionOK = FALSE;
277                     }
278                     if (sizeof($question->answer) < 1) {  // a question must have at least 1 answer
279                        $errors[] = get_string("missinganswer", "qformat_webct", $nQuestionStartLine);
280                        $QuestionOK = FALSE;
281                     }
282                     else {
283                         // Create empty feedback array
284                         foreach ($question->answer as $key => $dataanswer) {
285                             if(!isset( $question->feedback[$key])){
286                                 $question->feedback[$key] = '';
287                             }
288                         }
289                         // this tempgeneralfeedback allows the code to work with versions from 1.6 to 1.9
290                         // when question->generalfeedback is undefined, the webct feedback is added to each answer feedback
291                         if (isset($question->tempgeneralfeedback)){
292                             if (isset($question->generalfeedback)) {
293                                 $question->generalfeedback = $question->tempgeneralfeedback;
294                             } else {
295                                 foreach ($question->answer as $key => $dataanswer) {
296                                     if ($question->tempgeneralfeedback !=''){
297                                         $question->feedback[$key] = $question->tempgeneralfeedback.'<br/>'.$question->feedback[$key];
298                                     }
299                                 }
300                             }
301                             unset($question->tempgeneralfeedback);
302                         }
303                         $maxfraction = -1;
304                         $totalfraction = 0;
305                         foreach($question->fraction as $fraction) {
306                             if ($fraction > 0) {
307                                 $totalfraction += $fraction;
308                             }
309                             if ($fraction > $maxfraction) {
310                                 $maxfraction = $fraction;
311                             }
312                         }
313                         switch ($question->qtype) {
314                             case 'shortanswer':
315                                 if ($maxfraction != 1) {
316                                     $maxfraction = $maxfraction * 100;
317                                     $errors[] = "'$question->name': ".get_string("wronggrade", "qformat_webct", $nLineCounter).' '.get_string("fractionsnomax", "question", $maxfraction);
318                                     $QuestionOK = FALSE;
319                                 }
320                                 break;
322                             case 'multichoice':
323                                 if ($question->single) {
324                                     if ($maxfraction != 1) {
325                                         $maxfraction = $maxfraction * 100;
326                                         $errors[] = "'$question->name': ".get_string("wronggrade", "qformat_webct", $nLineCounter).' '.get_string("fractionsnomax", "question", $maxfraction);
327                                         $QuestionOK = FALSE;
328                                     }
329                                 } else {
330                                     $totalfraction = round($totalfraction,2);
331                                     if ($totalfraction != 1) {
332                                         $totalfraction = $totalfraction * 100;
333                                         $errors[] = "'$question->name': ".get_string("wronggrade", "qformat_webct", $nLineCounter).' '.get_string("fractionsaddwrong", "question", $totalfraction);
334                                         $QuestionOK = FALSE;
335                                     }
336                                 }
337                                 break;
339                             case 'calculated':
340                                 foreach ($question->answers as $answer) {
341                                     if ($formulaerror = qtype_calculated_find_formula_errors($answer)) {
342                                         $warnings[] = "'$question->name': ". $formulaerror;
343                                         $QuestionOK = FALSE;
344                                     }
345                                 }
346                                 foreach ($question->dataset as $dataset) {
347                                     $dataset->itemcount=count($dataset->datasetitem);
348                                 }
349                                 $question->import_process=TRUE ;
350                                 unset($question->answer); //not used in calculated question
351                                 break;
352                             case 'match':
353                                 // MDL-10680:
354                                 // switch subquestions and subanswers
355                                 foreach ($question->subquestions as $id=>$subquestion) {
356                                     $temp = $question->subquestions[$id];
357                                     $question->subquestions[$id] = $question->subanswers[$id];
358                                     $question->subanswers[$id] = $temp;
359                                 }
360                                 if (count($question->answer) < 3){
361                                     // add a dummy missing question
362                                     $question->name = 'Dummy question added '.$question->name ;
363                                     $question->answer[] = 'dummy';
364                                     $question->subanswers[] = 'dummy';
365                                     $question->subquestions[] = 'dummy';
366                                     $question->fraction[] = '0.0';
367                                     $question->feedback[] = '';
368                                  }
369                                  break;
370                             default:
371                                 // No problemo
372                         }
373                     }
375                     if ($QuestionOK) {
376                        // echo "<pre>"; print_r ($question);
377                         $questions[] = $question;    // store it
378                         unset($question);            // and prepare a new one
379                         $question = $this->defaultquestion();
380                     }
381                 }
382                 $nQuestionStartLine = $nLineCounter;
383             }
385             // Processing Question Header
387             if (preg_match("~^:TYPE:MC:1(.*)~i",$line,$webct_options)) {
388                 // Multiple Choice Question with only one good answer
389                 $question = $this->defaultquestion();
390                 $question->feedback = array();
391                 $question->qtype = 'multichoice';
392                 $question->single = 1;        // Only one answer is allowed
393                 $ignore_rest_of_question = FALSE;
394                 continue;
395             }
397             if (preg_match("~^:TYPE:MC:N(.*)~i",$line,$webct_options)) {
398                 // Multiple Choice Question with several good answers
399                 $question = $this->defaultquestion();
400                 $question->feedback = array();
401                 $question->qtype = 'multichoice';
402                 $question->single = 0;        // Many answers allowed
403                 $ignore_rest_of_question = FALSE;
404                 continue;
405             }
407             if (preg_match("~^:TYPE:S~i",$line)) {
408                 // Short Answer Question
409                 $question = $this->defaultquestion();
410                 $question->feedback = array();
411                 $question->qtype = 'shortanswer';
412                 $question->usecase = 0;       // Ignore case
413                 $ignore_rest_of_question = FALSE;
414                 continue;
415             }
417             if (preg_match("~^:TYPE:C~i",$line)) {
418                 // Calculated Question
419                 $question = $this->defaultquestion();
420                 $question->qtype = 'calculated';
421                 $question->answers = array(); // No problem as they go as :FORMULA: from webct
422                 $question->units = array();
423                 $question->dataset = array();
425                 // To make us pass the end-of-question sanity checks
426                 $question->answer = array('dummy');
427                 $question->fraction = array('1.0');
428                 $question->feedback = array();
430                 $currentchoice = -1;
431                 $ignore_rest_of_question = FALSE;
432                 continue;
433             }
435             if (preg_match("~^:TYPE:M~i",$line)) {
436                 // Match Question
437                 $question = $this->defaultquestion();
438                 $question->qtype = 'match';
439                 $question->feedback = array();
440                 $ignore_rest_of_question = FALSE;         // match question processing is not debugged
441                 continue;
442             }
444             if (preg_match("~^:TYPE:P~i",$line)) {
445                 // Paragraph Question
446                 $warnings[] = get_string("paragraphquestion", "qformat_webct", $nLineCounter);
447                 unset($question);
448                 $ignore_rest_of_question = TRUE;         // Question Type not handled by Moodle
449                 continue;
450             }
452             if (preg_match("~^:TYPE:~i",$line)) {
453                 // Unknow Question
454                 $warnings[] = get_string("unknowntype", "qformat_webct", $nLineCounter);
455                 unset($question);
456                 $ignore_rest_of_question = TRUE;         // Question Type not handled by Moodle
457                 continue;
458             }
460             if ($ignore_rest_of_question) {
461                 continue;
462             }
464             if (preg_match("~^:TITLE:(.*)~i",$line,$webct_options)) {
465                 $name = trim($webct_options[1]);
466                 $question->name = $this->clean_question_name($name);
467                 continue;
468             }
470             if (preg_match("~^:IMAGE:(.*)~i",$line,$webct_options)) {
471                 $filename = trim($webct_options[1]);
472                 if (preg_match("~^http://~i",$filename)) {
473                     $question->image = $filename;
474                 }
475                 continue;
476             }
478             // Need to put the parsing of calculated items here to avoid ambitiuosness:
479             // if question isn't defined yet there is nothing to do here (avoid notices)
480             if (!isset($question)) {
481                 continue;
482             }
483             if (isset($question->qtype ) && 'calculated' == $question->qtype && preg_match(
484                     "~^:([[:lower:]].*|::.*)-(MIN|MAX|DEC|VAL([0-9]+))::?:?($webctnumberregex)~", $line, $webct_options)) {
485                 $datasetname = preg_replace('/^::/', '', $webct_options[1]);
486                 $datasetvalue = qformat_webct_convert_formula($webct_options[4]);
487                 switch ($webct_options[2]) {
488                     case 'MIN':
489                         $question->dataset[$datasetname]->min = $datasetvalue;
490                         break;
491                     case 'MAX':
492                         $question->dataset[$datasetname]->max = $datasetvalue;
493                         break;
494                     case 'DEC':
495                         $datasetvalue = floor($datasetvalue); // int only!
496                         $question->dataset[$datasetname]->length = max(0, $datasetvalue);
497                         break;
498                     default:
499                         // The VAL case:
500                         $question->dataset[$datasetname]->datasetitem[$webct_options[3]] = new stdClass();
501                         $question->dataset[$datasetname]->datasetitem[$webct_options[3]]->itemnumber = $webct_options[3];
502                         $question->dataset[$datasetname]->datasetitem[$webct_options[3]]->value  = $datasetvalue;
503                         break;
504                 }
505                 continue;
506             }
509             $bIsHTMLText = preg_match("~:H$~i",$line);  // True if next lines are coded in HTML
510             if (preg_match("~^:QUESTION~i",$line)) {
511                 $questiontext="";               // Start gathering next lines
512                 continue;
513             }
515             if (preg_match("~^:ANSWER([0-9]+):([^:]+):([0-9\.\-]+):(.*)~i",$line,$webct_options)) { // Shortanswer.
516                 $currentchoice=$webct_options[1];
517                 $answertext=$webct_options[2];            // Start gathering next lines
518                 $question->fraction[$currentchoice]=($webct_options[3]/100);
519                 continue;
520             }
522             if (preg_match("~^:ANSWER([0-9]+):([0-9\.\-]+)~i",$line,$webct_options)) {
523                 $answertext="";                 // Start gathering next lines
524                 $currentchoice=$webct_options[1];
525                 $question->fraction[$currentchoice]=($webct_options[2]/100);
526                 continue;
527             }
529             if (preg_match('~^:FORMULA:(.*)~i', $line, $webct_options)) {
530                 // Answer for a calculated question
531                 ++$currentchoice;
532                 $question->answers[$currentchoice] =
533                         qformat_webct_convert_formula($webct_options[1]);
535                 // Default settings:
536                 $question->fraction[$currentchoice] = 1.0;
537                 $question->tolerance[$currentchoice] = 0.0;
538                 $question->tolerancetype[$currentchoice] = 2; // nominal (units in webct)
539                 $question->feedback[$currentchoice] = '';
540                 $question->correctanswerlength[$currentchoice] = 4;
542                 $datasetnames = question_bank::get_qtype('calculated')->
543                         find_dataset_names($webct_options[1]);
544                 foreach ($datasetnames as $datasetname) {
545                     $question->dataset[$datasetname] = new stdClass();
546                     $question->dataset[$datasetname]->datasetitem = array();
547                     $question->dataset[$datasetname]->name = $datasetname ;
548                     $question->dataset[$datasetname]->distribution = 'uniform';
549                     $question->dataset[$datasetname]->status ='private';
550                 }
551                 continue;
552             }
554             if (preg_match("~^:L([0-9]+)~i",$line,$webct_options)) {
555                 $answertext="";                 // Start gathering next lines
556                 $currentchoice=$webct_options[1];
557                 $question->fraction[$currentchoice]=1;
558                 continue;
559             }
561             if (preg_match("~^:R([0-9]+)~i",$line,$webct_options)) {
562                 $responsetext="";                // Start gathering next lines
563                 $currentchoice=$webct_options[1];
564                 continue;
565             }
567             if (preg_match("~^:REASON([0-9]+):?~i",$line,$webct_options)) {
568                 $feedbacktext="";               // Start gathering next lines
569                 $currentchoice=$webct_options[1];
570                 continue;
571             }
572             if (preg_match("~^:FEEDBACK([0-9]+):?~i",$line,$webct_options)) {
573                 $generalfeedbacktext="";               // Start gathering next lines
574                 $currentchoice=$webct_options[1];
575                 continue;
576             }
577             if (preg_match('~^:FEEDBACK:(.*)~i',$line,$webct_options)) {
578                 $generalfeedbacktext="";               // Start gathering next lines
579                 continue;
580             }
581             if (preg_match('~^:LAYOUT:(.*)~i',$line,$webct_options)) {
582             //    ignore  since layout in question_multichoice  is no more used in moodle
583             //    $webct_options[1] contains either vertical or horizontal ;
584                 continue;
585             }
587             if (isset($question->qtype ) && 'calculated' == $question->qtype && preg_match('~^:ANS-DEC:([1-9][0-9]*)~i', $line, $webct_options)) {
588                 // We can but hope that this always appear before the ANSTYPE property
589                 $question->correctanswerlength[$currentchoice] = $webct_options[1];
590                 continue;
591             }
593             if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match("~^:TOL:($webctnumberregex)~i", $line, $webct_options)) {
594                 // We can but hope that this always appear before the TOL property
595                 $question->tolerance[$currentchoice] =
596                         qformat_webct_convert_formula($webct_options[1]);
597                 continue;
598             }
600             if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match('~^:TOLTYPE:percent~i', $line)) {
601                 // Percentage case is handled as relative in Moodle:
602                 $question->tolerance[$currentchoice]  /= 100;
603                 $question->tolerancetype[$currentchoice] = 1; // Relative
604                 continue;
605             }
607             if (preg_match('~^:UNITS:(.+)~i', $line, $webct_options)
608                     and $webctunits = trim($webct_options[1])) {
609                 // This is a guess - I really do not know how different webct units are separated...
610                 $webctunits = explode(':', $webctunits);
611                 $unitrec->multiplier = 1.0; // Webct does not seem to support this
612                 foreach ($webctunits as $webctunit) {
613                     $unitrec->unit = trim($webctunit);
614                     $question->units[] = $unitrec;
615                 }
616                 continue;
617             }
619             if (!empty($question->units) && preg_match('~^:UNITREQ:(.*)~i', $line, $webct_options)
620                     && !$webct_options[1]) {
621                 // There are units but units are not required so add the no unit alternative
622                 // We can but hope that the UNITS property always appear before this property
623                 $unitrec->unit = '';
624                 $unitrec->multiplier = 1.0;
625                 $question->units[] = $unitrec;
626                 continue;
627             }
629             if (!empty($question->units) && preg_match('~^:UNITCASE:~i', $line)) {
630                 // This could be important but I was not able to figure out how
631                 // it works so I ignore it for now
632                 continue;
633             }
635             if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match('~^:ANSTYPE:dec~i', $line)) {
636                 $question->correctanswerformat[$currentchoice]='1';
637                 continue;
638             }
639             if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match('~^:ANSTYPE:sig~i', $line)) {
640                 $question->correctanswerformat[$currentchoice]='2';
641                 continue;
642             }
643         }
645         if (sizeof($errors) > 0) {
646             echo "<p>".get_string("errorsdetected", "qformat_webct", sizeof($errors))."</p><ul>";
647             foreach($errors as $error) {
648                 echo "<li>$error</li>";
649             }
650             echo "</ul>";
651             unset($questions);     // no questions imported
652         }
654         if (sizeof($warnings) > 0) {
655             echo "<p>".get_string("warningsdetected", "qformat_webct", sizeof($warnings))."</p><ul>";
656             foreach($warnings as $warning) {
657                 echo "<li>$warning</li>";
658             }
659             echo "</ul>";
660         }
661         return $questions;
662     }
665 ?>