diff options
author | Igor Scheller <igor.scheller@igorshp.de> | 2019-11-27 23:43:21 +0100 |
---|---|---|
committer | Igor Scheller <igor.scheller@igorshp.de> | 2019-12-08 02:20:48 +0100 |
commit | 42721e95726559b4a601240bb5b0fe4e5d755b2a (patch) | |
tree | 6810e05f845ca787acc1d02fa82d3df15cd0ef9b /src | |
parent | 377b390c97afb9106fd9a139819d00306f996f24 (diff) |
Added Schedule parsing and replaced old Fahrplan importer
Resolves #553 (Change Frab Import from xCal to XML)
Resolves #538 (Feature Request: Multi Frab Import)
Diffstat (limited to 'src')
-rw-r--r-- | src/Helpers/Schedule/CalculatesTime.php | 24 | ||||
-rw-r--r-- | src/Helpers/Schedule/Conference.php | 129 | ||||
-rw-r--r-- | src/Helpers/Schedule/Day.php | 88 | ||||
-rw-r--r-- | src/Helpers/Schedule/Event.php | 337 | ||||
-rw-r--r-- | src/Helpers/Schedule/Room.php | 52 | ||||
-rw-r--r-- | src/Helpers/Schedule/Schedule.php | 111 | ||||
-rw-r--r-- | src/Helpers/Schedule/XmlParser.php | 172 | ||||
-rw-r--r-- | src/Http/GuzzleServiceProvider.php | 25 | ||||
-rw-r--r-- | src/Middleware/LegacyMiddleware.php | 18 | ||||
-rw-r--r-- | src/Models/Shifts/Schedule.php | 31 | ||||
-rw-r--r-- | src/Models/Shifts/ScheduleShift.php | 38 |
11 files changed, 1017 insertions, 8 deletions
diff --git a/src/Helpers/Schedule/CalculatesTime.php b/src/Helpers/Schedule/CalculatesTime.php new file mode 100644 index 00000000..c9dbeea1 --- /dev/null +++ b/src/Helpers/Schedule/CalculatesTime.php @@ -0,0 +1,24 @@ +<?php + +declare(strict_types=1); + +namespace Engelsystem\Helpers\Schedule; + +trait CalculatesTime +{ + /** + * @param string $time + * @return int + */ + protected function secondsFromTime(string $time): int + { + $seconds = 0; + $duration = explode(':', $time); + + foreach (array_slice($duration, 0, 2) as $key => $times) { + $seconds += [60 * 60, 60][$key] * $times; + } + + return $seconds; + } +} diff --git a/src/Helpers/Schedule/Conference.php b/src/Helpers/Schedule/Conference.php new file mode 100644 index 00000000..0819d059 --- /dev/null +++ b/src/Helpers/Schedule/Conference.php @@ -0,0 +1,129 @@ +<?php + +declare(strict_types=1); + +namespace Engelsystem\Helpers\Schedule; + +class Conference +{ + use CalculatesTime; + + /** @var string required */ + protected $title; + + /** @var string required */ + protected $acronym; + + /** @var string|null */ + protected $start; + + /** @var string|null */ + protected $end; + + /** @var int|null */ + protected $days; + + /** @var string|null */ + protected $timeslotDuration; + + /** @var string|null */ + protected $baseUrl; + + /** + * Event constructor. + * + * @param string $title + * @param string $acronym + * @param string|null $start + * @param string|null $end + * @param int|null $days + * @param string|null $timeslotDuration + * @param string|null $baseUrl + */ + public function __construct( + string $title, + string $acronym, + ?string $start = null, + ?string $end = null, + ?int $days = null, + ?string $timeslotDuration = null, + ?string $baseUrl = null + ) { + $this->title = $title; + $this->acronym = $acronym; + $this->start = $start; + $this->end = $end; + $this->days = $days; + $this->timeslotDuration = $timeslotDuration; + $this->baseUrl = $baseUrl; + } + + /** + * @return string + */ + public function getTitle(): string + { + return $this->title; + } + + /** + * @return string + */ + public function getAcronym(): string + { + return $this->acronym; + } + + /** + * @return string|null + */ + public function getStart(): ?string + { + return $this->start; + } + + /** + * @return string|null + */ + public function getEnd(): ?string + { + return $this->end; + } + + /** + * @return int|null + */ + public function getDays(): ?int + { + return $this->days; + } + + /** + * @return string|null + */ + public function getTimeslotDuration(): ?string + { + return $this->timeslotDuration; + } + + /** + * @return int|null + */ + public function getTimeslotDurationSeconds(): ?int + { + $duration = $this->getTimeslotDuration(); + if (!$duration) { + return null; + } + + return $this->secondsFromTime($duration); + } + + /** + * @return string|null + */ + public function getBaseUrl(): ?string + { + return $this->baseUrl; + } +} diff --git a/src/Helpers/Schedule/Day.php b/src/Helpers/Schedule/Day.php new file mode 100644 index 00000000..03106e8f --- /dev/null +++ b/src/Helpers/Schedule/Day.php @@ -0,0 +1,88 @@ +<?php + +declare(strict_types=1); + +namespace Engelsystem\Helpers\Schedule; + +use Carbon\Carbon; + +class Day +{ + /** @var string required */ + protected $date; + + /** @var Carbon required */ + protected $start; + + /** @var Carbon required */ + protected $end; + + /** @var int required */ + protected $index; + + /** @var Room[] */ + protected $room; + + /** + * Day constructor. + * + * @param string $date + * @param Carbon $start + * @param Carbon $end + * @param int $index + * @param Room[] $rooms + */ + public function __construct( + string $date, + Carbon $start, + Carbon $end, + int $index, + array $rooms = [] + ) { + $this->date = $date; + $this->start = $start; + $this->end = $end; + $this->index = $index; + $this->room = $rooms; + } + + /** + * @return string + */ + public function getDate(): string + { + return $this->date; + } + + /** + * @return Carbon + */ + public function getStart(): Carbon + { + return $this->start; + } + + /** + * @return Carbon + */ + public function getEnd(): Carbon + { + return $this->end; + } + + /** + * @return int + */ + public function getIndex(): int + { + return $this->index; + } + + /** + * @return Room[] + */ + public function getRoom(): array + { + return $this->room; + } +} diff --git a/src/Helpers/Schedule/Event.php b/src/Helpers/Schedule/Event.php new file mode 100644 index 00000000..46970e7b --- /dev/null +++ b/src/Helpers/Schedule/Event.php @@ -0,0 +1,337 @@ +<?php + +declare(strict_types=1); + +namespace Engelsystem\Helpers\Schedule; + +use Carbon\Carbon; + +class Event +{ + use CalculatesTime; + + /** @var string required globally unique */ + protected $guid; + + /** @var int required globally unique */ + protected $id; + + /** @var Room required, string in XML */ + protected $room; + + /** @var string required */ + protected $title; + + /** @var string required */ + protected $subtitle; + + /** @var string required */ + protected $type; + + /** @var Carbon required */ + protected $date; + + /** @var string required time (hh:mm:ss || hh:mm) */ + protected $start; + + /** @var string required (h?h:mm:ss || h?h:mm) */ + protected $duration; + + /** @var string required */ + protected $abstract; + + /** @var string required globally unique */ + protected $slug; + + /** @var string required */ + protected $track; + + /** @var string|null */ + protected $logo; + + /** @var string[] id => name */ + protected $persons; + + /** @var string|null two letter code */ + protected $language; + + /** @var string|null */ + protected $description; + + /** @var string|null license (and opt out in XML, null if not recorded, empty if no license defined) */ + protected $recording; + + /** @var array href => title */ + protected $links; + + /** @var array href => name */ + protected $attachments; + + /** @var string|null */ + protected $url; + + /** @var string|null */ + protected $videoDownloadUrl; + + /** @var Carbon Calculated */ + protected $endDate; + + /** + * Event constructor. + * + * @param string $guid + * @param int $id + * @param Room $room + * @param string $title + * @param string $subtitle + * @param string $type + * @param Carbon $date + * @param string $start + * @param string $duration + * @param string $abstract + * @param string $slug + * @param string $track + * @param string|null $logo + * @param string[] $persons + * @param string|null $language + * @param string|null $description + * @param string|null $recording license + * @param array $links + * @param array $attachments + * @param string|null $url + * @param string|null $videoDownloadUrl + */ + public function __construct( + string $guid, + int $id, + Room $room, + string $title, + string $subtitle, + string $type, + Carbon $date, + string $start, + string $duration, + string $abstract, + string $slug, + string $track, + ?string $logo = null, + array $persons = [], + ?string $language = null, + ?string $description = null, + string $recording = '', + array $links = [], + array $attachments = [], + ?string $url = null, + ?string $videoDownloadUrl = null + ) { + $this->guid = $guid; + $this->id = $id; + $this->room = $room; + $this->title = $title; + $this->subtitle = $subtitle; + $this->type = $type; + $this->date = $date; + $this->start = $start; + $this->duration = $duration; + $this->abstract = $abstract; + $this->slug = $slug; + $this->track = $track; + $this->logo = $logo; + $this->persons = $persons; + $this->language = $language; + $this->description = $description; + $this->recording = $recording; + $this->links = $links; + $this->attachments = $attachments; + $this->url = $url; + $this->videoDownloadUrl = $videoDownloadUrl; + + $this->endDate = $this->date + ->copy() + ->addSeconds($this->getDurationSeconds()); + } + + /** + * @return string + */ + public function getGuid(): string + { + return $this->guid; + } + + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return Room + */ + public function getRoom(): Room + { + return $this->room; + } + + /** + * @return string + */ + public function getTitle(): string + { + return $this->title; + } + + /** + * @return string + */ + public function getSubtitle(): string + { + return $this->subtitle; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @return Carbon + */ + public function getDate(): Carbon + { + return $this->date; + } + + /** + * @return string + */ + public function getStart(): string + { + return $this->start; + } + + /** + * @return string + */ + public function getDuration(): string + { + return $this->duration; + } + + /** + * @return int + */ + public function getDurationSeconds(): int + { + return $this->secondsFromTime($this->duration); + } + + /** + * @return string + */ + public function getAbstract(): string + { + return $this->abstract; + } + + /** + * @return string + */ + public function getSlug(): string + { + return $this->slug; + } + + /** + * @return string + */ + public function getTrack(): string + { + return $this->track; + } + + /** + * @return string|null + */ + public function getLogo(): ?string + { + return $this->logo; + } + + /** + * @return string[] + */ + public function getPersons(): array + { + return $this->persons; + } + + /** + * @return string|null + */ + public function getLanguage(): ?string + { + return $this->language; + } + + /** + * @return string|null + */ + public function getDescription(): ?string + { + return $this->description; + } + + /** + * @return string|null + */ + public function getRecording(): ?string + { + return $this->recording; + } + + /** + * @return array + */ + public function getLinks(): array + { + return $this->links; + } + + /** + * @return array + */ + public function getAttachments(): array + { + return $this->attachments; + } + + /** + * @return string|null + */ + public function getUrl(): ?string + { + return $this->url; + } + + /** + * @return string|null + */ + public function getVideoDownloadUrl(): ?string + { + return $this->videoDownloadUrl; + } + + /** + * @return Carbon + */ + public function getEndDate(): Carbon + { + return $this->endDate; + } +} diff --git a/src/Helpers/Schedule/Room.php b/src/Helpers/Schedule/Room.php new file mode 100644 index 00000000..45a09f5f --- /dev/null +++ b/src/Helpers/Schedule/Room.php @@ -0,0 +1,52 @@ +<?php + +declare(strict_types=1); + +namespace Engelsystem\Helpers\Schedule; + +class Room +{ + /** @var string required */ + protected $name; + + /** @var Event[] */ + protected $event; + + /** + * Room constructor. + * + * @param string $name + * @param Event[] $events + */ + public function __construct( + string $name, + array $events = [] + ) { + $this->name = $name; + $this->event = $events; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return Event[] + */ + public function getEvent(): array + { + return $this->event; + } + + /** + * @param Event[] $event + */ + public function setEvent(array $event): void + { + $this->event = $event; + } +} diff --git a/src/Helpers/Schedule/Schedule.php b/src/Helpers/Schedule/Schedule.php new file mode 100644 index 00000000..7150480c --- /dev/null +++ b/src/Helpers/Schedule/Schedule.php @@ -0,0 +1,111 @@ +<?php + +declare(strict_types=1); + +namespace Engelsystem\Helpers\Schedule; + +use Carbon\Carbon; + +class Schedule +{ + /** @var string */ + protected $version; + + /** @var Conference */ + protected $conference; + + /** @var Day[] */ + protected $day; + + /** + * @param string $version + * @param Conference $conference + * @param Day[] $days + */ + public function __construct( + string $version, + Conference $conference, + array $days + ) { + $this->version = $version; + $this->conference = $conference; + $this->day = $days; + } + + /** + * @return string + */ + public function getVersion(): string + { + return $this->version; + } + + /** + * @return Conference + */ + public function getConference(): Conference + { + return $this->conference; + } + + /** + * @return Day[] + */ + public function getDay(): array + { + return $this->day; + } + + /** + * @return Room[] + */ + public function getRooms(): array + { + $rooms = []; + foreach ($this->day as $day) { + foreach ($day->getRoom() as $room) { + $name = $room->getName(); + $rooms[$name] = $room; + } + } + + return $rooms; + } + + + /** + * @return Carbon|null + */ + public function getStartDateTime(): ?Carbon + { + $start = null; + foreach ($this->day as $day) { + $time = $day->getStart(); + if ($time > $start && $start) { + continue; + } + + $start = $time; + } + + return $start; + } + + /** + * @return Carbon|null + */ + public function getEndDateTime(): ?Carbon + { + $end = null; + foreach ($this->day as $day) { + $time = $day->getEnd(); + if ($time < $end && $end) { + continue; + } + + $end = $time; + } + + return $end; + } +} diff --git a/src/Helpers/Schedule/XmlParser.php b/src/Helpers/Schedule/XmlParser.php new file mode 100644 index 00000000..1492aaca --- /dev/null +++ b/src/Helpers/Schedule/XmlParser.php @@ -0,0 +1,172 @@ +<?php + +declare(strict_types=1); + +namespace Engelsystem\Helpers\Schedule; + +use Carbon\Carbon; +use SimpleXMLElement; + +class XmlParser +{ + /** @var SimpleXMLElement */ + protected $scheduleXML; + + /** @var Schedule */ + protected $schedule; + + /** + * @param string $xml + * @return bool + */ + public function load(string $xml): bool + { + $this->scheduleXML = simplexml_load_string($xml); + + if (!$this->scheduleXML) { + return false; + } + + $this->parseXml(); + + return true; + } + + /** + * Parse the predefined XML content + */ + protected function parseXml(): void + { + $version = $this->getFirstXpathContent('version'); + $conference = new Conference( + $this->getFirstXpathContent('conference/title'), + $this->getFirstXpathContent('conference/acronym'), + $this->getFirstXpathContent('conference/start'), + $this->getFirstXpathContent('conference/end'), + (int)$this->getFirstXpathContent('conference/days'), + $this->getFirstXpathContent('conference/timeslot_duration'), + $this->getFirstXpathContent('conference/base_url') + ); + $days = []; + + foreach ($this->scheduleXML->xpath('day') as $day) { + $rooms = []; + + foreach ($day->xpath('room') as $roomElement) { + $room = new Room( + (string)$roomElement->attributes()['name'] + ); + + $events = $this->parseEvents($roomElement->xpath('event'), $room); + $room->setEvent($events); + $rooms[] = $room; + } + + $days[] = new Day( + (string)$day->attributes()['date'], + new Carbon($day->attributes()['start']), + new Carbon($day->attributes()['end']), + (int)$day->attributes()['index'], + $rooms + ); + } + + $this->schedule = new Schedule( + $version, + $conference, + $days + ); + } + + /** + * @param SimpleXMLElement[] $eventElements + * @param Room $room + * @return array + */ + protected function parseEvents(array $eventElements, Room $room): array + { + $events = []; + + foreach ($eventElements as $event) { + $persons = $this->getListFromSequence($event, 'persons', 'person', 'id'); + $links = $this->getListFromSequence($event, 'links', 'link', 'href'); + $attachments = $this->getListFromSequence($event, 'attachments', 'attachment', 'href'); + + $recording = ''; + $recordingElement = $event->xpath('recording')[0]; + if ($this->getFirstXpathContent('optout', $recordingElement) == 'false') { + $recording = $this->getFirstXpathContent('license', $recordingElement); + } + + $events[] = new Event( + (string)$event->attributes()['guid'], + (int)$event->attributes()['id'], + $room, + $this->getFirstXpathContent('title', $event), + $this->getFirstXpathContent('subtitle', $event), + $this->getFirstXpathContent('type', $event), + new Carbon($this->getFirstXpathContent('date', $event)), + $this->getFirstXpathContent('start', $event), + $this->getFirstXpathContent('duration', $event), + $this->getFirstXpathContent('abstract', $event), + $this->getFirstXpathContent('slug', $event), + $this->getFirstXpathContent('track', $event), + $this->getFirstXpathContent('logo', $event) ?: null, + $persons, + $this->getFirstXpathContent('language', $event) ?: null, + $this->getFirstXpathContent('description', $event) ?: null, + $recording, + $links, + $attachments, + $this->getFirstXpathContent('url', $event) ?: null, + $this->getFirstXpathContent('video_download_url', $event) ?: null + ); + } + + return $events; + } + + /** + * @param string $path + * @param SimpleXMLElement|null $xml + * @return string + */ + protected function getFirstXpathContent(string $path, ?SimpleXMLElement $xml = null): string + { + $element = ($xml ?: $this->scheduleXML)->xpath($path); + + return $element ? (string)$element[0] : ''; + } + + /** + * Resolves a list from a sequence of elements + * + * @param SimpleXMLElement $element + * @param string $firstElement + * @param string $secondElement + * @param string $idAttribute + * @return array + */ + protected function getListFromSequence( + SimpleXMLElement $element, + string $firstElement, + string $secondElement, + string $idAttribute + ): array { + $items = []; + + foreach ($element->xpath($firstElement)[0]->xpath($secondElement) as $item) { + $items[(string)$item->attributes()[$idAttribute]] = (string)$item; + } + + return $items; + } + + /** + * @return Schedule + */ + public function getSchedule(): Schedule + { + return $this->schedule; + } +} diff --git a/src/Http/GuzzleServiceProvider.php b/src/Http/GuzzleServiceProvider.php new file mode 100644 index 00000000..f81a91f5 --- /dev/null +++ b/src/Http/GuzzleServiceProvider.php @@ -0,0 +1,25 @@ +<?php + +namespace Engelsystem\Http; + +use Engelsystem\Container\ServiceProvider; +use GuzzleHttp\Client as GuzzleClient; + +class GuzzleServiceProvider extends ServiceProvider +{ + public function register() + { + $this->app->when(GuzzleClient::class) + ->needs('$config') + ->give( + function () { + return [ + // No exception on >= 400 responses + 'http_errors' => false, + // Wait max n seconds for a response + 'timeout' => 2.0, + ]; + } + ); + } +} diff --git a/src/Middleware/LegacyMiddleware.php b/src/Middleware/LegacyMiddleware.php index e516f1f3..5e08858d 100644 --- a/src/Middleware/LegacyMiddleware.php +++ b/src/Middleware/LegacyMiddleware.php @@ -205,10 +205,6 @@ class LegacyMiddleware implements MiddlewareInterface $title = admin_groups_title(); $content = admin_groups(); return [$title, $content]; - case 'admin_import': - $title = admin_import_title(); - $content = admin_import(); - return [$title, $content]; case 'admin_shifts': $title = admin_shifts_title(); $content = admin_shifts(); @@ -239,9 +235,15 @@ class LegacyMiddleware implements MiddlewareInterface return response($content, (int)$page); } - return response(view('layouts/app', [ - 'title' => $title, - 'content' => msg() . $content, - ]), 200); + return response( + view( + 'layouts/app', + [ + 'title' => $title, + 'content' => msg() . $content, + ] + ), + 200 + ); } } diff --git a/src/Models/Shifts/Schedule.php b/src/Models/Shifts/Schedule.php new file mode 100644 index 00000000..c1eb2d9e --- /dev/null +++ b/src/Models/Shifts/Schedule.php @@ -0,0 +1,31 @@ +<?php + +namespace Engelsystem\Models\Shifts; + +use Engelsystem\Models\BaseModel; +use Illuminate\Database\Eloquent\Collection; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Query\Builder as QueryBuilder; + +/** + * @property int $id + * @property string $url + * + * @property-read QueryBuilder|Collection|ScheduleShift[] $scheduleShifts + * + * @method static QueryBuilder|Schedule[] whereId($value) + * @method static QueryBuilder|Schedule[] whereUrl($value) + */ +class Schedule extends BaseModel +{ + /** @var array Values that are mass assignable */ + protected $fillable = ['url']; + + /** + * @return HasMany + */ + public function scheduleShifts() + { + return $this->hasMany(ScheduleShift::class); + } +} diff --git a/src/Models/Shifts/ScheduleShift.php b/src/Models/Shifts/ScheduleShift.php new file mode 100644 index 00000000..368c60b1 --- /dev/null +++ b/src/Models/Shifts/ScheduleShift.php @@ -0,0 +1,38 @@ +<?php + +namespace Engelsystem\Models\Shifts; + +use Engelsystem\Models\BaseModel; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Query\Builder as QueryBuilder; + +/** + * @property int $shift_id + * @property int $schedule_id + * @property string $guid + * + * @property-read QueryBuilder|Schedule $schedule + * + * @method static QueryBuilder|ScheduleShift[] whereShiftId($value) + * @method static QueryBuilder|ScheduleShift[] whereScheduleId($value) + * @method static QueryBuilder|ScheduleShift[] whereGuid($value) + */ +class ScheduleShift extends BaseModel +{ + /** @var string The primary key for the model */ + protected $primaryKey = 'shift_id'; + + /** @var string Required because it is not schedule_shifts */ + protected $table = 'schedule_shift'; + + /** @var array Values that are mass assignable */ + protected $fillable = ['shift_id', 'schedule_id', 'guid']; + + /** + * @return BelongsTo + */ + public function schedule() + { + return $this->belongsTo(Schedule::class); + } +} |