Refactor date creation during import

Split into own class to encapsulate the logic.
This commit is contained in:
Daniel Siepmann 2022-04-21 08:07:25 +02:00
parent 948f571f9d
commit 4ef67b9bae
4 changed files with 543 additions and 73 deletions

View file

@ -159,6 +159,17 @@ class Date extends AbstractEntity
return '';
}
public static function createFromDestinationDataDate(
array $date,
bool $canceled
): self {
return self::createFromDestinationData(
new \DateTime($date['start'], new \DateTimeZone($date['tz'])),
new \DateTime($date['end'], new \DateTimeZone($date['tz'])),
$canceled
);
}
public static function createFromDestinationData(
\DateTime $start,
\DateTime $end,

View file

@ -28,6 +28,7 @@ use Wrm\Events\Domain\Repository\DateRepository;
use Wrm\Events\Domain\Repository\EventRepository;
use Wrm\Events\Domain\Repository\OrganizerRepository;
use Wrm\Events\Service\DestinationDataImportService\DataFetcher;
use Wrm\Events\Service\DestinationDataImportService\DatesFactory;
class DestinationDataImportService
{
@ -91,6 +92,11 @@ class DestinationDataImportService
*/
private $dataFetcher;
/**
* @var DatesFactory
*/
private $datesFactory;
/**
* ImportService constructor.
* @param EventRepository $eventRepository
@ -112,7 +118,8 @@ class DestinationDataImportService
ConfigurationManager $configurationManager,
PersistenceManager $persistenceManager,
ObjectManager $objectManager,
DataFetcher $dataFetcher
DataFetcher $dataFetcher,
DatesFactory $datesFactory
) {
$this->eventRepository = $eventRepository;
$this->organizerRepository = $organizerRepository;
@ -123,6 +130,7 @@ class DestinationDataImportService
$this->persistenceManager = $persistenceManager;
$this->objectManager = $objectManager;
$this->dataFetcher = $dataFetcher;
$this->datesFactory = $datesFactory;
}
public function import(
@ -279,86 +287,18 @@ class DestinationDataImportService
//$currentEventDates = $this->tmpCurrentEvent->getDates();
//$this->tmpCurrentEvent->removeAllDates($currentEventDates);
// <--
// TODO: Workaround delete dates
$currentEventDates = $this->tmpCurrentEvent->getDates();
$this->logger->info('Found ' . count($currentEventDates) . ' to delete');
foreach ($currentEventDates as $currentDate) {
$this->dateRepository->remove($currentDate);
}
$today = new \DateTime('today');
$today = $today->getTimestamp();
foreach ($timeIntervals as $date) {
// Check if dates are given as interval or not
if (empty($date['interval'])) {
if (strtotime($date['start']) > $today) {
$this->logger->info('Setup single date');
$start = new \DateTime($date['start'], new \DateTimeZone($date['tz']));
$end = new \DateTime($date['end'], new \DateTimeZone($date['tz']));
$this->logger->info('Start transformed ' . $start->format('Y-m-d H:i'));
$this->logger->info('End transformed ' . $end->format('Y-m-d H:i'));
$this->tmpCurrentEvent->addDate(Date::createFromDestinationData(
$start,
$end,
$canceled
));
}
} else {
if ($date['freq'] == 'Daily' && empty($date['weekdays']) && !empty($date['repeatUntil'])) {
$this->logger->info('Setup daily interval dates');
$this->logger->info('Start ' . $date['start']);
$this->logger->info('End ' . $date['repeatUntil']);
$start = new \DateTime($date['start'], new \DateTimeZone($date['tz']));
$until = new \DateTime($date['repeatUntil'], new \DateTimeZone($date['tz']));
$i = (int) strtotime($start->format('l'), $start->getTimestamp());
while ($i !== 0 && $i <= $until->getTimestamp()) {
$i = (int) strtotime('+1 day', $i);
if ($i >= $today) {
$eventStart = new \DateTime();
$eventStart->setTimestamp($i);
$eventStart->setTime((int) $start->format('H'), (int) $start->format('i'));
$eventEnd = new \DateTime();
$eventEnd->setTimestamp($i);
$eventEnd->setTime((int) $until->format('H'), (int) $until->format('i'));
$this->tmpCurrentEvent->addDate(Date::createFromDestinationData(
$eventStart,
$eventEnd,
$canceled
));
}
}
} elseif ($date['freq'] == 'Weekly' && !empty($date['weekdays']) && !empty($date['repeatUntil'])) {
foreach ($date['weekdays'] as $day) {
$this->logger->info('Setup weekly interval dates for ' . $day);
$this->logger->info('Start ' . $date['start']);
$this->logger->info('End ' . $date['repeatUntil']);
$start = new \DateTime($date['start'], new \DateTimeZone($date['tz']));
$until = new \DateTime($date['repeatUntil'], new \DateTimeZone($date['tz']));
for ($i = strtotime($day, $start->getTimestamp()); $i <= $until->getTimestamp(); $i = strtotime('+1 week', $i)) {
if ($i >= $today) {
$eventStart = new \DateTime();
$eventStart->setTimestamp($i);
$eventStart->setTime((int) $start->format('H'), (int) $start->format('i'));
$eventEnd = new \DateTime();
$eventEnd->setTimestamp($i);
$eventEnd->setTime((int) $until->format('H'), (int) $until->format('i'));
$this->tmpCurrentEvent->addDate(Date::createFromDestinationData(
$eventStart,
$eventEnd,
$canceled
));
}
}
}
}
}
$dates = $this->datesFactory->createDates($timeIntervals, $canceled);
foreach ($dates as $date) {
$this->tmpCurrentEvent->addDate($date);
}
$this->logger->info('Finished setup dates');
}

View file

@ -0,0 +1,179 @@
<?php
namespace Wrm\Events\Service\DestinationDataImportService;
use Wrm\Events\Domain\Model\Date;
class DatesFactory
{
/**
* @return \Generator<Date>
*/
public function createDates(
array $timeIntervals,
bool $canceled
): \Generator {
foreach ($timeIntervals as $date) {
$dates = $this->createDate($date, $canceled);
if (!$dates instanceof \Generator) {
return null;
}
foreach ($dates as $createdDate) {
yield $createdDate;
}
}
}
/**
* @return \Generator<Date>|null
*/
private function createDate(
array $date,
bool $canceled
): ?\Generator {
if ($this->isDateSingleDate($date)) {
return $this->createSingleDate($date, $canceled);
}
if ($this->isDateInterval($date)) {
return $this->createDateFromInterval($date, $canceled);
}
return null;
}
private function isDateSingleDate(array $date): bool
{
return empty($date['interval']);
}
private function isDateInterval(array $date): bool
{
if (empty($date['repeatUntil'])) {
return false;
}
$frequency = $date['freq'] ?? '';
if ($frequency == 'Daily' && empty($date['weekdays'])) {
return true;
}
if ($frequency == 'Weekly' && !empty($date['weekdays'])) {
return true;
}
return false;
}
/**
* @return \Generator<Date>
*/
private function createSingleDate(
array $date,
bool $canceled
): \Generator {
if (strtotime($date['start']) > $this->getToday()) {
yield Date::createFromDestinationDataDate($date, $canceled);
}
}
/**
* @return \Generator<Date>|null
*/
private function createDateFromInterval(
array $date,
bool $canceled
): ?\Generator {
if ($date['freq'] == 'Daily') {
return $this->createDailyDates($date, $canceled);
}
if ($date['freq'] == 'Weekly') {
return $this->createWeeklyDates($date, $canceled);
}
return null;
}
/**
* @return \Generator<Date>
*/
private function createDailyDates(
array $date,
bool $canceled
): \Generator {
$today = $this->getToday();
$start = new \DateTime($date['start'], new \DateTimeZone($date['tz']));
$until = new \DateTime($date['repeatUntil'], new \DateTimeZone($date['tz']));
$i = (int) strtotime($start->format('l'), $start->getTimestamp());
while ($i !== 0 && $i <= $until->getTimestamp()) {
$i = (int) strtotime('+1 day', $i);
if ($i < $today) {
continue;
}
yield $this->createDateFromStartAndUntil(
(string) $i,
$start,
$until,
$canceled
);
}
}
/**
* @return \Generator<Date>
*/
private function createWeeklyDates(
array $date,
bool $canceled
): \Generator {
$today = $this->getToday();
$start = new \DateTime($date['start'], new \DateTimeZone($date['tz']));
$until = new \DateTime($date['repeatUntil'], new \DateTimeZone($date['tz']));
foreach ($date['weekdays'] as $day) {
$i = strtotime($day, $start->getTimestamp());
while ($i <= $until->getTimestamp()) {
if ($i < $today) {
continue;
}
yield $this->createDateFromStartAndUntil(
(string) $i,
$start,
$until,
$canceled
);
$i = strtotime('+1 week', $i);
}
}
}
private function createDateFromStartAndUntil(
string $timestamp,
\DateTime $start,
\DateTime $until,
bool $canceled
): Date {
$eventStart = new \DateTime('@' . $timestamp);
$eventStart->setTime((int) $start->format('H'), (int) $start->format('i'));
$eventEnd = new \DateTime('@' . $timestamp);
$eventEnd->setTime((int) $until->format('H'), (int) $until->format('i'));
return Date::createFromDestinationData(
$eventStart,
$eventEnd,
$canceled
);
}
private function getToday(): int
{
return (int) date('U', strtotime('midnight'));
}
}

View file

@ -0,0 +1,340 @@
<?php
declare(strict_types=1);
namespace Wrm\Events\Tests\Unit\Service\DestinationDataImportService;
use PHPUnit\Framework\TestCase;
use Wrm\Events\Domain\Model\Date;
use Wrm\Events\Service\DestinationDataImportService\DatesFactory;
/**
* @covers \Wrm\Events\Service\DestinationDataImportService\DatesFactory
*/
class DatesFactoryTest extends TestCase
{
/**
* @test
*/
public function canBeCreated(): void
{
$subject = new DatesFactory();
self::assertInstanceOf(
DatesFactory::class,
$subject
);
}
/**
* @test
* @dataProvider possibleUnkownInput
*/
public function returnsNoResultOnUnkownInput(array $unkownInput): void
{
$subject = new DatesFactory();
$result = $subject->createDates($unkownInput, false);
self::assertInstanceOf(\Generator::class, $result);
self::assertCount(0, $result);
}
public function possibleUnkownInput(): array
{
return [
'Empty Intervals' => [
'unkownInput' => [],
],
'Intervals with interval no repeatUntil' => [
'unkownInput' => [
[
'interval' => 1,
],
],
],
'Intervals with interval and repeatUntil bu no freq' => [
'unkownInput' => [
[
'interval' => 1,
'repeatUntil' => 'something',
],
],
],
];
}
/**
* @test
*/
public function returnsSingleNotCanceledDate(): void
{
$subject = new DatesFactory();
$result = $subject->createDates([[
'start' => '2099-06-21T16:00:00+02:00',
'end' => '2099-06-21T22:00:00+02:00',
'tz' => 'Europe/Berlin',
]], false);
self::assertInstanceOf(\Generator::class, $result);
$firstEntry = $result->current();
self::assertCount(1, $result);
self::assertInstanceOf(Date::class, $firstEntry);
self::assertSame('4085733600', $firstEntry->getStart()->format('U'));
self::assertSame('4085755200', $firstEntry->getEnd()->format('U'));
self::assertSame('no', $firstEntry->getCanceled());
}
/**
* @test
*/
public function returnsSingleCanceledDate(): void
{
$subject = new DatesFactory();
$result = $subject->createDates([[
'start' => '2099-06-21T16:00:00+02:00',
'end' => '2099-06-21T22:00:00+02:00',
'tz' => 'Europe/Berlin',
]], true);
self::assertInstanceOf(\Generator::class, $result);
$result = iterator_to_array($result);
self::assertCount(1, $result);
self::assertInstanceOf(Date::class, $result[0]);
self::assertSame('4085733600', $result[0]->getStart()->format('U'));
self::assertSame('4085755200', $result[0]->getEnd()->format('U'));
self::assertSame('canceled', $result[0]->getCanceled());
}
/**
* @test
*/
public function returnsCanceledDatesOnDailyBasis(): void
{
$subject = new DatesFactory();
$result = $subject->createDates([[
'start' => '2099-04-01T16:00:00+02:00',
'end' => '2099-04-01T17:00:00+02:00',
'repeatUntil' => '2099-04-03T18:00:00+02:00',
'tz' => 'Europe/Berlin',
'freq' => 'Daily',
'interval' => 1,
]], true);
self::assertInstanceOf(\Generator::class, $result);
$result = iterator_to_array($result);
self::assertCount(3, $result);
self::assertInstanceOf(Date::class, $result[0]);
self::assertSame('4078828800', $result[0]->getStart()->format('U'));
self::assertSame('4078836000', $result[0]->getEnd()->format('U'));
self::assertSame('canceled', $result[0]->getCanceled());
self::assertSame('4079001600', $result[2]->getStart()->format('U'));
self::assertSame('4079008800', $result[2]->getEnd()->format('U'));
self::assertSame('canceled', $result[2]->getCanceled());
}
/**
* @test
*/
public function returnsNotCanceledDatesOnDailyBasis(): void
{
$subject = new DatesFactory();
$result = $subject->createDates([[
'start' => '2099-04-01T16:00:00+02:00',
'end' => '2099-04-01T17:00:00+02:00',
'repeatUntil' => '2099-04-03T18:00:00+02:00',
'tz' => 'Europe/Berlin',
'freq' => 'Daily',
'interval' => 1,
]], false);
self::assertInstanceOf(\Generator::class, $result);
$result = iterator_to_array($result);
self::assertCount(3, $result);
self::assertInstanceOf(Date::class, $result[0]);
self::assertSame('4078828800', $result[0]->getStart()->format('U'));
self::assertSame('4078836000', $result[0]->getEnd()->format('U'));
self::assertSame('no', $result[0]->getCanceled());
self::assertSame('4079001600', $result[2]->getStart()->format('U'));
self::assertSame('4079008800', $result[2]->getEnd()->format('U'));
self::assertSame('no', $result[2]->getCanceled());
}
/**
* @test
*/
public function returnsCanceledDatesOnWeeklyBasis(): void
{
$subject = new DatesFactory();
$result = $subject->createDates([[
'weekdays' => [
'Saturday',
'Sunday',
],
'start' => '2099-03-02T11:00:00+01:00',
'end' => '2099-03-02T13:00:00+01:00',
'repeatUntil' => '2099-03-15T13:00:00+02:00',
'tz' => 'Europe/Berlin',
'freq' => 'Weekly',
'interval' => 1,
]], true);
self::assertInstanceOf(\Generator::class, $result);
$result = iterator_to_array($result);
self::assertCount(4, $result);
self::assertInstanceOf(Date::class, $result[0]);
self::assertSame('4076564400', $result[0]->getStart()->format('U'));
self::assertSame('4076571600', $result[0]->getEnd()->format('U'));
self::assertSame('canceled', $result[0]->getCanceled());
self::assertSame('4077255600', $result[3]->getStart()->format('U'));
self::assertSame('4077262800', $result[3]->getEnd()->format('U'));
self::assertSame('canceled', $result[3]->getCanceled());
}
/**
* @test
*/
public function returnsNotCanceledDatesOnWeeklyBasis(): void
{
$subject = new DatesFactory();
$result = $subject->createDates([[
'weekdays' => [
'Saturday',
'Sunday',
],
'start' => '2099-03-02T11:00:00+01:00',
'end' => '2099-03-02T13:00:00+01:00',
'repeatUntil' => '2099-03-15T13:00:00+02:00',
'tz' => 'Europe/Berlin',
'freq' => 'Weekly',
'interval' => 1,
]], false);
self::assertInstanceOf(\Generator::class, $result);
$result = iterator_to_array($result);
self::assertCount(4, $result);
self::assertInstanceOf(Date::class, $result[0]);
self::assertSame('4076564400', $result[0]->getStart()->format('U'));
self::assertSame('4076571600', $result[0]->getEnd()->format('U'));
self::assertSame('no', $result[0]->getCanceled());
self::assertSame('4077255600', $result[3]->getStart()->format('U'));
self::assertSame('4077262800', $result[3]->getEnd()->format('U'));
self::assertSame('no', $result[3]->getCanceled());
}
/**
* @test
*/
public function returnsCanceledDatesOnMixedIntervals(): void
{
$subject = new DatesFactory();
$result = $subject->createDates([
[
'start' => '2099-06-21T16:00:00+02:00',
'end' => '2099-06-21T22:00:00+02:00',
'tz' => 'Europe/Berlin',
],
[
'start' => '2099-04-01T16:00:00+02:00',
'end' => '2099-04-01T17:00:00+02:00',
'repeatUntil' => '2099-04-03T18:00:00+02:00',
'tz' => 'Europe/Berlin',
'freq' => 'Daily',
'interval' => 1,
],
[
'weekdays' => [
'Saturday',
'Sunday',
],
'start' => '2099-03-02T11:00:00+01:00',
'end' => '2099-03-02T13:00:00+01:00',
'repeatUntil' => '2099-03-15T13:00:00+02:00',
'tz' => 'Europe/Berlin',
'freq' => 'Weekly',
'interval' => 1,
],
], true);
self::assertInstanceOf(\Generator::class, $result);
$result = iterator_to_array($result);
self::assertCount(8, $result);
foreach ($result as $date) {
self::assertInstanceOf(Date::class, $date);
self::assertSame('canceled', $date->getCanceled());
}
}
/**
* @test
*/
public function returnsNotCanceledDatesOnMixedIntervals(): void
{
$subject = new DatesFactory();
$result = $subject->createDates([
[
'start' => '2099-06-21T16:00:00+02:00',
'end' => '2099-06-21T22:00:00+02:00',
'tz' => 'Europe/Berlin',
],
[
'start' => '2099-04-01T16:00:00+02:00',
'end' => '2099-04-01T17:00:00+02:00',
'repeatUntil' => '2099-04-03T18:00:00+02:00',
'tz' => 'Europe/Berlin',
'freq' => 'Daily',
'interval' => 1,
],
[
'weekdays' => [
'Saturday',
'Sunday',
],
'start' => '2099-03-02T11:00:00+01:00',
'end' => '2099-03-02T13:00:00+01:00',
'repeatUntil' => '2099-03-15T13:00:00+02:00',
'tz' => 'Europe/Berlin',
'freq' => 'Weekly',
'interval' => 1,
],
], false);
self::assertInstanceOf(\Generator::class, $result);
$result = iterator_to_array($result);
self::assertCount(8, $result);
foreach ($result as $date) {
self::assertInstanceOf(Date::class, $date);
self::assertSame('no', $date->getCanceled());
}
}
}