Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
CsvItemImportHandler
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 8
210
0.00% covered (danger)
0.00%
0 / 1
 import
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
42
 getParser
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getItemImportService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getItemService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTemplateProcessor
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMetadataResolver
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 importMetadata
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 rollbackItem
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
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) 2021 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 */
20
21declare(strict_types=1);
22
23namespace oat\taoQtiItem\model\import;
24
25use core_kernel_classes_Class;
26use core_kernel_classes_Resource;
27use helpers_TimeOutHelper;
28use oat\oatbox\filesystem\File;
29use oat\oatbox\reporting\Report;
30use oat\oatbox\reporting\ReportInterface;
31use oat\oatbox\service\ConfigurableService;
32use oat\taoQtiItem\model\import\Metadata\MetadataResolver;
33use oat\taoQtiItem\model\import\Parser\CsvParser;
34use oat\taoQtiItem\model\import\Parser\CsvSeparatorTrait;
35use oat\taoQtiItem\model\import\Parser\ParserInterface;
36use oat\taoQtiItem\model\import\Template\ItemsQtiTemplateRender;
37use oat\taoQtiItem\model\import\Validator\ErrorValidationException;
38use oat\taoQtiItem\model\qti\ImportService;
39use tao_models_classes_dataBinding_GenerisFormDataBinder;
40use tao_models_classes_dataBinding_GenerisFormDataBindingException;
41use taoItems_models_classes_ItemsService;
42use Throwable;
43use Zend\ServiceManager\ServiceLocatorAwareInterface;
44
45class CsvItemImportHandler extends ConfigurableService
46{
47    use CsvSeparatorTrait;
48
49    public function import(
50        File $uploadedFile,
51        TemplateInterface $template,
52        core_kernel_classes_Class $class
53    ): ItemImportResult {
54        helpers_TimeOutHelper::setTimeOutLimit(helpers_TimeOutHelper::LONG);
55
56        $logger = $this->getLogger();
57        $logger->debug('Tabular import: CSV parsing started');
58
59        $itemValidatorResults = $this->getParser()->parseFile($uploadedFile, $template);
60
61        $logger->debug('Tabular import: CSV parsing finished');
62
63        $successReportsImport = 0;
64        $importService = $this->getItemImportService();
65        $templateProcessor = $this->getTemplateProcessor();
66        $xmlItems = $templateProcessor->processResultSet($itemValidatorResults, $template);
67
68        foreach ($xmlItems as $lineNumber => $xmlItem) {
69            try {
70                $metaData = $this->getMetadataResolver()->resolve($class, $xmlItem->getMetadata());
71
72                $itemImportReport = $importService->importQTIFile($xmlItem->getItemXML(), $class, true);
73
74                if (Report::TYPE_SUCCESS !== $itemImportReport->getType()) {
75                    $itemValidatorResults->addException(
76                        $lineNumber,
77                        new ErrorValidationException($itemImportReport->getMessage())
78                    );
79
80                    continue;
81                }
82
83                $this->importMetadata($metaData, $itemImportReport);
84
85                $itemValidatorResults->setFirstItem($itemImportReport->getData());
86
87                $logger->debug(sprintf('Tabular import: successful import of item from line %s', $lineNumber));
88
89                $successReportsImport++;
90            } catch (Throwable $exception) {
91                if (isset($itemImportReport)) {
92                    $this->rollbackItem($itemImportReport, $lineNumber);
93                }
94
95                $logger->error(
96                    sprintf(
97                        'Tabular import: failed import of item from line %s due to %s',
98                        $lineNumber,
99                        $exception->getMessage()
100                    )
101                );
102
103                $itemValidatorResults->addException($lineNumber, $exception);
104            } finally {
105                if (isset($itemImportReport)) {
106                    unset($itemImportReport);
107                }
108            }
109        }
110
111        helpers_TimeOutHelper::reset();
112
113        $logger->debug(
114            sprintf(
115                'Tabular import: successful import %s items from %s',
116                $successReportsImport,
117                count($xmlItems)
118            )
119        );
120
121        $itemValidatorResults->setTotalSuccessfulImport($successReportsImport);
122
123        return $itemValidatorResults;
124    }
125
126    private function getParser(): ParserInterface
127    {
128        /** @var CsvParser $parser */
129        $parser = $this->getServiceLocator()->get(CsvParser::class);
130        $parser->setCsvSeparator($this->getCsvSeparator());
131
132        return $parser;
133    }
134
135    private function getItemImportService(): ImportService
136    {
137        return $this->getServiceLocator()->get(ImportService::SERVICE_ID);
138    }
139
140    private function getItemService(): taoItems_models_classes_ItemsService
141    {
142        return $this->getServiceLocator()->get(taoItems_models_classes_ItemsService::class);
143    }
144
145    private function getTemplateProcessor(): ItemsQtiTemplateRender
146    {
147        return $this->getServiceLocator()->get(ItemsQtiTemplateRender::class);
148    }
149
150    private function getMetadataResolver(): MetadataResolver
151    {
152        return $this->getServiceManager()->get(MetadataResolver::class);
153    }
154
155    /**
156     * @throws tao_models_classes_dataBinding_GenerisFormDataBindingException
157     */
158    private function importMetadata(array $metaData, ReportInterface $itemImportReport): void
159    {
160        $itemRdf = $itemImportReport->getData();
161        $binder = new tao_models_classes_dataBinding_GenerisFormDataBinder($itemRdf);
162        $binder->bind($metaData);
163    }
164
165    private function rollbackItem(ReportInterface $itemImportReport, int $lineNumber): void
166    {
167        $item = $itemImportReport->getData();
168
169        if ($item instanceof core_kernel_classes_Resource) {
170            $this->getItemService()->deleteResource($item);
171
172            $this->getLogger()->warning(
173                sprintf(
174                    'Tabular import: line `%s` rollback item with uri `%s` and label %s',
175                    $lineNumber,
176                    $item->getUri(),
177                    $item->getLabel()
178                )
179            );
180        }
181    }
182}