Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: export test with related properties/metadata #2452

Closed
4 changes: 3 additions & 1 deletion manifest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

use oat\tao\model\user\TaoRoles;
use oat\taoQtiTest\model\Container\TestQtiServiceProvider;
use oat\taoQtiTest\models\classes\metadata\MetadataServiceProvider;
// phpcs:disable Generic.Files.LineLength
use oat\taoQtiTest\models\classes\render\CustomInteraction\ServiceProvider\CustomInteractionPostProcessingServiceProvider;
// phpcs:enable Generic.Files.LineLength
Expand Down Expand Up @@ -182,6 +183,7 @@
CustomInteractionPostProcessingServiceProvider::class,
ItemsReferencesServiceProvider::class,
TestQtiServiceProvider::class,
TestSessionStateServiceProvider::class
TestSessionStateServiceProvider::class,
MetadataServiceProvider::class
],
];
11 changes: 11 additions & 0 deletions models/classes/export/AbstractQtiTestExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use DOMXPath;
use oat\oatbox\reporting\Report;
use oat\oatbox\reporting\ReportInterface;
use oat\taoQtiTest\models\classes\metadata\GenericLomOntologyExtractor;
use qtism\data\storage\xml\marshalling\MarshallingException;
use qtism\data\storage\xml\XmlDocument;
use oat\oatbox\filesystem\Directory;
Expand Down Expand Up @@ -173,6 +174,11 @@ public function export(array $options = []): Report

// 3. Export test metadata to manifest
$this->getMetadataExporter()->export($this->getItem(), $this->getManifest());
$this->genericLomOntologyExtractor()->extract(
array_merge([$this->getItem()], $this->getItems()),
$this->getManifest()
);


// 4. Persist manifest in archive.
$this->getZip()->addFromString('imsmanifest.xml', $this->getManifest()->saveXML());
Expand Down Expand Up @@ -350,4 +356,9 @@ protected function getServiceManager(): ServiceManager
{
return ServiceManager::getServiceManager();
}

private function genericLomOntologyExtractor(): GenericLomOntologyExtractor
{
return $this->getServiceManager()->getContainer()->get(GenericLomOntologyExtractor::class);
}
}
86 changes: 86 additions & 0 deletions models/classes/metadata/GenericLomOntologyExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoQtiTest\models\classes\metadata;

use core_kernel_classes_Resource as Resource;
use core_kernel_classes_Triple as Triple;
use DOMDocument;
use oat\generis\model\data\Ontology;
use oat\taoQtiItem\model\qti\metadata\MetadataExtractionException;
use oat\taoQtiTest\models\classes\metadata\metaMetadata\PropertyMapper;

class GenericLomOntologyExtractor implements GenericMetadataExtractor
{
private Ontology $ontology;
private PropertyMapper $propertyMapper;
private MetadataLomService $metadataLomService;

public function __construct(
Ontology $ontology,
PropertyMapper $propertyMapper,
MetadataLomService $metadataLomService
) {
$this->ontology = $ontology;
$this->propertyMapper = $propertyMapper;
$this->metadataLomService = $metadataLomService;
}

/**
* @param Resource[] $resourceCollection
* @throws MetadataExtractionException
*/
public function extract(array $resourceCollection, DOMDocument $manifest): void
{
$properties = [];

foreach ($resourceCollection as $resource) {
if (!$resource instanceof Resource) {
throw new MetadataExtractionException(
__('The given target is not an instance of core_kernel_classes_Resource')
);
}

foreach ($resource->getRdfTriples() as $triple) {
if ($this->mappingRequired($properties, $triple)) {
$properties[] = $this->propertyMapper
->getMetadataProperties(
$this->ontology->getProperty($triple->predicate)
);
}
}
}

$this->metadataLomService->addPropertiesToMetadataBlock($properties, $manifest);
}

/**
* Mapping action only applies for confirmed properties that are not already mapped
*/
private function mappingRequired(array $properties, Triple $triple): bool
{
return $this->ontology->getProperty($triple->predicate)->isProperty() &&
array_filter($properties, function ($property) use ($triple) {
return $property['uri'] === $triple->predicate;
}) === [];
}
}
35 changes: 35 additions & 0 deletions models/classes/metadata/GenericMetadataExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoQtiTest\models\classes\metadata;

use core_kernel_classes_Resource;
use DOMDocument;

interface GenericMetadataExtractor
{
/**
* @param core_kernel_classes_Resource[] $resourceCollection
* @param DOMDocument $manifest
*/
public function extract(array $resourceCollection, DOMDocument $manifest);
}
57 changes: 57 additions & 0 deletions models/classes/metadata/MetadataLomService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoQtiTest\models\classes\metadata;

use DOMDocument;
use DOMNodeList;

class MetadataLomService
{
public function addPropertiesToMetadataBlock(array $properties, DOMDocument $manifest): void
{
/** @var DOMNodeList $metadataBlock */
$metadataBlock = $manifest->getElementsByTagName('metadata');

if ($metadataBlock === null) {
$metadataBlock = $manifest->createElement('metadata');
$manifest->documentElement->appendChild($metadataBlock);
}

$metadataBlock->item(0)
->appendChild($manifest->createElement('imsmd:lom'))
->appendChild($manifest->createElement('imsmd:metaMetadata'))
->appendChild($manifest->createElement('extension'))
->appendChild($manifest->createElement('customProperties'));

foreach ($properties as $property) {
$propertyNode = $manifest->createElement('property');
foreach ($property as $key => $value) {
$propertyNode->appendChild($manifest->createElement($key, $value));
}
$metadataBlock->item(0)
->getElementsByTagName('customProperties')
->item(0)
->appendChild($propertyNode);
}
}
}
60 changes: 60 additions & 0 deletions models/classes/metadata/MetadataServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoQtiTest\models\classes\metadata;

use oat\generis\model\data\Ontology;
use oat\generis\model\DependencyInjection\ContainerServiceProviderInterface;
use oat\generis\model\GenerisRdf;
use oat\taoQtiTest\models\classes\metadata\metaMetadata\PropertyMapper;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

class MetadataServiceProvider implements ContainerServiceProviderInterface
{
public function __invoke(ContainerConfigurator $configurator): void
{
$services = $configurator->services();
$services->set(MetadataLomService::class, MetadataLomService::class);

$services->set(PropertyMapper::class, PropertyMapper::class)
->args([
service(Ontology::SERVICE_ID),
[
'label' => RDFS_LABEL,
'domain' => RDFS_DOMAIN,
'alias' => GenerisRdf::PROPERTY_ALIAS,
'multiple' => GenerisRdf::PROPERTY_MULTIPLE
]
]);

$services
->set(GenericLomOntologyExtractor::class, GenericMetadataExtractor::class)
->public()
->args([
service(Ontology::SERVICE_ID),
service(PropertyMapper::class),
service(MetadataLomService::class)
]);
}
}
97 changes: 97 additions & 0 deletions models/classes/metadata/metaMetadata/PropertyMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2024 (original work) Open Assessment Technologies SA;
*/

declare(strict_types=1);

namespace oat\taoQtiTest\models\classes\metadata\metaMetadata;

use core_kernel_classes_Property as Property;
use core_kernel_classes_Resource as Resource;
use oat\generis\model\data\Ontology;
use oat\generis\model\OntologyRdf;
use taoTests_models_classes_TestsService;

class PropertyMapper
{
public const DATATYPE_CHECKSUM = 'checksum';

private array $metaMetadataCollectionToExport;
private Ontology $ontology;

public function __construct(Ontology $ontology, array $metaMetadataCollectionToExport)
{
$this->metaMetadataCollectionToExport = $metaMetadataCollectionToExport;
$this->ontology = $ontology;
}

public function getMetadataProperties(Property $property): array
{
$fields = [];

foreach ($this->metaMetadataCollectionToExport as $key => $stringProperty) {
$fields['uri'] = $property->getUri();
$metaProperty = $property->getOnePropertyValue(new Property($stringProperty));
if ($metaProperty !== null) {
$fields[$key] = $metaProperty instanceof Resource
? $metaProperty->getUri()
: (string) $metaProperty;
}
}

if (!$this->isIgnoredForCollectionGathering($property)) {
$fields[self::DATATYPE_CHECKSUM] = $this->getRangeChecksum($property);
}

return $fields;
}

private function getRangeChecksum(Property $property): string
{
$checksum = '';

$listValues = array_filter($property->getRange()->getNestedResources(), function ($range) {
return $range['isclass'] === 0;
});

if (empty($listValues)) {
return '';
}

foreach ($listValues as $value) {
$checksum .= $this->ontology->getResource($value['id'])->getLabel();
}

return sha1($checksum);
}

private function isIgnoredForCollectionGathering(Property $property): bool
{
return in_array($property->getUri(), $this->getIgnoredProperties());
}

private function getIgnoredProperties(): array
{
return [
OntologyRdf::RDF_TYPE,
taoTests_models_classes_TestsService::PROPERTY_TEST_TESTMODEL,
RDFS_LABEL
];
}
}
Loading
Loading