fix-timezone-handling #13
13 changed files with 107 additions and 98 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -175,3 +175,6 @@ cython_debug/
|
|||
|
||||
# django
|
||||
static
|
||||
|
||||
# JetBrains
|
||||
.idea
|
||||
|
|
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
|
@ -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
|
8
.idea/dictionaries/project.xml
generated
8
.idea/dictionaries/project.xml
generated
|
@ -1,8 +0,0 @@
|
|||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="project">
|
||||
<words>
|
||||
<w>mpaa</w>
|
||||
<w>viewset</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
6
.idea/inspectionProfiles/profiles_settings.xml
generated
6
.idea/inspectionProfiles/profiles_settings.xml
generated
|
@ -1,6 +0,0 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
11
.idea/misc.xml
generated
11
.idea/misc.xml
generated
|
@ -1,11 +0,0 @@
|
|||
<?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
8
.idea/modules.xml
generated
|
@ -1,8 +0,0 @@
|
|||
<?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
30
.idea/movie-night-py.iml
generated
|
@ -1,30 +0,0 @@
|
|||
<?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
6
.idea/vcs.xml
generated
|
@ -1,6 +0,0 @@
|
|||
<?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 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:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -33,4 +33,4 @@ class ShowingViewset(viewsets.ModelViewSet):
|
|||
owner=request.user,
|
||||
)
|
||||
|
||||
return JsonResponse(ShowingSerializer(showing).data)
|
||||
return JsonResponse(ShowingSerializer(showing).data, status=201)
|
||||
|
|
|
@ -7,3 +7,4 @@ gunicorn==23.0.0
|
|||
psycopg2-binary==2.9.10
|
||||
django-cors-headers==4.7.0
|
||||
requests==2.32.3
|
||||
freezegun==1.5.2
|
Loading…
Add table
Add a link
Reference in a new issue