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: Delete old assets on test overwrite #2393

Merged
merged 43 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
793c8f4
feat: Delete old assets on test overwrite
hectoras Jul 6, 2023
e7a080d
chore: Cleanup
hectoras Jul 6, 2023
d23842b
chore: Cleanup
hectoras Jul 6, 2023
6e0c88c
chore: Cleanup
hectoras Jul 6, 2023
1b35bfd
chore: Cleanup
hectoras Jul 6, 2023
67b11d2
chore: Cleanup
hectoras Jul 6, 2023
5b396b0
fix: Invert condition
hectoras Jul 6, 2023
13c9113
chore: Rollback code style changes
hectoras Jul 10, 2023
883af73
chore: Rollback coding style changes
hectoras Jul 10, 2023
7bfe57e
chore: Rollback coding style changes
hectoras Jul 10, 2023
1751447
chore: Rollback coding style changes
hectoras Jul 10, 2023
1a7cb58
chore: Rollback coding style changes
hectoras Jul 10, 2023
06a65c3
test: Update unit test test
hectoras Jul 10, 2023
d8c3221
chore: Remove dummy test
hectoras Jul 10, 2023
565f86c
chore: Use integration events to trigger deletion of related assets
hectoras Jul 10, 2023
7df8ec0
chore: Remove TestDeleter
hectoras Jul 10, 2023
ef42604
chore: Cleanup
hectoras Jul 10, 2023
dcbc2f9
chore: Cleanup
hectoras Jul 10, 2023
21145a7
chore: Cleanup
hectoras Jul 10, 2023
e838b14
chore: Cleanup
hectoras Jul 10, 2023
effa31a
fix: Fix broken test
hectoras Jul 11, 2023
83ea87b
chore: Fix PSR-12 issue
hectoras Jul 11, 2023
4c0687e
chore: Address linter issues related to method length
hectoras Jul 11, 2023
c1f66bd
chore: Cleanup
hectoras Jul 11, 2023
bf0d33a
feat: Remove possible duplicated URIs from QtiTestDeletedEvent params
hectoras Jul 11, 2023
4f9d728
chore: Add missing license header
hectoras Jul 12, 2023
286922e
chore: Add missing license header
hectoras Jul 12, 2023
a30eabd
chore: Coding style fixes
hectoras Jul 12, 2023
fc71942
refactor: Collect referenced resources and item classes to delete in …
hectoras Jul 12, 2023
21e1d65
chore: Shorten var name on auxiliary method
hectoras Jul 12, 2023
b7adabb
chore: Delete test before collecting its URI
hectoras Jul 12, 2023
012a459
chore: Add doc blocks with variable type hints
hectoras Jul 12, 2023
87436f8
chore: Use DI container to get services in QtiTestService
hectoras Jul 12, 2023
a1c24cf
feat: Rename event, trigger event before the deletions
hectoras Jul 12, 2023
0dceb0f
chore: Remove PHPDoc var type declarations
hectoras Jul 13, 2023
5d38559
feat: Remove logic to extract media references form this extension
hectoras Jul 13, 2023
5c48617
chore: Remove QtiTestsDeletedEvent that is not used anymore
hectoras Jul 13, 2023
237b442
chore: Move subclasses' items deletion to its own method
hectoras Jul 13, 2023
708ab7b
chore: Update oat-sa/extension-tao-item version in composer.json
hectoras Jul 14, 2023
4b90f13
fix: dont delete classes whose label don't match the expected label
hectoras Jul 14, 2023
cabc3f4
chore: force higher dependency version to fix pipeline
gabrielfs7 Jul 14, 2023
8e376d2
chore: use delete command
gabrielfs7 Jul 14, 2023
185b15d
chore: Point to released version of extension-tao-item
hectoras Jul 25, 2023
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
115 changes: 99 additions & 16 deletions models/classes/class.QtiTestService.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,35 @@
* 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) 2013-2018 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
*
* Copyright (c) 2013-2023 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
*/

use League\Flysystem\FileExistsException;
use oat\oatbox\event\EventManager;
use oat\oatbox\filesystem\Directory;
use oat\oatbox\filesystem\File;
use oat\oatbox\filesystem\FileSystemService;
use oat\oatbox\service\ServiceNotFoundException;
use oat\tao\model\metadata\exception\MetadataImportException;
use oat\tao\model\resources\ResourceAccessDeniedException;
use oat\tao\model\resources\SecureResourceServiceInterface;
use oat\tao\model\TaoOntology;
use oat\taoBackOffice\model\tree\TreeService;
use oat\taoQtiItem\model\qti\ImportService;
use oat\taoQtiItem\model\qti\metadata\importer\MetadataImporter;
use oat\taoQtiItem\model\qti\metadata\MetadataGuardianResource;
use oat\taoQtiItem\model\qti\metadata\MetadataService;
use oat\taoQtiItem\model\qti\parser\ElementReferencesExtractor;
use oat\taoQtiItem\model\qti\Resource;
use oat\taoQtiItem\model\qti\Service;
use oat\taoQtiTest\models\cat\AdaptiveSectionInjectionException;
use oat\taoQtiTest\models\cat\CatEngineNotFoundException;
use oat\taoQtiTest\models\cat\CatService;
use oat\taoQtiTest\models\event\QtiTestsDeletedEvent;
use oat\taoQtiTest\models\metadata\MetadataTestContextAware;
use oat\taoQtiTest\models\render\QtiPackageImportPreprocessing;
use oat\taoQtiTest\models\test\AssessmentTestXmlFactory;
use oat\taoTests\models\event\TestUpdatedEvent;
use Psr\Container\ContainerInterface;
use qtism\common\utils\Format;
use qtism\data\AssessmentItemRef;
use qtism\data\QtiComponentCollection;
Expand All @@ -49,6 +53,7 @@
use qtism\data\storage\xml\XmlDocument;
use qtism\data\storage\xml\XmlStorageException;
use taoTests_models_classes_TestsService as TestService;
use oat\taoQtiItem\model\qti\Service as QtiItemService;

/**
* the QTI TestModel service.
Expand All @@ -57,7 +62,6 @@
* @author Bertrand Chevrier <bertrand@taotesting.com>
* @author Jerome Bogaerts <jerome@taotesting.com>
* @package taoQtiTest

*/
class taoQtiTest_models_classes_QtiTestService extends TestService
{
Expand Down Expand Up @@ -348,7 +352,6 @@ public function importMultipleTests(
bool $overwriteTest = false,
?string $itemClassUri = null
) {

$testClass = $targetClass;
$report = new common_report_Report(common_report_Report::TYPE_INFO);
$validPackage = false;
Expand Down Expand Up @@ -866,24 +869,62 @@ protected function importTest(
return $report;
}

/**
* @throws common_Exception
*/
private function deleteTestsFromClassByLabel(
string $testLabel,
string $itemsClassLabel,
core_kernel_classes_Resource $testClass,
core_kernel_classes_Class $itemClass
): void {
$testService = $this->getTestService();
$itemTreeService = $this->getItemTreeService();
$itemClassesToDelete = $this->getSubclassesByLabel($itemClass, $itemsClassLabel);

/** @var string[] $refs */
$refs = $this->collectResourceReferences($itemClassesToDelete);
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved

/** @var string[] $deletedTestsUris */
$deletedTestsUris = [];
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
foreach ($testClass->getInstances() as $testInstance) {
/** @var core_kernel_classes_Resource $testInstance */
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
if ($testInstance->getLabel() === $testLabel) {
$testService->deleteTest($testInstance);
$deletedTestsUris[] = $testInstance->getUri();
}
}

foreach ($itemClass->getSubClasses() as $subClass) {
if ($subClass->getLabel() === $itemsClassLabel) {
$itemTreeService->deleteClass($subClass);
/** @var string[] $deletedItemClassesUris */
$deletedItemClassesUris = [];
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
foreach ($itemClassesToDelete as $subClass) {
$deletedItemClassesUris[] = $subClass->getUri();
}

// Trigger the deletion event so extensions holding references to
// the deleted test or items can remove them first.
//
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
$this->getEventManager()->trigger(
new QtiTestsDeletedEvent(
$deletedTestsUris,
$deletedItemClassesUris,
$refs
)
);

// Actually delete the items (if this crashes the tests will
// hold refs to items that don't exist anymore).
//
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
$itemTreeService = $this->getItemTreeService();
foreach ($itemClassesToDelete as $subClass) {
$itemTreeService->deleteClass($subClass);
}

// Actually delete the tests, which already hold refs to
// non-existing items.
//
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
$testService = $this->getTestService();
foreach ($testClass->getInstances() as $testInstance) {
/** @var core_kernel_classes_Resource $testInstance */
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
if ($testInstance->getLabel() === $testLabel) {
$testService->deleteTest($testInstance);
}
}
}
Expand Down Expand Up @@ -1447,20 +1488,62 @@ private function verifyItemPermissions(core_kernel_classes_Resource $oldTest, st
}

/**
* @return QtiPackageImportPreprocessing
* @param core_kernel_classes_Class[] $itemClasses
* @throws common_Exception
* @return string[]
*/
private function getQtiPackageImportPreprocessing()
private function collectResourceReferences(array $itemClasses): array
{
return $this->getServiceLocator()->get(QtiPackageImportPreprocessing::SERVICE_ID);
$refs = [];

foreach ($itemClasses as $itemClass) {
foreach ($itemClass->getInstances(true) as $rdfItem) {
$qtiItem = $this->getQtiItemService()->getDataItemByRdfItem($rdfItem);
$itemReferences = $this->getElementReferencesExtractor()->extractAll($qtiItem);
$refs = array_merge($refs, $itemReferences->getAllReferences());
}
}

return $refs;
}

private function getSubclassesByLabel(core_kernel_classes_Class $root, string $label)
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
{
return array_filter(
$root->getSubClasses(),
function (core_kernel_classes_Class $subClass) use ($label) {
return $subClass->getLabel() === $label;
}
);
}

private function getQtiPackageImportPreprocessing(): QtiPackageImportPreprocessing
{
return $this->getPsrContainer()->get(QtiPackageImportPreprocessing::SERVICE_ID);
}

private function getQtiItemService(): QtiItemService
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
{
return $this->getPsrContainer()->get(QtiItemService::class);
}

private function getElementReferencesExtractor()
{
return $this->getPsrContainer()->get(ElementReferencesExtractor::class);
}

private function getItemTreeService(): taoItems_models_classes_ItemsService
{
return taoItems_models_classes_ItemsService::singleton();
return $this->getPsrContainer()->get(taoItems_models_classes_ItemsService::class);
}

private function getTestService(): taoTests_models_classes_TestsService
{
return taoTests_models_classes_TestsService::singleton();
return $this->getPsrContainer()->get(taoTests_models_classes_TestsService::class);
}

private function getPsrContainer(): ContainerInterface
{
return $this->getServiceLocator()->getContainer();
}
}
74 changes: 74 additions & 0 deletions models/classes/event/QtiTestsDeletedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?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) 2023 (original work) Open Assessment Technologies SA.
*/

declare(strict_types=1);

namespace oat\taoQtiTest\models\event;

use oat\oatbox\event\Event;

class QtiTestsDeletedEvent implements Event
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
{
/** @var string[] */
private array $testUris;

/** @var string[] */
private array $itemClassesUri;

/** @var string[] */
private array $referencedResources;

public function __construct(
gabrielfs7 marked this conversation as resolved.
Show resolved Hide resolved
array $testUris,
array $itemClassesUri,
array $referencedResources
) {
$this->testUris = array_values(array_unique($testUris));
$this->itemClassesUri = array_values(array_unique($itemClassesUri));
$this->referencedResources = array_values(array_unique($referencedResources));
}

/**
* (non-PHPdoc)
* @see Event::getName
*/
public function getName()
{
return self::class;
}

public function getTestUris(): array
{
return $this->testUris;
}

public function getItemClassesUri(): array
{
return $this->itemClassesUri;
}

/**
* @return string[]
*/
public function getReferencedResources(): array
{
return $this->referencedResources;
}
}
105 changes: 105 additions & 0 deletions test/unit/models/classes/event/QtiTestsDeletedEventTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?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) 2023 (original work) Open Assessment Technologies SA.
*/

declare(strict_types=1);

namespace oat\taoQtiTest\test\unit\models\classes;

use oat\taoQtiTest\models\event\QtiTestsDeletedEvent;
use PHPUnit\Framework\TestCase;

class QtiTestsDeletedEventTest extends TestCase
{
/**
* @dataProvider removesDuplicatesDataProvider
*/
public function testRemovesDuplicates(
array $expectedTestUris,
array $expectedItemClassesUris,
array $expectedReferencedResources,
array $testUris,
array $itemClassesUris,
array $referencedResources
): void {
$event = new QtiTestsDeletedEvent(
$testUris,
$itemClassesUris,
$referencedResources
);

$this->assertEquals(QtiTestsDeletedEvent::class, $event->getName());
$this->assertEquals(
$expectedTestUris,
$event->getTestUris()
);
$this->assertEquals(
$expectedItemClassesUris,
$event->getItemClassesUri()
);
$this->assertEquals(
$expectedReferencedResources,
$event->getReferencedResources()
);
}

public function removesDuplicatesDataProvider(): array
{
return [
'Duplicated URIs on all parameters' => [
'expectedTestUris' => [
'http://host/test1',
'http://host/test2',
'http://host/test3',
],
'expectedItemClassesUris' => [
'http://host/itemClass1',
'http://host/itemClass2',
'http://host/itemClass3',
],
'expectedReferencedResources' => [
'http://host/resource1',
'http://host/resource2',
'http://host/resource3',
],
'testUris' => [
'http://host/test1',
'http://host/test1',
'http://host/test2',
'http://host/test2',
'http://host/test3',
'http://host/test3',
],
'itemClassesUris' => [
'http://host/itemClass1',
'http://host/itemClass2',
'http://host/itemClass3',
'http://host/itemClass3',
],
'referencedResources' => [
'http://host/resource1',
'http://host/resource1',
'http://host/resource2',
'http://host/resource2',
'http://host/resource3',
],
]
];
}
}
Loading