Example #1
0
 public function parse($xmlString, $validate = true)
 {
     // TODO: Remove this, and move it higher up
     LogService::flush();
     $xmlDocument = new XmlDocument();
     if ($validate === false) {
         LogService::log('QTI pre-validation is turned off, some invalid attributes might be stripped from XML content upon conversion');
     }
     $xmlDocument->loadFromString($xmlString, $validate);
     /** @var AssessmentItem $assessmentItem */
     $assessmentTest = $xmlDocument->getDocumentComponent();
     if (!$assessmentTest instanceof AssessmentTest) {
         throw new MappingException('XML is not a valid <assessmentItem> document');
     }
     // Ignore `testPart` and `assessmentSection`. Grab every item references and merge in array
     $itemReferences = [];
     foreach ($assessmentTest->getComponentsByClassName('assessmentItemRef', true) as $assessmentItemRef) {
         $itemReferences[] = $assessmentItemRef->getIdentifier();
     }
     LogService::log('Support for mapping is very limited. Elements such `testPart`, `assessmentSections`, `seclection`, `rubricBlock`, ' . 'etc are ignored. Please see developer docs for more details');
     $data = new activity_data();
     $data->set_items($itemReferences);
     $activity = new activity($assessmentTest->getIdentifier(), $data);
     // Flush out all the error messages stored in this static class, also ensure they are unique
     $messages = array_values(array_unique(LogService::flush()));
     return [$activity, $messages];
 }
 /**
  * @dataProvider qtiImplementationGuideAssessmentTestFiles
  *
  * @param string $uri The URI describing the file to load.
  */
 public function testLoadFromStringNoSchemaValidate($uri)
 {
     $doc = new XmlDocument('2.1');
     $doc->loadFromString(file_get_contents($uri));
     $this->assertInstanceOf('qtism\\data\\storage\\xml\\XmlDocument', $doc);
     $this->assertInstanceOf('qtism\\data\\AssessmentTest', $doc->getDocumentComponent());
 }
    public function testMappingMcqQuestion()
    {
        $questionJson = $this->getFixtureFileContents('learnosityjsons/item_mcq.json');
        $question = json_decode($questionJson, true);
        list($xmlString, $messages) = Converter::convertLearnosityToQtiItem($question);
        $this->assertNotNull($xmlString);
        $this->assertTrue(StringUtil::startsWith($xmlString, '<?xml version="1.0" encoding="UTF-8"?>
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1"'));
        $document = new XmlDocument();
        $document->loadFromString($xmlString);
        $this->assertNotNull($document);
    }
 /**
  * Item's ResponseProcessing.
  *
  * @param core_kernel_classes_Resource $item The Item you want to apply ResponseProcessing.
  * @throws \RuntimeException If an error occurs while processing responses or transmitting results
  */
 protected function processResponses(core_kernel_classes_Resource $item)
 {
     $jsonPayload = taoQtiCommon_helpers_Utils::readJsonPayload();
     try {
         $qtiXmlFileContent = QtiFile::getQtiFileContent($item);
         $qtiXmlDoc = new XmlDocument();
         $qtiXmlDoc->loadFromString($qtiXmlFileContent);
     } catch (StorageException $e) {
         $msg = "An error occurred while loading QTI-XML file at expected location '{$qtiXmlFilePath}'.";
         common_Logger::e($e->getPrevious() !== null ? $e->getPrevious()->getMessage() : $e->getMessage());
         throw new \RuntimeException($msg, 0, $e);
     }
     $itemSession = new AssessmentItemSession($qtiXmlDoc->getDocumentComponent(), new SessionManager());
     $itemSession->beginItemSession();
     $variables = array();
     $filler = new taoQtiCommon_helpers_PciVariableFiller($qtiXmlDoc->getDocumentComponent());
     // Convert client-side data as QtiSm Runtime Variables.
     foreach ($jsonPayload as $id => $response) {
         try {
             $var = $filler->fill($id, $response);
             // Do not take into account QTI Files at preview time.
             // Simply delete the created file.
             if (taoQtiCommon_helpers_Utils::isQtiFile($var, false) === true) {
                 $fileManager = taoQtiCommon_helpers_Utils::getFileDatatypeManager();
                 $fileManager->delete($var->getValue());
             } else {
                 $variables[] = $var;
             }
         } catch (OutOfRangeException $e) {
             // A variable value could not be converted, ignore it.
             // Developer's note: QTI Pairs with a single identifier (missing second identifier of the pair) are transmitted as an array of length 1,
             // this might cause problem. Such "broken" pairs are simply ignored.
             common_Logger::d("Client-side value for variable '{$id}' is ignored due to data malformation.");
         } catch (OutOfBoundsException $e) {
             // No such identifier found in item.
             common_Logger::d("The variable with identifier '{$id}' is not declared in the item definition.");
         }
     }
     try {
         $itemSession->beginAttempt();
         $itemSession->endAttempt(new State($variables));
         // Return the item session state to the client-side.
         echo json_encode(array('success' => true, 'displayFeedback' => true, 'itemSession' => self::buildOutcomeResponse($itemSession)));
     } catch (AssessmentItemSessionException $e) {
         $msg = "An error occurred while processing the responses.";
         throw new \RuntimeException($msg, 0, $e);
     } catch (taoQtiCommon_helpers_ResultTransmissionException $e) {
         $msg = "An error occurred while transmitting a result to the target Result Server.";
         throw new \RuntimeException($msg, 0, $e);
     }
 }
 protected function convertToAssessmentItem(array $data)
 {
     list($xml, $manifest) = Converter::convertLearnosityToQtiItem($data);
     // Assert the XML string is formed and not empty
     // Also, assert manifest is in form of array, regardless it was empty or not
     $this->assertTrue(is_string($xml) && !empty($xml));
     $this->assertTrue(is_array($manifest));
     $document = new XmlDocument();
     $document->loadFromString($xml);
     /** @var AssessmentItem $assessmentItem */
     $assessmentItem = $document->getDocumentComponent();
     // Basic assert on <assessmentItem> object
     $this->assertNotNull($assessmentItem);
     $this->assertTrue($assessmentItem instanceof AssessmentItem);
     return $assessmentItem;
 }
Example #6
0
 public function parse($xmlString, $validate = true)
 {
     // TODO: Remove this, and move it higher up
     LogService::flush();
     $xmlDocument = new XmlDocument();
     if ($validate === false) {
         LogService::log('QTI pre-validation is turned off, some invalid attributes might be stripped from XML content upon conversion');
     }
     $xmlDocument->loadFromString($xmlString, $validate);
     /** @var AssessmentItem $assessmentItem */
     $assessmentItem = $xmlDocument->getDocumentComponent();
     if (!$assessmentItem instanceof AssessmentItem) {
         throw new MappingException('XML is not a valid <assessmentItem> document');
     }
     return $this->parseWithAssessmentItemComponent($assessmentItem);
 }
Example #7
0
 public static function convertLearnosityToQtiItem(array $data)
 {
     $jsonType = self::guessLearnosityJsonDataType($data);
     // Handle `item` which contains both a single item and one or more questions/features
     if ($jsonType === self::LEARNOSITY_DATA_ITEM) {
         list($xmlString, $messages) = self::convertLearnosityItem($data);
         // Handle if just question
     } else {
         if ($jsonType === self::LEARNOSITY_DATA_QUESTION) {
             list($xmlString, $messages) = self::convertLearnosityQuestion($data);
             // Handle if just question data
         } else {
             if ($jsonType === self::LEARNOSITY_DATA_QUESTION_DATA) {
                 list($xmlString, $messages) = self::convertLearnosityQuestionData($data);
             } else {
                 throw new \Exception('Unknown JSON format');
             }
         }
     }
     // Validate them before proceeding by feeding it back
     try {
         $document = new XmlDocument();
         $document->loadFromString($xmlString);
     } catch (\Exception $e) {
         LogService::log('Unknown error occurred. The QTI XML produced may not be valid');
     }
     return [$xmlString, $messages];
 }
 /**
  * Compile the RubricBlocRefs' contents into a separate rubric block PHP template.
  * 
  * @param AssessmentTest $assessmentTest The AssessmentTest object you want to compile the rubrickBlocks.
  */
 protected function compileRubricBlocks(AssessmentTest $assessmentTest)
 {
     $rubricBlockRefs = $assessmentTest->getComponentsByClassName('rubricBlockRef');
     $testService = taoQtiTest_models_classes_QtiTestService::singleton();
     $sourceDir = $testService->getQtiTestDir($this->getResource());
     foreach ($rubricBlockRefs as $rubricRef) {
         $rubricRefHref = $rubricRef->getHref();
         $cssScoper = $this->getCssScoper();
         $renderingEngine = $this->getRenderingEngine();
         $markupPostRenderer = $this->getMarkupPostRenderer();
         $publicCompiledDocDir = $this->getPublicDirectory();
         $privateCompiledDocDir = $this->getPrivateDirectory();
         // -- loading...
         common_Logger::t("Loading rubricBlock '" . $rubricRefHref . "'...");
         $rubricDoc = new XmlDocument();
         $rubricDoc->loadFromString($this->getPrivateDirectory()->read($rubricRefHref));
         common_Logger::t("rubricBlock '" . $rubricRefHref . "' successfully loaded.");
         // -- rendering...
         common_Logger::t("Rendering rubricBlock '" . $rubricRefHref . "'...");
         $pathinfo = pathinfo($rubricRefHref);
         $renderingFile = $pathinfo['filename'] . '.php';
         $rubric = $rubricDoc->getDocumentComponent();
         $rubricStylesheets = $rubric->getStylesheets();
         $stylesheets = new StylesheetCollection();
         // In any case, include the base QTI Stylesheet.
         $stylesheets->merge($rubricStylesheets);
         $rubric->setStylesheets($stylesheets);
         // -- If the rubricBlock has no id, give it a auto-generated one in order
         // to be sure that CSS rescoping procedure works fine (it needs at least an id
         // to target its scoping).
         if ($rubric->hasId() === false) {
             // Prepend 'tao' to the generated id because the CSS
             // ident token must begin by -|[a-zA-Z]
             $rubric->setId('tao' . uniqid());
         }
         // -- Copy eventual remote resources of the rubricBlock.
         $this->copyRemoteResources($rubric);
         $domRendering = $renderingEngine->render($rubric);
         $mainStringRendering = $markupPostRenderer->render($domRendering);
         // Prepend stylesheets rendering to the main rendering.
         $styleRendering = $renderingEngine->getStylesheets();
         $mainStringRendering = $styleRendering->ownerDocument->saveXML($styleRendering) . $mainStringRendering;
         foreach ($stylesheets as $rubricStylesheet) {
             $relPath = trim($this->getExtraPath(), '/');
             $relPath = (empty($relPath) ? '' : $relPath . DIRECTORY_SEPARATOR) . $rubricStylesheet->getHref();
             $sourceFile = $sourceDir->getFile($relPath);
             if (!$publicCompiledDocDir->has($relPath)) {
                 try {
                     $data = $sourceFile->read();
                     $tmpDir = \tao_helpers_File::createTempDir();
                     $tmpFile = $tmpDir . 'tmp.css';
                     file_put_contents($tmpFile, $data);
                     $scopedCss = $cssScoper->render($tmpFile, $rubric->getId());
                     unlink($tmpFile);
                     rmdir($tmpDir);
                     $publicCompiledDocDir->write($relPath, $scopedCss);
                 } catch (\InvalidArgumentException $e) {
                     common_Logger::e('Unable to copy file into public directory: ' . $relPath);
                 }
             }
         }
         // -- Replace the artificial 'tao://qti-directory' base path with a runtime call to the delivery time base path.
         $mainStringRendering = str_replace(TAOQTITEST_PLACEHOLDER_BASE_URI, '<?php echo $' . TAOQTITEST_BASE_PATH_NAME . '; ?>', $mainStringRendering);
         if (!$privateCompiledDocDir->has($renderingFile)) {
             try {
                 $privateCompiledDocDir->write($renderingFile, $mainStringRendering);
                 common_Logger::t("rubricBlockRef '" . $rubricRefHref . "' successfully rendered.");
             } catch (\InvalidArgumentException $e) {
                 common_Logger::e('Unable to copy file into public directory: ' . $renderingFile);
             }
         }
         // -- Clean up old rubric block and reference the new rubric block template.
         $privateCompiledDocDir->delete($rubricRefHref);
         $rubricRef->setHref('./' . $pathinfo['filename'] . '.php');
     }
 }
 /**
  * Item's ResponseProcessing.
  * 
  * @param core_kernel_file_File $itemPath The Item file resource you want to apply ResponseProcessing.
  * @throws RuntimeException If an error occurs while processing responses or transmitting results
  */
 protected function processResponses(core_kernel_classes_Resource $item)
 {
     $jsonPayload = taoQtiCommon_helpers_Utils::readJsonPayload();
     try {
         $qtiXmlFileContent = QtiFile::getQtiFileContent($item);
         $qtiXmlDoc = new XmlDocument();
         $qtiXmlDoc->loadFromString($qtiXmlFileContent);
     } catch (StorageException $e) {
         $msg = "An error occured while loading QTI-XML file at expected location '{$qtiXmlFilePath}'.";
         throw new \RuntimeException($msg, 0, $e);
     }
     $itemSession = new AssessmentItemSession($qtiXmlDoc->getDocumentComponent(), new SessionManager());
     $itemSession->beginItemSession();
     $variables = array();
     // Convert client-side data as QtiSm Runtime Variables.
     foreach ($jsonPayload as $identifier => $response) {
         $filler = new taoQtiCommon_helpers_PciVariableFiller($qtiXmlDoc->getDocumentComponent());
         try {
             $var = $filler->fill($identifier, $response);
             // Do not take into account QTI File placeholders.
             if (taoQtiCommon_helpers_Utils::isQtiFilePlaceHolder($var) === false) {
                 $variables[] = $var;
             }
         } catch (\OutOfRangeException $e) {
             // A variable value could not be converted, ignore it.
             // Developer's note: QTI Pairs with a single identifier (missing second identifier of the pair) are transmitted as an array of length 1,
             // this might cause problem. Such "broken" pairs are simply ignored.
             common_Logger::d("Client-side value for variable '{$identifier}' is ignored due to data malformation.");
         } catch (\OutOfBoundsException $e) {
             // The response identifier does not match any response declaration.
             common_Logger::d("Uknown item variable declaration '{$identifier}.");
         }
     }
     try {
         $itemSession->beginAttempt();
         $itemSession->endAttempt(new State($variables));
         // Transmit results to the Result Server.
         $this->transmitResults($item, $itemSession);
         // Return the item session state to the client-side.
         echo json_encode(array('success' => true, 'displayFeedback' => true, 'itemSession' => self::buildOutcomeResponse($itemSession), 'feedbacks' => $this->getFeedbacks($itemSession)));
     } catch (AssessmentItemSessionException $e) {
         $msg = "An error occured while processing the responses.";
         throw new \RuntimeException($msg, 0, $e);
     } catch (taoQtiCommon_helpers_ResultTransmissionException $e) {
         $msg = "An error occured while transmitting variable '{$identifier}' to the target Result Server.";
         throw new \RuntimeException($msg, 0, $e);
     }
 }
Example #10
0
 public function testUnknownClassWhileLoadingBecauseOfVersion3()
 {
     $expectedMsg = "'bdo' components are not supported in QTI version '2.0.0'";
     $this->setExpectedException('qtism\\data\\storage\\xml\\XmlStorageException', $expectedMsg, XmlStorageException::VERSION);
     $doc = new XmlDocument('2.0.0');
     $doc->loadFromString('
         <div>
             <bdo dir="rtl">I am reversed!</bdo>            
         </div>');
 }
 /**
  * Get the QTI reprensentation of a test content.
  *
  * @param core_kernel_classes_Resource $test the test to get the content from
  * @param type $validate enable validation
  * @return XmlDocument the QTI representation from the test content
  * @throws taoQtiTest_models_classes_QtiTestServiceException
  */
 public function getDoc(core_kernel_classes_Resource $test)
 {
     $doc = new XmlDocument('2.1');
     $doc->loadFromString($this->getQtiTestFile($test)->read());
     return $doc;
 }