Example #1
0
 public function readquestions($lines)
 {
     $webctnumberregex = '[+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)((e|E|\\*10\\*\\*)([+-]?[0-9]+|\\([+-]?[0-9]+\\)))?';
     $questions = array();
     $errors = array();
     $warnings = array();
     $webctoptions = array();
     $ignorerestofquestion = false;
     $nlinecounter = 0;
     $nquestionstartline = 0;
     $bishtmltext = false;
     $lines[] = ":EOF:";
     // For an easiest processing of the last line.
     // We don't call defaultquestion() here, it will be called later.
     foreach ($lines as $line) {
         $nlinecounter++;
         $line = textlib::convert($line, 'windows-1252', 'utf-8');
         // Processing multiples lines strings.
         if (isset($questiontext) and is_string($questiontext)) {
             if (preg_match("~^:~", $line)) {
                 $questiontext = $this->text_field(trim($questiontext));
                 $question->questiontext = $questiontext['text'];
                 $question->questiontextformat = $questiontext['format'];
                 if (isset($questiontext['itemid'])) {
                     $question->questiontextitemid = $questiontext['itemid'];
                 }
                 unset($questiontext);
             } else {
                 $questiontext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         if (isset($answertext) and is_string($answertext)) {
             if (preg_match("~^:~", $line)) {
                 $answertext = trim($answertext);
                 if ($question->qtype == 'multichoice' || $question->qtype == 'match') {
                     $question->answer[$currentchoice] = $this->text_field($answertext);
                     $question->subanswers[$currentchoice] = $question->answer[$currentchoice];
                 } else {
                     $question->answer[$currentchoice] = $answertext;
                     $question->subanswers[$currentchoice] = $answertext;
                 }
                 unset($answertext);
             } else {
                 $answertext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         if (isset($responsetext) and is_string($responsetext)) {
             if (preg_match("~^:~", $line)) {
                 $question->subquestions[$currentchoice] = trim($responsetext);
                 unset($responsetext);
             } else {
                 $responsetext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         if (isset($feedbacktext) and is_string($feedbacktext)) {
             if (preg_match("~^:~", $line)) {
                 $question->feedback[$currentchoice] = $this->text_field(trim($feedbacktext));
                 unset($feedbacktext);
             } else {
                 $feedbacktext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         if (isset($generalfeedbacktext) and is_string($generalfeedbacktext)) {
             if (preg_match("~^:~", $line)) {
                 $question->tempgeneralfeedback = trim($generalfeedbacktext);
                 unset($generalfeedbacktext);
             } else {
                 $generalfeedbacktext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         if (isset($graderinfo) and is_string($graderinfo)) {
             if (preg_match("~^:~", $line)) {
                 $question->graderinfo['text'] = trim($graderinfo);
                 $question->graderinfo['format'] = FORMAT_HTML;
                 unset($graderinfo);
             } else {
                 $graderinfo .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         $line = trim($line);
         if (preg_match("~^:(TYPE|EOF):~i", $line)) {
             // New Question or End of File.
             if (isset($question)) {
                 // If previous question exists, complete, check and save it.
                 // Setup default value of missing fields.
                 if (!isset($question->name)) {
                     $question->name = $this->create_default_question_name($question->questiontext, get_string('questionname', 'question'));
                 }
                 if (!isset($question->defaultmark)) {
                     $question->defaultmark = 1;
                 }
                 if (!isset($question->image)) {
                     $question->image = '';
                 }
                 // Perform sanity checks.
                 $questionok = true;
                 if (strlen($question->questiontext) == 0) {
                     $warnings[] = get_string('missingquestion', 'qformat_webct', $nquestionstartline);
                     $questionok = false;
                 }
                 if (count($question->answer) < 1) {
                     // A question must have at least 1 answer.
                     $errors[] = get_string('missinganswer', 'qformat_webct', $nquestionstartline);
                     $questionok = false;
                 } else {
                     // Create empty feedback array.
                     foreach ($question->answer as $key => $dataanswer) {
                         if (!isset($question->feedback[$key])) {
                             $question->feedback[$key]['text'] = '';
                             $question->feedback[$key]['format'] = FORMAT_HTML;
                         }
                     }
                     // This tempgeneralfeedback allows the code to work with versions from 1.6 to 1.9.
                     // When question->generalfeedback is undefined, the webct feedback is added to each answer feedback.
                     if (isset($question->tempgeneralfeedback)) {
                         if (isset($question->generalfeedback)) {
                             $generalfeedback = $this->text_field($question->tempgeneralfeedback);
                             $question->generalfeedback = $generalfeedback['text'];
                             $question->generalfeedbackformat = $generalfeedback['format'];
                             if (isset($generalfeedback['itemid'])) {
                                 $question->genralfeedbackitemid = $generalfeedback['itemid'];
                             }
                         } else {
                             foreach ($question->answer as $key => $dataanswer) {
                                 if ($question->tempgeneralfeedback != '') {
                                     $question->feedback[$key]['text'] = $question->tempgeneralfeedback . '<br/>' . $question->feedback[$key]['text'];
                                 }
                             }
                         }
                         unset($question->tempgeneralfeedback);
                     }
                     $maxfraction = -1;
                     $totalfraction = 0;
                     foreach ($question->fraction as $fraction) {
                         if ($fraction > 0) {
                             $totalfraction += $fraction;
                         }
                         if ($fraction > $maxfraction) {
                             $maxfraction = $fraction;
                         }
                     }
                     switch ($question->qtype) {
                         case 'shortanswer':
                             if ($maxfraction != 1) {
                                 $maxfraction = $maxfraction * 100;
                                 $errors[] = "'{$question->name}': " . get_string('wronggrade', 'qformat_webct', $nlinecounter) . ' ' . get_string('fractionsnomax', 'question', $maxfraction);
                                 $questionok = false;
                             }
                             break;
                         case 'multichoice':
                             $question = $this->add_blank_combined_feedback($question);
                             if ($question->single) {
                                 if ($maxfraction != 1) {
                                     $maxfraction = $maxfraction * 100;
                                     $errors[] = "'{$question->name}': " . get_string('wronggrade', 'qformat_webct', $nlinecounter) . ' ' . get_string('fractionsnomax', 'question', $maxfraction);
                                     $questionok = false;
                                 }
                             } else {
                                 $totalfraction = round($totalfraction, 2);
                                 if ($totalfraction != 1) {
                                     $totalfraction = $totalfraction * 100;
                                     $errors[] = "'{$question->name}': " . get_string('wronggrade', 'qformat_webct', $nlinecounter) . ' ' . get_string('fractionsaddwrong', 'question', $totalfraction);
                                     $questionok = false;
                                 }
                             }
                             break;
                         case 'calculated':
                             foreach ($question->answers as $answer) {
                                 if ($formulaerror = qtype_calculated_find_formula_errors($answer)) {
                                     $warnings[] = "'{$question->name}': " . $formulaerror;
                                     $questionok = false;
                                 }
                             }
                             foreach ($question->dataset as $dataset) {
                                 $dataset->itemcount = count($dataset->datasetitem);
                             }
                             $question->import_process = true;
                             unset($question->answer);
                             // Not used in calculated question.
                             break;
                         case 'match':
                             // MDL-10680:
                             // Switch subquestions and subanswers.
                             $question = $this->add_blank_combined_feedback($question);
                             foreach ($question->subquestions as $id => $subquestion) {
                                 $temp = $question->subquestions[$id];
                                 $question->subquestions[$id] = $question->subanswers[$id];
                                 $question->subanswers[$id] = $temp;
                             }
                             if (count($question->answer) < 3) {
                                 // Add a dummy missing question.
                                 $question->name = 'Dummy question added ' . $question->name;
                                 $question->answer[] = 'dummy';
                                 $question->subanswers[] = 'dummy';
                                 $question->subquestions[] = 'dummy';
                                 $question->fraction[] = '0.0';
                                 $question->feedback[] = '';
                             }
                             break;
                         default:
                             // No problemo.
                     }
                 }
                 if ($questionok) {
                     $questions[] = $question;
                     // Store it.
                     unset($question);
                     // And prepare a new one.
                     $question = $this->defaultquestion();
                 }
             }
             $nquestionstartline = $nlinecounter;
         }
         // Processing Question Header.
         if (preg_match("~^:TYPE:MC:1(.*)~i", $line, $webctoptions)) {
             // Multiple Choice Question with only one good answer.
             $question = $this->defaultquestion();
             $question->feedback = array();
             $question->qtype = 'multichoice';
             $question->single = 1;
             // Only one answer is allowed.
             $ignorerestofquestion = false;
             continue;
         }
         if (preg_match("~^:TYPE:MC:N(.*)~i", $line, $webctoptions)) {
             // Multiple Choice Question with several good answers.
             $question = $this->defaultquestion();
             $question->feedback = array();
             $question->qtype = 'multichoice';
             $question->single = 0;
             // Many answers allowed.
             $ignorerestofquestion = false;
             continue;
         }
         if (preg_match("~^:TYPE:S~i", $line)) {
             // Short Answer Question.
             $question = $this->defaultquestion();
             $question->feedback = array();
             $question->qtype = 'shortanswer';
             $question->usecase = 0;
             // Ignore case.
             $ignorerestofquestion = false;
             continue;
         }
         if (preg_match("~^:TYPE:C~i", $line)) {
             // Calculated Question.
             $question = $this->defaultquestion();
             $question->qtype = 'calculated';
             $question->answer = array();
             // No problem as they go as :FORMULA: from webct.
             $question->units = array();
             $question->dataset = array();
             $question->fraction = array('1.0');
             $question->feedback = array();
             $currentchoice = -1;
             $ignorerestofquestion = false;
             continue;
         }
         if (preg_match("~^:TYPE:M~i", $line)) {
             // Match Question.
             $question = $this->defaultquestion();
             $question->qtype = 'match';
             $question->feedback = array();
             $ignorerestofquestion = false;
             // Match question processing is not debugged.
             continue;
         }
         if (preg_match("~^:TYPE:P~i", $line)) {
             // Paragraph Question.
             $question = $this->defaultquestion();
             $question->qtype = 'essay';
             $question->responseformat = 'editor';
             $question->responsefieldlines = 15;
             $question->attachments = 0;
             $question->graderinfo = array('text' => '', 'format' => FORMAT_HTML);
             $question->feedback = array();
             $question->generalfeedback = '';
             $question->generalfeedbackformat = FORMAT_HTML;
             $question->generalfeedbackfiles = array();
             $question->responsetemplate = $this->text_field('');
             $question->questiontextformat = FORMAT_HTML;
             $ignorerestofquestion = false;
             // To make us pass the end-of-question sanity checks.
             $question->answer = array('dummy');
             $question->fraction = array('1.0');
             continue;
         }
         if (preg_match("~^:TYPE:~i", $line)) {
             // Unknow question type.
             $warnings[] = get_string('unknowntype', 'qformat_webct', $nlinecounter);
             unset($question);
             $ignorerestofquestion = true;
             // Question Type not handled by Moodle.
             continue;
         }
         if ($ignorerestofquestion) {
             continue;
         }
         if (preg_match("~^:TITLE:(.*)~i", $line, $webctoptions)) {
             $name = trim($webctoptions[1]);
             $question->name = $this->clean_question_name($name);
             continue;
         }
         if (preg_match("~^:IMAGE:(.*)~i", $line, $webctoptions)) {
             $filename = trim($webctoptions[1]);
             if (preg_match("~^http://~i", $filename)) {
                 $question->image = $filename;
             }
             continue;
         }
         // Need to put the parsing of calculated items here to avoid ambitiuosness:
         // if question isn't defined yet there is nothing to do here (avoid notices).
         if (!isset($question)) {
             continue;
         }
         if (isset($question->qtype) && 'calculated' == $question->qtype && preg_match("~^:([[:lower:]].*|::.*)-(MIN|MAX|DEC|VAL([0-9]+))::?:?({$webctnumberregex})~", $line, $webctoptions)) {
             $datasetname = preg_replace('/^::/', '', $webctoptions[1]);
             $datasetvalue = qformat_webct_convert_formula($webctoptions[4]);
             switch ($webctoptions[2]) {
                 case 'MIN':
                     $question->dataset[$datasetname]->min = $datasetvalue;
                     break;
                 case 'MAX':
                     $question->dataset[$datasetname]->max = $datasetvalue;
                     break;
                 case 'DEC':
                     $datasetvalue = floor($datasetvalue);
                     // Int only!
                     $question->dataset[$datasetname]->length = max(0, $datasetvalue);
                     break;
                 default:
                     // The VAL case.
                     $question->dataset[$datasetname]->datasetitem[$webctoptions[3]] = new stdClass();
                     $question->dataset[$datasetname]->datasetitem[$webctoptions[3]]->itemnumber = $webctoptions[3];
                     $question->dataset[$datasetname]->datasetitem[$webctoptions[3]]->value = $datasetvalue;
                     break;
             }
             continue;
         }
         $bishtmltext = preg_match("~:H\$~i", $line);
         // True if next lines are coded in HTML.
         if (preg_match("~^:QUESTION~i", $line)) {
             $questiontext = '';
             // Start gathering next lines.
             continue;
         }
         if (preg_match("~^:ANSWER([0-9]+):([^:]+):([0-9\\.\\-]+):(.*)~i", $line, $webctoptions)) {
             // Shortanswer.
             $currentchoice = $webctoptions[1];
             $answertext = $webctoptions[2];
             // Start gathering next lines.
             $question->fraction[$currentchoice] = $webctoptions[3] / 100;
             continue;
         }
         if (preg_match("~^:ANSWER([0-9]+):([0-9\\.\\-]+)~i", $line, $webctoptions)) {
             $answertext = '';
             // Start gathering next lines.
             $currentchoice = $webctoptions[1];
             $question->fraction[$currentchoice] = $webctoptions[2] / 100;
             continue;
         }
         if (preg_match('~^:ANSWER:~i', $line)) {
             // Essay.
             $graderinfo = '';
             // Start gathering next lines.
             continue;
         }
         if (preg_match('~^:FORMULA:(.*)~i', $line, $webctoptions)) {
             // Answer for a calculated question.
             ++$currentchoice;
             $question->answer[$currentchoice] = qformat_webct_convert_formula($webctoptions[1]);
             // Default settings.
             $question->fraction[$currentchoice] = 1.0;
             $question->tolerance[$currentchoice] = 0.0;
             $question->tolerancetype[$currentchoice] = 2;
             // Nominal (units in webct).
             $question->feedback[$currentchoice]['text'] = '';
             $question->feedback[$currentchoice]['format'] = FORMAT_HTML;
             $question->correctanswerlength[$currentchoice] = 4;
             $datasetnames = question_bank::get_qtype('calculated')->find_dataset_names($webctoptions[1]);
             foreach ($datasetnames as $datasetname) {
                 $question->dataset[$datasetname] = new stdClass();
                 $question->dataset[$datasetname]->datasetitem = array();
                 $question->dataset[$datasetname]->name = $datasetname;
                 $question->dataset[$datasetname]->distribution = 'uniform';
                 $question->dataset[$datasetname]->status = 'private';
             }
             continue;
         }
         if (preg_match("~^:L([0-9]+)~i", $line, $webctoptions)) {
             $answertext = '';
             // Start gathering next lines.
             $currentchoice = $webctoptions[1];
             $question->fraction[$currentchoice] = 1;
             continue;
         }
         if (preg_match("~^:R([0-9]+)~i", $line, $webctoptions)) {
             $responsetext = '';
             // Start gathering next lines.
             $currentchoice = $webctoptions[1];
             continue;
         }
         if (preg_match("~^:REASON([0-9]+):?~i", $line, $webctoptions)) {
             $feedbacktext = '';
             // Start gathering next lines.
             $currentchoice = $webctoptions[1];
             continue;
         }
         if (preg_match("~^:FEEDBACK([0-9]+):?~i", $line, $webctoptions)) {
             $generalfeedbacktext = '';
             // Start gathering next lines.
             $currentchoice = $webctoptions[1];
             continue;
         }
         if (preg_match('~^:FEEDBACK:(.*)~i', $line, $webctoptions)) {
             $generalfeedbacktext = '';
             // Start gathering next lines.
             continue;
         }
         if (preg_match('~^:LAYOUT:(.*)~i', $line, $webctoptions)) {
             // Ignore  since layout in question_multichoice  is no more used in Moodle.
             // $webctoptions[1] contains either vertical or horizontal.
             continue;
         }
         if (isset($question->qtype) && 'calculated' == $question->qtype && preg_match('~^:ANS-DEC:([1-9][0-9]*)~i', $line, $webctoptions)) {
             // We can but hope that this always appear before the ANSTYPE property.
             $question->correctanswerlength[$currentchoice] = $webctoptions[1];
             continue;
         }
         if (isset($question->qtype) && 'calculated' == $question->qtype && preg_match("~^:TOL:({$webctnumberregex})~i", $line, $webctoptions)) {
             // We can but hope that this always appear before the TOL property.
             $question->tolerance[$currentchoice] = qformat_webct_convert_formula($webctoptions[1]);
             continue;
         }
         if (isset($question->qtype) && 'calculated' == $question->qtype && preg_match('~^:TOLTYPE:percent~i', $line)) {
             // Percentage case is handled as relative in Moodle.
             $question->tolerance[$currentchoice] /= 100;
             $question->tolerancetype[$currentchoice] = 1;
             // Relative.
             continue;
         }
         if (preg_match('~^:UNITS:(.+)~i', $line, $webctoptions) and $webctunits = trim($webctoptions[1])) {
             // This is a guess - I really do not know how different webct units are separated...
             $webctunits = explode(':', $webctunits);
             $unitrec->multiplier = 1.0;
             // Webct does not seem to support this.
             foreach ($webctunits as $webctunit) {
                 $unitrec->unit = trim($webctunit);
                 $question->units[] = $unitrec;
             }
             continue;
         }
         if (!empty($question->units) && preg_match('~^:UNITREQ:(.*)~i', $line, $webctoptions) && !$webctoptions[1]) {
             // There are units but units are not required so add the no unit alternative.
             // We can but hope that the UNITS property always appear before this property.
             $unitrec->unit = '';
             $unitrec->multiplier = 1.0;
             $question->units[] = $unitrec;
             continue;
         }
         if (!empty($question->units) && preg_match('~^:UNITCASE:~i', $line)) {
             // This could be important but I was not able to figure out how
             // it works so I ignore it for now.
             continue;
         }
         if (isset($question->qtype) && 'calculated' == $question->qtype && preg_match('~^:ANSTYPE:dec~i', $line)) {
             $question->correctanswerformat[$currentchoice] = '1';
             continue;
         }
         if (isset($question->qtype) && 'calculated' == $question->qtype && preg_match('~^:ANSTYPE:sig~i', $line)) {
             $question->correctanswerformat[$currentchoice] = '2';
             continue;
         }
     }
     if (count($errors) > 0) {
         echo '<p>' . get_string('errorsdetected', 'qformat_webct', count($errors)) . '</p><ul>';
         foreach ($errors as $error) {
             echo "<li>{$error}</li>";
         }
         echo '</ul>';
         unset($questions);
         // No questions imported.
     }
     if (count($warnings) > 0) {
         echo '<p>' . get_string('warningsdetected', 'qformat_webct', count($warnings)) . '</p><ul>';
         foreach ($warnings as $warning) {
             echo "<li>{$warning}</li>";
         }
         echo '</ul>';
     }
     return $questions;
 }
Example #2
0
 function readquestions($lines)
 {
     global $QTYPES;
     //  $qtypecalculated = new qformat_webct_modified_calculated_qtype();
     $webctnumberregex = '[+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)((e|E|\\*10\\*\\*)([+-]?[0-9]+|\\([+-]?[0-9]+\\)))?';
     $questions = array();
     $errors = array();
     $warnings = array();
     $webct_options = array();
     $ignore_rest_of_question = FALSE;
     $nLineCounter = 0;
     $nQuestionStartLine = 0;
     $bIsHTMLText = FALSE;
     $lines[] = ":EOF:";
     // for an easiest processing of the last line
     //    $question = $this->defaultquestion();
     foreach ($lines as $line) {
         $nLineCounter++;
         $line = iconv("Windows-1252", "UTF-8", $line);
         // Processing multiples lines strings
         if (isset($questiontext) and is_string($questiontext)) {
             if (preg_match("~^:~", $line)) {
                 $question->questiontext = trim($questiontext);
                 unset($questiontext);
             } else {
                 $questiontext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         if (isset($answertext) and is_string($answertext)) {
             if (preg_match("~^:~", $line)) {
                 $answertext = trim($answertext);
                 $question->answer[$currentchoice] = $answertext;
                 $question->subanswers[$currentchoice] = $answertext;
                 unset($answertext);
             } else {
                 $answertext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         if (isset($responsetext) and is_string($responsetext)) {
             if (preg_match("~^:~", $line)) {
                 $question->subquestions[$currentchoice] = trim($responsetext);
                 unset($responsetext);
             } else {
                 $responsetext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         if (isset($feedbacktext) and is_string($feedbacktext)) {
             if (preg_match("~^:~", $line)) {
                 $question->feedback[$currentchoice] = trim($feedbacktext);
                 unset($feedbacktext);
             } else {
                 $feedbacktext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         if (isset($generalfeedbacktext) and is_string($generalfeedbacktext)) {
             if (preg_match("~^:~", $line)) {
                 $question->tempgeneralfeedback = trim($generalfeedbacktext);
                 unset($generalfeedbacktext);
             } else {
                 $generalfeedbacktext .= str_replace('\\:', ':', $line);
                 continue;
             }
         }
         $line = trim($line);
         if (preg_match("~^:(TYPE|EOF):~i", $line)) {
             // New Question or End of File
             if (isset($question)) {
                 // if previous question exists, complete, check and save it
                 // Setup default value of missing fields
                 if (!isset($question->name)) {
                     $question->name = $question->questiontext;
                 }
                 if (strlen($question->name) > 255) {
                     $question->name = substr($question->name, 0, 250) . "...";
                     $warnings[] = get_string("questionnametoolong", "quiz", $nQuestionStartLine);
                 }
                 if (!isset($question->defaultgrade)) {
                     $question->defaultgrade = 1;
                 }
                 if (!isset($question->image)) {
                     $question->image = "";
                 }
                 // Perform sanity checks
                 $QuestionOK = TRUE;
                 if (strlen($question->questiontext) == 0) {
                     $warnings[] = get_string("missingquestion", "quiz", $nQuestionStartLine);
                     $QuestionOK = FALSE;
                 }
                 if (sizeof($question->answer) < 1) {
                     // a question must have at least 1 answer
                     $errors[] = get_string("missinganswer", "quiz", $nQuestionStartLine);
                     $QuestionOK = FALSE;
                 } else {
                     // Create empty feedback array
                     foreach ($question->answer as $key => $dataanswer) {
                         if (!isset($question->feedback[$key])) {
                             $question->feedback[$key] = '';
                         }
                     }
                     // this tempgeneralfeedback allows the code to work with versions from 1.6 to 1.9
                     // when question->generalfeedback is undefined, the webct feedback is added to each answer feedback
                     if (isset($question->tempgeneralfeedback)) {
                         if (isset($question->generalfeedback)) {
                             $question->generalfeedback = $question->tempgeneralfeedback;
                         } else {
                             foreach ($question->answer as $key => $dataanswer) {
                                 if ($question->tempgeneralfeedback != '') {
                                     $question->feedback[$key] = $question->tempgeneralfeedback . '<br/>' . $question->feedback[$key];
                                 }
                             }
                         }
                         unset($question->tempgeneralfeedback);
                     }
                     $maxfraction = -1;
                     $totalfraction = 0;
                     foreach ($question->fraction as $fraction) {
                         if ($fraction > 0) {
                             $totalfraction += $fraction;
                         }
                         if ($fraction > $maxfraction) {
                             $maxfraction = $fraction;
                         }
                     }
                     switch ($question->qtype) {
                         case SHORTANSWER:
                             if ($maxfraction != 1) {
                                 $maxfraction = $maxfraction * 100;
                                 $errors[] = "'{$question->name}': " . get_string("wronggrade", "quiz", $nLineCounter) . ' ' . get_string("fractionsnomax", "quiz", $maxfraction);
                                 $QuestionOK = FALSE;
                             }
                             break;
                         case MULTICHOICE:
                             if ($question->single) {
                                 if ($maxfraction != 1) {
                                     $maxfraction = $maxfraction * 100;
                                     $errors[] = "'{$question->name}': " . get_string("wronggrade", "quiz", $nLineCounter) . ' ' . get_string("fractionsnomax", "quiz", $maxfraction);
                                     $QuestionOK = FALSE;
                                 }
                             } else {
                                 $totalfraction = round($totalfraction, 2);
                                 if ($totalfraction != 1) {
                                     $totalfraction = $totalfraction * 100;
                                     $errors[] = "'{$question->name}': " . get_string("wronggrade", "quiz", $nLineCounter) . ' ' . get_string("fractionsaddwrong", "quiz", $totalfraction);
                                     $QuestionOK = FALSE;
                                 }
                             }
                             break;
                         case CALCULATED:
                             foreach ($question->answers as $answer) {
                                 if ($formulaerror = qtype_calculated_find_formula_errors($answer)) {
                                     //$QTYPES['calculated']->
                                     $warnings[] = "'{$question->name}': " . $formulaerror;
                                     $QuestionOK = FALSE;
                                 }
                             }
                             foreach ($question->dataset as $dataset) {
                                 $dataset->itemcount = count($dataset->datasetitem);
                             }
                             $question->import_process = TRUE;
                             unset($question->answer);
                             //not used in calculated question
                             break;
                         case MATCH:
                             // MDL-10680:
                             // switch subquestions and subanswers
                             foreach ($question->subquestions as $id => $subquestion) {
                                 $temp = $question->subquestions[$id];
                                 $question->subquestions[$id] = $question->subanswers[$id];
                                 $question->subanswers[$id] = $temp;
                             }
                             if (count($question->answer) < 3) {
                                 // add a dummy missing question
                                 $question->name = 'Dummy question added ' . $question->name;
                                 $question->answer[] = 'dummy';
                                 $question->subanswers[] = 'dummy';
                                 $question->subquestions[] = 'dummy';
                                 $question->fraction[] = '0.0';
                                 $question->feedback[] = '';
                             }
                             break;
                         default:
                             // No problemo
                     }
                 }
                 if ($QuestionOK) {
                     // echo "<pre>"; print_r ($question);
                     $questions[] = $question;
                     // store it
                     unset($question);
                     // and prepare a new one
                     $question = $this->defaultquestion();
                 }
             }
             $nQuestionStartLine = $nLineCounter;
         }
         // Processing Question Header
         if (preg_match("~^:TYPE:MC:1(.*)~i", $line, $webct_options)) {
             // Multiple Choice Question with only one good answer
             $question = $this->defaultquestion();
             $question->feedback = array();
             $question->qtype = MULTICHOICE;
             $question->single = 1;
             // Only one answer is allowed
             $ignore_rest_of_question = FALSE;
             continue;
         }
         if (preg_match("~^:TYPE:MC:N(.*)~i", $line, $webct_options)) {
             // Multiple Choice Question with several good answers
             $question = $this->defaultquestion();
             $question->feedback = array();
             $question->qtype = MULTICHOICE;
             $question->single = 0;
             // Many answers allowed
             $ignore_rest_of_question = FALSE;
             continue;
         }
         if (preg_match("~^:TYPE:S~i", $line)) {
             // Short Answer Question
             $question = $this->defaultquestion();
             $question->feedback = array();
             $question->qtype = SHORTANSWER;
             $question->usecase = 0;
             // Ignore case
             $ignore_rest_of_question = FALSE;
             continue;
         }
         if (preg_match("~^:TYPE:C~i", $line)) {
             // Calculated Question
             /*     $warnings[] = get_string("calculatedquestion", "quiz", $nLineCounter);
                       unset($question);
                       $ignore_rest_of_question = TRUE;         // Question Type not handled by Moodle
                    */
             $question = $this->defaultquestion();
             $question->qtype = CALCULATED;
             $question->answers = array();
             // No problem as they go as :FORMULA: from webct
             $question->units = array();
             $question->dataset = array();
             // To make us pass the end-of-question sanity checks
             $question->answer = array('dummy');
             $question->fraction = array('1.0');
             $question->feedback = array();
             $currentchoice = -1;
             $ignore_rest_of_question = FALSE;
             continue;
         }
         if (preg_match("~^:TYPE:M~i", $line)) {
             // Match Question
             $question = $this->defaultquestion();
             $question->qtype = MATCH;
             $question->feedback = array();
             $ignore_rest_of_question = FALSE;
             // match question processing is not debugged
             continue;
         }
         if (preg_match("~^:TYPE:P~i", $line)) {
             // Paragraph Question
             $warnings[] = get_string("paragraphquestion", "quiz", $nLineCounter);
             unset($question);
             $ignore_rest_of_question = TRUE;
             // Question Type not handled by Moodle
             continue;
         }
         if (preg_match("~^:TYPE:~i", $line)) {
             // Unknow Question
             $warnings[] = get_string("unknowntype", "quiz", $nLineCounter);
             unset($question);
             $ignore_rest_of_question = TRUE;
             // Question Type not handled by Moodle
             continue;
         }
         if ($ignore_rest_of_question) {
             continue;
         }
         if (preg_match("~^:TITLE:(.*)~i", $line, $webct_options)) {
             $name = trim($webct_options[1]);
             if (strlen($name) > 255) {
                 $name = substr($name, 0, 250) . "...";
                 $warnings[] = get_string("questionnametoolong", "quiz", $nLineCounter);
             }
             $question->name = $name;
             continue;
         }
         if (preg_match("~^:IMAGE:(.*)~i", $line, $webct_options)) {
             $filename = trim($webct_options[1]);
             if (preg_match("~^http://~i", $filename)) {
                 $question->image = $filename;
             }
             continue;
         }
         // Need to put the parsing of calculated items here to avoid ambitiuosness:
         // if question isn't defined yet there is nothing to do here (avoid notices)
         if (!isset($question)) {
             continue;
         }
         if (isset($question->qtype) && CALCULATED == $question->qtype && preg_match("~^:([[:lower:]].*|::.*)-(MIN|MAX|DEC|VAL([0-9]+))::?:?({$webctnumberregex})~", $line, $webct_options)) {
             $datasetname = preg_replace('/^::/', '', $webct_options[1]);
             $datasetvalue = qformat_webct_convert_formula($webct_options[4]);
             switch ($webct_options[2]) {
                 case 'MIN':
                     $question->dataset[$datasetname]->min = $datasetvalue;
                     break;
                 case 'MAX':
                     $question->dataset[$datasetname]->max = $datasetvalue;
                     break;
                 case 'DEC':
                     $datasetvalue = floor($datasetvalue);
                     // int only!
                     $question->dataset[$datasetname]->length = max(0, $datasetvalue);
                     break;
                 default:
                     // The VAL case:
                     $question->dataset[$datasetname]->datasetitem[$webct_options[3]] = new stdClass();
                     $question->dataset[$datasetname]->datasetitem[$webct_options[3]]->itemnumber = $webct_options[3];
                     $question->dataset[$datasetname]->datasetitem[$webct_options[3]]->value = $datasetvalue;
                     break;
             }
             continue;
         }
         $bIsHTMLText = preg_match("~:H\$~i", $line);
         // True if next lines are coded in HTML
         if (preg_match("~^:QUESTION~i", $line)) {
             $questiontext = "";
             // Start gathering next lines
             continue;
         }
         if (preg_match("~^:ANSWER([0-9]+):([^:]+):([0-9\\.\\-]+):(.*)~i", $line, $webct_options)) {
             /// SHORTANSWER
             $currentchoice = $webct_options[1];
             $answertext = $webct_options[2];
             // Start gathering next lines
             $question->fraction[$currentchoice] = $webct_options[3] / 100;
             continue;
         }
         if (preg_match("~^:ANSWER([0-9]+):([0-9\\.\\-]+)~i", $line, $webct_options)) {
             $answertext = "";
             // Start gathering next lines
             $currentchoice = $webct_options[1];
             $question->fraction[$currentchoice] = $webct_options[2] / 100;
             continue;
         }
         if (preg_match('~^:FORMULA:(.*)~i', $line, $webct_options)) {
             // Answer for a CALCULATED question
             ++$currentchoice;
             $question->answers[$currentchoice] = qformat_webct_convert_formula($webct_options[1]);
             // Default settings:
             $question->fraction[$currentchoice] = 1.0;
             $question->tolerance[$currentchoice] = 0.0;
             $question->tolerancetype[$currentchoice] = 2;
             // nominal (units in webct)
             $question->feedback[$currentchoice] = '';
             $question->correctanswerlength[$currentchoice] = 4;
             $datasetnames = $QTYPES[CALCULATED]->find_dataset_names($webct_options[1]);
             foreach ($datasetnames as $datasetname) {
                 $question->dataset[$datasetname] = new stdClass();
                 $question->dataset[$datasetname]->datasetitem = array();
                 $question->dataset[$datasetname]->name = $datasetname;
                 $question->dataset[$datasetname]->distribution = 'uniform';
                 $question->dataset[$datasetname]->status = 'private';
             }
             continue;
         }
         if (preg_match("~^:L([0-9]+)~i", $line, $webct_options)) {
             $answertext = "";
             // Start gathering next lines
             $currentchoice = $webct_options[1];
             $question->fraction[$currentchoice] = 1;
             continue;
         }
         if (preg_match("~^:R([0-9]+)~i", $line, $webct_options)) {
             $responsetext = "";
             // Start gathering next lines
             $currentchoice = $webct_options[1];
             continue;
         }
         if (preg_match("~^:REASON([0-9]+):?~i", $line, $webct_options)) {
             $feedbacktext = "";
             // Start gathering next lines
             $currentchoice = $webct_options[1];
             continue;
         }
         if (preg_match("~^:FEEDBACK([0-9]+):?~i", $line, $webct_options)) {
             $generalfeedbacktext = "";
             // Start gathering next lines
             $currentchoice = $webct_options[1];
             continue;
         }
         if (preg_match('~^:FEEDBACK:(.*)~i', $line, $webct_options)) {
             $generalfeedbacktext = "";
             // Start gathering next lines
             continue;
         }
         if (preg_match('~^:LAYOUT:(.*)~i', $line, $webct_options)) {
             //    ignore  since layout in question_multichoice  is no more used in moodle
             //    $webct_options[1] contains either vertical or horizontal ;
             continue;
         }
         if (isset($question->qtype) && CALCULATED == $question->qtype && preg_match('~^:ANS-DEC:([1-9][0-9]*)~i', $line, $webct_options)) {
             // We can but hope that this always appear before the ANSTYPE property
             $question->correctanswerlength[$currentchoice] = $webct_options[1];
             continue;
         }
         if (isset($question->qtype) && CALCULATED == $question->qtype && preg_match("~^:TOL:({$webctnumberregex})~i", $line, $webct_options)) {
             // We can but hope that this always appear before the TOL property
             $question->tolerance[$currentchoice] = qformat_webct_convert_formula($webct_options[1]);
             continue;
         }
         if (isset($question->qtype) && CALCULATED == $question->qtype && preg_match('~^:TOLTYPE:percent~i', $line)) {
             // Percentage case is handled as relative in Moodle:
             $question->tolerance[$currentchoice] /= 100;
             $question->tolerancetype[$currentchoice] = 1;
             // Relative
             continue;
         }
         if (preg_match('~^:UNITS:(.+)~i', $line, $webct_options) and $webctunits = trim($webct_options[1])) {
             // This is a guess - I really do not know how different webct units are separated...
             $webctunits = explode(':', $webctunits);
             $unitrec->multiplier = 1.0;
             // Webct does not seem to support this
             foreach ($webctunits as $webctunit) {
                 $unitrec->unit = trim($webctunit);
                 $question->units[] = $unitrec;
             }
             continue;
         }
         if (!empty($question->units) && preg_match('~^:UNITREQ:(.*)~i', $line, $webct_options) && !$webct_options[1]) {
             // There are units but units are not required so add the no unit alternative
             // We can but hope that the UNITS property always appear before this property
             $unitrec->unit = '';
             $unitrec->multiplier = 1.0;
             $question->units[] = $unitrec;
             continue;
         }
         if (!empty($question->units) && preg_match('~^:UNITCASE:~i', $line)) {
             // This could be important but I was not able to figure out how
             // it works so I ignore it for now
             continue;
         }
         if (isset($question->qtype) && CALCULATED == $question->qtype && preg_match('~^:ANSTYPE:dec~i', $line)) {
             $question->correctanswerformat[$currentchoice] = '1';
             continue;
         }
         if (isset($question->qtype) && CALCULATED == $question->qtype && preg_match('~^:ANSTYPE:sig~i', $line)) {
             $question->correctanswerformat[$currentchoice] = '2';
             continue;
         }
     }
     if (sizeof($errors) > 0) {
         echo "<p>" . get_string("errorsdetected", "quiz", sizeof($errors)) . "</p><ul>";
         foreach ($errors as $error) {
             echo "<li>{$error}</li>";
         }
         echo "</ul>";
         unset($questions);
         // no questions imported
     }
     if (sizeof($warnings) > 0) {
         echo "<p>" . get_string("warningsdetected", "quiz", sizeof($warnings)) . "</p><ul>";
         foreach ($warnings as $warning) {
             echo "<li>{$warning}</li>";
         }
         echo "</ul>";
     }
     return $questions;
 }