From 4ef67b9bae2534cfd6faea6d006a49574f19e0c8 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 21 Apr 2022 08:07:25 +0200 Subject: [PATCH] Refactor date creation during import Split into own class to encapsulate the logic. --- Classes/Domain/Model/Date.php | 11 + .../Service/DestinationDataImportService.php | 86 +---- .../DatesFactory.php | 179 +++++++++ .../DatesFactoryTest.php | 340 ++++++++++++++++++ 4 files changed, 543 insertions(+), 73 deletions(-) create mode 100644 Classes/Service/DestinationDataImportService/DatesFactory.php create mode 100644 Tests/Unit/Service/DestinationDataImportService/DatesFactoryTest.php diff --git a/Classes/Domain/Model/Date.php b/Classes/Domain/Model/Date.php index 8479a92..a5ff9ec 100644 --- a/Classes/Domain/Model/Date.php +++ b/Classes/Domain/Model/Date.php @@ -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, diff --git a/Classes/Service/DestinationDataImportService.php b/Classes/Service/DestinationDataImportService.php index 2b07900..51e05d1 100644 --- a/Classes/Service/DestinationDataImportService.php +++ b/Classes/Service/DestinationDataImportService.php @@ -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'); } diff --git a/Classes/Service/DestinationDataImportService/DatesFactory.php b/Classes/Service/DestinationDataImportService/DatesFactory.php new file mode 100644 index 0000000..d3cedd1 --- /dev/null +++ b/Classes/Service/DestinationDataImportService/DatesFactory.php @@ -0,0 +1,179 @@ + + */ + 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|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 + */ + private function createSingleDate( + array $date, + bool $canceled + ): \Generator { + if (strtotime($date['start']) > $this->getToday()) { + yield Date::createFromDestinationDataDate($date, $canceled); + } + } + + /** + * @return \Generator|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 + */ + 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 + */ + 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')); + } +} diff --git a/Tests/Unit/Service/DestinationDataImportService/DatesFactoryTest.php b/Tests/Unit/Service/DestinationDataImportService/DatesFactoryTest.php new file mode 100644 index 0000000..6638627 --- /dev/null +++ b/Tests/Unit/Service/DestinationDataImportService/DatesFactoryTest.php @@ -0,0 +1,340 @@ +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()); + } + } +}