Compare commits
No commits in common. "b69b2d367eee94fddb97d2bcd4204b1a9e8f61e1" and "5a42addc441eaae2fbbc40e433b423a35bd34297" have entirely different histories.
b69b2d367e
...
5a42addc44
13 changed files with 98 additions and 107 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -175,6 +175,3 @@ cython_debug/
|
||||||
|
|
||||||
# django
|
# django
|
||||||
static
|
static
|
||||||
|
|
||||||
# JetBrains
|
|
||||||
.idea
|
|
||||||
|
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
8
.idea/dictionaries/project.xml
generated
Normal file
8
.idea/dictionaries/project.xml
generated
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="project">
|
||||||
|
<words>
|
||||||
|
<w>mpaa</w>
|
||||||
|
<w>viewset</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
11
.idea/misc.xml
generated
Normal file
11
.idea/misc.xml
generated
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="enabledOnReformat" value="true" />
|
||||||
|
<option name="enabledOnSave" value="true" />
|
||||||
|
<option name="executionMode" value="BINARY" />
|
||||||
|
<option name="pathToExecutable" value="$USER_HOME$/.local/bin/black" />
|
||||||
|
<option name="sdkName" value="Python 3.13 virtualenv at ~/Projects/MovieNight/movie-night-api/.venv" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 virtualenv at ~/Projects/MovieNight/movie-night-api/.venv" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/movie-night-py.iml" filepath="$PROJECT_DIR$/.idea/movie-night-py.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
30
.idea/movie-night-py.iml
generated
Normal file
30
.idea/movie-night-py.iml
generated
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="django" name="Django">
|
||||||
|
<configuration>
|
||||||
|
<option name="rootFolder" value="$MODULE_DIR$" />
|
||||||
|
<option name="settingsModule" value="movienight/settings.py" />
|
||||||
|
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
|
||||||
|
<option name="environment" value="<map/>" />
|
||||||
|
<option name="doNotUseTestRunner" value="false" />
|
||||||
|
<option name="trackFilePattern" value="migrations" />
|
||||||
|
</configuration>
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv1" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.13 virtualenv at ~/Projects/MovieNight/movie-night-api/.venv" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="PyDocumentationSettings">
|
||||||
|
<option name="format" value="PLAIN" />
|
||||||
|
<option name="myDocStringFormat" value="Plain" />
|
||||||
|
</component>
|
||||||
|
<component name="TemplatesService">
|
||||||
|
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||||
|
</component>
|
||||||
|
</module>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -1,6 +1,6 @@
|
||||||
|
from django.utils import timezone
|
||||||
from gunicorn.config import User
|
from gunicorn.config import User
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from movie_manager.models import Movie, MovieList, Schedule, Showing
|
from movie_manager.models import Movie, MovieList, Schedule, Showing
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ class MovieSerializer(serializers.ModelSerializer):
|
||||||
def get_has_been_scheduled(self, obj):
|
def get_has_been_scheduled(self, obj):
|
||||||
return Showing.objects.filter(movie_id=obj.id).exists()
|
return Showing.objects.filter(movie_id=obj.id).exists()
|
||||||
|
|
||||||
|
|
||||||
class MovieListListSerializer(serializers.ModelSerializer):
|
class MovieListListSerializer(serializers.ModelSerializer):
|
||||||
movie_count = serializers.SerializerMethodField()
|
movie_count = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
@ -46,13 +45,17 @@ class MovieListSerializer(serializers.ModelSerializer):
|
||||||
owner = serializers.PrimaryKeyRelatedField(read_only=True)
|
owner = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return MovieList.objects.prefetch_related("movies", "movies__showing_set")
|
return MovieList.objects.prefetch_related(
|
||||||
|
"movies",
|
||||||
|
"movies__showing_set"
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MovieList
|
model = MovieList
|
||||||
fields = ["id", "name", "owner", "public", "movies"]
|
fields = ["id", "name", "owner", "public", "movies"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.Serializer):
|
class UserSerializer(serializers.Serializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
@ -66,18 +69,19 @@ class ShowingSerializer(serializers.ModelSerializer):
|
||||||
model = Showing
|
model = Showing
|
||||||
fields = ["id", "public", "showtime", "movie", "owner"]
|
fields = ["id", "public", "showtime", "movie", "owner"]
|
||||||
|
|
||||||
# def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
# validated_data = super().to_internal_value(data)
|
validated_data = super().to_internal_value(data)
|
||||||
|
|
||||||
# if "showtime" in validated_data and timezone.is_naive(
|
if "showtime" in validated_data and timezone.is_naive(
|
||||||
# validated_data["showtime"]
|
validated_data["showtime"]
|
||||||
# ):
|
):
|
||||||
# validated_data["showtime"] = timezone.make_aware(validated_data["showtime"])
|
validated_data["showtime"] = timezone.make_aware(validated_data["showtime"])
|
||||||
|
|
||||||
# return validated_data
|
return validated_data
|
||||||
|
|
||||||
|
|
||||||
class ScheduleSerializer(serializers.ModelSerializer):
|
class ScheduleSerializer(serializers.ModelSerializer):
|
||||||
|
name = serializers.CharField(read_only=True)
|
||||||
showings = ShowingSerializer(source="showing_set", read_only=True, many=True)
|
showings = ShowingSerializer(source="showing_set", read_only=True, many=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -1,89 +1,3 @@
|
||||||
import json
|
from django.test import TestCase
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
# Create your tests here.
|
||||||
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")
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.utils import timezone
|
|
||||||
from knox.auth import TokenAuthentication
|
from knox.auth import TokenAuthentication
|
||||||
from rest_framework import viewsets, permissions
|
from rest_framework import viewsets, permissions
|
||||||
|
|
||||||
|
@ -24,14 +23,15 @@ class ScheduleViewset(viewsets.ModelViewSet):
|
||||||
def retrieve(self, request, pk=None, *args, **kwargs):
|
def retrieve(self, request, pk=None, *args, **kwargs):
|
||||||
# Get the schedule instance
|
# Get the schedule instance
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
now = timezone.now()
|
now = datetime.datetime.now()
|
||||||
# get time from start of day
|
# get time from start of day
|
||||||
today = timezone.make_aware(datetime.datetime(now.year, now.month, now.day))
|
today = datetime.datetime(now.year, now.month, now.day)
|
||||||
|
|
||||||
upcoming_showings = Showing.objects.filter(
|
upcoming_showings = Showing.objects.filter(
|
||||||
showtime__gte=today, schedule=instance
|
showtime__gte=today, schedule=instance
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Create a serialized response
|
||||||
serializer = self.get_serializer(instance)
|
serializer = self.get_serializer(instance)
|
||||||
data = serializer.data
|
data = serializer.data
|
||||||
|
|
||||||
|
|
|
@ -33,4 +33,4 @@ class ShowingViewset(viewsets.ModelViewSet):
|
||||||
owner=request.user,
|
owner=request.user,
|
||||||
)
|
)
|
||||||
|
|
||||||
return JsonResponse(ShowingSerializer(showing).data, status=201)
|
return JsonResponse(ShowingSerializer(showing).data)
|
||||||
|
|
|
@ -6,5 +6,4 @@ django-filter
|
||||||
gunicorn==23.0.0
|
gunicorn==23.0.0
|
||||||
psycopg2-binary==2.9.10
|
psycopg2-binary==2.9.10
|
||||||
django-cors-headers==4.7.0
|
django-cors-headers==4.7.0
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
freezegun==1.5.2
|
|
Loading…
Add table
Add a link
Reference in a new issue