Skip to content

Commit

Permalink
Added capability to resolve response processing template locations
Browse files Browse the repository at this point in the history
  • Loading branch information
= committed Jan 18, 2016
1 parent 150dd39 commit 50cf68c
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/qtism/data/storage/xml/XmlCompactDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,11 @@ protected static function resolveAssessmentItemRef(ExtendedAssessmentItemRef $co

$doc = new XmlDocument();
$doc->load($href);

// Resolve external documents.
$doc->xInclude();
$doc->resolveTemplateLocation();

$item = $doc->getDocumentComponent();

foreach ($item->getResponseDeclarations() as $resp) {
Expand Down
49 changes: 47 additions & 2 deletions src/qtism/data/storage/xml/XmlDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
use qtism\data\QtiComponentCollection;
use qtism\data\QtiComponentIterator;
use qtism\data\QtiDocument;
use qtism\data\AssessmentItem;
use qtism\data\processing\ResponseProcessing;
use qtism\data\storage\xml\marshalling\Qti20MarshallerFactory;
use qtism\data\storage\xml\marshalling\Qti21MarshallerFactory;
use qtism\data\storage\xml\marshalling\Qti211MarshallerFactory;
Expand Down Expand Up @@ -365,13 +367,13 @@ public function schemaValidate($filename = '')
* the include components can be resolved by calling this method. Files will
* be included following the rules described by the XInclude specification.
*
* @param boolean $validate Whether or not validate files being included.
* @param boolean $validate Whether or not validate files being included. Default is false.
* @throws \LogicException If the method is called prior the load or loadFromString method was called.
* @throws \qtism\data\storage\xml\XmlStorageException If an error occured while parsing or validating files to be included.
*/
public function xInclude($validate = false) {

if (($root = $this->getDocumentComponent()) !== false) {
if (($root = $this->getDocumentComponent()) !== null) {

$baseUri = str_replace('\\', '/', $this->getDomDocument()->documentElement->baseURI);
$pathinfo = pathinfo($baseUri);
Expand Down Expand Up @@ -413,6 +415,49 @@ public function xInclude($validate = false) {
throw new LogicException($msg);
}
}

/**
* Resolve responseProcessing elements with template location.
*
* If the root element of the currently loaded QTI file is an assessmentItem element,
* this method will try to resolve responseProcessing fragments referenced by responseProcessing
* elements having a templateLocation attribute.
*
* @param boolean $validate Whether or not validate files being included. Default is false.
* @throws \LogicException If the method is called prior the load or loadFromString method was called.
* @throws \qtism\data\storage\xml\XmlStorageException If an error occured while parsing or validating files to be included.
*/
public function resolveTemplateLocation($validate = false)
{
if (($root = $this->getDocumentComponent()) !== null) {

if ($root instanceof AssessmentItem && ($responseProcessing = $root->getResponseProcessing()) !== null && ($templateLocation = $responseProcessing->getTemplateLocation()) !== '') {

if (Url::isRelative($templateLocation) === true) {

$baseUri = str_replace('\\', '/', $this->getDomDocument()->documentElement->baseURI);
$pathinfo = pathinfo($baseUri);
$basePath = $pathinfo['dirname'];
$templateLocation = Url::rtrim($basePath) . '/' . Url::ltrim($templateLocation);

$doc = new XmlDocument();
$doc->load($templateLocation, $validate);

$newResponseProcessing = $doc->getDocumentComponent();

if ($newResponseProcessing instanceof ResponseProcessing) {
$root->setResponseProcessing($newResponseProcessing);
} else {
$msg = "The template at location '${templateLocation}' is not a document containing a QTI responseProcessing element.";
throw new XmlStorageException($msg, XmlStorageException::RESOLUTION);
}
}
}
} else {
$msg = "Cannot resolve template location loading any file.";
throw new LogicException($msg);
}
}

/**
* Decorate the root element of the XmlAssessmentDocument with the appropriate
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
namespace qtismtest\data\storage\xml;

use qtismtest\QtiSmTestCase;
use qtism\data\storage\xml\XmlDocument;
use qtism\data\storage\xml\XmlStorageException;

class XmlDocumentTemplateLocationTest extends QtiSmTestCase {

public function testCorrectlyFormed() {
$doc = new XmlDocument();
$doc->load(self::samplesDir() . 'custom/items/template_location/template_location_item.xml', true);

$responseProcessings = $doc->getDocumentComponent()->getComponentsByClassName('responseProcessing');
$this->assertEquals(1, count($responseProcessings));
$this->assertEquals('template_location_rp.xml', $responseProcessings[0]->getTemplateLocation());

$doc->resolveTemplateLocation(true);

$responseProcessings = $doc->getDocumentComponent()->getComponentsByClassName('responseProcessing');
$this->assertEquals(1, count($responseProcessings));
$this->assertEquals('http://www.imsglobal.org/question/qti_v2p1/rptemplates/match_correct', $responseProcessings[0]->getTemplate());
}

public function testNotLoaded() {
$doc = new XmlDocument();

$this->setExpectedException('\\LogicException', 'Cannot resolve template location loading any file.');
$doc->resolveTemplateLocation();
}

public function testWrongTarget() {
$doc = new XmlDocument();
$doc->load(self::samplesDir() . 'custom/items/template_location/template_location_item_wrong_target.xml', true);

$this->setExpectedException('qtism\\data\\storage\\xml\\XmlStorageException');
$doc->resolveTemplateLocation();
}

public function testInvalidTargetNoValidation() {
$doc = new XmlDocument();
$doc->load(self::samplesDir() . 'custom/items/template_location/template_location_item_invalid_target.xml', true);

$this->setExpectedException('qtism\\data\\storage\\xml\\XmlStorageException', "'responseProcessingZ' components are not supported in QTI version '2.1.0'.", XmlStorageException::VERSION);
$doc->resolveTemplateLocation();
}

public function testInvalidTargetValidation() {
$doc = new XmlDocument();
$doc->load(self::samplesDir() . 'custom/items/template_location/template_location_item_invalid_target.xml', true);

$this->setExpectedException('qtism\\data\\storage\\xml\\XmlStorageException', null, XmlStorageException::XSD_VALIDATION);
$doc->resolveTemplateLocation(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/imsqti_v2p1.xsd"
identifier="telephone" title="Inventor of Telephone" adaptive="false" timeDependent="false">
<responseDeclaration identifier="RESPONSE" cardinality="single" baseType="identifier">
<correctResponse>
<value>ChoiceA</value>
</correctResponse>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float">
<defaultValue>
<value>0</value>
</defaultValue>
</outcomeDeclaration>
<itemBody>
<choiceInteraction responseIdentifier="RESPONSE" shuffle="false" maxChoices="1">
<prompt>Who is the inventor of the telephone?</prompt>
<simpleChoice identifier="ChoiceA">Alexander Graham Bell</simpleChoice>
<simpleChoice identifier="ChoiceB">Henri Owen Tudor</simpleChoice>
<simpleChoice identifier="ChoiceC">Bill Gates</simpleChoice>
</choiceInteraction>
</itemBody>
<responseProcessing templateLocation="template_location_rp.xml"/>
</assessmentItem>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/imsqti_v2p1.xsd"
identifier="telephone" title="Inventor of Telephone" adaptive="false" timeDependent="false">
<responseDeclaration identifier="RESPONSE" cardinality="single" baseType="identifier">
<correctResponse>
<value>ChoiceA</value>
</correctResponse>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float">
<defaultValue>
<value>0</value>
</defaultValue>
</outcomeDeclaration>
<itemBody>
<choiceInteraction responseIdentifier="RESPONSE" shuffle="false" maxChoices="1">
<prompt>Who is the inventor of the telephone?</prompt>
<simpleChoice identifier="ChoiceA">Alexander Graham Bell</simpleChoice>
<simpleChoice identifier="ChoiceB">Henri Owen Tudor</simpleChoice>
<simpleChoice identifier="ChoiceC">Bill Gates</simpleChoice>
</choiceInteraction>
</itemBody>
<responseProcessing templateLocation="template_location_rp_invalid.xml"/>
</assessmentItem>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/imsqti_v2p1.xsd"
identifier="telephone" title="Inventor of Telephone" adaptive="false" timeDependent="false">
<responseDeclaration identifier="RESPONSE" cardinality="single" baseType="identifier">
<correctResponse>
<value>ChoiceA</value>
</correctResponse>
</responseDeclaration>
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float">
<defaultValue>
<value>0</value>
</defaultValue>
</outcomeDeclaration>
<itemBody>
<choiceInteraction responseIdentifier="RESPONSE" shuffle="false" maxChoices="1">
<prompt>Who is the inventor of the telephone?</prompt>
<simpleChoice identifier="ChoiceA">Alexander Graham Bell</simpleChoice>
<simpleChoice identifier="ChoiceB">Henri Owen Tudor</simpleChoice>
<simpleChoice identifier="ChoiceC">Bill Gates</simpleChoice>
</choiceInteraction>
</itemBody>
<!-- templateLocation refers to an item and will then not be included. -->
<responseProcessing templateLocation="infocontrol.xml"/>
</assessmentItem>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<responseProcessing xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/imsqti_v2p1.xsd" template="http://www.imsglobal.org/question/qti_v2p1/rptemplates/match_correct"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Invalid QTI 2.1 File. -->
<responseProcessingZ xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/imsqti_v2p1.xsd" template="http://www.imsglobal.org/question/qti_v2p1/rptemplates/match_correct"/>

0 comments on commit 50cf68c

Please sign in to comment.