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

feature: Export QTI3 Test Package #2566

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config/default/TestModel.conf.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* 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) 2016-2017 (original work) Open Assessment Technologies SA;
* Copyright (c) 2016-2024 (original work) Open Assessment Technologies SA;
*/

use oat\taoQtiTest\models\compilation\CompilationService;
Expand All @@ -26,6 +26,7 @@
new oat\taoQtiTest\models\export\Formats\Metadata\TestPackageExport(),
new oat\taoQtiTest\models\export\Formats\Package2p1\TestPackageExport(),
new oat\taoQtiTest\models\export\Formats\Package2p2\TestPackageExport(),
new oat\taoQtiTest\models\export\Formats\Package3p0\TestPackageExport(),
],
'importHandlers' => [
new taoQtiTest_models_classes_import_TestImport()
Expand Down
66 changes: 66 additions & 0 deletions migrations/Version202501141150192260_taoQtiTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace oat\taoQtiTest\migrations;

use Doctrine\DBAL\Schema\Schema;
use oat\tao\scripts\tools\migrations\AbstractMigration;
use oat\taoQtiTest\models\TestModelService;
use oat\taoQtiTest\models\export\Formats\Package3p0\TestPackageExport;

/**
* Auto-generated Migration: Please modify to your needs!
*
* phpcs:disable Squiz.Classes.ValidClassName
*/
final class Version202501141150192260_taoQtiTest extends AbstractMigration
{
public function getDescription(): string
{
return 'Add QTI 3.0 Package Export handler to TestModel service configuration';
}

public function up(Schema $schema): void
{
$testModelService = $this->getServiceLocator()->get(TestModelService::SERVICE_ID);

if (!$testModelService->hasOption('exportHandlers')) {
return;
}

$handlers = $testModelService->getOption('exportHandlers');

$hasHandler = false;
foreach ($handlers as $handler) {
if ($handler instanceof TestPackageExport) {
$hasHandler = true;
break;
}
}

if (!$hasHandler) {
$handlers[] = new TestPackageExport();
$testModelService->setOption('exportHandlers', $handlers);
$this->getServiceLocator()->register(TestModelService::SERVICE_ID, $testModelService);
}
}

public function down(Schema $schema): void
{
$testModelService = $this->getServiceLocator()->get(TestModelService::SERVICE_ID);

if (!$testModelService->hasOption('exportHandlers')) {
return;
}

$handlers = $testModelService->getOption('exportHandlers');

$filteredHandlers = array_filter($handlers, function($handler) {
return !($handler instanceof TestPackageExport);
});

$testModelService->setOption('exportHandlers', $filteredHandlers);
$this->getServiceLocator()->register(TestModelService::SERVICE_ID, $testModelService);
}
}
18 changes: 14 additions & 4 deletions models/classes/QtiTestUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@

namespace oat\taoQtiTest\models;

use InvalidArgumentException;
use oat\generis\Helper\SystemHelper;
use oat\taoQtiItem\model\qti\Resource;
use qtism\data\storage\xml\XmlDocument;
use oat\oatbox\filesystem\FileSystemService;
use oat\oatbox\filesystem\Directory;
use qtism\data\AssessmentTest;
use oat\oatbox\service\ConfigurableService;
use taoItems_models_classes_TemplateRenderer;

/**
* Miscellaneous utility methods for the QtiTest extension.
Expand All @@ -54,7 +56,7 @@ class QtiTestUtils extends ConfigurableService
* @param boolean $copy If set to false, the file will not be actually copied.
* @param string $rename A new filename e.g. 'file.css' to be used at storage time.
* @return string The path were the file was copied/has to be copied (depending on the $copy argument).
* @throws \InvalidArgumentException If one of the above arguments is invalid.
* @throws InvalidArgumentException If one of the above arguments is invalid.
* @throws \common_Exception
*/
public function storeQtiResource(Directory $testContent, $qtiResource, $origin, $copy = true, $rename = '')
Expand All @@ -71,7 +73,7 @@ public function storeQtiResource(Directory $testContent, $qtiResource, $origin,
} elseif (is_string($qtiResource) === true) {
$filePath = $qtiResource;
} else {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
"The 'qtiResource' argument must be a string or a taoQTI_models_classes_QTI_Resource object."
);
}
Expand Down Expand Up @@ -130,8 +132,16 @@ public function storeQtiResource(Directory $testContent, $qtiResource, $origin,
*/
public function emptyImsManifest($version = '2.1')
{
$manifestFileName = ($version === '2.1') ? 'imsmanifest' : 'imsmanifestQti22';
$templateRenderer = new \taoItems_models_classes_TemplateRenderer(
$manifestFileName = match ($version) {
'2.1' => 'imsmanifest',
'2.2' => 'imsmanifestQti22',
'3.0' => 'imsmanifestQti30',
default => throw new InvalidArgumentException(
'Invalid version provided. Only "2.1", "2.2" and "3.0" are supported.'
),
};

$templateRenderer = new taoItems_models_classes_TemplateRenderer(
ROOT_PATH . 'taoQtiItem/model/qti/templates/' . $manifestFileName . '.tpl.php',
[
'qtiItems' => [],
Expand Down
44 changes: 44 additions & 0 deletions models/classes/export/Formats/Package3p0/QtiItemExporter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?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\export\Formats\Package3p0;

use oat\taoQtiItem\model\Export\Qti3Package\Exporter;
use oat\taoQtiTest\models\export\QtiItemExporterTrait;
use oat\taoQtiTest\models\export\QtiItemExporterInterface;

class QtiItemExporter implements QtiItemExporterInterface
{
use QtiItemExporterTrait;

private Exporter $exporter;

public function __construct(Exporter $exporter)
{
$this->exporter = $exporter;
}

public function __call(string $name, array $arguments)
{
return $this->exporter->$name(...$arguments);
}
}
94 changes: 94 additions & 0 deletions models/classes/export/Formats/Package3p0/QtiTestExporter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?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\export\Formats\Package3p0;

use core_kernel_classes_Resource as Resource;
use DOMDocument;
use oat\taoQtiItem\model\Export\Qti3Package\Exporter;
use oat\taoQtiItem\model\Export\Qti3Package\ExporterFactory;
use oat\taoQtiItem\model\Export\Qti3Package\TransformationService;
use oat\taoQtiTest\models\export\AbstractQtiTestExporter;
use oat\taoQtiTest\models\export\QtiItemExporterInterface;

class QtiTestExporter extends AbstractQtiTestExporter
{
protected const TEST_RESOURCE_TYPE = 'imsqti_test_xmlv3p0';

private const QTI_SCHEMA_NAMESPACE = 'http://www.imsglobal.org/xsd/imsqtiasi_v3p0';
private const XML_SCHEMA_INSTANCE = 'http://www.w3.org/2001/XMLSchema-instance';
private const XSI_SCHEMA_LOCATION = 'http://www.imsglobal.org/xsd/imsqtiasi_v3p0';
// phpcs:ignore Generic.Files.LineLength.TooLong
private const XSI_SCHEMA_LOCATION_XSD = 'https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0_v1p0.xsd';
private TransformationService $transformationService;

private Exporter $exporter;


protected function getItemExporter(Resource $item): QtiItemExporterInterface
{
$factory = $this->getExporterFactory();
$this->exporter = $factory->create($item, $this->getZip(), $this->getManifest());

return new QtiItemExporter($this->exporter);
}

protected function adjustTestXml(string $xml): string
{
return $this->itemContentPostProcessing($xml);
}

protected function itemContentPostProcessing($content): string
{
$transformationService = $this->exporter->getTransformationService();
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXML($content);

$newDom = new DOMDocument('1.0', 'UTF-8');
$newDom->preserveWhiteSpace = false;
$newDom->formatOutput = true;

$oldRoot = $dom->documentElement;
$newRoot = $newDom->createElement($transformationService->createQtiElementName($oldRoot->nodeName));

//QTI3 namespace
$newRoot->setAttribute('xmlns', self::QTI_SCHEMA_NAMESPACE);
$newRoot->setAttribute('xmlns:xsi', self::XML_SCHEMA_INSTANCE);
$newRoot->setAttribute(
'xsi:schemaLocation',
sprintf('%s %s', self::XSI_SCHEMA_LOCATION, self::XSI_SCHEMA_LOCATION_XSD)
);

$transformationService->transformAttributes($oldRoot, $newRoot);

$newDom->appendChild($newRoot);

$transformationService->transformChildren($oldRoot, $newRoot, $newDom);

return $newDom->saveXML();
}

private function getExporterFactory(): ExporterFactory
{
return $this->getServiceManager()->getContainer()->get(ExporterFactory::class);
}
}
56 changes: 56 additions & 0 deletions models/classes/export/Formats/Package3p0/TestPackageExport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?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\export\Formats\Package3p0;

use core_kernel_classes_Resource as Resource;
use oat\taoQtiTest\models\export\AbstractTestExport;
use oat\taoQtiTest\models\export\QtiTestExporterInterface;
use taoQtiTest_models_classes_QtiTestServiceException as QtiTestServiceException;

class TestPackageExport extends AbstractTestExport
{
protected const VERSION = '3.0';

public function getLabel(): string
{
return __('QTI Test Package %s', self::VERSION);
}

protected function getFormTitle(): string
{
return __('Export QTI %s Test Package', self::VERSION);
}

/**
* @throws QtiTestServiceException
*/
protected function getTestExporter(Resource $instance): QtiTestExporterInterface
{
return new QtiTestExporter($instance, $this->getZip(), $this->getEmptyManifest());
}
}

// for backward compatibility
// phpcs:disable PSR1.Files.SideEffects
class_alias(TestPackageExport::class, 'taoQtiTest_models_classes_export_TestExport30');
// phpcs:enable PSR1.Files.SideEffects
Loading