Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.65% covered (success)
93.65%
59 / 63
62.50% covered (warning)
62.50%
5 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
TestTranslator
93.65% covered (success)
93.65%
59 / 63
62.50% covered (warning)
62.50%
5 / 8
18.08
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 translate
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 assertIsTest
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 doTranslation
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 collectItemUris
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 recursiveSectionParts
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
4.59
 getItemTranslationUris
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 updateTranslationCompletionStatus
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2024 (original work) Open Assessment Technologies SA.
19 */
20
21declare(strict_types=1);
22
23namespace oat\taoQtiTest\models\Translation\Service;
24
25use core_kernel_classes_Resource;
26use core_kernel_persistence_Exception;
27use oat\generis\model\data\Ontology;
28use oat\tao\model\TaoOntology;
29use oat\tao\model\Translation\Entity\ResourceTranslation;
30use oat\tao\model\Translation\Exception\ResourceTranslationException;
31use oat\tao\model\Translation\Query\ResourceTranslationQuery;
32use oat\tao\model\Translation\Repository\ResourceTranslationRepository;
33use oat\taoTests\models\TaoTestOntology;
34use taoQtiTest_models_classes_QtiTestConverterException;
35use taoQtiTest_models_classes_QtiTestService;
36use taoQtiTest_models_classes_QtiTestServiceException;
37
38class TestTranslator
39{
40    private taoQtiTest_models_classes_QtiTestService $testQtiService;
41    private Ontology $ontology;
42    private ResourceTranslationRepository $resourceTranslationRepository;
43
44    public function __construct(
45        taoQtiTest_models_classes_QtiTestService $testQtiService,
46        Ontology $ontology,
47        ResourceTranslationRepository $resourceTranslationRepository
48    ) {
49        $this->testQtiService = $testQtiService;
50        $this->ontology = $ontology;
51        $this->resourceTranslationRepository = $resourceTranslationRepository;
52    }
53
54    /**
55     * @throws taoQtiTest_models_classes_QtiTestConverterException
56     * @throws taoQtiTest_models_classes_QtiTestServiceException
57     * @throws core_kernel_persistence_Exception
58     * @throws ResourceTranslationException
59     */
60    public function translate(core_kernel_classes_Resource $translationTest): core_kernel_classes_Resource
61    {
62        $this->assertIsTest($translationTest);
63
64        $originalTestUri = $translationTest->getOnePropertyValue(
65            $this->ontology->getProperty(TaoOntology::PROPERTY_TRANSLATION_ORIGINAL_RESOURCE_URI)
66        );
67        $originalTest = $this->ontology->getResource($originalTestUri->getUri());
68
69        $jsonTranslationTest = $this->testQtiService->getJsonTest($translationTest);
70        $jsonOriginalTest = $this->testQtiService->getJsonTest($originalTest);
71        $translationTestData = json_decode($jsonTranslationTest, true, 512, JSON_THROW_ON_ERROR);
72        $originalTestData = json_decode($jsonOriginalTest, true, 512, JSON_THROW_ON_ERROR);
73
74        $originalItemUris = $this->collectItemUris($originalTestData);
75        $translationUris = $this->getItemTranslationUris($translationTest, $originalItemUris);
76
77        $translatedTestData = $this->doTranslation($translationTestData, $translationUris);
78
79        $this->testQtiService->saveJsonTest($translationTest, json_encode($translatedTestData));
80        $this->updateTranslationCompletionStatus($translationTest, $originalItemUris, $translationUris);
81
82        return $translationTest;
83    }
84
85    private function assertIsTest(core_kernel_classes_Resource $resource): void
86    {
87        if (!$resource->isInstanceOf($this->ontology->getClass(TaoOntology::CLASS_URI_TEST))) {
88            throw new ResourceTranslationException('Provided resources is not a Test');
89        }
90    }
91
92    private function doTranslation(array $testData, array $translationUris): array
93    {
94        foreach ($testData['testParts'] as &$testPart) {
95            foreach ($testPart['assessmentSections'] as &$assessmentSection) {
96                $this->recursiveSectionParts(
97                    $assessmentSection['sectionParts'],
98                    function (&$sectionPart) use ($translationUris) {
99                        $sectionPart['href'] = $translationUris[$sectionPart['href']] ?? $sectionPart['href'];
100                    }
101                );
102            }
103        }
104
105        return $testData;
106    }
107
108    private function collectItemUris(array $testData): array
109    {
110        $uris = [];
111
112        foreach ($testData['testParts'] as $testPart) {
113            foreach ($testPart['assessmentSections'] as $assessmentSection) {
114                $this->recursiveSectionParts(
115                    $assessmentSection['sectionParts'],
116                    function ($sectionPart) use (&$uris) {
117                        $uris[$sectionPart['href']] = $sectionPart['href'];
118                    }
119                );
120            }
121        }
122
123        return array_values($uris);
124    }
125
126    private function recursiveSectionParts(&$sectionParts, callable $itemAction): void
127    {
128        foreach ($sectionParts as &$sectionPart) {
129            if ($sectionPart['qti-type'] === 'assessmentSection') {
130                $this->recursiveSectionParts($sectionPart['sectionParts'], $itemAction);
131
132                continue;
133            }
134
135            if ($sectionPart['qti-type'] === 'assessmentItemRef') {
136                $itemAction($sectionPart);
137            }
138        }
139    }
140
141    /**
142     * @param string[] $originalItemUris
143     * @return array<string, string>
144     */
145    private function getItemTranslationUris(core_kernel_classes_Resource $test, array $originalItemUris): array
146    {
147        $language = $test->getOnePropertyValue($this->ontology->getProperty(TaoOntology::PROPERTY_LANGUAGE));
148        $translations = $this->resourceTranslationRepository->find(
149            new ResourceTranslationQuery(
150                $originalItemUris,
151                $language->getUri()
152            )
153        );
154
155        $translationUris = [];
156
157        /** @var ResourceTranslation $translation */
158        foreach ($translations->jsonSerialize()['resources'] as $translation) {
159            $translationUris[$translation->getOriginResourceUri()] = $translation->getResourceUri();
160        }
161
162        return $translationUris;
163    }
164
165    private function updateTranslationCompletionStatus(
166        core_kernel_classes_Resource $test,
167        array $uniqueIds,
168        array $translationUris
169    ): void {
170        $status = count($uniqueIds) > count($translationUris)
171            ? TaoTestOntology::PROPERTY_VALUE_TRANSLATION_COMPLETION_MISSING_TRANSLATIONS
172            : TaoTestOntology::PROPERTY_VALUE_TRANSLATION_COMPLETION_TRANSLATED;
173
174        $test->editPropertyValues(
175            $this->ontology->getProperty(TaoTestOntology::PROPERTY_TRANSLATION_COMPLETION),
176            $status
177        );
178    }
179}