From 556a89d025bce6dd91371262a3f91054cfd9aff4 Mon Sep 17 00:00:00 2001 From: Edward Tirado Jr Date: Tue, 8 Apr 2025 17:03:27 -0500 Subject: [PATCH 1/2] .idea update --- .idea/misc.xml | 4 ++-- .idea/movie-night-py.iml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 3684797..9f60641 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,7 +3,7 @@ - + \ No newline at end of file diff --git a/.idea/movie-night-py.iml b/.idea/movie-night-py.iml index cdbb1e7..f13645b 100644 --- a/.idea/movie-night-py.iml +++ b/.idea/movie-night-py.iml @@ -15,8 +15,9 @@ + - + From 62129a52f297467401dad0cf46998972b347b91d Mon Sep 17 00:00:00 2001 From: Edward Tirado Jr Date: Tue, 8 Apr 2025 17:04:32 -0500 Subject: [PATCH 2/2] added schedule support --- ...ter_movie_last_watched_showing_schedule.py | 50 ++++++++++++ movie_manager/migrations/0005_showing_slug.py | 18 +++++ .../0006_remove_showing_slug_schedule_slug.py | 22 ++++++ movie_manager/models.py | 25 +++++- movie_manager/serializers.py | 29 ++++++- movie_manager/views.py | 77 +++++++++++++++++-- movienight/urls.py | 2 + 7 files changed, 212 insertions(+), 11 deletions(-) create mode 100644 movie_manager/migrations/0004_alter_movie_last_watched_showing_schedule.py create mode 100644 movie_manager/migrations/0005_showing_slug.py create mode 100644 movie_manager/migrations/0006_remove_showing_slug_schedule_slug.py diff --git a/movie_manager/migrations/0004_alter_movie_last_watched_showing_schedule.py b/movie_manager/migrations/0004_alter_movie_last_watched_showing_schedule.py new file mode 100644 index 0000000..9895ec9 --- /dev/null +++ b/movie_manager/migrations/0004_alter_movie_last_watched_showing_schedule.py @@ -0,0 +1,50 @@ +# Generated by Django 5.1.4 on 2025-04-08 03:39 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('movie_manager', '0003_movie_added_by'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='movie', + name='last_watched', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.CreateModel( + name='Showing', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('public', models.BooleanField(default=False)), + ('showtime', models.DateTimeField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('deleted_at', models.DateTimeField(blank=True, null=True)), + ('movie', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='movie_manager.movie')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['showtime'], + }, + ), + migrations.CreateModel( + name='Schedule', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('public', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('deleted_at', models.DateTimeField(blank=True, null=True)), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('showings', models.ManyToManyField(to='movie_manager.showing')), + ], + ), + ] diff --git a/movie_manager/migrations/0005_showing_slug.py b/movie_manager/migrations/0005_showing_slug.py new file mode 100644 index 0000000..4d5c0b1 --- /dev/null +++ b/movie_manager/migrations/0005_showing_slug.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.4 on 2025-04-08 03:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('movie_manager', '0004_alter_movie_last_watched_showing_schedule'), + ] + + operations = [ + migrations.AddField( + model_name='showing', + name='slug', + field=models.SlugField(default='', max_length=100), + ), + ] diff --git a/movie_manager/migrations/0006_remove_showing_slug_schedule_slug.py b/movie_manager/migrations/0006_remove_showing_slug_schedule_slug.py new file mode 100644 index 0000000..dfe8b61 --- /dev/null +++ b/movie_manager/migrations/0006_remove_showing_slug_schedule_slug.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.4 on 2025-04-08 04:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('movie_manager', '0005_showing_slug'), + ] + + operations = [ + migrations.RemoveField( + model_name='showing', + name='slug', + ), + migrations.AddField( + model_name='schedule', + name='slug', + field=models.SlugField(default='', max_length=100), + ), + ] diff --git a/movie_manager/models.py b/movie_manager/models.py index 1fcb1ba..171e9e2 100644 --- a/movie_manager/models.py +++ b/movie_manager/models.py @@ -1,7 +1,6 @@ from django.db import models from django.contrib.auth.models import User -# Create your models here. class Movie(models.Model): title = models.CharField(max_length=100) imdb_id = models.CharField(max_length=100) @@ -12,7 +11,7 @@ class Movie(models.Model): actors = models.CharField(max_length=500) plot = models.CharField(max_length=500) poster = models.CharField(max_length=500) - last_watched = models.DateTimeField() + last_watched = models.DateTimeField(null=True, blank=True) added_by = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -39,3 +38,25 @@ class MovieList(models.Model): def __str__(self): return self.name + +class Schedule(models.Model): + name = models.CharField(max_length=100) + owner = models.ForeignKey(User, on_delete=models.CASCADE) + public = models.BooleanField(default=False) + showings = models.ManyToManyField("Showing") + slug = models.SlugField(max_length=100, default="") + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + deleted_at = models.DateTimeField(null=True, blank=True) + +class Showing(models.Model): + movie = models.ForeignKey(Movie, on_delete=models.CASCADE) + owner = models.ForeignKey(User, on_delete=models.CASCADE) + public = models.BooleanField(default=False) + showtime = models.DateTimeField() + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + deleted_at = models.DateTimeField(null=True, blank=True) + + class Meta: + ordering = ["showtime"] diff --git a/movie_manager/serializers.py b/movie_manager/serializers.py index e79ceff..bebebb0 100644 --- a/movie_manager/serializers.py +++ b/movie_manager/serializers.py @@ -1,7 +1,7 @@ -from itertools import count - +from gunicorn.config import User from rest_framework import serializers -from movie_manager.models import Movie, MovieList +from movie_manager.models import Movie, MovieList, Schedule, Showing + class MovieSerializer(serializers.ModelSerializer): class Meta: @@ -20,3 +20,26 @@ class MovieListSerializer(serializers.ModelSerializer): def get_movie_count(self, obj): return len(obj.movies.all()) + +class UserSerializer(serializers.Serializer): + class Meta: + model = User + fields = ["id", "username"] + + +class ShowingSerializer(serializers.ModelSerializer): + movie = MovieSerializer(read_only=True) + + class Meta: + model = Showing + fields = ["public", "showtime", "movie", "owner"] + + +class ScheduleSerializer(serializers.ModelSerializer): + name = serializers.CharField(read_only=True) + showings = ShowingSerializer(read_only=True, many=True) + + class Meta: + model = Schedule + fields = ["name", "owner","public","slug", "showings"] + diff --git a/movie_manager/views.py b/movie_manager/views.py index d814302..e08d86e 100644 --- a/movie_manager/views.py +++ b/movie_manager/views.py @@ -1,17 +1,18 @@ -from django.http import HttpResponse, JsonResponse +import datetime + +from django.http import JsonResponse from django.contrib.auth.models import User from rest_framework import permissions, viewsets from knox.auth import TokenAuthentication from rest_framework.decorators import action from rest_framework.exceptions import NotFound -from movie_manager.models import Movie, MovieList -from movie_manager.serializers import MovieListSerializer, MovieSerializer +from movie_manager.models import Movie, MovieList, Schedule, Showing +from movie_manager.serializers import MovieListSerializer, MovieSerializer, ScheduleSerializer, ShowingSerializer # Create your views here. class MovieViewset(viewsets.ModelViewSet): - fields = '__all__' queryset = Movie.objects.all().order_by("title") authentication_classes = [TokenAuthentication] permission_classes = [permissions.IsAuthenticated] @@ -19,7 +20,6 @@ class MovieViewset(viewsets.ModelViewSet): serializer_class = MovieSerializer class MovieListViewset(viewsets.ModelViewSet): - fields = '__all__' queryset = MovieList.objects.all().order_by("name") authentication_classes = [TokenAuthentication] permission_classes = [permissions.IsAuthenticated] @@ -66,8 +66,73 @@ class MovieListViewset(viewsets.ModelViewSet): return JsonResponse(MovieListSerializer(movie_list).data) def remove_movie(self, request, pk=None, movie_id=None, *args, **kwargs): - movie_list = MovieList.objects.get(pk=pk) movie = Movie.objects.get(pk=movie_id) + + movie_list = MovieList.objects.get(pk=pk) movie_list.movies.remove(movie) return JsonResponse(MovieListSerializer(movie_list).data) + +class ScheduleViewset(viewsets.ModelViewSet): + queryset = Schedule.objects.all().order_by("name") + authentication_classes = [TokenAuthentication] + permission_classes = [permissions.IsAuthenticated] + + serializer_class = ScheduleSerializer + + def retrieve(self, request, pk=None, *args, **kwargs): + # Get the schedule instance + instance = self.get_object() + today = datetime.datetime.now() + + upcoming_showings = instance.showings.filter(showtime__gte=today) + + # Create a serialized response + serializer = self.get_serializer(instance) + data = serializer.data + + # Replace all showings with only future showings + data['showings'] = ShowingSerializer(upcoming_showings, many=True).data + + + if request.GET.get('past_showings') == 'true': + past_showings = instance.showings.filter(showtime__lt=today) + + # Add both to the response + data['past_showings'] = [ + {'id': showing.id, 'showtime': showing.showtime.isoformat(), "movie": MovieSerializer(showing.movie).data} + for showing in past_showings + ] + else: + data['past_showings'] = [] + + return JsonResponse(data) + + +class ShowingViewset(viewsets.ModelViewSet): + queryset = Showing.objects.all().order_by("showtime") + authentication_classes = [TokenAuthentication] + permission_classes = [permissions.IsAuthenticated] + + serializer_class = ShowingSerializer + + def create(self, request, *args, **kwargs): + movie_id = request.data.get('movie') + movie = Movie.objects.get(pk=movie_id) + + schedule_id = request.data.get('schedule') + schedule = Schedule.objects.get(pk=schedule_id) + + showing = Showing.objects.create( + movie=movie, + schedule=schedule, + showtime=request.data.get("showtime"), + public=request.data.get("public"), + owner=User.objects.get(pk=request.data.get("owner")) + ) + + schedule.showings.add(showing) + + return JsonResponse(ShowingSerializer(showing).data) + + diff --git a/movienight/urls.py b/movienight/urls.py index 1cb7bfd..6889ee7 100644 --- a/movienight/urls.py +++ b/movienight/urls.py @@ -19,6 +19,8 @@ router.register(r"api/users", user_views.UserViewSet) router.register(r"api/groups", user_views.GroupViewSet) router.register(r"api/movies", movie_views.MovieViewset) router.register(r"api/lists", movie_views.MovieListViewset) +router.register(r"api/schedules", movie_views.ScheduleViewset) +router.register(r"api/showings", movie_views.ShowingViewset) urlpatterns = [ path("", include(router.urls)),