public function fromXML(SimpleXMLElement $xml)
 {
     $data = array("itemtype" => $this->itemType(), "title" => (string) $xml["title"], "stimulus" => qti_get_stimulus($xml->itemBody));
     // check for a div with the item class name
     $itembodycontainer = null;
     foreach ($xml->itemBody->div as $div) {
         if (!isset($div["class"]) || (string) $div["class"] != "eqiat-mcr") {
             continue;
         }
         // get elements from here
         $itembodycontainer = $div;
         break;
     }
     // if there was none, get elements from itemBody
     if (is_null($itembodycontainer)) {
         $itembodycontainer = $xml->itemBody;
     }
     // there is one choiceInteraction
     if (count($itembodycontainer->choiceInteraction) != 1) {
         return 0;
     }
     // there is one responseDeclaration
     if (count($xml->responseDeclaration) != 1) {
         return 0;
     }
     // check cardinality is as expected
     if ($this->itemType() == "multipleResponse" && (string) $xml->responseDeclaration["cardinality"] != "multiple") {
         return 0;
     }
     if ($this->itemType() == "multipleChoice" && (string) $xml->responseDeclaration["cardinality"] != "single") {
         return 0;
     }
     // multiple choice must have maxchoices 1 and one correct response value
     if ($this->itemType() == "multipleChoice") {
         if (!isset($itembodycontainer->choiceInteraction["maxChoices"]) || (string) $itembodycontainer->choiceInteraction["maxChoices"] != "1") {
             return 0;
         }
         if (!isset($xml->responseDeclaration->correctResponse)) {
             return 0;
         }
         if (count($xml->responseDeclaration->correctResponse->value) != 1) {
             return 0;
         }
     }
     // get shuffle value
     $shuffle = isset($itembodycontainer->choiceInteraction["shuffle"]) && (string) $itembodycontainer->choiceInteraction["shuffle"] == "true";
     if ($shuffle) {
         $data["shuffle"] = "on";
     }
     // there is at least one option
     if (count($itembodycontainer->choiceInteraction->simpleChoice) == 0) {
         return 0;
     }
     // collect options and their identifiers
     $o = 0;
     $options = array();
     foreach ($itembodycontainer->choiceInteraction->simpleChoice as $sc) {
         $options[] = (string) $sc["identifier"];
         $data["option_{$o}_optiontext"] = (string) $sc;
         if ($shuffle && isset($sc["fixed"]) && (string) $sc["fixed"] == "true") {
             $data["option_{$o}_fixed"] = "on";
         }
         $o++;
     }
     // check correct response makes sense; collect correct responses
     $correct = array();
     if (count($xml->responseDeclaration->correctResponse) > 0) {
         foreach ($xml->responseDeclaration->correctResponse->value as $value) {
             $pos = array_search((string) $value, $options);
             if ($pos === false) {
                 return 0;
             }
             $correct[] = $pos;
         }
     }
     if ($this->itemType() == "multipleChoice") {
         $data["correct"] = "option_" . $correct[0];
     } else {
         foreach ($correct as $o) {
             $data["option_{$o}_correct"] = "on";
         }
     }
     // get max and min choices
     if ($this->itemType() == "multipleResponse") {
         $data["maxchoices"] = isset($itembodycontainer->choiceInteraction["maxChoices"]) ? (string) $itembodycontainer->choiceInteraction["maxChoices"] : "0";
         $data["minchoices"] = isset($itembodycontainer->choiceInteraction["minChoices"]) ? (string) $itembodycontainer->choiceInteraction["minChoices"] : "0";
     }
     // get prompt
     $data["prompt"] = (string) $itembodycontainer->choiceInteraction->prompt;
     // not checking this properly at all but if modalFeedback elements exist
     // which look about right, use them for feedback
     foreach ($xml->modalFeedback as $mf) {
         $id = explode("_", (string) $mf["identifier"]);
         if (count($id) == 3 && $id[2] < count($options) && (string) $mf["outcomeIdentifier"] == "FEEDBACK") {
             $data["feedback"] = true;
             if ((string) $mf["showHide"] == "show") {
                 $data["option_{$id[2]}_feedback_chosen"] = xml_remove_wrapper_element($mf->asXML());
             } else {
                 $data["option_{$id[2]}_feedback_unchosen"] = xml_remove_wrapper_element($mf->asXML());
             }
         }
     }
     // get min score
     foreach ($xml->responseProcessing->responseCondition as $rc) {
         if (count($rc->responseIf) != 1) {
             continue;
         }
         if (count($rc->responseIf->lt) != 1) {
             continue;
         }
         $ltchildren = $rc->responseIf->lt->children();
         if ($ltchildren[0]->getName() != "variable" || (string) $ltchildren[0]["identifier"] != "SCORE") {
             continue;
         }
         if ($ltchildren[1]->getName() != "baseValue") {
             continue;
         }
         $val = (string) $ltchildren[1];
         if (count($rc->responseIf->setOutcomeValue) != 1) {
             continue;
         }
         if ((string) $rc->responseIf->setOutcomeValue["identifier"] != "SCORE") {
             continue;
         }
         if (count($rc->responseIf->setOutcomeValue->baseValue) != 1) {
             continue;
         }
         if ((string) $rc->responseIf->setOutcomeValue->baseValue != $val) {
             continue;
         }
         $data["minscore"] = $val;
     }
     // get max score
     foreach ($xml->responseProcessing->responseCondition as $rc) {
         if (count($rc->responseIf) != 1) {
             continue;
         }
         if (count($rc->responseIf->gt) != 1) {
             continue;
         }
         $gtchildren = $rc->responseIf->gt->children();
         if ($gtchildren[0]->getName() != "variable" || (string) $gtchildren[0]["identifier"] != "SCORE") {
             continue;
         }
         if ($gtchildren[1]->getName() != "baseValue") {
             continue;
         }
         $val = (string) $gtchildren[1];
         if (count($rc->responseIf->setOutcomeValue) != 1) {
             continue;
         }
         if ((string) $rc->responseIf->setOutcomeValue["identifier"] != "SCORE") {
             continue;
         }
         if (count($rc->responseIf->setOutcomeValue->baseValue) != 1) {
             continue;
         }
         if ((string) $rc->responseIf->setOutcomeValue->baseValue != $val) {
             continue;
         }
         $data["maxscore"] = $val;
     }
     // check response conditions for incrementing score if a particular
     // response is given
     $data["scoring"] = "exact";
     foreach ($xml->responseProcessing->responseCondition as $rc) {
         if (count($rc->responseIf) != 1) {
             continue;
         }
         if (count($rc->responseIf->member) != 1) {
             continue;
         }
         $mchildren = $rc->responseIf->member->children();
         if ($mchildren[0]->getName() != "baseValue" || (string) $mchildren[0]["baseType"] != "identifier") {
             continue;
         }
         $oid = (string) $mchildren[0];
         if ($mchildren[1]->getName() != "variable" || (string) $mchildren[1]["identifier"] != "RESPONSE") {
             continue;
         }
         if (count($rc->responseIf->setOutcomeValue) != 1) {
             continue;
         }
         if ((string) $rc->responseIf->setOutcomeValue["identifier"] != "SCORE") {
             continue;
         }
         if (count($rc->responseIf->setOutcomeValue->sum) != 1) {
             continue;
         }
         if (count($rc->responseIf->setOutcomeValue->sum->children()) != 2) {
             continue;
         }
         if (count($rc->responseIf->setOutcomeValue->sum->variable) != 1 || (string) $rc->responseIf->setOutcomeValue->sum->variable["identifier"] != "SCORE") {
             continue;
         }
         if (count($rc->responseIf->setOutcomeValue->sum->baseValue) != 1) {
             continue;
         }
         $val = (string) $rc->responseIf->setOutcomeValue->sum->baseValue;
         $pos = array_search((string) $oid, $options);
         if ($pos === false) {
             continue;
         }
         $data["option_{$pos}_score"] = $val;
         $data["scoring"] = "cumulative";
     }
     // see if it is in fact cumulative
     if ($data["scoring"] == "cumulative" && (!isset($data["minscore"]) || $data["minscore"] != 0 || isset($data["maxscore"]))) {
         $data["scoring"] = "custom";
     }
     if ($data["scoring"] == "cumulative") {
         foreach ($options as $o => $option) {
             if (!isset($data["option_{$o}_score"]) || $data["option_{$o}_score"] != 1 && $data["option_{$o}_score"] != -1) {
                 $data["scoring"] = "custom";
                 break;
             }
         }
     }
     // happy with that -- set data property and identifier
     $this->data = $data;
     $this->setQTIID((string) $xml["identifier"]);
     // multiple response with one correct answer is less than ideal
     if ($this->itemType() == "multipleresponse" && count($options) == 1) {
         return 192;
     }
     return 255;
 }
Пример #2
0
 public function fromXML(SimpleXMLElement $xml)
 {
     $data = array("itemtype" => $this->itemType(), "title" => (string) $xml["title"], "stimulus" => qti_get_stimulus($xml->itemBody));
     // check for a div with the item class name
     $itembodycontainer = null;
     foreach ($xml->itemBody->div as $div) {
         if (!isset($div["class"]) || (string) $div["class"] != "eqiat-qm") {
             continue;
         }
         // get elements from here
         $itembodycontainer = $div;
         break;
     }
     // if there was none, get elements from itemBody
     if (is_null($itembodycontainer)) {
         $itembodycontainer = $xml->itemBody;
     }
     // count the choiceInteractions
     $questioncount = count($itembodycontainer->choiceInteraction);
     // no good if there are no questions
     if ($questioncount == 0) {
         return 0;
     }
     // ensure there are the same number of responseDeclarations
     if (count($xml->responseDeclaration) != $questioncount) {
         return 0;
     }
     // ensure there are the same number of responseConditions
     if (count($xml->responseProcessing->responseCondition) != $questioncount) {
         return 0;
     }
     // ensure some stuff for each question
     $q = 0;
     foreach ($itembodycontainer->choiceInteraction as $ci) {
         // candidate can only choose one answer
         if ((string) $ci["maxChoices"] != "1") {
             return 0;
         }
         // there are two possible answers
         if (count($ci->simpleChoice) != 2) {
             return 0;
         }
         // answers are true and false
         $answers = array(null, null);
         foreach ($ci->simpleChoice as $sc) {
             if (strtolower((string) $sc) == "false") {
                 $answers[0] = (string) $sc["identifier"];
             } else {
                 if (strtolower((string) $sc) == "true") {
                     $answers[1] = (string) $sc["identifier"];
                 } else {
                     return 0;
                 }
             }
         }
         // check some responseDeclaration things
         $declarationsfound = 0;
         foreach ($xml->responseDeclaration as $rd) {
             if ((string) $rd["identifier"] != (string) $ci["responseIdentifier"]) {
                 continue;
             }
             $declarationsfound++;
             // has a correct response
             if (!isset($rd->correctResponse)) {
                 return 0;
             }
             // has one correct response
             if (count($rd->correctResponse->value) != 1) {
                 return 0;
             }
             // the correct response is one of the options
             $answer = array_search((string) $rd->correctResponse->value, $answers);
             if ($answer === false) {
                 return 0;
             }
             // add answer to data
             $data["question_{$q}_answer"] = $answer ? true : false;
         }
         // there was a good responseDeclaration for this question
         if ($declarationsfound != 1) {
             return 0;
         }
         // add prompt to data
         $data["question_{$q}_prompt"] = (string) $ci->prompt;
         $q++;
     }
     // happy with that -- set data property and identifier
     $this->data = $data;
     $this->setQTIID((string) $xml["identifier"]);
     // rather strange question matrix if it's only one question
     if ($questioncount == 1) {
         return 127;
     }
     return 255;
 }
Пример #3
0
 public function fromXML(SimpleXMLElement $xml)
 {
     $data = array("itemtype" => $this->itemType(), "title" => (string) $xml["title"], "stimulus" => qti_get_stimulus($xml->itemBody));
     // check for a div with the item class name
     $itembodycontainer = null;
     foreach ($xml->itemBody->div as $div) {
         if (!isset($div["class"]) || (string) $div["class"] != "eqiat-te") {
             continue;
         }
         // get elements from here
         $itembodycontainer = $div;
         break;
     }
     // if there was none, get elements from itemBody
     if (is_null($itembodycontainer)) {
         $itembodycontainer = $xml->itemBody;
     }
     // get the text body and remove it from the tree
     $tb = null;
     foreach ($itembodycontainer->children() as $child) {
         if ($child->getName() == "div" && isset($child["class"]) && (string) $child["class"] == "textentrytextbody") {
             $tb = dom_import_simplexml($child);
             $tb->parentNode->removeChild($tb);
             break;
         }
     }
     if (is_null($tb)) {
         return 0;
     }
     // get text body fragments
     $fragments = self::getTextEntryInteractionsAndText($tb);
     $data["textbody"] = "";
     $gaps = array();
     // fail if there was a problem with a textEntryInteraction (for example
     // it doesn't have a responseDeclaration id associated)
     if ($fragments === false) {
         return 0;
     }
     // go through the textbody fragments, match them to responseDeclarations
     // and collect responses
     foreach ($fragments as $fid => $fragment) {
         if (!preg_match('%^\\[.*\\]$%', $fragment)) {
             $data["textbody"] .= $fragment;
             continue;
         }
         // we have a textEntryInteraction id -- look for a matching
         // responseDeclaration
         $rdi = substr($fragment, 1, -1);
         $rd = null;
         foreach ($xml->responseDeclaration as $d) {
             if ((string) $d["identifier"] == $rdi) {
                 $rd = $d;
                 break;
             }
         }
         if (is_null($rd)) {
             return 0;
         }
         // get all the responses for this gap and their scores
         if (!isset($rd->mapping)) {
             return 0;
         }
         // fail if the default score isn't 0
         if ((string) $rd->mapping["defaultValue"] !== "0") {
             return 0;
         }
         // fail if there are no responses
         if (!isset($rd->mapping->mapEntry)) {
             return 0;
         }
         // collect responses and their scores and find the best response
         $gaps[$fid] = array();
         foreach ($rd->mapping->mapEntry as $me) {
             $gaps[$fid][(string) $me["mapKey"]] = (string) $me["mappedValue"];
         }
         // sort responses by descending score
         arsort($gaps[$fid]);
         // add the plaintext gap to the textbody string
         $data["textbody"] .= "[" . key($gaps[$fid]) . "]";
     }
     // turn HTML paragraphs and line breaks into plaintext newlines
     $data["textbody"] = preg_replace(array('%\\s*</p>\\s*<p>\\s*%', '%\\s*</?p>\\s*%', '%\\s*<br\\s*/?>\\s*%'), array("\n\n", "", "\n"), trim($data["textbody"]));
     // fail if there are no gaps
     if (count($gaps) == 0) {
         return 0;
     }
     // add responses and their scores to data
     $g = 0;
     foreach ($gaps as $responses) {
         $r = 0;
         foreach ($responses as $response => $score) {
             $data["gap_{$g}_response_{$r}"] = $response;
             $data["gap_{$g}_response_{$r}_score"] = $score;
             $r++;
         }
         $g++;
     }
     // happy with that -- set data property and identifier
     $this->data = $data;
     $this->setQTIID((string) $xml["identifier"]);
     return 255;
 }
 public function fromXML(SimpleXMLElement $xml)
 {
     $data = array("itemtype" => $this->itemType(), "title" => (string) $xml["title"], "stimulus" => qti_get_stimulus($xml->itemBody));
     // check for a div with the item class name
     $itembodycontainer = null;
     foreach ($xml->itemBody->div as $div) {
         if (!isset($div["class"]) || (string) $div["class"] != "eqiat-emi") {
             continue;
         }
         // get elements from here
         $itembodycontainer = $div;
         break;
     }
     // if there was none, get elements from itemBody
     if (is_null($itembodycontainer)) {
         $itembodycontainer = $xml->itemBody;
     }
     // count the choiceInteractions
     $questioncount = count($itembodycontainer->choiceInteraction);
     // no good if there are no questions
     if ($questioncount == 0) {
         return 0;
     }
     // ensure there are the same number of responseDeclarations
     if (count($xml->responseDeclaration) != $questioncount) {
         return 0;
     }
     // ensure there are the same number of responseConditions
     if (count($xml->responseProcessing->responseCondition) != $questioncount) {
         return 0;
     }
     // check the stimulus for the options and collect them
     $options = array();
     foreach ($itembodycontainer->ol as $ol) {
         if (!isset($ol["class"]) || (string) $ol["class"] != "emioptions") {
             continue;
         }
         if (count($ol->li) < 2) {
             return 0;
         }
         foreach ($ol->li as $listitem) {
             $options[] = (string) $listitem;
         }
         break;
     }
     if (empty($options)) {
         // check for table for backwards compatibility
         foreach ($itembodycontainer->table as $table) {
             if (!isset($table["class"]) || (string) $table["class"] != "emioptions") {
                 continue;
             }
             if (count($table->tbody) != 1 || count($table->tbody->tr) < 2) {
                 return 0;
             }
             foreach ($table->tbody->tr as $row) {
                 if (count($row->td) != 1) {
                     return 0;
                 }
                 $options[] = (string) $row->td;
             }
             break;
         }
     }
     if (empty($options)) {
         return 0;
     }
     // add options to data
     foreach ($options as $k => $option) {
         $data["option_{$k}_optiontext"] = $option;
     }
     // ensure some stuff for each question
     $q = 0;
     foreach ($itembodycontainer->choiceInteraction as $ci) {
         // questions are multiple response so fail if maxChoices is 1. don't
         // care about minChoices
         if ((string) $ci["maxChoices"] == "1") {
             return 0;
         }
         // there are the right number of choices
         if (count($ci->simpleChoice) != count($options)) {
             return 0;
         }
         // answers are ascending single letters; collect their identifiers
         $i = 0;
         $answers = array();
         foreach ($ci->simpleChoice as $sc) {
             if (strtolower((string) $sc) != chr(ord("a") + $i)) {
                 return 0;
             }
             $answers[] = (string) $sc["identifier"];
             $i++;
         }
         // check some responseDeclaration things
         $declarationsfound = 0;
         foreach ($xml->responseDeclaration as $rd) {
             if ((string) $rd["identifier"] != (string) $ci["responseIdentifier"]) {
                 continue;
             }
             $declarationsfound++;
             if (count($rd->correctResponse)) {
                 // the correct response values are some of the options;
                 // collect them
                 $correct = array();
                 foreach ($rd->correctResponse->value as $value) {
                     $answer = array_search((string) $value, $answers);
                     if ($answer === false) {
                         return 0;
                     }
                     $correct[] = $answer;
                 }
                 // add answers to data
                 foreach ($correct as $o) {
                     $data["question_{$q}_option_{$o}_correct"] = "on";
                 }
             }
             // else an empty response is correct -- nothing to check
         }
         // there was a good responseDeclaration for this question
         if ($declarationsfound != 1) {
             return 0;
         }
         // add prompt to data
         $data["question_{$q}_prompt"] = (string) $ci->prompt;
         $q++;
     }
     // happy with that -- set data property and identifier
     $this->data = $data;
     $this->setQTIID((string) $xml["identifier"]);
     // rather strange extended matching item if it's only one question
     if ($questioncount == 1) {
         return 127;
     }
     return 255;
 }