Skip to content

Commit

Permalink
Merge pull request #33 from Learnosity/Learnosity/flexible-item-ident…
Browse files Browse the repository at this point in the history
…ifier

Fix various PHP 7, qti:to:learnosity issues; support different item reference sources
  • Loading branch information
ttton authored Apr 24, 2019
2 parents 3b161ec + a96f3af commit 3c4fa70
Show file tree
Hide file tree
Showing 22 changed files with 247 additions and 94 deletions.
3 changes: 3 additions & 0 deletions data/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
input/*
output/*
!.gitignore
2 changes: 0 additions & 2 deletions data/input/.gitignore

This file was deleted.

2 changes: 0 additions & 2 deletions data/output/.gitignore

This file was deleted.

40 changes: 40 additions & 0 deletions src/Commands/ConvertToLearnosityCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ protected function configure()
'The identifier of the item bank you want to import content into',
''
)
->addOption(
'item-reference-source',
'',
InputOption::VALUE_OPTIONAL,
'The source to use to extract the reference for the item. ' .
'Valid values are the following: ' . PHP_EOL .
' item - uses the identifier attribute on the <assessmentItem> element' . PHP_EOL .
' metadata - uses the <identifier> element from the LOM metadata in the manifest, if available. If no <identifier> is found, then this parameter operates in "item" mode' . PHP_EOL .
' resource - uses the identifier attribute on the <resource> element in the manifest' . PHP_EOL .
' filename - uses the basename of the <assessmentItem> XML file' . PHP_EOL,
'metadata'
)
;
}

Expand All @@ -47,6 +59,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$inputPath = $input->getOption('input');
$outputPath = $input->getOption('output');
$organisationId = $input->getOption('organisation_id');
$itemReferenceSource = $input->getOption('item-reference-source');

// Validate the required options
if (empty($inputPath) || empty($outputPath)) {
Expand All @@ -73,6 +86,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
array_push($validationErrors, "The <info>organisation_id</info> option is required for asset uploads.");
}

$validItemReferenceSources = ['item', 'metadata', 'filename', 'resource'];
if (isset($itemReferenceSource) && !in_array($itemReferenceSource, $validItemReferenceSources)) {
array_push(
$validationErrors,
"The <info>item-reference-source</info> must be one of the following values: " . join(', ', $validItemReferenceSources)
);
}

if (!empty($validationErrors)) {
$output->writeln([
'',
Expand All @@ -87,7 +108,26 @@ protected function execute(InputInterface $input, OutputInterface $output)
" <info>mo convert:to:learnosity --input /path/to/qti --output /path/to/save/folder --organisation_id [integer]</info>"
]);
} else {

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

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

$result = $Convert->process();
if ($result['status'] === false) {
$output->writeln('<error>Error running job</error>');
Expand Down
5 changes: 4 additions & 1 deletion src/Converter.php
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ public static function convertQtiItemToLearnosity($xmlString, $baseAssetsUrl = '
$sourceDirectoryPath = dirname($filePath);
}
// TODO: Handle additional (related) items being passed back
$result = $itemMapper->parse($xmlString, $validate, $sourceDirectoryPath, $metadata);
$result = $itemMapper->parse($xmlString, $validate, $sourceDirectoryPath, $metadata, $customItemReference);
$item = $result['item'];
$questions = $result['questions'];
$features = $result['features'];
Expand Down Expand Up @@ -247,6 +247,9 @@ public static function convertQtiItemToLearnosity($xmlString, $baseAssetsUrl = '
}
}

$layoutService = new \LearnosityQti\Services\ItemLayoutService();
$itemData = $layoutService->migrateItem($itemData, array_merge($questionsData, $featuresData));

return [
'item' => $itemData,
'questions' => $questionsData,
Expand Down
28 changes: 24 additions & 4 deletions src/Entities/QuestionTypes/imageclozeassociation.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,22 @@ class imageclozeassociation extends BaseQuestionType {
protected $response_positions;
protected $aria_labels;
protected $group_possible_responses;
protected $possible_responses;
protected $img_src;
protected $duplicate_responses;
protected $shuffle_options;

public function __construct(
$type,
imageclozeassociation_image $image,
array $response_positions
array $response_positions,
array $possible_responses
)
{
$this->type = $type;
$this->image = $image;
$this->response_positions = $response_positions;
$this->possible_responses = $possible_responses;
}

/**
Expand Down Expand Up @@ -76,6 +79,24 @@ public function set_metadata (imageclozeassociation_metadata $metadata) {
$this->metadata = $metadata;
}

/**
* Get Possible Responses \
* The question possible_responses. Can include text, tables, images. \
* @return string $possible_responses \
*/
public function get_possible_responses() {
return $this->possible_responses;
}

/**
* Set Possible Responses \
* The question possible_responses. Can include text, tables, images. \
* @param string $possible_responses \
*/
public function set_possible_responses (array $possible_responses) {
$this->possible_responses = $possible_responses;
}

/**
* Get Stimulus \
* The question stimulus. Can include text, tables, images. \
Expand Down Expand Up @@ -396,9 +417,8 @@ public function set_shuffle_options ($shuffle_options) {
$this->shuffle_options = $shuffle_options;
}


public function get_widget_type() {
return 'response';
}
}

16 changes: 8 additions & 8 deletions src/Processors/IMSCP/In/ManifestMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use LearnosityQti\Exceptions\MappingException;
use LearnosityQti\Processors\IMSCP\Entities\Manifest;
use qtism\data\storage\xml\marshalling\Marshaller;
use qtism\data\storage\xml\Utils as XmlUtils;

class ManifestMapper
{
Expand All @@ -23,34 +23,34 @@ public function parseManifestElement(\DOMElement $rootElement)

// Manifest mapping start!
$manifest = new Manifest();
$manifest->setIdentifier(Marshaller::getDOMElementAttributeAs($rootElement, 'identifier'));
$manifest->setIdentifier(XmlUtils::getDOMElementAttributeAs($rootElement, 'identifier'));

// Mapping <resource>(s) to Resource model
$resourcesElement = Marshaller::getChildElementsByTagName($rootElement, 'resources');
$resourcesElement = XmlUtils::getChildElementsByTagName($rootElement, 'resources');
if (!empty($resourcesElement)) {
if (count($resourcesElement) !== 1) {
throw new MappingException('Resources tag must occur once');
}
$resourceMapper = new ResourcesMapper();
$resourcesListElements = Marshaller::getChildElementsByTagName($resourcesElement[0], 'resource');
$resourcesListElements = XmlUtils::getChildElementsByTagName($resourcesElement[0], 'resource');
$resources = $resourceMapper->map($resourcesListElements);
$manifest->setResources($resources);
}

// Mapping <organisation>(s) to Organisation model
$organizationElements = Marshaller::getChildElementsByTagName($rootElement, 'organizations');
$organizationElements = XmlUtils::getChildElementsByTagName($rootElement, 'organizations');
if (!empty($organizationElements)) {
if (count($organizationElements) !== 1) {
throw new MappingException('Organisations tag must occur once');
}
$organisationsMapper = new OrganizationsMapper();
$organisationListElements = Marshaller::getChildElementsByTagName($organizationElements[0], 'organization');
$organisationListElements = XmlUtils::getChildElementsByTagName($organizationElements[0], 'organization');
$organisations = $organisationsMapper->map($organisationListElements);
$manifest->setOrganizations($organisations);
}

// Mapping package Metadata
$metadataElement = Marshaller::getChildElementsByTagName($rootElement, 'metadata');
$metadataElement = XmlUtils::getChildElementsByTagName($rootElement, 'metadata');
if (!empty($metadataElement)) {
if (count($metadataElement) !== 1) {
throw new MappingException('Metadata tag must occur once');
Expand All @@ -60,7 +60,7 @@ public function parseManifestElement(\DOMElement $rootElement)
}

// Mapping sub-manifest
$subManifestElement = Marshaller::getChildElementsByTagName($rootElement, 'manifest');
$subManifestElement = XmlUtils::getChildElementsByTagName($rootElement, 'manifest');
if (!empty($subManifestElement)) {
if (count($subManifestElement) !== 1) {
throw new MappingException('Manifest tag must occur once');
Expand Down
18 changes: 9 additions & 9 deletions src/Processors/IMSCP/In/OrganizationsMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use LearnosityQti\Processors\IMSCP\Entities\Item;
use LearnosityQti\Processors\IMSCP\Entities\Organization;
use qtism\data\storage\xml\marshalling\Marshaller;
use qtism\data\storage\xml\Utils as XmlUtils;

class OrganizationsMapper
{
Expand All @@ -13,21 +13,21 @@ public function map(array $organisationElements)
$organisations = [];
foreach ($organisationElements as $organisationElement) {
$organisation = new Organization();
$organisation->setIdentifier(Marshaller::getDOMElementAttributeAs($organisationElement, 'identifier'));
$organisation->setStructure(Marshaller::getDOMElementAttributeAs($organisationElement, 'structure'));
$organisation->setIdentifier(XmlUtils::getDOMElementAttributeAs($organisationElement, 'identifier'));
$organisation->setStructure(XmlUtils::getDOMElementAttributeAs($organisationElement, 'structure'));

$titleElements = Marshaller::getChildElementsByTagName($organisationElement, 'title');
$titleElements = XmlUtils::getChildElementsByTagName($organisationElement, 'title');
if (!empty($titleElements)) {
$organisation->setTitle($titleElements[0]->nodeValue);
}
$itemElements = Marshaller::getChildElementsByTagName($organisationElement, 'item');
$itemElements = XmlUtils::getChildElementsByTagName($organisationElement, 'item');
$items = [];
foreach ($itemElements as $itemElement) {
$item = new Item();
$item->setTitle(Marshaller::getChildElementsByTagName($itemElement, 'title'));
$item->setIdentifier(Marshaller::getDOMElementAttributeAs($itemElement, 'identifier'));
$item->setIdentifierref(Marshaller::getDOMElementAttributeAs($itemElement, 'identifierref'));
$item->setIsvisible(Marshaller::getDOMElementAttributeAs($itemElement, 'isvisible'));
$item->setTitle(XmlUtils::getChildElementsByTagName($itemElement, 'title'));
$item->setIdentifier(XmlUtils::getDOMElementAttributeAs($itemElement, 'identifier'));
$item->setIdentifierref(XmlUtils::getDOMElementAttributeAs($itemElement, 'identifierref'));
$item->setIsvisible(XmlUtils::getDOMElementAttributeAs($itemElement, 'isvisible'));
$items[] = $item;
}
$organisation->setItems($items);
Expand Down
18 changes: 9 additions & 9 deletions src/Processors/IMSCP/In/ResourcesMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use LearnosityQti\Processors\IMSCP\Entities\Dependency;
use LearnosityQti\Processors\IMSCP\Entities\File;
use LearnosityQti\Processors\IMSCP\Entities\Resource;
use qtism\data\storage\xml\marshalling\Marshaller;
use qtism\data\storage\xml\Utils as XmlUtils;

class ResourcesMapper
{
Expand All @@ -20,20 +20,20 @@ public function map(array $resourcesListElements)
$resources = [];
foreach ($resourcesListElements as $resourceElement) {
$resource = new Resource();
$resource->setHref(Marshaller::getDOMElementAttributeAs($resourceElement, 'href'));
$resource->setIdentifier(Marshaller::getDOMElementAttributeAs($resourceElement, 'identifier'));
$resource->setType(Marshaller::getDOMElementAttributeAs($resourceElement, 'type'));
$resource->setHref(XmlUtils::getDOMElementAttributeAs($resourceElement, 'href'));
$resource->setIdentifier(XmlUtils::getDOMElementAttributeAs($resourceElement, 'identifier'));
$resource->setType(XmlUtils::getDOMElementAttributeAs($resourceElement, 'type'));

// Mapping to File models
$fileListElements = Marshaller::getChildElementsByTagName($resourceElement, 'file');
$fileListElements = XmlUtils::getChildElementsByTagName($resourceElement, 'file');
$resource->setFiles($this->mapFileElements($fileListElements));

// Mapping to Dependency models
$dependencyListElements = Marshaller::getChildElementsByTagName($resourceElement, 'dependency');
$dependencyListElements = XmlUtils::getChildElementsByTagName($resourceElement, 'dependency');
$resource->setDependencies($this->mapDependencyElements($dependencyListElements));

// Mapping its metadata
$metadataListElements = Marshaller::getChildElementsByTagName($resourceElement, 'metadata');
$metadataListElements = XmlUtils::getChildElementsByTagName($resourceElement, 'metadata');
if (!empty($metadataListElements)) {
$metadataMapper = new MetadataMapper();
$flattenedMetadatas = $metadataMapper->map($metadataListElements[0]);
Expand All @@ -58,7 +58,7 @@ public function mapFileElements(array $fileListElements)
if (is_array($fileListElements)) {
foreach ($fileListElements as $fileElement) {
$file = new File();
$file->setHref(Marshaller::getDOMElementAttributeAs($fileElement, 'href'));
$file->setHref(XmlUtils::getDOMElementAttributeAs($fileElement, 'href'));
$files[] = $file;
}
}
Expand All @@ -78,7 +78,7 @@ public function mapDependencyElements(array $dependencyListElements)
foreach ($dependencyListElements as $dependencyElement) {
$dependency = new Dependency();
$dependency->setIdentifierref(
Marshaller::getDOMElementAttributeAs($dependencyElement, 'identifierref')
XmlUtils::getDOMElementAttributeAs($dependencyElement, 'identifierref')
);
$dependencies[] = $dependency;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function getQuestionType()
{
/** @var QtiGraphicGapMatchInteraction $interaction */
$interaction = $this->interaction;
/** @var Object $imageObject */
/** @var ObjectElement $imageObject */
$imageObject = $interaction->getObject();

// Yes, width and height is necessary unfortunately
Expand Down Expand Up @@ -48,9 +48,9 @@ public function getQuestionType()
}

$question = new imageclozeassociation(
'imageclozeassociation',
$image,
$responsePosition,
'imageclozeassociation',
array_values($possibleResponseMapping)
);
$question->set_response_containers($responseContainers);
Expand Down Expand Up @@ -84,7 +84,7 @@ protected function buildPossibleResponseMapping(QtiGraphicGapMatchInteraction $i
return $possibleResponseMapping;
}

protected function buildTemplate(QtiGraphicGapMatchInteraction $interaction, Object $object)
protected function buildTemplate(QtiGraphicGapMatchInteraction $interaction, ObjectElement $object)
{
$associableHotspots = [];
foreach ($interaction->getAssociableHotspots() as $associableHotspot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function getQuestionType()
return $hotspot;
}

private function buildHotspotImage(Object $imageObject)
private function buildHotspotImage(ObjectElement $imageObject)
{
$image = new hotspot_image();
$image->set_source($imageObject->getData());
Expand All @@ -95,7 +95,7 @@ private function buildHotspotImage(Object $imageObject)
return $image;
}

private function buildAreas(HotspotChoiceCollection $hotspotChoices, Object $imageObject)
private function buildAreas(HotspotChoiceCollection $hotspotChoices, ObjectElement $imageObject)
{
/* @var $choice HotspotChoice */
$areas = [];
Expand All @@ -114,7 +114,7 @@ private function buildAreas(HotspotChoiceCollection $hotspotChoices, Object $ima
return $areas;
}

private function transformCoordinates(QtiCoords $coords, $shape, Object $imageObject)
private function transformCoordinates(QtiCoords $coords, $shape, ObjectElement $imageObject)
{
$width = $imageObject->getWidth();
$height = $imageObject->getHeight();
Expand Down
Loading

0 comments on commit 3c4fa70

Please sign in to comment.