Schedule Import: Moved file, initial cleanup, updated schedules

This commit is contained in:
Igor Scheller 2023-07-30 18:23:01 +02:00 committed by xuwhite
parent aef53a306b
commit 790a04dc14
6 changed files with 115 additions and 152 deletions

View File

@ -187,11 +187,11 @@ $route->addGroup(
$route->addGroup( $route->addGroup(
'/schedule', '/schedule',
function (RouteCollector $route): void { function (RouteCollector $route): void {
$route->get('', 'Admin\\Schedule\\ImportSchedule@index'); $route->get('', 'Admin\\ScheduleController@index');
$route->get('/edit[/{schedule_id:\d+}]', 'Admin\\Schedule\\ImportSchedule@edit'); $route->get('/edit[/{schedule_id:\d+}]', 'Admin\\ScheduleController@edit');
$route->post('/edit[/{schedule_id:\d+}]', 'Admin\\Schedule\\ImportSchedule@save'); $route->post('/edit[/{schedule_id:\d+}]', 'Admin\\ScheduleController@save');
$route->get('/load/{schedule_id:\d+}', 'Admin\\Schedule\\ImportSchedule@loadSchedule'); $route->get('/load/{schedule_id:\d+}', 'Admin\\ScheduleController@loadSchedule');
$route->post('/import/{schedule_id:\d+}', 'Admin\\Schedule\\ImportSchedule@importSchedule'); $route->post('/import/{schedule_id:\d+}', 'Admin\\ScheduleController@importSchedule');
} }
); );

View File

@ -59,8 +59,6 @@ $includeFiles = [
__DIR__ . '/../includes/pages/admin_user.php', __DIR__ . '/../includes/pages/admin_user.php',
__DIR__ . '/../includes/pages/user_myshifts.php', __DIR__ . '/../includes/pages/user_myshifts.php',
__DIR__ . '/../includes/pages/user_shifts.php', __DIR__ . '/../includes/pages/user_shifts.php',
__DIR__ . '/../includes/pages/schedule/ImportSchedule.php',
]; ];
foreach ($includeFiles as $file) { foreach ($includeFiles as $file) {

View File

@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Engelsystem\Controllers\Admin\Schedule; namespace Engelsystem\Controllers\Admin;
use Engelsystem\Controllers\NotificationType; use Engelsystem\Controllers\NotificationType;
use Engelsystem\Helpers\Carbon; use Engelsystem\Helpers\Carbon;
@ -17,7 +17,7 @@ use Engelsystem\Helpers\Uuid;
use Engelsystem\Http\Request; use Engelsystem\Http\Request;
use Engelsystem\Http\Response; use Engelsystem\Http\Response;
use Engelsystem\Models\Location; use Engelsystem\Models\Location;
use Engelsystem\Models\Shifts\Schedule as ScheduleUrl; use Engelsystem\Models\Shifts\Schedule as ScheduleModel;
use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
@ -25,56 +25,28 @@ use Engelsystem\Models\User\User;
use ErrorException; use ErrorException;
use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Database\Connection as DatabaseConnection; use Illuminate\Database\Connection as DatabaseConnection;
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
use Illuminate\Database\Eloquent\Collection as DatabaseCollection;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class ImportSchedule extends BaseController class ScheduleController extends BaseController
{ {
use HasUserNotifications; use HasUserNotifications;
/** @var DatabaseConnection */
protected $db;
/** @var LoggerInterface */
protected $log;
protected array $permissions = [ protected array $permissions = [
'schedule.import', 'schedule.import',
]; ];
/** @var XmlParser */ protected string $url = '/admin/schedule';
protected $parser;
/** @var Response */
protected $response;
/** @var SessionInterface */
protected $session;
/** @var string */
protected $url = '/admin/schedule';
/** @var GuzzleClient */
protected $guzzle;
public function __construct( public function __construct(
Response $response, protected Response $response,
SessionInterface $session, protected GuzzleClient $guzzle,
GuzzleClient $guzzle, protected XmlParser $parser,
XmlParser $parser, protected DatabaseConnection $db,
DatabaseConnection $db, protected LoggerInterface $log
LoggerInterface $log
) { ) {
$this->guzzle = $guzzle;
$this->parser = $parser;
$this->response = $response;
$this->session = $session;
$this->db = $db;
$this->log = $log;
} }
public function index(): Response public function index(): Response
@ -83,7 +55,7 @@ class ImportSchedule extends BaseController
'admin/schedule/index.twig', 'admin/schedule/index.twig',
[ [
'is_index' => true, 'is_index' => true,
'schedules' => ScheduleUrl::all(), 'schedules' => ScheduleModel::all(),
] ]
); );
} }
@ -91,7 +63,7 @@ class ImportSchedule extends BaseController
public function edit(Request $request): Response public function edit(Request $request): Response
{ {
$scheduleId = $request->getAttribute('schedule_id'); // optional $scheduleId = $request->getAttribute('schedule_id'); // optional
$schedule = ScheduleUrl::find($scheduleId); $schedule = ScheduleModel::find($scheduleId);
return $this->response->withView( return $this->response->withView(
'admin/schedule/edit.twig', 'admin/schedule/edit.twig',
@ -103,12 +75,15 @@ class ImportSchedule extends BaseController
); );
} }
/**
* @throws ErrorException
*/
public function save(Request $request): Response public function save(Request $request): Response
{ {
$scheduleId = $request->getAttribute('schedule_id'); // optional $scheduleId = $request->getAttribute('schedule_id'); // optional
/** @var ScheduleUrl $schedule */ /** @var ScheduleModel $schedule */
$schedule = ScheduleUrl::findOrNew($scheduleId); $schedule = ScheduleModel::findOrNew($scheduleId);
if ($request->request->has('delete')) { if ($request->request->has('delete')) {
return $this->delete($schedule); return $this->delete($schedule);
@ -173,7 +148,7 @@ class ImportSchedule extends BaseController
return redirect('/admin/schedule/load/' . $schedule->id); return redirect('/admin/schedule/load/' . $schedule->id);
} }
protected function delete(ScheduleUrl $schedule): Response protected function delete(ScheduleModel $schedule): Response
{ {
foreach ($schedule->scheduleShifts as $scheduleShift) { foreach ($schedule->scheduleShifts as $scheduleShift) {
// Only guid is needed here // Only guid is needed here
@ -196,6 +171,7 @@ class ImportSchedule extends BaseController
} }
$schedule->delete(); $schedule->delete();
$this->log->info('Schedule {name} deleted', ['name' => $schedule->name]);
$this->addNotification('schedule.delete.success'); $this->addNotification('schedule.delete.success');
return redirect('/admin/schedule'); return redirect('/admin/schedule');
} }
@ -209,7 +185,7 @@ class ImportSchedule extends BaseController
* @var Event[] $deleteEvents * @var Event[] $deleteEvents
* @var Room[] $newRooms * @var Room[] $newRooms
* @var int $shiftType * @var int $shiftType
* @var ScheduleUrl $scheduleUrl * @var ScheduleModel $scheduleModel
* @var Schedule $schedule * @var Schedule $schedule
* @var int $minutesBefore * @var int $minutesBefore
* @var int $minutesAfter * @var int $minutesAfter
@ -220,7 +196,7 @@ class ImportSchedule extends BaseController
$deleteEvents, $deleteEvents,
$newRooms, $newRooms,
, ,
$scheduleUrl, $scheduleModel,
$schedule $schedule
) = $this->getScheduleData($request); ) = $this->getScheduleData($request);
} catch (ErrorException $e) { } catch (ErrorException $e) {
@ -231,7 +207,7 @@ class ImportSchedule extends BaseController
return $this->response->withView( return $this->response->withView(
'admin/schedule/load.twig', 'admin/schedule/load.twig',
[ [
'schedule_id' => $scheduleUrl->id, 'schedule_id' => $scheduleModel->id,
'schedule' => $schedule, 'schedule' => $schedule,
'locations' => [ 'locations' => [
'add' => $newRooms, 'add' => $newRooms,
@ -254,7 +230,7 @@ class ImportSchedule extends BaseController
* @var Event[] $deleteEvents * @var Event[] $deleteEvents
* @var Room[] $newRooms * @var Room[] $newRooms
* @var int $shiftType * @var int $shiftType
* @var ScheduleUrl $scheduleUrl * @var ScheduleModel $schedule
*/ */
list( list(
$newEvents, $newEvents,
@ -262,14 +238,14 @@ class ImportSchedule extends BaseController
$deleteEvents, $deleteEvents,
$newRooms, $newRooms,
$shiftType, $shiftType,
$scheduleUrl $schedule
) = $this->getScheduleData($request); ) = $this->getScheduleData($request);
} catch (ErrorException $e) { } catch (ErrorException $e) {
$this->addNotification($e->getMessage(), NotificationType::ERROR); $this->addNotification($e->getMessage(), NotificationType::ERROR);
return back(); return back();
} }
$this->log('Started schedule "{name}" import', ['name' => $scheduleUrl->name]); $this->log->info('Started schedule "{name}" import', ['name' => $schedule->name]);
foreach ($newRooms as $room) { foreach ($newRooms as $room) {
$this->createLocation($room); $this->createLocation($room);
@ -283,7 +259,7 @@ class ImportSchedule extends BaseController
$locations $locations
->where('name', $event->getRoom()->getName()) ->where('name', $event->getRoom()->getName())
->first(), ->first(),
$scheduleUrl $schedule
); );
} }
@ -294,16 +270,16 @@ class ImportSchedule extends BaseController
$locations $locations
->where('name', $event->getRoom()->getName()) ->where('name', $event->getRoom()->getName())
->first(), ->first(),
$scheduleUrl $schedule
); );
} }
foreach ($deleteEvents as $event) { foreach ($deleteEvents as $event) {
$this->deleteEvent($event, $scheduleUrl); $this->deleteEvent($event, $schedule);
} }
$scheduleUrl->touch(); $schedule->touch();
$this->log('Ended schedule "{name}" import', ['name' => $scheduleUrl->name]); $this->log->info('Ended schedule "{name}" import', ['name' => $schedule->name]);
$this->addNotification('schedule.import.success'); $this->addNotification('schedule.import.success');
return redirect($this->url, 303); return redirect($this->url, 303);
@ -315,7 +291,7 @@ class ImportSchedule extends BaseController
$location->name = $room->getName(); $location->name = $room->getName();
$location->save(); $location->save();
$this->log('Created schedule location "{location}"', ['location' => $room->getName()]); $this->log->info('Created schedule location "{location}"', ['location' => $room->getName()]);
} }
protected function fireDeleteShiftEntryEvents(Event $event, ScheduleUrl $schedule): void protected function fireDeleteShiftEntryEvents(Event $event, ScheduleUrl $schedule): void
@ -349,7 +325,7 @@ class ImportSchedule extends BaseController
} }
} }
protected function createEvent(Event $event, int $shiftTypeId, Location $location, ScheduleUrl $scheduleUrl): void protected function createEvent(Event $event, int $shiftTypeId, Location $location, ScheduleModel $scheduleUrl): void
{ {
$user = auth()->user(); $user = auth()->user();
$eventTimeZone = Carbon::now()->timezone; $eventTimeZone = Carbon::now()->timezone;
@ -370,7 +346,7 @@ class ImportSchedule extends BaseController
$scheduleShift->shift()->associate($shift); $scheduleShift->shift()->associate($shift);
$scheduleShift->save(); $scheduleShift->save();
$this->log( $this->log->info(
'Created schedule shift "{shift}" in "{location}" ({from} {to}, {guid})', 'Created schedule shift "{shift}" in "{location}" ({from} {to}, {guid})',
[ [
'shift' => $shift->title, 'shift' => $shift->title,
@ -402,7 +378,7 @@ class ImportSchedule extends BaseController
$this->fireUpdateShiftUpdateEvent($oldShift, $shift); $this->fireUpdateShiftUpdateEvent($oldShift, $shift);
$this->log( $this->log->info(
'Updated schedule shift "{shift}" in "{location}" ({from} {to}, {guid})', 'Updated schedule shift "{shift}" in "{location}" ({from} {to}, {guid})',
[ [
'shift' => $shift->title, 'shift' => $shift->title,
@ -424,7 +400,7 @@ class ImportSchedule extends BaseController
$this->fireDeleteShiftEntryEvents($event, $schedule); $this->fireDeleteShiftEntryEvents($event, $schedule);
$this->log( $this->log->info(
'Deleted schedule shift "{shift}" in {location} ({from} {to}, {guid})', 'Deleted schedule shift "{shift}" in {location} ({from} {to}, {guid})',
[ [
'shift' => $shift->title, 'shift' => $shift->title,
@ -445,20 +421,19 @@ class ImportSchedule extends BaseController
} }
/** /**
* @param Request $request
* @return Event[]|Room[]|Location[] * @return Event[]|Room[]|Location[]
* @throws ErrorException * @throws ErrorException
*/ */
protected function getScheduleData(Request $request) protected function getScheduleData(Request $request): array
{ {
$scheduleId = (int) $request->getAttribute('schedule_id'); $scheduleId = (int) $request->getAttribute('schedule_id');
/** @var ScheduleUrl $scheduleUrl */ /** @var ScheduleModel $scheduleUrl */
$scheduleUrl = ScheduleUrl::findOrFail($scheduleId); $scheduleUrl = ScheduleModel::findOrFail($scheduleId);
try { try {
$scheduleResponse = $this->guzzle->get($scheduleUrl->url); $scheduleResponse = $this->guzzle->get($scheduleUrl->url);
} catch (ConnectException $e) { } catch (ConnectException | GuzzleException) {
throw new ErrorException('schedule.import.request-error'); throw new ErrorException('schedule.import.request-error');
} }
@ -503,16 +478,11 @@ class ImportSchedule extends BaseController
} }
/** /**
* @param Schedule $schedule
* @param ScheduleUrl $scheduleUrl
* @param int $shiftType
* @param int $minutesBefore
* @param int $minutesAfter
* @return Event[] * @return Event[]
*/ */
protected function shiftsDiff( protected function shiftsDiff(
Schedule $schedule, Schedule $schedule,
ScheduleUrl $scheduleUrl, ScheduleModel $scheduleUrl,
int $shiftType, int $shiftType,
int $minutesBefore, int $minutesBefore,
int $minutesAfter int $minutesAfter
@ -554,6 +524,7 @@ class ImportSchedule extends BaseController
$guid = $scheduleShift->guid; $guid = $scheduleShift->guid;
$shift = $scheduleShift->shift; $shift = $scheduleShift->shift;
$event = $scheduleEvents[$guid]; $event = $scheduleEvents[$guid];
/** @var Location $location */
$location = $locations->where('name', $event->getRoom()->getName())->first(); $location = $locations->where('name', $event->getRoom()->getName())->first();
if ( if (
@ -607,17 +578,17 @@ class ImportSchedule extends BaseController
/** /**
* @return Location[]|Collection * @return Location[]|Collection
*/ */
protected function getAllLocations(): Collection protected function getAllLocations(): Collection | array
{ {
return Location::all(); return Location::all();
} }
/** /**
* @param ScheduleUrl $scheduleUrl
* @param string[] $events * @param string[] $events
* @return QueryBuilder[]|DatabaseCollection|Collection|ScheduleShift[] *
* @return Collection|ScheduleShift[]
*/ */
protected function getScheduleShiftsByGuid(ScheduleUrl $scheduleUrl, array $events) protected function getScheduleShiftsByGuid(ScheduleModel $scheduleUrl, array $events): Collection | array
{ {
return ScheduleShift::with('shift.location') return ScheduleShift::with('shift.location')
->whereIn('guid', $events) ->whereIn('guid', $events)
@ -626,24 +597,14 @@ class ImportSchedule extends BaseController
} }
/** /**
* @param ScheduleUrl $scheduleUrl
* @param string[] $events * @param string[] $events
* @return QueryBuilder[]|DatabaseCollection|Collection|ScheduleShift[] * @return Collection|ScheduleShift[]
*/ */
protected function getScheduleShiftsWhereNotGuid(ScheduleUrl $scheduleUrl, array $events) protected function getScheduleShiftsWhereNotGuid(ScheduleModel $scheduleUrl, array $events): Collection | array
{ {
return ScheduleShift::with('shift.location') return ScheduleShift::with('shift.location')
->whereNotIn('guid', $events) ->whereNotIn('guid', $events)
->where('schedule_id', $scheduleUrl->id) ->where('schedule_id', $scheduleUrl->id)
->get(); ->get();
} }
/**
* @param string $message
* @param array $context
*/
protected function log(string $message, array $context = []): void
{
$this->log->info($message, $context);
}
} }

View File

@ -1,12 +1,12 @@
<?xml version='1.0' encoding='utf-8' ?> <?xml version='1.0' encoding='utf-8' ?>
<schedule <schedule
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/voc/voctosched/master/schema/basic.xsd" xsi:noNamespaceSchemaLocation="https://c3voc.de/schedule/schema.xsd"
> >
<version>dolor</version> <version>dolor</version>
<conference> <conference>
<title>Basic Test Event</title> <title>Basic Test Event</title>
<acronym>Test2</acronym> <acronym>test-2</acronym>
<start>2042-01-03</start> <start>2042-01-03</start>
<end>2042-01-03</end> <end>2042-01-03</end>
<days>1</days> <days>1</days>
@ -21,7 +21,7 @@
<start>14:15</start> <start>14:15</start>
<duration>00:45</duration> <duration>00:45</duration>
<room>Some Room</room> <room>Some Room</room>
<slug>lorem/ipsum</slug> <slug>lorem-42-ipsum</slug>
<recording> <recording>
<license>WTFPL</license> <license>WTFPL</license>
<optout>true</optout> <optout>true</optout>

View File

@ -1,20 +1,22 @@
<?xml version='1.0' encoding='utf-8' ?> <?xml version='1.0' encoding='utf-8' ?>
<schedule <schedule
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/voc/voctosched/master/schema/extended.xsd" xsi:noNamespaceSchemaLocation="https://c3voc.de/schedule/schema.xsd"
> >
<generator name="Engelsystem" version="1.2.3"/>
<version>Some version string</version> <version>Some version string</version>
<conference> <conference>
<title>Test Event</title> <title>Test Event</title>
<acronym>Test3</acronym> <acronym>test-3</acronym>
<start>2042-01-01</start> <start>2042-01-01T01:00:00+02:00</start>
<end>2042-01-01</end> <end>2042-01-01T22:59:00+02:00</end>
<days>1</days> <days>1</days>
<timeslot_duration>00:15</timeslot_duration> <timeslot_duration>00:15</timeslot_duration>
<base_url>https://foo.bar/baz/schedule/</base_url> <base_url>https://foo.bar/baz/schedule/</base_url>
<time_zone_name>Europe/Berlin</time_zone_name>
</conference> </conference>
<day index='1' date='2042-01-01' start='2042-01-01T01:00:00+02:00' end='2042-01-01T22:59:00+02:00'> <day index='1' date='2042-01-01' start='2042-01-01T01:00:00+02:00' end='2042-01-01T22:59:00+02:00'>
<room name='Rooming'> <room name='Rooming' guid="bf5f1132-82bd-4da2-bbe0-1abbf8daf4ab">
<event guid='e427cfa9-9ba1-4b14-a99f-bce83ffe5a1c' id='1337'> <event guid='e427cfa9-9ba1-4b14-a99f-bce83ffe5a1c' id='1337'>
<date>2042-01-01T12:30:00+02:00</date> <date>2042-01-01T12:30:00+02:00</date>
<title>Foo Bar Test</title> <title>Foo Bar Test</title>
@ -22,10 +24,12 @@
<start>12:30</start> <start>12:30</start>
<duration>00:30</duration> <duration>00:30</duration>
<room>Rooming</room> <room>Rooming</room>
<slug>foo-bar-test</slug> <slug>some-3-test</slug>
<recording> <recording>
<license>WTFPL</license> <license>WTFPL</license>
<optout>false</optout> <optout>false</optout>
<url>https://recorder.test/some-3-test/recorded</url>
<link>https://recorder.test/some-3-test</link>
</recording> </recording>
<video_download_url>https://foo.bar/baz/schedule/ipsum/recording.mp4</video_download_url> <video_download_url>https://foo.bar/baz/schedule/ipsum/recording.mp4</video_download_url>
<track>Testing</track> <track>Testing</track>

View File

@ -2,7 +2,7 @@
<version>0.0.1</version> <version>0.0.1</version>
<conference> <conference>
<title>Some Test Event</title> <title>Some Test Event</title>
<acronym>Test1</acronym> <acronym>test-1</acronym>
</conference> </conference>
<day index='1' date='2042-01-02' start='2042-01-02T00:00:00+00:00' end='2042-01-02T22:59:00+00:00'> <day index='1' date='2042-01-02' start='2042-01-02T00:00:00+00:00' end='2042-01-02T22:59:00+00:00'>
<room name='Random Room'> <room name='Random Room'>
@ -13,7 +13,7 @@
<start>13:30</start> <start>13:30</start>
<duration>00:30</duration> <duration>00:30</duration>
<room>Rooming</room> <room>Rooming</room>
<slug>minimum-setup-test</slug> <slug>minimum-1-setup</slug>
<track>Testing</track> <track>Testing</track>
<type>Talk</type> <type>Talk</type>
<abstract>A minimal description</abstract> <abstract>A minimal description</abstract>