commit e9b41dadab3bc79e653cf454ac63d412a919dd14 Author: benjamyn Date: Wed Oct 14 19:20:34 2020 -0400 Initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..151485d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +env/ +.vscode/ diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/__pycache__/__init__.cpython-36.pyc b/api/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..4ca77bb Binary files /dev/null and b/api/__pycache__/__init__.cpython-36.pyc differ diff --git a/api/__pycache__/admin.cpython-36.pyc b/api/__pycache__/admin.cpython-36.pyc new file mode 100644 index 0000000..6663483 Binary files /dev/null and b/api/__pycache__/admin.cpython-36.pyc differ diff --git a/api/__pycache__/models.cpython-36.pyc b/api/__pycache__/models.cpython-36.pyc new file mode 100644 index 0000000..330f65a Binary files /dev/null and b/api/__pycache__/models.cpython-36.pyc differ diff --git a/api/__pycache__/serializers.cpython-36.pyc b/api/__pycache__/serializers.cpython-36.pyc new file mode 100644 index 0000000..8d3cdcc Binary files /dev/null and b/api/__pycache__/serializers.cpython-36.pyc differ diff --git a/api/__pycache__/urls.cpython-36.pyc b/api/__pycache__/urls.cpython-36.pyc new file mode 100644 index 0000000..939bc98 Binary files /dev/null and b/api/__pycache__/urls.cpython-36.pyc differ diff --git a/api/__pycache__/views.cpython-36.pyc b/api/__pycache__/views.cpython-36.pyc new file mode 100644 index 0000000..a521ae3 Binary files /dev/null and b/api/__pycache__/views.cpython-36.pyc differ diff --git a/api/admin.py b/api/admin.py new file mode 100644 index 0000000..f758121 --- /dev/null +++ b/api/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import Movie, Rating + +admin.site.register(Movie) +admin.site.register(Rating) diff --git a/api/apps.py b/api/apps.py new file mode 100644 index 0000000..d87006d --- /dev/null +++ b/api/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + name = 'api' diff --git a/api/migrations/0001_initial.py b/api/migrations/0001_initial.py new file mode 100644 index 0000000..b14f60b --- /dev/null +++ b/api/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 3.0.6 on 2020-05-28 10:13 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Movie', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=32)), + ('description', models.TextField(max_length=360)), + ], + ), + migrations.CreateModel( + name='Rating', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('stars', models.IntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)])), + ('movie', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Movie')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'movie')}, + 'index_together': {('user', 'movie')}, + }, + ), + ] diff --git a/api/migrations/__init__.py b/api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/migrations/__pycache__/0001_initial.cpython-36.pyc b/api/migrations/__pycache__/0001_initial.cpython-36.pyc new file mode 100644 index 0000000..c0b00a6 Binary files /dev/null and b/api/migrations/__pycache__/0001_initial.cpython-36.pyc differ diff --git a/api/migrations/__pycache__/__init__.cpython-36.pyc b/api/migrations/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..3618cb2 Binary files /dev/null and b/api/migrations/__pycache__/__init__.cpython-36.pyc differ diff --git a/api/models.py b/api/models.py new file mode 100644 index 0000000..f4203eb --- /dev/null +++ b/api/models.py @@ -0,0 +1,38 @@ +from django.db import models +from django.contrib.auth.models import User +from django.core.validators import MaxValueValidator, MinValueValidator + + +class Movie(models.Model): + title = models.CharField(max_length=32) + description = models.TextField(max_length=360) + + def no_of_ratings(self): + ratings = Rating.objects.filter(movie=self) + return len(ratings) + + def average_ratings(self): + ratings = Rating.objects.filter(movie=self) + average = 0 + for rating in ratings: + average += rating.stars + if len(ratings) > 0: + return average / len(ratings) + return 0 + + def __str__(self): + return self.title + + +class Rating(models.Model): + movie = models.ForeignKey(Movie, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + stars = models.IntegerField( + validators=[MinValueValidator(1), MaxValueValidator(5)]) + + class Meta: + unique_together = (('user'), 'movie') + index_together = (('user'), 'movie') + + def __str__(self): + return f"{self.movie.title} - {self.stars}" diff --git a/api/serializers.py b/api/serializers.py new file mode 100644 index 0000000..4cd1064 --- /dev/null +++ b/api/serializers.py @@ -0,0 +1,29 @@ +from rest_framework import serializers +from .models import Movie, Rating +from django.contrib.auth.models import User +from rest_framework.authtoken.models import Token + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ["id", "username", "password"] + extra_kwargs = {'password': {'write_only': True, 'required': True}} + + def create(self, validated_data): + user = User.objects.create_user(**validated_data) + Token.objects.create(user=user) + return user + + +class MovieSerializer(serializers.ModelSerializer): + class Meta: + model = Movie + fields = ["id", "title", "description", + "no_of_ratings", "average_ratings"] + + +class RatingSerializer(serializers.ModelSerializer): + class Meta: + model = Rating + fields = ["id", "stars", "user", "movie"] diff --git a/api/tests.py b/api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 0000000..ebebfc5 --- /dev/null +++ b/api/urls.py @@ -0,0 +1,13 @@ +from django.contrib import admin +from django.urls import path, include +from rest_framework import routers +from .views import MovieViewSet, RatingViewSet, UserViewSet + +router = routers.DefaultRouter() +router.register('movies', MovieViewSet) +router.register('ratings', RatingViewSet) +router.register('users', UserViewSet) + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/api/views.py b/api/views.py new file mode 100644 index 0000000..d180b35 --- /dev/null +++ b/api/views.py @@ -0,0 +1,65 @@ +from django.contrib.auth.models import User +from django.shortcuts import render +from rest_framework import status, viewsets +from rest_framework.authentication import TokenAuthentication +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework.response import Response + +from .models import Movie, Rating +from .serializers import MovieSerializer, RatingSerializer, UserSerializer + + +class UserViewSet(viewsets.ModelViewSet): + queryset = User.objects.all() + serializer_class = UserSerializer + permission_classes = (AllowAny,) + +class MovieViewSet(viewsets.ModelViewSet): + queryset = Movie.objects.all() + serializer_class = MovieSerializer + authentication_classes = (TokenAuthentication,) + permission_classes = (IsAuthenticated,) + + @action(detail=True, methods=['POST']) + def rate_movie(self, request, pk=None): + if 'stars' in request.data: + + movie = Movie.objects.get(id=pk) + stars = request.data['stars'] + user = request.user + + try: + rating = Rating.objects.get(user=user.id, movie=movie.id) + rating.stars = stars + rating.save() + serializer = RatingSerializer(rating, many=False) + response = {'message': f"Rating Updated", + "result": serializer.data} + return Response(response, status=status.HTTP_200_OK) + except: + rating = Rating.objects.create( + user=user, movie=movie, stars=stars) + serializer = RatingSerializer(rating, many=False) + response = {'message': f"Rating Created", + "result": serializer.data} + return Response(response, status=status.HTTP_200_OK) + + else: + response = {'message': "stars field required"} + return Response(response, status=status.HTTP_400_BAD_REQUEST) + + +class RatingViewSet(viewsets.ModelViewSet): + queryset = Rating.objects.all() + serializer_class = RatingSerializer + authentication_classes = (TokenAuthentication,) + permission_classes = (IsAuthenticated,) + + def update(self, request, *args, **kwargs): + response = {'message': "Can't update rating like that"} + return Response(response, status=status.HTTP_400_BAD_REQUEST) + + def create(self, request, *args, **kwargs): + response = {'message': "Can't update rating like that"} + return Response(response, status=status.HTTP_400_BAD_REQUEST) diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000..65d3cd6 Binary files /dev/null and b/db.sqlite3 differ diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..afd9e0e --- /dev/null +++ b/manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'movierater.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/movierater/__init__.py b/movierater/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/movierater/__pycache__/__init__.cpython-36.pyc b/movierater/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..216454b Binary files /dev/null and b/movierater/__pycache__/__init__.cpython-36.pyc differ diff --git a/movierater/__pycache__/settings.cpython-36.pyc b/movierater/__pycache__/settings.cpython-36.pyc new file mode 100644 index 0000000..8db21db Binary files /dev/null and b/movierater/__pycache__/settings.cpython-36.pyc differ diff --git a/movierater/__pycache__/urls.cpython-36.pyc b/movierater/__pycache__/urls.cpython-36.pyc new file mode 100644 index 0000000..7afabdc Binary files /dev/null and b/movierater/__pycache__/urls.cpython-36.pyc differ diff --git a/movierater/__pycache__/wsgi.cpython-36.pyc b/movierater/__pycache__/wsgi.cpython-36.pyc new file mode 100644 index 0000000..697a354 Binary files /dev/null and b/movierater/__pycache__/wsgi.cpython-36.pyc differ diff --git a/movierater/asgi.py b/movierater/asgi.py new file mode 100644 index 0000000..59103e9 --- /dev/null +++ b/movierater/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for movierater project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'movierater.settings') + +application = get_asgi_application() diff --git a/movierater/settings.py b/movierater/settings.py new file mode 100644 index 0000000..01fb57e --- /dev/null +++ b/movierater/settings.py @@ -0,0 +1,137 @@ +""" +Django settings for movierater project. + +Generated by 'django-admin startproject' using Django 3.0.6. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.0/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'ss&*9xr7jag^*hjlk=3t=uvh&5pg&5*vl@-^!5=b6pm4&g==eo' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['10.6.9.67'] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'rest_framework.authtoken', + 'corsheaders', + 'api', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'movierater.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'movierater.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ) +} + +# Password validation +# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.0/howto/static-files/ + +STATIC_URL = '/static/' + + +CORS_ORIGIN_WHITELIST = ( + 'http://localhost', + 'http://10.6.9.67:3000', + 'http://10.6.9.76:8000', +) diff --git a/movierater/urls.py b/movierater/urls.py new file mode 100644 index 0000000..76e47de --- /dev/null +++ b/movierater/urls.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from django.urls import path, include +from rest_framework.authtoken.views import obtain_auth_token + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/', include('api.urls')), + path('auth/', obtain_auth_token) +] diff --git a/movierater/wsgi.py b/movierater/wsgi.py new file mode 100644 index 0000000..e3fa136 --- /dev/null +++ b/movierater/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for movierater project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'movierater.settings') + +application = get_wsgi_application()