Skip to content

Commit

Permalink
QTI in: Fix handling of object HTML, put feedbackInline into question…
Browse files Browse the repository at this point in the history
… metadata

JIRA: WBL-39
  • Loading branch information
rakeshqdsa authored and ttton committed Jun 24, 2019
1 parent e59e2d4 commit e93ee89
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 44 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ reports
.DS_Store
_vault
data/convert:to:learnosity.log.json
/nbproject/private/
27 changes: 14 additions & 13 deletions src/Commands/ConvertToLearnosityCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

class ConvertToLearnosityCommand extends Command
{

protected function configure()
{
$this
Expand Down Expand Up @@ -109,23 +110,23 @@ protected function execute(InputInterface $input, OutputInterface $output)
]);
} else {

$Convert = new ConvertToLearnosityService($inputPath, $outputPath, $output, $organisationId);
$Convert = ConvertToLearnosityService::initClass($inputPath, $outputPath, $output, $organisationId);

$Convert->useMetadataIdentifier = true;
$Convert->useResourceIdentifier = false;
$Convert->useFileNameAsIdentifier = false;
$Convert->useMetadataIdentifier(true);
$Convert->useResourceIdentifier(false);
$Convert->useFileNameAsIdentifier(false);
if ($itemReferenceSource === 'item') {
$Convert->useMetadataIdentifier = false;
$Convert->useResourceIdentifier = false;
$Convert->useFileNameAsIdentifier = false;
$Convert->useMetadataIdentifier(false);
$Convert->useResourceIdentifier(false);
$Convert->useFileNameAsIdentifier(false);
} elseif ($itemReferenceSource === 'filename') {
$Convert->useMetadataIdentifier = false;
$Convert->useResourceIdentifier = false;
$Convert->useFileNameAsIdentifier = true;
$Convert->useMetadataIdentifier(false);
$Convert->useResourceIdentifier(false);
$Convert->useFileNameAsIdentifier(true);
} elseif ($itemReferenceSource === 'resource') {
$Convert->useMetadataIdentifier = false;
$Convert->useResourceIdentifier = true;
$Convert->useFileNameAsIdentifier = false;
$Convert->useMetadataIdentifier(false);
$Convert->useResourceIdentifier(true);
$Convert->useFileNameAsIdentifier(false);
}

$result = $Convert->process();
Expand Down
79 changes: 77 additions & 2 deletions src/Processors/QtiV2/In/Interactions/ChoiceInteractionMapper.php
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
<?php

namespace LearnosityQti\Processors\QtiV2\In\Interactions;

use LearnosityQti\Entities\QuestionTypes\mcq;
use LearnosityQti\Entities\QuestionTypes\mcq_metadata;
use LearnosityQti\Entities\QuestionTypes\mcq_ui_style;
use LearnosityQti\Services\ConvertToLearnosityService;
use LearnosityQti\Utils\HtmlExtractorUtil;
use LearnosityQti\Utils\QtiMarshallerUtil;
use LearnosityQti\Processors\QtiV2\In\Validation\ChoiceInteractionValidationBuilder;
use LearnosityQti\Services\LogService;
use qtism\data\content\FeedbackInline;
use qtism\data\content\interactions\Orientation;
use qtism\data\content\interactions\Prompt;
use qtism\data\content\interactions\SimpleChoice;
use qtism\data\content\interactions\SimpleChoiceCollection;
use qtism\data\content\interactions\ChoiceInteraction as QtiChoiceInteraction;

class ChoiceInteractionMapper extends AbstractInteractionMapper
{

public function getQuestionType()
{
/* @var QtiChoiceInteraction $interaction */
$interaction = $this->interaction;

$options = $this->buildOptions($interaction->getSimpleChoices());
$feedbackMetadata = $this->buildFeedbackMetadata($interaction->getSimpleChoices());
$mcq = new mcq('mcq', $options);

// Support for @mcq-metadata
foreach ($feedbackMetadata as $value) {
if (!empty($value)) {
$metaData = new mcq_metadata();
$metaData->set_distractor_rationale_response_level($feedbackMetadata);
$mcq->set_metadata($metaData);
}
}

// Support for @shuffle
$mustShuffle = $interaction->mustShuffle();
if ($mustShuffle) {
Expand Down Expand Up @@ -90,4 +103,66 @@ private function buildOptions(SimpleChoiceCollection $simpleChoices)
}
return $options;
}

/**
* This function is used to create distractor_rationale_response_level from feedbackInline
*
* @param SimpleChoiceCollection $simpleChoices
* @return string
*/
private function buildFeedbackMetadata(SimpleChoiceCollection $simpleChoices)
{
/* @var $choice SimpleChoice */
$metadata = [];
foreach ($simpleChoices as $choice) {
$flow = $choice->getContent();
if (property_exists($flow, 'dataPlaceHolder')) {
$class = new \ReflectionClass(get_class($flow));
$property = $class->getProperty('dataPlaceHolder');
$property->setAccessible(true);
$feed = $property->getValue($flow);
$count = 0;
foreach ($feed as $feeddata) {
if ($feeddata instanceof FeedbackInline) {
$count++;
$metadata[] = $this->buildMetadataForFeedbackInline($feeddata);
}
}
if ($count == 0) {
$metadata[] = "";
}
}
}

return $metadata;
}

/**
* This function is used to create feedbackInline data
*
* @param FeedbackInline $feeddata
* @return string
*/
protected function buildMetadataForFeedbackInline(FeedbackInline $feeddata)
{
$metadata = "";
$feeddataArray = array_values((array) $feeddata);
$feedbackArray = array_values((array) $feeddataArray[3]);
if (sizeof($feedbackArray[0]) >= 2) {
$feedInlineArray = array_values((array) $feedbackArray[0][1]);
if (!empty($feedInlineArray) && $feedInlineArray[2] == 'text/html') {
$learnosityServiceObject = ConvertToLearnosityService::getInstance();
$inputPath = $learnosityServiceObject->getInputpath();
$htmlfile = $inputPath . '/' . $feedInlineArray[1];
$metadata = HtmlExtractorUtil::getHtmlData($htmlfile);
}
} else {
$feeddataArray = array_values((array) $feedbackArray[0][0]);
if (!empty($feeddataArray[0])) {
$metadata = trim($feeddataArray[0]);
}
}

return $metadata;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ protected function marshallChildrenKnown(QtiComponent $component, array $element
$fragment->appendXML(QtiMarshallerUtil::marshallCollection(ContentCollectionBuilder::buildFlowCollectionContent($component->getComponents())));
$element = self::getDOMCradle()->createElement('div');
$element->setAttribute('data-type', 'sharedpassage');
$element->appendChild($fragment);
if ($fragment->hasChildNodes()) {
$element->appendChild($fragment);
}
return $element;
break;
default:
Expand Down
102 changes: 78 additions & 24 deletions src/Services/ConvertToLearnosityService.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?php

namespace LearnosityQti\Services;

use LearnosityQti\AppContainer;
Expand All @@ -17,7 +16,6 @@
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use qtism\data\AssessmentItem;
use qtism\data\content\ItemBody;
use qtism\data\storage\xml\XmlDocument;

class ConvertToLearnosityService
Expand All @@ -41,15 +39,18 @@ class ConvertToLearnosityService

/* Job-specific configurations */
// Overrides identifiers to be the same as the filename
public $useFileNameAsIdentifier = false;

protected $useFileNameAsIdentifier = false;
// Uses the identifier found in learning object metadata if available
public $useMetadataIdentifier = true;
protected $useMetadataIdentifier = true;
// Resource identifiers sometimes (but not always) match the assessmentItem identifier, so this can be useful
public $useResourceIdentifier = false;
protected $useResourceIdentifier = false;

private $assetsFixer;
// Hold the class instance.
private static $instance = null;

public function __construct($inputPath, $outputPath, OutputInterface $output, $organisationId)
protected function __construct($inputPath, $outputPath, OutputInterface $output, $organisationId)
{
$this->inputPath = $inputPath;
$this->outputPath = $outputPath;
Expand All @@ -60,6 +61,57 @@ public function __construct($inputPath, $outputPath, OutputInterface $output, $o
$this->rawPath = 'raw';
}

// The object is created from within the class itself
// only if the class has no instance.
public static function initClass($inputPath, $outputPath, OutputInterface $output, $organisationId)
{
if (!self::$instance) {
self::$instance = new ConvertToLearnosityService($inputPath, $outputPath, $output, $organisationId);
}
return self::$instance;
}

// Return instance of the class
public static function getInstance()
{
return self::$instance;
}

public function getInputPath()
{
return $this->inputPath;
}

public function isUsingMetadataIdentifier()
{
return $this->useMetadataIdentifier;
}

public function useMetadataIdentifier($useMetadataIdentifier)
{
$this->useMetadataIdentifier = $useMetadataIdentifier;
}

public function isUsingResourceIdentifier()
{
return $this->useResourceIdentifier;
}

public function useResourceIdentifier($useResourceIdentifier)
{
$this->useResourceIdentifier = $useResourceIdentifier;
}

public function isUsingFileNameAsIdentifier()
{
return $this->useFileNameAsIdentifier;
}

public function useFileNameAsIdentifier($useFileNameAsIdentifier)
{
$this->useFileNameAsIdentifier = $useFileNameAsIdentifier;
}

public function process()
{
$errors = $this->validate();
Expand Down Expand Up @@ -105,12 +157,7 @@ private function parseContentPackage()
foreach ($manifestFolders as $dir) {
$tempDirectoryParts = explode(DIRECTORY_SEPARATOR, dirname($dir));
$dirName = $tempDirectoryParts[count($tempDirectoryParts) - 1];

$results = $this->convertQtiContentPackagesInDirectory(dirname($dir), $dirName);
// if (!isset($results['qtiitems'])) {
// continue;
// }

$this->updateJobManifest($finalManifest, $results);
$this->persistResultsFile($results, realpath($this->outputPath) . DIRECTORY_SEPARATOR . $this->rawPath . DIRECTORY_SEPARATOR . $dirName);
}
Expand Down Expand Up @@ -152,10 +199,10 @@ private function convertQtiContentPackagesInDirectory($sourceDirectory, $relativ
$totalItemCount = 0;
foreach ($manifestFinderPath as $manifestFile) {
/** @var SplFileInfo $manifestFile */
$currentDir = realpath($manifestFile->getPath());
$currentDir = realpath($manifestFile->getPath());
$fullFilePath = realpath($manifestFile->getPathname());
$relativeDir = rtrim($relativeSourceDirectoryPath.'/'.$manifestFile->getRelativePath(), '/');
$relativePath = rtrim($relativeSourceDirectoryPath.'/'.$manifestFile->getRelativePathname(), '/');
$relativeDir = rtrim($relativeSourceDirectoryPath . '/' . $manifestFile->getRelativePath(), '/');
$relativePath = rtrim($relativeSourceDirectoryPath . '/' . $manifestFile->getRelativePathname(), '/');

$this->output->writeln("<info>" . static::INFO_OUTPUT_PREFIX . "Processing manifest file: {$relativePath} </info>");

Expand All @@ -170,10 +217,10 @@ private function convertQtiContentPackagesInDirectory($sourceDirectory, $relativ
foreach ($itemResources as $resource) {
$itemCount++;
$totalItemCount++;
$resourceHref = $resource['href'];
$resourceHref = $resource['href'];
$relatedResource = $resource['resource'];
$assessmentItemContents = file_get_contents($currentDir . '/' . $resourceHref);
$itemReference = $this->getItemReferenceFromResource(
$itemReference = $this->getItemReferenceFromResource(
$relatedResource,
$this->useMetadataIdentifier,
$this->useResourceIdentifier,
Expand All @@ -199,7 +246,7 @@ private function convertQtiContentPackagesInDirectory($sourceDirectory, $relativ
$convertedContent = $this->convertAssessmentItemInFile($assessmentItemContents, $itemReference, $metadata, $currentDir, $resourceHref, $itemTagsArray);

if (!empty($convertedContent)) {
$results['qtiitems'][basename($relativeDir).'/'.$resourceHref] = $convertedContent;
$results['qtiitems'][basename($relativeDir) . '/' . $resourceHref] = $convertedContent;
}
}
}
Expand Down Expand Up @@ -318,10 +365,10 @@ private function getTaxonPathEntryForItemTags(\DOMNode $resource)
* Converts a single <assessmentItem> file.
*
* @param SplFileInfo $file
* @param string $itemReference - Optional
* @param string $itemReference - Optional
*
* @return array - the results of the conversion
*/
*/
private function convertAssessmentItemInFile($contents, $itemReference = null, array $metadata = [], $currentDir, $resourceHref, $itemTagsArray = [])
{
$results = null;
Expand All @@ -335,11 +382,12 @@ private function convertAssessmentItemInFile($contents, $itemReference = null, a
}

$resourcePath = $currentDir . '/' . $resourceHref;
$results = $this->convertAssessmentItem($xmlString, $itemReference, $resourcePath, $metadata, $itemTagsArray);
$results = $this->convertAssessmentItem($xmlString, $itemReference, $resourcePath, $metadata, $itemTagsArray);

} catch (\Exception $e) {
$targetFilename = $resourceHref;
$message = $e->getMessage();
$results = [ 'exception' => $targetFilename . '-' . $message ];
$message = $e->getMessage();
$results = ['exception' => $targetFilename . '-' . $message];
if (!StringHelper::contains($message, 'This is intro or outro')) {
$this->output->writeln(' <error>EXCEPTION with item ' . str_replace($currentDir, '', $resourceHref) . ' : ' . $message . '</error>');
}
Expand Down Expand Up @@ -436,7 +484,7 @@ private function getItemReferenceFromResource(

if ($useFileNameAsIdentifier) {
// This flag should override anything else that is set above
$resourceHref = $resource->getAttribute('href');
$resourceHref = $resource->getAttribute('href');
$itemReference = $this->getIdentifierFromResourceHref($resourceHref);
}
return $itemReference;
Expand Down Expand Up @@ -493,7 +541,7 @@ private function getPointValueFromResource(\DOMNode $resource)
$xpath = $this->getXPathForQtiDocument($resource->ownerDocument);
$pointValueEntries = ($xpath->query('./qti:metadata/lom:lom/lom:classification/lom:taxonPath/lom:source/lom:string[text() = \'cf$Point Value\']/../../lom:taxon/lom:entry', $resource));
if ($pointValueEntries->length > 0) {
$pointValue = (int)$pointValueEntries->item(0)->nodeValue;
$pointValue = (int) $pointValueEntries->item(0)->nodeValue;
}

return $pointValue;
Expand Down Expand Up @@ -647,6 +695,12 @@ private function updateJobManifest(array &$manifest, array $results)

private function tearDown()
{

}

public function showWarnings($message)
{
$this->output->writeln("<info>" . static::INFO_OUTPUT_PREFIX .$message." </info>");
}

private function validate()
Expand Down
Loading

0 comments on commit e93ee89

Please sign in to comment.