diff --git a/.gitignore b/.gitignore index 6bd33c1..41818d1 100644 --- a/.gitignore +++ b/.gitignore @@ -175,3 +175,6 @@ cython_debug/ # django static + +# JetBrains +.idea diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml deleted file mode 100644 index 779eda8..0000000 --- a/.idea/dictionaries/project.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - mpaa - viewset - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index f6f7e02..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 60235a5..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/movie-night-py.iml b/.idea/movie-night-py.iml deleted file mode 100644 index 7a7a8e5..0000000 --- a/.idea/movie-night-py.iml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/movie_manager/serializers.py b/movie_manager/serializers.py index 3b7a517..c9bb829 100644 --- a/movie_manager/serializers.py +++ b/movie_manager/serializers.py @@ -1,6 +1,6 @@ -from django.utils import timezone from gunicorn.config import User from rest_framework import serializers + from movie_manager.models import Movie, MovieList, Schedule, Showing @@ -28,6 +28,7 @@ class MovieSerializer(serializers.ModelSerializer): def get_has_been_scheduled(self, obj): return Showing.objects.filter(movie_id=obj.id).exists() + class MovieListListSerializer(serializers.ModelSerializer): movie_count = serializers.SerializerMethodField() @@ -45,17 +46,13 @@ class MovieListSerializer(serializers.ModelSerializer): owner = serializers.PrimaryKeyRelatedField(read_only=True) def get_queryset(self): - return MovieList.objects.prefetch_related( - "movies", - "movies__showing_set" - ) + return MovieList.objects.prefetch_related("movies", "movies__showing_set") class Meta: model = MovieList fields = ["id", "name", "owner", "public", "movies"] - class UserSerializer(serializers.Serializer): class Meta: model = User @@ -69,19 +66,18 @@ class ShowingSerializer(serializers.ModelSerializer): model = Showing fields = ["id", "public", "showtime", "movie", "owner"] - def to_internal_value(self, data): - validated_data = super().to_internal_value(data) + # def to_internal_value(self, data): + # validated_data = super().to_internal_value(data) - if "showtime" in validated_data and timezone.is_naive( - validated_data["showtime"] - ): - validated_data["showtime"] = timezone.make_aware(validated_data["showtime"]) + # if "showtime" in validated_data and timezone.is_naive( + # validated_data["showtime"] + # ): + # validated_data["showtime"] = timezone.make_aware(validated_data["showtime"]) - return validated_data + # return validated_data class ScheduleSerializer(serializers.ModelSerializer): - name = serializers.CharField(read_only=True) showings = ShowingSerializer(source="showing_set", read_only=True, many=True) class Meta: diff --git a/movie_manager/tests.py b/movie_manager/tests.py index 7ce503c..8ab499b 100644 --- a/movie_manager/tests.py +++ b/movie_manager/tests.py @@ -1,3 +1,89 @@ -from django.test import TestCase +import json -# Create your tests here. +from django.contrib.auth.models import User +from django.utils import timezone +from freezegun import freeze_time +from rest_framework import status +from rest_framework.test import APITestCase, APIClient + +from movie_manager.models import Movie, Schedule, Showing + + +class ShowingViewsetTestCase(APITestCase): + def setUp(self): + self.client: APIClient = APIClient() + self.movie: Movie = Movie.objects.create(title="Test Movie") + self.owner: User = User.objects.create(id=1, username="test_user") + self.schedule: Schedule = Schedule.objects.create( + owner=self.owner, name="Test Schedule" + ) + + def test_it_creates_a_new_showing(self): + self.client.force_authenticate(user=self.owner) + + showing_time = timezone.now().isoformat().replace("+00:00", "Z") + response = self.client.post( + "/v1/showings/", + { + "movie": self.movie.id, + "public": True, + "schedule": self.schedule.id, + "showtime": showing_time, + }, + ) + + response_data = json.loads(response.content) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response_data.get("showtime"), showing_time) + self.assertEqual(response_data.get("movie").get("title"), "Test Movie") + + @freeze_time("2025-07-02 23:59:00", tz_offset=-5) + def test_it_returns_active_showings(self): + self.client.force_authenticate(user=self.schedule.owner) + + showtimes_america_chicago_utc = [ + "2025-07-03T04:00:59.000Z", # 7/2/25 11:59pm + "2025-07-01T04:00:59.000Z", # 6/30/25 11:59pm + "2025-07-08T04:00:59.000Z", # 7/7/25 11:59pm + ] + + for showtime in showtimes_america_chicago_utc: + Showing.objects.create( + movie=self.movie, + schedule=self.schedule, + showtime=showtime, + public=True, + owner=self.schedule.owner, + ) + + response = self.client.get(f"/v1/schedules/{self.schedule.id}/") + parsed_schedule = json.loads(response.content) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(parsed_schedule.get("showings")), 2) + + +class ScheduleViewsetTestCase(APITestCase): + def setUp(self): + self.client: APIClient = APIClient() + self.test_user: User = User.objects.create(id=1, username="test_user") + + def test_it_creates_a_new_schedule(self): + self.client.force_authenticate(user=self.test_user) + response = self.client.post( + "/v1/schedules/", + { + "name": "Test Schedule", + "owner": self.test_user.id, + "public": True, + "slug": "test-schedule", + }, + ) + + response_data = json.loads(response.content) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response_data.get("name"), "Test Schedule") + self.assertEqual(response_data.get("owner"), 1) + self.assertEqual(response_data.get("public"), True) + self.assertEqual(response_data.get("slug"), "test-schedule") diff --git a/movie_manager/viewsets/schedule.py b/movie_manager/viewsets/schedule.py index 856605a..57d5ec2 100644 --- a/movie_manager/viewsets/schedule.py +++ b/movie_manager/viewsets/schedule.py @@ -1,6 +1,7 @@ import datetime from django.http import JsonResponse +from django.utils import timezone from knox.auth import TokenAuthentication from rest_framework import viewsets, permissions @@ -23,15 +24,14 @@ class ScheduleViewset(viewsets.ModelViewSet): def retrieve(self, request, pk=None, *args, **kwargs): # Get the schedule instance instance = self.get_object() - now = datetime.datetime.now() + now = timezone.now() # get time from start of day - today = datetime.datetime(now.year, now.month, now.day) + today = timezone.make_aware(datetime.datetime(now.year, now.month, now.day)) upcoming_showings = Showing.objects.filter( showtime__gte=today, schedule=instance ) - # Create a serialized response serializer = self.get_serializer(instance) data = serializer.data diff --git a/movie_manager/viewsets/showing.py b/movie_manager/viewsets/showing.py index 1f3c887..a52f7c1 100644 --- a/movie_manager/viewsets/showing.py +++ b/movie_manager/viewsets/showing.py @@ -33,4 +33,4 @@ class ShowingViewset(viewsets.ModelViewSet): owner=request.user, ) - return JsonResponse(ShowingSerializer(showing).data) + return JsonResponse(ShowingSerializer(showing).data, status=201) diff --git a/requirements.txt b/requirements.txt index 7350802..1a4cbee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,5 @@ django-filter gunicorn==23.0.0 psycopg2-binary==2.9.10 django-cors-headers==4.7.0 -requests==2.32.3 \ No newline at end of file +requests==2.32.3 +freezegun==1.5.2 \ No newline at end of file