Skip to content

Commit

Permalink
Handle broken opening hours
Browse files Browse the repository at this point in the history
Those are skipped during import and written as error to TYPO3 logs.
That way entries with broken opening hours can still be imported.

Resolves: #11377
  • Loading branch information
d-s-codappix committed Sep 9, 2024
1 parent 98eb5ff commit b13750f
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 10 deletions.
31 changes: 31 additions & 0 deletions Classes/Domain/Import/Entity/InvalidDataException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

/*
* Copyright (C) 2024 Daniel Siepmann <[email protected]>
*
* 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; either version 2
* of the License, or (at your option) any later version.
*
* 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.
*/

namespace WerkraumMedia\ThueCat\Domain\Import\Entity;

use Exception;

final class InvalidDataException extends Exception
{

}
13 changes: 11 additions & 2 deletions Classes/Domain/Import/Entity/Properties/OpeningHour.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,17 @@
namespace WerkraumMedia\ThueCat\Domain\Import\Entity\Properties;

use DateTimeImmutable;
use WerkraumMedia\ThueCat\Domain\Import\Entity\InvalidDataException;

class OpeningHour
{
protected ?DateTimeImmutable $validFrom = null;

protected ?DateTimeImmutable $validThrough = null;

protected DateTimeImmutable $opens;
protected ?DateTimeImmutable $opens = null;

protected DateTimeImmutable $closes;
protected ?DateTimeImmutable $closes = null;

/**
* @var string[]
Expand All @@ -52,11 +53,19 @@ public function getValidThrough(): ?DateTimeImmutable

public function getOpens(): DateTimeImmutable
{
if ($this->opens === null) {
throw new InvalidDataException('Opens was empty for opening hour.');
}

return $this->opens;
}

public function getCloses(): DateTimeImmutable
{
if ($this->closes === null) {
throw new InvalidDataException('Closes was empty for opening hour.');
}

return $this->closes;
}

Expand Down
24 changes: 17 additions & 7 deletions Classes/Domain/Import/Typo3Converter/GeneralConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
namespace WerkraumMedia\ThueCat\Domain\Import\Typo3Converter;

use Exception;
use TypeError;
use TYPO3\CMS\Core\Log\Logger;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use WerkraumMedia\ThueCat\Domain\Import\Entity\AccessibilitySpecification;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Base;
use WerkraumMedia\ThueCat\Domain\Import\Entity\InvalidDataException;
use WerkraumMedia\ThueCat\Domain\Import\Entity\MapsToType;
use WerkraumMedia\ThueCat\Domain\Import\Entity\MediaObject;
use WerkraumMedia\ThueCat\Domain\Import\Entity\Minimum;
Expand Down Expand Up @@ -378,13 +380,21 @@ private function getOpeningHours(array $openingHours): string
$data = [];

foreach ($openingHours as $openingHour) {
$data[] = array_filter([
'opens' => $openingHour->getOpens()->format('H:i:s'),
'closes' => $openingHour->getCloses()->format('H:i:s'),
'from' => $openingHour->getValidFrom() ?? '',
'through' => $openingHour->getValidThrough() ?? '',
'daysOfWeek' => $openingHour->getDaysOfWeek(),
]);
try {
$data[] = array_filter([
'opens' => $openingHour->getOpens()->format('H:i:s'),
'closes' => $openingHour->getCloses()->format('H:i:s'),
'from' => $openingHour->getValidFrom() ?? '',
'through' => $openingHour->getValidThrough() ?? '',
'daysOfWeek' => $openingHour->getDaysOfWeek(),
]);
} catch (InvalidDataException $e) {
$this->logger->error('Could not import opening hour due to type error: {errorMessage}', [
'errorMessage' => $e->getMessage(),
'openingHour' => var_export($openingHour, true),
]);
continue;
}
}

return json_encode($data, JSON_THROW_ON_ERROR) ?: '';
Expand Down
4 changes: 4 additions & 0 deletions Documentation/Changelog/3.0.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ Fixes
* Add missing dependency to `typo3/cms-install`.
As this provides the upgrade wizard feature.

* Handle broken opening hours.
Those are skipped during import and written as error to TYPO3 logs.
That way entries with broken opening hours can still be imported.

Tasks
-----

Expand Down
7 changes: 6 additions & 1 deletion Tests/Functional/AbstractImportTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,12 @@ private function getLogFiles(): array
{
return [
self::getInstancePath() . '/typo3temp/var/log/typo3_0493d91d8e.log',
self::getInstancePath() . '/typo3temp/var/log/typo3_error_0493d91d8e.log',
$this->getErrorLogFile(),
];
}

protected function getErrorLogFile(): string
{
return self::getInstancePath() . '/typo3temp/var/log/typo3_error_0493d91d8e.log';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"@context": {
"cdb": "https://thuecat.org/ontology/cdb/1.0/",
"dachkg": "https://thuecat.org/ontology/dachkg/1.0/",
"dbo": "http://dbpedia.org/ontology/",
"dsv": "http://ontologies.sti-innsbruck.at/dsv/",
"foaf": "http://xmlns.com/foaf/0.1/",
"owl": "http://www.w3.org/2002/07/owl#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"schema": "http://schema.org/",
"sh": "http://www.w3.org/ns/shacl#",
"thuecat": "https://thuecat.org/ontology/thuecat/1.0/",
"ttgds": "https://thuecat.org/ontology/ttgds/1.0/",
"xsd": "http://www.w3.org/2001/XMLSchema#"
},
"@graph": [
{
"@id": "https://thuecat.org/resources/attraction-with-broken-opening-hour",
"@type": [
"schema:Place",
"schema:Thing",
"schema:TouristAttraction",
"ttgds:PointOfInterest"
],
"schema:name": {
"@language": "de",
"@value": "One specialOpeningHoursSpecification misses opens and closes, leading to type error"
},
"schema:availableLanguage": [
{
"@type": "thuecat:Language",
"@value": "thuecat:German"
},
{
"@type": "thuecat:Language",
"@value": "thuecat:English"
}
],
"schema:specialOpeningHoursSpecification": [
{
"@id": "genid-1dccaefc0e06401c93068c8081b7ea8c294892-b11",
"@type": [
"schema:Thing",
"schema:Intangible",
"schema:StructuredValue",
"schema:OpeningHoursSpecification"
],
"schema:dayOfWeek": {
"@type": "schema:DayOfWeek",
"@value": "schema:Thursday"
},
"schema:validThrough": {
"@type": "schema:Date",
"@value": "2024-09-19"
},
"schema:validFrom": {
"@type": "schema:Date",
"@value": "2024-09-19"
}
},
{
"@id": "genid-1dccaefc0e06401c93068c8081b7ea8c294892-b12",
"@type": [
"schema:Thing",
"schema:Intangible",
"schema:StructuredValue",
"schema:OpeningHoursSpecification"
],
"schema:opens": {
"@type": "schema:Time",
"@value": "09:30:00"
},
"schema:closes": {
"@type": "schema:Time",
"@value": "18:00:00"
},
"schema:dayOfWeek": {
"@type": "schema:DayOfWeek",
"@value": "schema:Thursday"
},
"schema:validThrough": {
"@type": "schema:Date",
"@value": "2024-09-19"
},
"schema:validFrom": {
"@type": "schema:Date",
"@value": "2024-09-19"
}
}
],
"thuecat:contentResponsible": {
"@id": "https://thuecat.org/resources/018132452787-ngbe"
}
}
]
}
65 changes: 65 additions & 0 deletions Tests/Functional/Fixtures/Import/ImportsWithBrokenOpeningHour.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

use TYPO3\CMS\Core\Domain\Repository\PageRepository;

return [
'pages' => [
0 => [
'uid' => '1',
'pid' => '0',
'tstamp' => '1613400587',
'crdate' => '1613400558',
'doktype' => PageRepository::DOKTYPE_DEFAULT,
'title' => 'Rootpage',
'is_siteroot' => '1',
],
1 => [
'uid' => '10',
'pid' => '1',
'tstamp' => '1613400587',
'crdate' => '1613400558',
'doktype' => PageRepository::DOKTYPE_SYSFOLDER,
'title' => 'Storage folder',
],
],
'tx_thuecat_import_configuration' => [
0 => [
'uid' => '1',
'pid' => '0',
'tstamp' => '1613400587',
'crdate' => '1613400558',
'disable' => '0',
'title' => 'Tourist Attraction',
'type' => 'static',
'configuration' => '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<T3FlexForms>
<data>
<sheet index="sDEF">
<language index="lDEF">
<field index="storagePid">
<value index="vDEF">10</value>
</field>
<field index="urls">
<el index="el">
<field index="602a89f54d694654233086">
<value index="url">
<el>
<field index="url">
<value index="vDEF">https://thuecat.org/resources/attraction-with-broken-opening-hour</value>
</field>
</el>
</value>
<value index="_TOGGLE">0</value>
</field>
</el>
</field>
</language>
</sheet>
</data>
</T3FlexForms>
',
],
],
];
26 changes: 26 additions & 0 deletions Tests/Functional/ImportTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,32 @@ public function importsTouristAttractionWithSingleSlogan(): void
$this->assertPHPDataSet(__DIR__ . '/Assertions/Import/ImportsTouristAttractionWithSingleSlogan.php');
}

#[Test]
public function importsWithBrokenOpeningHour(): void
{
$this->importPHPDataSet(__DIR__ . '/Fixtures/Import/ImportsWithBrokenOpeningHour.php');

GuzzleClientFaker::appendResponseFromFile(__DIR__ . '/Fixtures/Import/Guzzle/thuecat.org/resources/attraction-with-broken-opening-hour.json');
GuzzleClientFaker::appendResponseFromFile(__DIR__ . '/Fixtures/Import/Guzzle/thuecat.org/resources/018132452787-ngbe.json');

$this->importConfiguration();

$records = $this->getAllRecords('tx_thuecat_tourist_attraction');
self::assertCount(1, $this->getAllRecords('tx_thuecat_tourist_attraction'));
$specialOpeningHours = json_decode($records[0]['special_opening_hours'], true, 512, JSON_THROW_ON_ERROR);
self::assertIsArray($specialOpeningHours);
self::assertCount(1, $specialOpeningHours);

$this->expectErrors = true;
$loggedErrors = file_get_contents($this->getErrorLogFile());
self::assertIsString($loggedErrors);
self::assertStringContainsString(
'Could not import opening hour due to type error: Opens was empty for opening hour.',
$loggedErrors
);
self::assertStringContainsString('\'closes\' => NULL,', $loggedErrors);
}

private function importConfiguration(): void
{
$configuration = $this->get(ImportConfigurationRepository::class)->findByUid(1);
Expand Down

0 comments on commit b13750f

Please sign in to comment.