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

Handle broken opening hours #121

Merged
merged 1 commit into from
Sep 10, 2024
Merged
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
30 changes: 30 additions & 0 deletions Classes/Domain/Import/Entity/InvalidDataException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

/*
* Copyright (C) 2024 Daniel Siepmann <daniel.siepmann@codappix.com>
*
* 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
23 changes: 16 additions & 7 deletions Classes/Domain/Import/Typo3Converter/GeneralConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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 +379,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
Loading