feat(api): split api into multiple apps (#1626)

Fixes #1622

- split the api into 4 apps: core, history, schedule, storage
- exploded the settings into testing/prod
This commit is contained in:
Jonas L 2022-04-04 14:38:50 +02:00 committed by GitHub
parent 87d2da9d84
commit fce988aef1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
120 changed files with 1499 additions and 1078 deletions

View file

@ -0,0 +1,57 @@
from datetime import datetime, timedelta
from django.test import TestCase
from ...models import Schedule, ShowInstance
class TestSchedule(TestCase):
@classmethod
def setUpTestData(cls):
cls.show_instance = ShowInstance(
created=datetime(year=2021, month=10, day=1, hour=12),
starts=datetime(year=2021, month=10, day=2, hour=1),
ends=datetime(year=2021, month=10, day=2, hour=2),
)
cls.length = timedelta(minutes=10)
cls.cue_in = timedelta(seconds=1)
cls.cue_out = cls.length - timedelta(seconds=4)
def create_schedule(self, starts):
return Schedule(
starts=starts,
ends=starts + self.length,
cue_in=self.cue_in,
cue_out=self.cue_out,
instance=self.show_instance,
)
def test_get_cueout(self):
# No overlapping schedule datetimes, normal usecase:
s1_starts = datetime(year=2021, month=10, day=2, hour=1, minute=30)
s1 = self.create_schedule(s1_starts)
self.assertEqual(s1.get_cueout(), self.cue_out)
self.assertEqual(s1.get_ends(), s1_starts + self.length)
# Mixed overlapping schedule datetimes (only ends is overlapping):
s2_starts = datetime(year=2021, month=10, day=2, hour=1, minute=55)
s2 = self.create_schedule(s2_starts)
self.assertEqual(s2.get_cueout(), timedelta(minutes=5))
self.assertEqual(s2.get_ends(), self.show_instance.ends)
# Fully overlapping schedule datetimes (starts and ends are overlapping):
s3_starts = datetime(year=2021, month=10, day=2, hour=2, minute=1)
s3 = self.create_schedule(s3_starts)
self.assertEqual(s3.get_cueout(), self.cue_out)
self.assertEqual(s3.get_ends(), self.show_instance.ends)
def test_is_valid(self):
# Starts before the schedule ends
s1_starts = datetime(year=2021, month=10, day=2, hour=1, minute=30)
s1 = self.create_schedule(s1_starts)
self.assertTrue(s1.is_valid)
# Starts after the schedule ends
s2_starts = datetime(year=2021, month=10, day=2, hour=3)
s2 = self.create_schedule(s2_starts)
self.assertFalse(s2.is_valid)

View file

@ -0,0 +1,186 @@
import os
from datetime import datetime, timedelta, timezone
from django.conf import settings
from django.utils import dateparse
from model_bakery import baker
from rest_framework.test import APITestCase
from ...._fixtures import AUDIO_FILENAME, fixture_path
class TestScheduleViewSet(APITestCase):
@classmethod
def setUpTestData(cls):
cls.path = "/api/v2/schedule/"
cls.token = settings.CONFIG.general.api_key
def test_schedule_item_full_length(self):
music_dir = baker.make(
"storage.MusicDir",
directory=str(fixture_path),
)
f = baker.make(
"storage.File",
directory=music_dir,
mime="audio/mp3",
filepath=AUDIO_FILENAME,
length=timedelta(seconds=40.86),
cuein=timedelta(seconds=0),
cueout=timedelta(seconds=40.8131),
)
show = baker.make(
"schedule.ShowInstance",
starts=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
ends=datetime.now(tz=timezone.utc) + timedelta(minutes=5),
)
scheduleItem = baker.make(
"schedule.Schedule",
starts=datetime.now(tz=timezone.utc),
ends=datetime.now(tz=timezone.utc) + f.length,
cue_out=f.cueout,
instance=show,
file=f,
)
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {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(
"storage.MusicDir",
directory=str(fixture_path),
)
f = baker.make(
"storage.File",
directory=music_dir,
mime="audio/mp3",
filepath=AUDIO_FILENAME,
length=timedelta(seconds=40.86),
cuein=timedelta(seconds=0),
cueout=timedelta(seconds=40.8131),
)
show = baker.make(
"schedule.ShowInstance",
starts=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
ends=datetime.now(tz=timezone.utc) + timedelta(seconds=20),
)
scheduleItem = baker.make(
"schedule.Schedule",
starts=datetime.now(tz=timezone.utc),
ends=datetime.now(tz=timezone.utc) + f.length,
instance=show,
file=f,
)
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {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
)
def test_schedule_item_invalid(self):
music_dir = baker.make(
"storage.MusicDir",
directory=str(fixture_path),
)
f = baker.make(
"storage.File",
directory=music_dir,
mime="audio/mp3",
filepath=AUDIO_FILENAME,
length=timedelta(seconds=40.86),
cuein=timedelta(seconds=0),
cueout=timedelta(seconds=40.8131),
)
show = baker.make(
"schedule.ShowInstance",
starts=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
ends=datetime.now(tz=timezone.utc) + timedelta(minutes=5),
)
schedule_item = baker.make(
"schedule.Schedule",
starts=datetime.now(tz=timezone.utc),
ends=datetime.now(tz=timezone.utc) + f.length,
cue_out=f.cueout,
instance=show,
file=f,
)
invalid_schedule_item = baker.make(
"schedule.Schedule",
starts=show.ends + timedelta(minutes=1),
ends=show.ends + timedelta(minutes=1) + f.length,
cue_out=f.cueout,
instance=show,
file=f,
)
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
response = self.client.get(self.path, {"is_valid": True})
self.assertEqual(response.status_code, 200)
result = response.json()
# The invalid item should be filtered out and not returned
self.assertEqual(len(result), 1)
self.assertEqual(
dateparse.parse_datetime(result[0]["ends"]), schedule_item.ends
)
self.assertEqual(dateparse.parse_duration(result[0]["cue_out"]), f.cueout)
def test_schedule_item_range(self):
music_dir = baker.make(
"storage.MusicDir",
directory=str(fixture_path),
)
f = baker.make(
"storage.File",
directory=music_dir,
mime="audio/mp3",
filepath=AUDIO_FILENAME,
length=timedelta(seconds=40.86),
cuein=timedelta(seconds=0),
cueout=timedelta(seconds=40.8131),
)
filter_point = datetime.now(tz=timezone.utc)
show = baker.make(
"schedule.ShowInstance",
starts=filter_point - timedelta(minutes=5),
ends=filter_point + timedelta(minutes=5),
)
schedule_item = baker.make(
"schedule.Schedule",
starts=filter_point,
ends=filter_point + f.length,
cue_out=f.cueout,
instance=show,
file=f,
)
previous_item = baker.make(
"schedule.Schedule",
starts=filter_point - timedelta(minutes=5),
ends=filter_point - timedelta(minutes=5) + f.length,
cue_out=f.cueout,
instance=show,
file=f,
)
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
range_start = (filter_point - timedelta(minutes=1)).isoformat(
timespec="seconds"
)
range_end = (filter_point + timedelta(minutes=1)).isoformat(timespec="seconds")
response = self.client.get(
self.path, {"starts__range": f"{range_start},{range_end}"}
)
self.assertEqual(response.status_code, 200)
result = response.json()
# The previous_item should be filtered out and not returned
self.assertEqual(len(result), 1)
self.assertEqual(
dateparse.parse_datetime(result[0]["starts"]), schedule_item.starts
)