From 7e1be7b0280e411057be06d5c41dcd3365fd8dab Mon Sep 17 00:00:00 2001 From: Kyle Robbertze Date: Tue, 10 Aug 2021 16:30:02 +0200 Subject: [PATCH] Truncate schedule items that run over the time of the containing show Fixes: #1272 --- api/libretimeapi/models/schedule.py | 22 ++++++++ api/libretimeapi/serializers.py | 2 + api/libretimeapi/tests/test_views.py | 80 ++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/api/libretimeapi/models/schedule.py b/api/libretimeapi/models/schedule.py index ce87a06b1..314ef5ec2 100644 --- a/api/libretimeapi/models/schedule.py +++ b/api/libretimeapi/models/schedule.py @@ -22,6 +22,28 @@ class Schedule(models.Model): def get_owner(self): return self.instance.get_owner() + def get_cueout(self): + """ + Returns a cueout that is based on the current show. Cueout of a specific + item can potentially overrun the show that it is scheduled in. In that + case, the cueout should be the end of the show. This prevents the next + show having overlapping items playing. + """ + if self.instance.ends < self.ends: + return self.instance.ends - self.starts + return self.cue_out + + def get_ends(self): + """ + Returns an item end that is based on the current show. Ends of a + specific item can potentially overrun the show that it is scheduled in. + In that case, the end should be the end of the show. This prevents the + next show having overlapping items playing. + """ + if self.instance.ends < self.ends: + return self.instance.ends + return self.ends + class Meta: managed = False db_table = "cc_schedule" diff --git a/api/libretimeapi/serializers.py b/api/libretimeapi/serializers.py index 4ecd8e8db..200e3e2ce 100644 --- a/api/libretimeapi/serializers.py +++ b/api/libretimeapi/serializers.py @@ -128,6 +128,8 @@ class ScheduleSerializer(serializers.HyperlinkedModelSerializer): file_id = serializers.IntegerField(source="file.id", read_only=True) stream_id = serializers.IntegerField(source="stream.id", read_only=True) instance_id = serializers.IntegerField(source="instance.id", read_only=True) + cue_out = serializers.DurationField(source="get_cueout", read_only=True) + ends = serializers.DateTimeField(source="get_ends", read_only=True) class Meta: model = Schedule diff --git a/api/libretimeapi/tests/test_views.py b/api/libretimeapi/tests/test_views.py index cb00ce222..a4693a14f 100644 --- a/api/libretimeapi/tests/test_views.py +++ b/api/libretimeapi/tests/test_views.py @@ -1,7 +1,9 @@ import os +from datetime import datetime, timedelta from django.conf import settings from django.contrib.auth.models import AnonymousUser +from django.utils import dateparse from libretimeapi.views import FileViewSet from model_bakery import baker from rest_framework.test import APIRequestFactory, APITestCase @@ -40,3 +42,81 @@ class TestFileViewSet(APITestCase): self.client.credentials(HTTP_AUTHORIZATION="Api-Key {}".format(self.token)) response = self.client.get(path) self.assertEqual(response.status_code, 200) + + +class TestScheduleViewSet(APITestCase): + @classmethod + def setUpTestData(cls): + cls.path = "/api/v2/schedule/" + cls.token = settings.CONFIG.get("general", "api_key") + + def test_schedule_item_full_length(self): + music_dir = baker.make( + "libretimeapi.MusicDir", + directory=os.path.join(os.path.dirname(__file__), "resources"), + ) + f = baker.make( + "libretimeapi.File", + directory=music_dir, + mime="audio/mp3", + filepath="song.mp3", + length=timedelta(seconds=40.86), + cuein=timedelta(seconds=0), + cueout=timedelta(seconds=40.8131), + ) + show = baker.make( + "libretimeapi.ShowInstance", + starts=datetime.now(tz=datetime.timezone.utc) - timedelta(minutes=5), + ends=datetime.now(tz=datetime.timezone.utc) + timedelta(minutes=5), + ) + scheduleItem = baker.make( + "libretimeapi.Schedule", + starts=datetime.now(tz=datetime.timezone.utc), + ends=datetime.now(tz=datetime.timezone.utc) + f.length, + cue_out=f.cueout, + instance=show, + file=f, + ) + self.client.credentials(HTTP_AUTHORIZATION="Api-Key {}".format(self.token)) + response = self.client.get(self.path) + self.assertEqual(response.status_code, 200) + result = response.json() + self.assertEqual(dateparse.parse_datetime(result[0]["ends"]), scheduleItem.ends) + self.assertEqual(dateparse.parse_duration(result[0]["cue_out"]), f.cueout) + + def test_schedule_item_trunc(self): + music_dir = baker.make( + "libretimeapi.MusicDir", + directory=os.path.join(os.path.dirname(__file__), "resources"), + ) + f = baker.make( + "libretimeapi.File", + directory=music_dir, + mime="audio/mp3", + filepath="song.mp3", + length=timedelta(seconds=40.86), + cuein=timedelta(seconds=0), + cueout=timedelta(seconds=40.8131), + ) + show = baker.make( + "libretimeapi.ShowInstance", + starts=datetime.now(tz=datetime.timezone.utc) - timedelta(minutes=5), + ends=datetime.now(tz=datetime.timezone.utc) + timedelta(seconds=20), + ) + scheduleItem = baker.make( + "libretimeapi.Schedule", + starts=datetime.now(tz=datetime.timezone.utc), + ends=datetime.now(tz=datetime.timezone.utc) + f.length, + instance=show, + file=f, + ) + self.client.credentials(HTTP_AUTHORIZATION="Api-Key {}".format(self.token)) + response = self.client.get(self.path) + self.assertEqual(response.status_code, 200) + result = response.json() + self.assertEqual(dateparse.parse_datetime(result[0]["ends"]), show.ends) + expected = show.ends - scheduleItem.starts + self.assertEqual(dateparse.parse_duration(result[0]["cue_out"]), expected) + self.assertNotEqual( + dateparse.parse_datetime(result[0]["ends"]), scheduleItem.ends + )