diff --git a/app/Enums/ShowRepeatType.php b/app/Enums/ShowRepeatType.php index a1c30cd..1660e0e 100644 --- a/app/Enums/ShowRepeatType.php +++ b/app/Enums/ShowRepeatType.php @@ -15,7 +15,14 @@ enum ShowRepeatType: int return constant("self::$name"); } - public static function fromValue(int $value){ - return constant("self::$value"); + + public static function fromValue(string $value){ + foreach (self::cases() as $case) { + if ($case->value === $value) { + return $case; + } + } + + throw new \InvalidArgumentException("No matching UserType found for value: $value"); } } diff --git a/app/Filters/Show/ShowDaysFilters.php b/app/Filters/Show/ShowDaysFilters.php new file mode 100644 index 0000000..1e56121 --- /dev/null +++ b/app/Filters/Show/ShowDaysFilters.php @@ -0,0 +1,37 @@ + IsFilter::class, + ]; + + public function apply($query, $filters) + { + foreach ($this->receivedFilters($filters) as $name => $value) { + switch ($name) { + case 'all': + $name = array_diff(array_keys($this->filters), ['all']); + $filterInstance = new $this->filters['all'](); + break; + default: + $filterInstance = new $this->filters[$name](); + break; + } + $query = $filterInstance($query, $name, $value); + } + + return $query; + } + + + public function receivedFilters($filters) + { + return $filters->only(array_keys($this->filters)); + } +} \ No newline at end of file diff --git a/app/Helpers/CarbonShowRepetition.php b/app/Helpers/CarbonShowRepetition.php index 989afe3..beee78c 100644 --- a/app/Helpers/CarbonShowRepetition.php +++ b/app/Helpers/CarbonShowRepetition.php @@ -40,6 +40,7 @@ class CarbonShowRepetition while ($currentDate->lte($dateStopInterval)) { if ($repeatTypeEnum === ShowRepeatType::monthly) { $nextMonth = $currentDate->addMonth(); + // With a flag we can skip this check and repeating based on the given month number if ($nextMonth->day > $currentDate->day) { $nextMonth->day = $currentDate->day; } @@ -52,4 +53,13 @@ class CarbonShowRepetition return $dates; } + + public static function getNextStartDate(string $showFirstDate, int $showDayNumber): Carbon { + $date = Carbon::createFromFormat('Y-m-d', $showFirstDate); + $dayOfWeek = $date->dayOfWeek; + if($dayOfWeek != $showDayNumber){ + $date = $date->next($showDayNumber); + } + return $date; + } } \ No newline at end of file diff --git a/app/Http/Controllers/Show/ShowController.php b/app/Http/Controllers/Show/ShowController.php index e4d217c..fe03d6a 100644 --- a/app/Http/Controllers/Show/ShowController.php +++ b/app/Http/Controllers/Show/ShowController.php @@ -42,16 +42,17 @@ class ShowController extends Controller public function store(Request $request) { try { - $showInfos = $request->show; - $showDaysRules = $request->showDaysRules; - $showDjs = $request->showDjs; - $show = Show::firstOrCreate($showInfos); - $this->manageShowDays($show, $showDaysRules); - $this->manageShowDjs($showDjs, $show); - }catch(Exception $e){ + $showData = $request->all(); + $showDays = $showData['show_days']; + $showDJs = $showData['show_djs']; + $this->createShow($showData, $showDays, $showDJs); + return response()->json([ + 'status' => 'success', + 'message' => 'Show saved successfully!' + ]); + } catch (Exception $e) { return response()->json(['message' => $e->getMessage()], 500); } - return response()->json(['message' => 'Show created successfully']); } public function show(ShowResource $show) diff --git a/app/Http/Controllers/Show/ShowDaysController.php b/app/Http/Controllers/Show/ShowDaysController.php new file mode 100644 index 0000000..0c8e96f --- /dev/null +++ b/app/Http/Controllers/Show/ShowDaysController.php @@ -0,0 +1,49 @@ +except('flattenShowDays')); + $showDays = (new ShowDays())->searchFilter($queryParams)->get(); + if($request->flattenShowDays) $showDays = $this->showDaysFlat($showDays); + return response()->json($showDays); + } + + public function store(ShowDaysRequest $request) + { + return new ShowDaysResource(ShowDays::create($request->validated())); + } + + public function show(ShowDays $ShowDays) + { + return new ShowDaysResource($ShowDays); + } + + public function update(ShowDaysRequest $request, ShowDays $ShowDays) + { + $ShowDays->update($request->validated()); + + return new ShowDaysResource($ShowDays); + } + + public function destroy(ShowDays $ShowDays) + { + $ShowDays->delete(); + + return response()->json(); + } +} diff --git a/app/Models/Show/ShowDays.php b/app/Models/Show/ShowDays.php index 6aca293..ff74aa0 100644 --- a/app/Models/Show/ShowDays.php +++ b/app/Models/Show/ShowDays.php @@ -2,6 +2,7 @@ namespace App\Models\Show; +use App\Filters\Show\ShowDaysFilters; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; @@ -41,4 +42,9 @@ class ShowDays extends Model { return $this->belongsTo(Show::class, 'show_id'); } + + public function scopeSearchFilter($query, $request) { + $filters = new ShowDaysFilters(); + return $filters->apply($query, $request); + } } diff --git a/app/Traits/Show/ShowDaysTrait.php b/app/Traits/Show/ShowDaysTrait.php index 7b301b4..a6df44b 100644 --- a/app/Traits/Show/ShowDaysTrait.php +++ b/app/Traits/Show/ShowDaysTrait.php @@ -3,9 +3,12 @@ namespace App\Traits\Show; use App\Enums\ShowRepeatType; +use App\Helpers\CarbonShowRepetition; +use App\Helpers\Preferences; use App\Lib\RabbitMQSender; +use Illuminate\Support\Collection; -Trait ShowDaysTrait +trait ShowDaysTrait { /** * Each show may have a repetition rules, we set them in the db @@ -13,42 +16,43 @@ Trait ShowDaysTrait * @param $showDaysRules * * @return void + * @throws \Exception */ - public function createShowDays($show, $showDays) + public function createShowDays($show, $showDays): void { - for ($i = 0; $i < count($showDays['day']); $i++) { - $showDay = $showDays; - $showDay['repeat_type'] = ShowRepeatType::fromName($showDays['repeat_type']); - $showDay['day'] = $showDays['day'][$i]; - - // Check if showDay already exists for this day and show ID - $existingShowDay = $show->showDays()->where('day', $showDay['day'])->first(); - - if ($existingShowDay) { - $existingShowDay->update([ - 'first_show' => $showDay['first_show'], - 'start_time' => $showDay['start_time'], - 'timezone' => $showDay['timezone'], - 'duration' => $showDay['duration'], - 'repeat_type' => $showDay['repeat_type']->value, - ]); - continue; + try { + // TODO this is incorrect, look up the preference helper, getDefaultTimezone is called statically but it should not + $timezone = config('app.timezone'); + foreach ($showDays['day'] as $day) { + $showDay = $showDays; + $showDay['first_show'] = CarbonShowRepetition::getNextStartDate($showDays['first_show'], $day); + $showDay['repeat_type'] = ShowRepeatType::fromName($showDays['repeat_type'])->value; + $showDay['day'] = $day; + $show->showDays()->create( + [ + 'first_show' => $showDay['first_show'], + 'start_time' => $showDay['start_time'], + 'timezone' => $showDay['timezone'], + 'duration' => $showDay['duration'], + 'repeat_type' => $showDay['repeat_type'], + 'day' => $showDay['day'], + 'timezone' => $timezone, + ] + ); } - $show->showDays()->create($showDay); + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); } } - public function manageShowDays($show, $showDays) - { - // TODO Determine if the show days rules have been changed - // By editing the createShowDays, returning the days on which the rules have been changed - // one could trigger the creation of only specific instances - // The hard part relies on finding out which show instances - // belong to which showDays rules, as they do not have any fk to showDays - // Probably its complexity is higher than its advantages - // if it is, then proceed to edit the show days rules and generate the show instances - $this->createShowDays($show, $showDays); - $this->manageShowInstances($show); - RabbitMQSender::SendMessageToPypo('update_schedule', []); + public function showDaysFlat(Collection $showDays): array { + $baseProps = $showDays->first()->toArray(); + $earliestDate = $showDays->min('first_show'); + $daysArray = $showDays->pluck('day')->toArray(); + + return array_merge($baseProps, [ + 'first_show' => $earliestDate, + 'day' => $daysArray, + ]); } } \ No newline at end of file diff --git a/app/Traits/Show/ShowDjTrait.php b/app/Traits/Show/ShowDjTrait.php index fa7281f..e9da290 100644 --- a/app/Traits/Show/ShowDjTrait.php +++ b/app/Traits/Show/ShowDjTrait.php @@ -11,7 +11,7 @@ trait ShowDjTrait /** * @throws Exception */ - public function manageShowDjs(array $showDjs, Show $show) + public function manageShowDjs(Show $show, array $showDjs) { // TODO Add protection so only Editor and Admin can edit the djs @@ -22,21 +22,26 @@ trait ShowDjTrait } } - // Fetch current DJs for the show - $currentDjs = $show->showDjs()->pluck('subjs_id')->toArray(); + try { + // Fetch current DJs for the show + $currentDjs = $show->showDjs()->pluck('subjs_id')->toArray(); - // Compute the difference - $djsToAdd = array_diff($showDjs, $currentDjs); - $djsToRemove = array_diff($currentDjs, $showDjs); + // Compute the difference + $djsToAdd = array_diff($showDjs, $currentDjs); + $djsToRemove = array_diff($currentDjs, $showDjs); - // Remove DJs not in the list - foreach ($djsToRemove as $djId) { - $show->showDjs()->where('subjs_id', $djId)->delete(); + // Remove DJs not in the list + foreach ($djsToRemove as $djId) { + $show->showDjs()->where('subjs_id', $djId)->delete(); + } + + // Add new DJs + foreach ($djsToAdd as $djId) { + $show->showDjs()->make(['subjs_id' => $djId]); + } + } catch (\Exception $e) { + return $e->getMessage(); } - // Add new DJs - foreach ($djsToAdd as $djId) { - $show->showDjs()->updateOrCreate(['subjs_id' => $djId]); - } } } \ No newline at end of file diff --git a/app/Traits/Show/ShowInstancesTrait.php b/app/Traits/Show/ShowInstancesTrait.php index c17a802..c6c0cde 100644 --- a/app/Traits/Show/ShowInstancesTrait.php +++ b/app/Traits/Show/ShowInstancesTrait.php @@ -6,6 +6,7 @@ use App\Helpers\CarbonShowRepetition; use App\Helpers\LengthFormatter; use App\Http\Resources\ShowResource; use App\Models\Preference; +use App\Models\Show\Show; use App\Models\ShowInstances\ShowInstances; use Carbon\Carbon; use Carbon\CarbonInterval; @@ -18,33 +19,31 @@ trait ShowInstancesTrait */ public function checkOverlappingInstances(array $instancesDates, $duration, int $showId): void { + $existingRanges = ShowInstances::where('show_id', '!=', $showId) + ->get(['starts', 'ends']) + ->map(fn($i) => [$i->starts, $i->ends]); + foreach ($instancesDates as $date) { - $instanceStartDate = $date; - $instanceEndDate = clone $date; - $instanceEndDate->addHours($duration); + $newStart = $date; + $newEnd = (clone $date)->addHours($duration); - // Query to check for overlaps - $overlapExists = ShowInstances::where( - function ($query) use ($instanceStartDate, $instanceEndDate, $showId) { - $query->where('starts', '<=', $instanceEndDate) - ->where('ends', '>=', $instanceStartDate) - ->where('show_id','!=', $showId); - } - )->exists(); + $hasOverlap = $existingRanges->contains( + fn($range) => $newStart < $range[1] && $newEnd > $range[0] + ); - if ($overlapExists) { - throw new Exception('Overlapping shows'); // Overlap found + if ($hasOverlap) { + throw new Exception("Overlap detected between $newStart and $newEnd"); } } } - public function createShowInstances($instancesDates, $showDay, $show) + public function createShowInstances($instancesDates, $showDayDuration, $showId) { $showInstances = []; foreach ($instancesDates as $instanceDate) { $showStartDate = $instanceDate->copy(); - $duration = CarbonInterval::createFromFormat('H:i:s', $showDay->duration); + $duration = CarbonInterval::createFromFormat('H:i', $showDayDuration); $showEndDate = $showStartDate->copy()->add($duration); $timeFilled = LengthFormatter::generateStringTimeFilled($showStartDate, $showEndDate); @@ -53,7 +52,7 @@ trait ShowInstancesTrait $showInstances[] = [ 'starts' => $showStartDate, 'ends' => $showEndDate, - 'show_id' => $show->id, + 'show_id' => $showId, 'record' => 0, 'rebroadcast' => 0, 'time_filled' => $timeFilled, @@ -73,34 +72,36 @@ trait ShowInstancesTrait * @param ShowResource $ShowResource * * @return void - * @throws \Exception + * @throws Exception */ - public function manageShowInstances($show) + public function manageShowInstances(Show $show) { - $showDays = $show->showDays; - $generationLimitDate = Preference::where('keystr', 'shows_populated_until')->value('valstr'); - $generationLimitDate = Carbon::createFromFormat('Y-m-d H:i:s', $generationLimitDate); - $showInstances = []; + try { + $generationLimitDate = Preference::where('keystr', 'shows_populated_until')->value('valstr'); + $generationLimitDate = Carbon::createFromFormat('Y-m-d H:i:s', $generationLimitDate); + $showInstances = []; + foreach ($show->showDays as $showDay) { + try { + $lastShowDate = $showDay->last_show ?? $generationLimitDate; + $instancesDates = CarbonShowRepetition::generateDates( + $showDay->repeat_type, + $showDay->first_show, + $showDay->start_time, + $lastShowDate + ); - foreach ($showDays as $showDay) { - try { - $lastShowDate = $showDay->last_show ?? $generationLimitDate; - $instancesDates = CarbonShowRepetition::generateDates( - $showDay->repeat_type, - $showDay->first_show, - $showDay->start_time, - $lastShowDate - ); + $this->checkOverlappingInstances($instancesDates, $showDay->duration, $show->id); - $this->checkOverlappingInstances($instancesDates, $showDay->duration, $show->id); - - $showInstances[] = $this->createShowInstances($instancesDates, $showDay, $show); - } catch (\Exception $e) { - throw new \Exception($e->getMessage(), 500); + $showInstances[] = $this->createShowInstances($instancesDates, $showDay->duration, $show->id); + } catch (Exception $e) { + throw new Exception($e->getMessage(), 500); + } } - } - foreach ($showInstances as $showInstance) { - $show->showInstances()->createMany($showInstance); + foreach ($showInstances as $showInstance) { + $show->showInstances()->createMany($showInstance); + } + } catch (Exception $e) { + throw new Exception($e->getMessage(), 500); } } } \ No newline at end of file diff --git a/app/Traits/Show/ShowTrait.php b/app/Traits/Show/ShowTrait.php index a2d191b..76136b3 100644 --- a/app/Traits/Show/ShowTrait.php +++ b/app/Traits/Show/ShowTrait.php @@ -2,7 +2,39 @@ namespace App\Traits\Show; +use App\Lib\RabbitMQSender; +use App\Models\Show\Show; +use App\Models\Show\ShowHosts; +use Illuminate\Support\Facades\DB; + trait ShowTrait { - + /** + * @param array $showData + * @param ShowDays[] $showDays + * @param ShowHosts[] $showDJs + * + * @return string|void + * @throws \Exception + */ + public function createShow(array $showData, array $showDays, array|null $showDJs) + { + try { + DB::beginTransaction(); + $show = Show::create($showData); + $this->createShowDays($show, $showDays); + if (isset($showDJs)) { + $this->manageShowDjs($show, $showDJs); + } + $this->manageShowInstances($show); + $show->save(); + DB::commit(); + RabbitMQSender::SendMessageToPypo('update_schedule', []); + } catch (\Exception $e) { + DB::rollBack(); + throw new \Exception($e->getMessage()); + } catch (\Throwable $e) { + throw new \Exception($e->getMessage()); + } + } } diff --git a/resources/js/components/content/Dashboard.vue b/resources/js/components/content/Dashboard.vue index fb1b805..b21384f 100644 --- a/resources/js/components/content/Dashboard.vue +++ b/resources/js/components/content/Dashboard.vue @@ -1,20 +1,21 @@