From feefb3ff986d6927e52b3c3c776cbc8bc6e7155a Mon Sep 17 00:00:00 2001 From: Akshath Date: Sun, 31 Jan 2021 12:43:09 +0530 Subject: [PATCH 01/15] Initialized django project in backend folder. Created basic requirements file --- backend/Transcripts/__init__.py | 0 backend/Transcripts/asgi.py | 16 +++++ backend/Transcripts/settings.py | 120 ++++++++++++++++++++++++++++++++ backend/Transcripts/urls.py | 21 ++++++ backend/Transcripts/wsgi.py | 16 +++++ backend/manage.py | 22 ++++++ backend/requirements.txt | 5 ++ 7 files changed, 200 insertions(+) create mode 100644 backend/Transcripts/__init__.py create mode 100644 backend/Transcripts/asgi.py create mode 100644 backend/Transcripts/settings.py create mode 100644 backend/Transcripts/urls.py create mode 100644 backend/Transcripts/wsgi.py create mode 100644 backend/manage.py create mode 100644 backend/requirements.txt diff --git a/backend/Transcripts/__init__.py b/backend/Transcripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/Transcripts/asgi.py b/backend/Transcripts/asgi.py new file mode 100644 index 0000000..b992ede --- /dev/null +++ b/backend/Transcripts/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for Transcripts 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.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Transcripts.settings') + +application = get_asgi_application() diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py new file mode 100644 index 0000000..3611f40 --- /dev/null +++ b/backend/Transcripts/settings.py @@ -0,0 +1,120 @@ +""" +Django settings for Transcripts project. + +Generated by 'django-admin startproject' using Django 3.1.5. + +For more information on this file, see +https://docs.djangoproject.com/en/3.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.1/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'p!ptauvv-$*q(o#_3-xztu6w9c@kqe+dr-gd&e+jhl!4+rey$e' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'Transcripts.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 = 'Transcripts.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.1/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.1/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.1/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/backend/Transcripts/urls.py b/backend/Transcripts/urls.py new file mode 100644 index 0000000..921b261 --- /dev/null +++ b/backend/Transcripts/urls.py @@ -0,0 +1,21 @@ +"""Transcripts URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path + +urlpatterns = [ + path('admin/', admin.site.urls), +] diff --git a/backend/Transcripts/wsgi.py b/backend/Transcripts/wsgi.py new file mode 100644 index 0000000..460ed12 --- /dev/null +++ b/backend/Transcripts/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for Transcripts 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.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Transcripts.settings') + +application = get_wsgi_application() diff --git a/backend/manage.py b/backend/manage.py new file mode 100644 index 0000000..184adf7 --- /dev/null +++ b/backend/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Transcripts.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/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..85ce88a --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,5 @@ +asgiref==3.3.1 +Django==3.1.5 +djangorestframework==3.12.2 +pytz==2020.5 +sqlparse==0.4.1 From a2e71faaf0c0218d2ee118a425e7cddcdb9fc568 Mon Sep 17 00:00:00 2001 From: Jinay01 Date: Mon, 1 Feb 2021 20:47:43 +0530 Subject: [PATCH 02/15] User model created --- backend/Transcripts/settings.py | 7 +++ backend/accounts/__init__.py | 0 backend/accounts/admin.py | 3 + backend/accounts/apps.py | 5 ++ backend/accounts/migrations/__init__.py | 0 backend/accounts/models.py | 81 +++++++++++++++++++++++++ backend/accounts/tests.py | 3 + backend/accounts/views.py | 3 + backend/requirements.txt | 4 +- 9 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 backend/accounts/__init__.py create mode 100644 backend/accounts/admin.py create mode 100644 backend/accounts/apps.py create mode 100644 backend/accounts/migrations/__init__.py create mode 100644 backend/accounts/models.py create mode 100644 backend/accounts/tests.py create mode 100644 backend/accounts/views.py diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index 3611f40..b4d65a5 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -37,6 +37,8 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + # apps + 'accounts', ] MIDDLEWARE = [ @@ -118,3 +120,8 @@ # https://docs.djangoproject.com/en/3.1/howto/static-files/ STATIC_URL = '/static/' + + +AUTH_USER_MODEL = 'accounts.AppUser' +# to check if user has profile +# AUTH_PROFILE_MODEL = 'accounts.modelname' diff --git a/backend/accounts/__init__.py b/backend/accounts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/accounts/admin.py b/backend/accounts/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/backend/accounts/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/backend/accounts/apps.py b/backend/accounts/apps.py new file mode 100644 index 0000000..9b3fc5a --- /dev/null +++ b/backend/accounts/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AccountsConfig(AppConfig): + name = 'accounts' diff --git a/backend/accounts/migrations/__init__.py b/backend/accounts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/accounts/models.py b/backend/accounts/models.py new file mode 100644 index 0000000..cc00d54 --- /dev/null +++ b/backend/accounts/models.py @@ -0,0 +1,81 @@ +from django.db import models +from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager +import jsonfield + + +# Create your models here. +# User Starts # +class AppUserManager(BaseUserManager): + def create_user(self, email, password=None, **extra_fields): + if email is None: + raise TypeError("Email is required") + user = self.model(email=self.normalize_email(email)) + user.set_password(password) + user.save(using=self._db) + return user + + def create_superuser(self, email, password=None, **extra_fields): + if password is None: + raise TypeError('Password should not be none') + user = self.create_user(email, password) + user.is_active = True + user.is_superuser = True + user.is_staff = True + user.is_admin = True + user.save(using=self._db) + return user + + +class AppUser(AbstractBaseUser): + email = models.EmailField() # check if @djsce.in for faculty + is_management = models.BooleanField(default=False) # boolean fields + is_active = models.BooleanField(default=False) + is_admin = models.BooleanField(default=False) + is_staff = models.BooleanField(default=False) + + USERNAME_FIELD = 'email' + objects = AppUserManager() + + def __str__(self): + return self.email + + +class StudentProfile(models.Model): + departments = ( + ('CS', 'COMPUTERS'), + ('IT', 'INFORMATION TECHNOLOGY'), + ('EXTC', 'ELECTRONICS AND TELECOMMUNICATION'), + ('ELEX', 'ELECTRONICS'), + ('MECH', 'MECHANICAL'), + ('CHEM', 'CHEMICAL'), + ('BIOMED', 'BIOMED'), + ('PROD', 'PRODUCTION'), + ('OTHERS', 'OTHERS'), + ) + user = models.OneToOneField(AppUser, on_delete=models.CASCADE) + # other details + sap_id = models.CharField(max_lenght=15, primary_key=True) + name = models.CharField(max_length=100) + contact_no = models.CharField(max_length=13) + department = models.CharField(max_length=40, choices=departments) + # can also take date as the input (change it to datefield) + admission_year = models.CharField(max_lenght=5) + marksheet = jsonfield.JSONField() + + def __str__(self): + return self.name + + +class ManagementProfile(models.Model): + user = models.OneToOneField(AppUser, on_delete=models.CASCADE) + staff_id = models.CharField(primary_key=True) + name = models.CharField(max_length=100) + contact_no = models.CharField(max_length=13) + accepted = models.IntegerField(default=0) + rejected = models.IntegerField(default=0) + + def __str__(self): + return self.name + + +# USER ENDED # diff --git a/backend/accounts/tests.py b/backend/accounts/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/accounts/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/accounts/views.py b/backend/accounts/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/accounts/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/requirements.txt b/backend/requirements.txt index 85ce88a..26def2e 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,5 +1,7 @@ asgiref==3.3.1 Django==3.1.5 +django-jsonfield==1.4.1 djangorestframework==3.12.2 pytz==2020.5 -sqlparse==0.4.1 +six==1.15.0 +sqlparse==0.4.1 \ No newline at end of file From 129c6bdb37599e4684b3be82327f5032ecbe03eb Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 8 Feb 2021 10:09:37 +0530 Subject: [PATCH 03/15] Authentication Api Created and also add ApiList.txt file --- .gitignore | 3 + backend/ApiList.txt | 66 +++++++++++++++++++++ backend/Transcripts/settings.py | 52 +++++++++++++++- backend/Transcripts/urls.py | 5 +- backend/accounts/admin.py | 28 ++++++++- backend/accounts/forms.py | 14 +++++ backend/accounts/migrations/0001_initial.py | 58 ++++++++++++++++++ backend/accounts/models.py | 51 ++++++++++++---- backend/accounts/serializers.py | 13 ++++ backend/accounts/urls.py | 9 +++ backend/accounts/views.py | 14 ++++- backend/requirements.txt | 29 +++++++-- 12 files changed, 318 insertions(+), 24 deletions(-) create mode 100644 backend/ApiList.txt create mode 100644 backend/accounts/forms.py create mode 100644 backend/accounts/migrations/0001_initial.py create mode 100644 backend/accounts/serializers.py create mode 100644 backend/accounts/urls.py diff --git a/.gitignore b/.gitignore index 21c7067..84e3e67 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,6 @@ venv.bak/ .pyre/ # VS Code settings .vscode + +# Backend gitignore +/backend/.gitignore \ No newline at end of file diff --git a/backend/ApiList.txt b/backend/ApiList.txt new file mode 100644 index 0000000..5c6c8b5 --- /dev/null +++ b/backend/ApiList.txt @@ -0,0 +1,66 @@ +1) User creation +request :- http://127.0.0.1:8000/account/auth/users/ +type :- POST +inputs :- { + "email":"", + "is_management":, + "password":"", + "re_password":"" + } +output :- { + "is_management": , + "email": "", + "id": + } + +2) User Activation +request :- http://127.0.0.1:8000/account/auth/users/activation/ +type :- POST + Send a mail on given email + http://127.0.0.1:8000/activate/(uid)/{token} +inputs :- { + "uid":"", + "token":"" + } + +3) User Login using JWT +request :- http://127.0.0.1:8000/account/auth/jwt/create/ +type :- POST +inputs :- { + "email":"", + "password":"" + } +output :- { + "refresh": "", + "access": "" + } + +4) User info +request :- http://127.0.0.1:8000/account/auth/me/ +type :- Get +output :- { + "email": "", + "is_management": , + "profile_created": + } + +5) User Reset password +request :- http://127.0.0.1:8000/account/auth/users/reset_password/ +type :- POST +inputs :- { + "email":"" + } +output :- send a mail on given email + http://127.0.0.1:8000/password/reset/confirm/{uid}/{token} + + +6) Confirm Reset Password +request :- http://127.0.0.1:8000/account/auth/users/reset_password_confirm/ +type :- POST +inputs :- { + "uid":"", + "token":"", + "new_password":"", + "re_new_password":"" + } + \ No newline at end of file diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index b4d65a5..1b589ce 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -11,16 +11,17 @@ """ from pathlib import Path - +from decouple import config # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent - +config.encoding = 'cp1251' # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'p!ptauvv-$*q(o#_3-xztu6w9c@kqe+dr-gd&e+jhl!4+rey$e' +SECRET_KEY = config('SECRET_KEY') + # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -38,6 +39,8 @@ 'django.contrib.messages', 'django.contrib.staticfiles', # apps + 'rest_framework', + 'djoser', 'accounts', ] @@ -71,6 +74,14 @@ WSGI_APPLICATION = 'Transcripts.wsgi.application' +#Email Setup + +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = 'smtp.gmail.com' +EMAIL_PORT = 587 +EMAIL_HOST_USER = config('EMAIL_HOST_USER') +EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') +EMAIL_USE_TLS = True # Database # https://docs.djangoproject.com/en/3.1/ref/settings/#databases @@ -125,3 +136,38 @@ AUTH_USER_MODEL = 'accounts.AppUser' # to check if user has profile # AUTH_PROFILE_MODEL = 'accounts.modelname' + +# simple jwt setup + +SIMPLE_JWT = { + 'AUTH_HEADER_TYPES': ('JWT',), +} + + +# REST FRAMEWORK SETUP + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ), +} + +# DJOSER SETUP +DJOSER = { + 'LOGIN_FIELD':'email', + 'PASSWORD_RESET_CONFIRM_URL': 'password/reset/confirm/{uid}/{token}', + 'ACTIVATION_URL': 'activate/{uid}/{token}', + 'SEND_ACTIVATION_EMAIL': True, + 'SEND_CONFIRMATION_EMAIL':True, + 'PASSWORD_CHANGED_EMAIL_CONFIRMATION':True, + 'PASSWORD_RESET_CONFIRM_RETYPE':True, + 'SEND_CONFIRMATION_EMAIL': True, + 'USER_CREATE_PASSWORD_RETYPE':True, + 'SET_PASSWORD_RETYPE':True, + 'SERIALIZERS': { + 'user_create':'accounts.serializers.UserCreateSerializer', + 'user':'accounts.serializers.UserCreateSerializer', + 'user_delete':'djoser.serializers.UserDeleteSerializer', + }, +} + diff --git a/backend/Transcripts/urls.py b/backend/Transcripts/urls.py index 921b261..61abbd9 100644 --- a/backend/Transcripts/urls.py +++ b/backend/Transcripts/urls.py @@ -13,9 +13,12 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin -from django.urls import path +from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), + path('account/', include('accounts.urls')), ] + diff --git a/backend/accounts/admin.py b/backend/accounts/admin.py index 8c38f3f..c9fc3dd 100644 --- a/backend/accounts/admin.py +++ b/backend/accounts/admin.py @@ -1,3 +1,29 @@ from django.contrib import admin - +from .models import * +from django.contrib.auth.admin import UserAdmin +from .forms import CustomUserChangeForm,CustomUserCreationForm # Register your models here. +class myUserAdmin(UserAdmin): + add_form = CustomUserCreationForm + form = CustomUserChangeForm + model = AppUser + list_display=('email','date_joined','last_login','is_admin','is_management') + search_fields=('email',) + readonly_fields=('date_joined','last_login') + ordering=('email',) + filter_horizontal=() + list_filter=('email','is_staff','is_active','is_management',) + fieldsets=( + (None,{'fields':('email','password','is_management')}), + ('Permission',{'fields':('is_staff','is_active')}), + ) + add_fieldsets = ( + (None,{ + 'classes':('wide',), + 'fields':('email','password1','password2','is_management','is_staff','is_active') + }), + ) + +admin.site.register(AppUser,myUserAdmin) +admin.site.register(StudentProfile) +admin.site.register(ManagementProfile) diff --git a/backend/accounts/forms.py b/backend/accounts/forms.py new file mode 100644 index 0000000..c33a211 --- /dev/null +++ b/backend/accounts/forms.py @@ -0,0 +1,14 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm , UserChangeForm +from .models import AppUser +class CustomUserCreationForm(UserCreationForm): + email = forms.EmailField(max_length=255,help_text='Required. Add a valid email address') + class meta(UserCreationForm) : + model = AppUser + fields = ('email',) + +class CustomUserChangeForm(UserChangeForm): + email = forms.EmailField(max_length=255,help_text='Required. Add a valid email address') + class meta : + model = AppUser + fields = ('email',) \ No newline at end of file diff --git a/backend/accounts/migrations/0001_initial.py b/backend/accounts/migrations/0001_initial.py new file mode 100644 index 0000000..3ae4f05 --- /dev/null +++ b/backend/accounts/migrations/0001_initial.py @@ -0,0 +1,58 @@ +# Generated by Django 3.1.1 on 2021-02-08 04:34 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import jsonfield.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='AppUser', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('email', models.EmailField(max_length=255, unique=True, verbose_name='email')), + ('date_joined', models.DateTimeField(auto_now_add=True, verbose_name='date join')), + ('last_login', models.DateTimeField(auto_now=True, verbose_name='last login')), + ('is_admin', models.BooleanField(default=False)), + ('is_active', models.BooleanField(default=True)), + ('is_staff', models.BooleanField(default=False)), + ('is_superuser', models.BooleanField(default=False)), + ('is_management', models.BooleanField(default=False)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='StudentProfile', + fields=[ + ('sap_id', models.CharField(max_length=15, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100)), + ('contact_no', models.CharField(max_length=13)), + ('department', models.CharField(choices=[('CS', 'COMPUTERS'), ('IT', 'INFORMATION TECHNOLOGY'), ('EXTC', 'ELECTRONICS AND TELECOMMUNICATION'), ('ELEX', 'ELECTRONICS'), ('MECH', 'MECHANICAL'), ('CHEM', 'CHEMICAL'), ('BIOMED', 'BIOMED'), ('PROD', 'PRODUCTION'), ('OTHERS', 'OTHERS')], max_length=40)), + ('admission_year', models.CharField(max_length=5)), + ('marksheet', jsonfield.fields.JSONField(default=dict)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='ManagementProfile', + fields=[ + ('staff_id', models.CharField(max_length=20, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100)), + ('contact_no', models.CharField(max_length=13)), + ('accepted', models.IntegerField(default=0)), + ('rejected', models.IntegerField(default=0)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/backend/accounts/models.py b/backend/accounts/models.py index cc00d54..47d4892 100644 --- a/backend/accounts/models.py +++ b/backend/accounts/models.py @@ -6,18 +6,26 @@ # Create your models here. # User Starts # class AppUserManager(BaseUserManager): - def create_user(self, email, password=None, **extra_fields): + def create_user(self, email,is_management, password=None, **extra_fields): if email is None: raise TypeError("Email is required") - user = self.model(email=self.normalize_email(email)) + user = self.model( + email=self.normalize_email(email), + is_management=is_management, + ) + user.set_password(password) user.save(using=self._db) return user - def create_superuser(self, email, password=None, **extra_fields): + def create_superuser(self, email,is_management, password=None, **extra_fields): if password is None: raise TypeError('Password should not be none') - user = self.create_user(email, password) + user = self.create_user( + email=self.normalize_email(email), + password=password, + is_management=is_management, + ) user.is_active = True user.is_superuser = True user.is_staff = True @@ -27,18 +35,35 @@ def create_superuser(self, email, password=None, **extra_fields): class AppUser(AbstractBaseUser): - email = models.EmailField() # check if @djsce.in for faculty - is_management = models.BooleanField(default=False) # boolean fields - is_active = models.BooleanField(default=False) - is_admin = models.BooleanField(default=False) - is_staff = models.BooleanField(default=False) + email = models.EmailField(verbose_name="email",max_length=255,unique=True) + date_joined = models.DateTimeField(verbose_name="date join",auto_now_add=True) + last_login = models.DateTimeField(verbose_name="last login",auto_now=True) + is_admin = models.BooleanField(default=False) + is_active = models.BooleanField(default=True) + is_staff = models.BooleanField(default=False) + is_superuser = models.BooleanField(default=False) + is_management = models.BooleanField(default=False) USERNAME_FIELD = 'email' objects = AppUserManager() + REQUIRED_FIELDS = ['is_management'] + @property + def profileCreated(self): + user = self + if user.is_management : + return ManagementProfile.objects.filter(user=user).exists() + else : + return StudentProfile.objects.filter(user=user).exists() def __str__(self): return self.email + def has_perm(self,perm,obj=None): + return self.is_admin + + def has_module_perms(self,app_label): + return True + class StudentProfile(models.Model): departments = ( @@ -54,12 +79,12 @@ class StudentProfile(models.Model): ) user = models.OneToOneField(AppUser, on_delete=models.CASCADE) # other details - sap_id = models.CharField(max_lenght=15, primary_key=True) + sap_id = models.CharField(max_length=15, primary_key=True) name = models.CharField(max_length=100) contact_no = models.CharField(max_length=13) department = models.CharField(max_length=40, choices=departments) # can also take date as the input (change it to datefield) - admission_year = models.CharField(max_lenght=5) + admission_year = models.CharField(max_length=5) marksheet = jsonfield.JSONField() def __str__(self): @@ -68,7 +93,7 @@ def __str__(self): class ManagementProfile(models.Model): user = models.OneToOneField(AppUser, on_delete=models.CASCADE) - staff_id = models.CharField(primary_key=True) + staff_id = models.CharField(max_length=20, primary_key=True) name = models.CharField(max_length=100) contact_no = models.CharField(max_length=13) accepted = models.IntegerField(default=0) @@ -78,4 +103,4 @@ def __str__(self): return self.name -# USER ENDED # +# USER ENDED # \ No newline at end of file diff --git a/backend/accounts/serializers.py b/backend/accounts/serializers.py new file mode 100644 index 0000000..b06be6e --- /dev/null +++ b/backend/accounts/serializers.py @@ -0,0 +1,13 @@ +from djoser.serializers import UserCreateSerializer +from django.contrib.auth import get_user_model +from rest_framework import serializers +from django.contrib import auth +from rest_framework.exceptions import AuthenticationFailed +import jsonfield + +User = get_user_model() + +class UserCreateSerializer(UserCreateSerializer): + class Meta(UserCreateSerializer.Meta): + model = User + fields = ('id','email','username','is_management','password') \ No newline at end of file diff --git a/backend/accounts/urls.py b/backend/accounts/urls.py new file mode 100644 index 0000000..cdeaadf --- /dev/null +++ b/backend/accounts/urls.py @@ -0,0 +1,9 @@ + +from django.urls import path,include +from .views import * + +urlpatterns = [ + path('auth/', include('djoser.urls')), + path('auth/', include('djoser.urls.jwt')), + path('auth/me/', UserInfoApi.as_view(),name='user_info'), +] \ No newline at end of file diff --git a/backend/accounts/views.py b/backend/accounts/views.py index 91ea44a..3f7d1f5 100644 --- a/backend/accounts/views.py +++ b/backend/accounts/views.py @@ -1,3 +1,15 @@ from django.shortcuts import render - +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated # Create your views here. +class UserInfoApi(APIView): + permission_classes = [IsAuthenticated] + def get(self,request): + user = request.user + data = { + 'email': user.email, + 'is_management':user.is_management, + 'profile_created':user.profileCreated + } + return Response(data) \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt index 26def2e..cac2653 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,7 +1,26 @@ -asgiref==3.3.1 -Django==3.1.5 +appdirs==1.4.4 +asgiref==3.2.10 +colorama==0.4.3 +distlib==0.3.1 +Django==3.1.1 +django-cors-headers==3.5.0 +django-filter==2.3.0 django-jsonfield==1.4.1 -djangorestframework==3.12.2 -pytz==2020.5 +django-templated-mail==1.1.1 +djangorestframework==3.11.1 +djangorestframework-simplejwt==4.6.0 +djoser==2.0.5 +filelock==3.0.12 +isort==5.4.2 +lazy-object-proxy==1.4.3 +Markdown==3.2.2 +mysql-connector-python==8.0.21 +Pillow==7.2.0 +psycopg2==2.8.5 +PyJWT==2.0.1 +python-decouple==3.4 +pytz==2020.1 six==1.15.0 -sqlparse==0.4.1 \ No newline at end of file +sqlparse==0.3.1 +toml==0.10.1 +virtualenv==20.4.0 From 07a34edff690b27d17572d950b3e96d0838f65ed Mon Sep 17 00:00:00 2001 From: chris Date: Mon, 8 Feb 2021 13:52:07 +0530 Subject: [PATCH 04/15] requirements.txt file updated --- backend/requirements.txt | 49 ++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index cac2653..60cdebe 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,26 +1,35 @@ -appdirs==1.4.4 -asgiref==3.2.10 -colorama==0.4.3 -distlib==0.3.1 -Django==3.1.1 -django-cors-headers==3.5.0 -django-filter==2.3.0 +asgiref==3.3.1 +certifi==2020.12.5 +cffi==1.14.4 +chardet==4.0.0 +coreapi==2.3.3 +coreschema==0.0.4 +cryptography==3.4.1 +defusedxml==0.7.0rc2 +Django==3.1.5 django-jsonfield==1.4.1 django-templated-mail==1.1.1 -djangorestframework==3.11.1 +djangorestframework==3.12.2 djangorestframework-simplejwt==4.6.0 -djoser==2.0.5 -filelock==3.0.12 -isort==5.4.2 -lazy-object-proxy==1.4.3 -Markdown==3.2.2 -mysql-connector-python==8.0.21 -Pillow==7.2.0 -psycopg2==2.8.5 +djoser==2.1.0 +idna==2.10 +itypes==1.2.0 +Jinja2==2.11.3 +MarkupSafe==1.1.1 +oauthlib==3.1.0 +pycparser==2.20 PyJWT==2.0.1 python-decouple==3.4 -pytz==2020.1 +python3-openid==3.2.0 +pytz==2020.5 +requests==2.25.1 +requests-oauthlib==1.3.0 +semantic-version==2.8.5 +setuptools-rust==0.11.6 six==1.15.0 -sqlparse==0.3.1 -toml==0.10.1 -virtualenv==20.4.0 +social-auth-app-django==4.0.0 +social-auth-core==4.0.3 +sqlparse==0.4.1 +toml==0.10.2 +uritemplate==3.0.1 +urllib3==1.26.3 From 10076919f255d3ba964f9ed27318748c65491eb2 Mon Sep 17 00:00:00 2001 From: Akshath Date: Sun, 14 Feb 2021 17:07:45 +0530 Subject: [PATCH 05/15] Added custom contexts for all djoser emails Emails sent through djoser will now have urls with the domain defined in settings.FRONTEND_DOMAIN. New templates for emails can also be created now as per requirement. Updated settings for hosting API on pythonanywhere --- backend/Transcripts/settings.py | 15 +++++++++++++-- backend/accounts/email.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 backend/accounts/email.py diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index 1b589ce..d35fdf9 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -12,6 +12,7 @@ from pathlib import Path from decouple import config +import os # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -27,7 +28,8 @@ DEBUG = True ALLOWED_HOSTS = [] - +if DEBUG: + ALLOWED_HOSTS = ['*'] # Application definition @@ -129,7 +131,7 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.1/howto/static-files/ - +STATIC_ROOT = os.path.join(BASE_DIR, "static") STATIC_URL = '/static/' @@ -152,6 +154,7 @@ ), } +FRONTEND_DOMAIN = "www.abc.com" # DJOSER SETUP DJOSER = { 'LOGIN_FIELD':'email', @@ -169,5 +172,13 @@ 'user':'accounts.serializers.UserCreateSerializer', 'user_delete':'djoser.serializers.UserDeleteSerializer', }, + "EMAIL": { + "activation": "accounts.email.ActivationEmail", + "confirmation": "accounts.email.ConfirmationEmail", + "password_reset": "accounts.email.PasswordResetEmail", + "password_changed_confirmation": "accounts.email.PasswordChangedConfirmationEmail", + "username_changed_confirmation": "accounts.email.UsernameChangedConfirmationEmail", + "username_reset": "accounts.email.UsernameResetEmail" + } } diff --git a/backend/accounts/email.py b/backend/accounts/email.py new file mode 100644 index 0000000..d967663 --- /dev/null +++ b/backend/accounts/email.py @@ -0,0 +1,31 @@ +from djoser.email import ActivationEmail, ConfirmationEmail, PasswordResetEmail, PasswordChangedConfirmationEmail, UsernameChangedConfirmationEmail, UsernameResetEmail +from django.conf import settings + +def customContext(context): + context['domain'] = settings.FRONTEND_DOMAIN + context['site_name'] = settings.FRONTEND_DOMAIN + return context +class ActivationEmail(ActivationEmail): + def get_context_data(self): + context = super().get_context_data() + return customContext(context) +class ConfirmationEmail(ConfirmationEmail): + def get_context_data(self): + context = super().get_context_data() + return customContext(context) +class PasswordResetEmail(PasswordResetEmail): + def get_context_data(self): + context = super().get_context_data() + return customContext(context) +class PasswordChangedConfirmationEmail(PasswordChangedConfirmationEmail): + def get_context_data(self): + context = super().get_context_data() + return customContext(context) +class UsernameChangedConfirmationEmail(UsernameChangedConfirmationEmail): + def get_context_data(self): + context = super().get_context_data() + return customContext(context) +class UsernameResetEmail(UsernameResetEmail): + def get_context_data(self): + context = super().get_context_data() + return customContext(context) \ No newline at end of file From e046fbeb2ad7a0e87f57f981d39b163620614d12 Mon Sep 17 00:00:00 2001 From: Akshath Date: Mon, 15 Feb 2021 17:05:05 +0530 Subject: [PATCH 06/15] Added profile creation to user registration Added profile creation by extending djoser's UserViewSet and rewriting the perform_create function to accommodate generating user profiles depending on the type of user. Added in StudentProfileSerializer and ManagementProfileSerialzier to assist in the same --- backend/accounts/serializers.py | 18 ++++++++++++---- backend/accounts/urls.py | 9 ++++++-- backend/accounts/views.py | 37 ++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/backend/accounts/serializers.py b/backend/accounts/serializers.py index b06be6e..5fd442b 100644 --- a/backend/accounts/serializers.py +++ b/backend/accounts/serializers.py @@ -1,13 +1,23 @@ from djoser.serializers import UserCreateSerializer from django.contrib.auth import get_user_model from rest_framework import serializers -from django.contrib import auth -from rest_framework.exceptions import AuthenticationFailed -import jsonfield + +from .models import ManagementProfile, StudentProfile User = get_user_model() class UserCreateSerializer(UserCreateSerializer): class Meta(UserCreateSerializer.Meta): model = User - fields = ('id','email','username','is_management','password') \ No newline at end of file + fields = ('id','email','username','is_management','password') + +class ManagementProfileSerializer(serializers.ModelSerializer): + class Meta: + model = ManagementProfile + fields = ('user', 'staff_id', 'name', 'contact_no') + +class StudentProfileSerializer(serializers.ModelSerializer): + class Meta: + model = StudentProfile + fields = ('user', 'sap_id', 'name', 'contact_no', 'department', 'admission_year') + \ No newline at end of file diff --git a/backend/accounts/urls.py b/backend/accounts/urls.py index cdeaadf..8383e25 100644 --- a/backend/accounts/urls.py +++ b/backend/accounts/urls.py @@ -1,9 +1,14 @@ from django.urls import path,include from .views import * +from rest_framework.routers import DefaultRouter urlpatterns = [ - path('auth/', include('djoser.urls')), path('auth/', include('djoser.urls.jwt')), path('auth/me/', UserInfoApi.as_view(),name='user_info'), -] \ No newline at end of file +] + +router = DefaultRouter() +router.register("auth/users", UserViewSet) + +urlpatterns += router.urls \ No newline at end of file diff --git a/backend/accounts/views.py b/backend/accounts/views.py index 3f7d1f5..ee88568 100644 --- a/backend/accounts/views.py +++ b/backend/accounts/views.py @@ -2,6 +2,15 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated +from rest_framework import status +from rest_framework.exceptions import ValidationError +from .models import ManagementProfile, StudentProfile +from .serializers import ManagementProfileSerializer, StudentProfileSerializer +from djoser.views import UserViewSet +from djoser import signals +from djoser.compat import get_user_email +from djoser.conf import settings as DjoserSettings + # Create your views here. class UserInfoApi(APIView): permission_classes = [IsAuthenticated] @@ -12,4 +21,30 @@ def get(self,request): 'is_management':user.is_management, 'profile_created':user.profileCreated } - return Response(data) \ No newline at end of file + return Response(data) + +class UserViewSet(UserViewSet): + def perform_create(self, serializer): + user = serializer.save() + signals.user_registered.send( + sender=self.__class__, user=user, request=self.request + ) + if self.request.data.get('profile', False): + self.request.data['profile']['user'] = user.id + if self.request.data.get('is_management', False): + profile = ManagementProfileSerializer(data = self.request.data.get('profile', None)) + else: + profile = StudentProfileSerializer(data = self.request.data.get('profile', None)) + if not profile.is_valid(): + user.delete() + raise ValidationError(profile.errors) + profile.save() + # Calling super().perform_create(serializer) saves serializer again and messes up password + + # Send activation email - + context = {"user": user} + to = [get_user_email(user)] + if DjoserSettings.SEND_ACTIVATION_EMAIL: + DjoserSettings.EMAIL.activation(self.request, context).send(to) + elif DjoserSettings.SEND_CONFIRMATION_EMAIL: + DjoserSettings.EMAIL.confirmation(self.request, context).send(to) \ No newline at end of file From bc2dde0370ff5248b54cf8aa0d814a5ffdfe8be6 Mon Sep 17 00:00:00 2001 From: Jinay01 Date: Thu, 18 Feb 2021 10:13:46 +0530 Subject: [PATCH 07/15] update profile --- backend/Transcripts/settings.py | 1 + backend/Transcripts/urls.py | 1 + backend/core/__init__.py | 0 backend/core/admin.py | 3 ++ backend/core/apps.py | 5 +++ backend/core/migrations/__init__.py | 0 backend/core/models.py | 3 ++ backend/core/serializers.py | 19 ++++++++++ backend/core/tests.py | 3 ++ backend/core/urls.py | 8 +++++ backend/core/views.py | 54 +++++++++++++++++++++++++++++ 11 files changed, 97 insertions(+) create mode 100644 backend/core/__init__.py create mode 100644 backend/core/admin.py create mode 100644 backend/core/apps.py create mode 100644 backend/core/migrations/__init__.py create mode 100644 backend/core/models.py create mode 100644 backend/core/serializers.py create mode 100644 backend/core/tests.py create mode 100644 backend/core/urls.py create mode 100644 backend/core/views.py diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index d35fdf9..c18ef75 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -44,6 +44,7 @@ 'rest_framework', 'djoser', 'accounts', + 'core', ] MIDDLEWARE = [ diff --git a/backend/Transcripts/urls.py b/backend/Transcripts/urls.py index 61abbd9..6d729f9 100644 --- a/backend/Transcripts/urls.py +++ b/backend/Transcripts/urls.py @@ -20,5 +20,6 @@ urlpatterns = [ path('admin/', admin.site.urls), path('account/', include('accounts.urls')), + path('core/', include('core.urls')), ] diff --git a/backend/core/__init__.py b/backend/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/core/admin.py b/backend/core/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/backend/core/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/backend/core/apps.py b/backend/core/apps.py new file mode 100644 index 0000000..26f78a8 --- /dev/null +++ b/backend/core/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + name = 'core' diff --git a/backend/core/migrations/__init__.py b/backend/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/core/models.py b/backend/core/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/backend/core/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/backend/core/serializers.py b/backend/core/serializers.py new file mode 100644 index 0000000..68d110b --- /dev/null +++ b/backend/core/serializers.py @@ -0,0 +1,19 @@ +from rest_framework import serializers +from accounts.models import StudentProfile, ManagementProfile, AppUser + +# delete this serializer if everthing works fine +# class UpdateStudentProfileSerializer(serializers.ModelSerializer): +# class Meta: +# model = StudentProfile +# fields = ('sap_id', "name", "contact_no", "department", 'admission_year') + +# class UpdateManagementProfileSerializer(serializers.ModelSerializer): +# class Meta: +# model = ManagementProfile +# fields= ('staff_id', 'name','contact_no') + +# donot delete this serializer +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = AppUser + fields = ('email',) diff --git a/backend/core/tests.py b/backend/core/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/core/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/core/urls.py b/backend/core/urls.py new file mode 100644 index 0000000..313deac --- /dev/null +++ b/backend/core/urls.py @@ -0,0 +1,8 @@ +from django.urls import path,include +from .views import * + + +urlpatterns = [ + path('studentdata/', StudentUpdateProfile.as_view(), name='studentprofile'), + path('managementdata/', ManagementUpdateProfile.as_view(), name='managementprofile'), +] \ No newline at end of file diff --git a/backend/core/views.py b/backend/core/views.py new file mode 100644 index 0000000..9f7c0ba --- /dev/null +++ b/backend/core/views.py @@ -0,0 +1,54 @@ +from django.shortcuts import render +from rest_framework.views import APIView +from rest_framework.permissions import IsAuthenticated +from .serializers import * +from accounts.serializers import ManagementProfileSerializer, StudentProfileSerializer +from rest_framework.response import Response +from rest_framework import status + +# Create your views here. + +# student profile section +class StudentUpdateProfile(APIView): + permission_classes = [IsAuthenticated] + def put(self, request): + user = request.user + users_data = StudentProfile.objects.get(user= user) + user_serializer = StudentProfileSerializer(users_data, data=request.data) + if user_serializer.is_valid(): + user_serializer.save() + return Response(user_serializer.data,status=status.HTTP_200_OK) + else: + return Response(status=status.HTTP_404_NOT_FOUND) + + def get(self, request): + user = request.user + # objects + users_data = StudentProfile.objects.get(user= user) + user_email_obj = users_data.user + user_serializer = UserSerializer(user_email_obj) + user_profile_serializer = StudentProfileSerializer(users_data) + return Response({'email':user_serializer.data,"profile":user_profile_serializer.data},status=status.HTTP_200_OK) + +class ManagementUpdateProfile(APIView): + permission_classes = [IsAuthenticated] + def get(self, request): + user = request.user + # objects + users_data = ManagementProfile.objects.get(user= user) + user_email_obj = users_data.user + user_serializer = UserSerializer(user_email_obj) + user_profile_serializer = ManagementProfileSerializer(users_data) + return Response({'email':user_serializer.data,"profile":user_profile_serializer.data},status=status.HTTP_200_OK) + + def put(self, request): + user = request.user + users_data = ManagementProfile.objects.get(user= user) + user_serializer = ManagementProfileSerializer(users_data, data=request.data) + if user_serializer.is_valid(): + user_serializer.save() + return Response(user_serializer.data,status=status.HTTP_200_OK) + else: + return Response(status=status.HTTP_404_NOT_FOUND) + + From 2628bd7fa4136b79f92e8e06585a55a42ef604ca Mon Sep 17 00:00:00 2001 From: Chris correia Date: Thu, 18 Feb 2021 10:23:03 +0530 Subject: [PATCH 08/15] Application Model Created --- backend/StudentManagement/__init__.py | 0 backend/StudentManagement/admin.py | 3 +++ backend/StudentManagement/apps.py | 5 ++++ .../StudentManagement/migrations/__init__.py | 0 backend/StudentManagement/models.py | 3 +++ backend/StudentManagement/tests.py | 3 +++ backend/StudentManagement/urls.py | 7 +++++ backend/StudentManagement/views.py | 3 +++ backend/Transcripts/settings.py | 6 ++--- backend/Transcripts/urls.py | 1 + backend/accounts/admin.py | 1 + .../accounts/migrations/0002_application.py | 27 +++++++++++++++++++ backend/accounts/models.py | 17 ++++++++++-- 13 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 backend/StudentManagement/__init__.py create mode 100644 backend/StudentManagement/admin.py create mode 100644 backend/StudentManagement/apps.py create mode 100644 backend/StudentManagement/migrations/__init__.py create mode 100644 backend/StudentManagement/models.py create mode 100644 backend/StudentManagement/tests.py create mode 100644 backend/StudentManagement/urls.py create mode 100644 backend/StudentManagement/views.py create mode 100644 backend/accounts/migrations/0002_application.py diff --git a/backend/StudentManagement/__init__.py b/backend/StudentManagement/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/StudentManagement/admin.py b/backend/StudentManagement/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/backend/StudentManagement/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/backend/StudentManagement/apps.py b/backend/StudentManagement/apps.py new file mode 100644 index 0000000..a3bedc0 --- /dev/null +++ b/backend/StudentManagement/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class StudentmanagementConfig(AppConfig): + name = 'StudentManagement' diff --git a/backend/StudentManagement/migrations/__init__.py b/backend/StudentManagement/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/StudentManagement/models.py b/backend/StudentManagement/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/backend/StudentManagement/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/backend/StudentManagement/tests.py b/backend/StudentManagement/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/StudentManagement/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/StudentManagement/urls.py b/backend/StudentManagement/urls.py new file mode 100644 index 0000000..0d7a858 --- /dev/null +++ b/backend/StudentManagement/urls.py @@ -0,0 +1,7 @@ + +from django.urls import path,include +from .views import * + +urlpatterns = [ + +] \ No newline at end of file diff --git a/backend/StudentManagement/views.py b/backend/StudentManagement/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/StudentManagement/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index d35fdf9..270b60f 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -21,7 +21,7 @@ # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = config('SECRET_KEY') +SECRET_KEY = 'p!ptauvv-$*q(o#_3-xztu6w9c@kqe+dr-gd&e+jhl!4+rey$e' # SECURITY WARNING: don't run with debug turned on in production! @@ -81,8 +81,8 @@ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.gmail.com' EMAIL_PORT = 587 -EMAIL_HOST_USER = config('EMAIL_HOST_USER') -EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') +EMAIL_HOST_USER = 'transcriptbackend@gmail.com' +EMAIL_HOST_PASSWORD = 'okgovzhcycihtrcl' EMAIL_USE_TLS = True # Database diff --git a/backend/Transcripts/urls.py b/backend/Transcripts/urls.py index 61abbd9..bbc06e6 100644 --- a/backend/Transcripts/urls.py +++ b/backend/Transcripts/urls.py @@ -20,5 +20,6 @@ urlpatterns = [ path('admin/', admin.site.urls), path('account/', include('accounts.urls')), + path('student/',include('StudentManagement.urls')), ] diff --git a/backend/accounts/admin.py b/backend/accounts/admin.py index c9fc3dd..a0b3ac6 100644 --- a/backend/accounts/admin.py +++ b/backend/accounts/admin.py @@ -27,3 +27,4 @@ class myUserAdmin(UserAdmin): admin.site.register(AppUser,myUserAdmin) admin.site.register(StudentProfile) admin.site.register(ManagementProfile) +admin.site.register(Application) \ No newline at end of file diff --git a/backend/accounts/migrations/0002_application.py b/backend/accounts/migrations/0002_application.py new file mode 100644 index 0000000..173d467 --- /dev/null +++ b/backend/accounts/migrations/0002_application.py @@ -0,0 +1,27 @@ +# Generated by Django 3.1.5 on 2021-02-18 04:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Application', + fields=[ + ('application_number', models.AutoField(primary_key=True, serialize=False)), + ('date_of_application', models.DateTimeField(auto_now_add=True)), + ('status', models.CharField(choices=[('In Queue', 'In Queue'), ('In Review', 'In Review'), ('Rejected', 'Rejected'), ('Accepted', 'Accepted')], max_length=40)), + ('comment', models.TextField(blank=True, null=True)), + ('in_review', models.BooleanField(default=False)), + ('application_file', models.FileField(upload_to='')), + ('faculty', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.managementprofile')), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.studentprofile')), + ], + ), + ] diff --git a/backend/accounts/models.py b/backend/accounts/models.py index 47d4892..320b341 100644 --- a/backend/accounts/models.py +++ b/backend/accounts/models.py @@ -102,5 +102,18 @@ class ManagementProfile(models.Model): def __str__(self): return self.name - -# USER ENDED # \ No newline at end of file +class Application(models.Model): + Status = ( + ('In Queue', 'In Queue'), + ('In Review', 'In Review'), + ('Rejected', 'Rejected'), + ('Accepted', 'Accepted'), + ) + student = models.ForeignKey(StudentProfile,on_delete=models.CASCADE) + faculty = models.ForeignKey(ManagementProfile,on_delete=models.CASCADE,null=True,blank=True) + application_number = models.AutoField(primary_key=True) + date_of_application = models.DateTimeField(auto_now_add=True) + status = models.CharField(max_length=40, choices=Status) + comment = models.TextField(null=True,blank=True) + in_review = models.BooleanField(default=False) + application_file = models.FileField() \ No newline at end of file From 0abc4b2ee71f64f145db32442c29ab4cdbbee664 Mon Sep 17 00:00:00 2001 From: Chris correia Date: Thu, 18 Feb 2021 11:03:34 +0530 Subject: [PATCH 09/15] Made Model changes --- backend/StudentManagement/__init__.py | 0 backend/StudentManagement/admin.py | 3 --- backend/StudentManagement/apps.py | 5 ----- backend/StudentManagement/migrations/__init__.py | 0 backend/StudentManagement/models.py | 3 --- backend/StudentManagement/tests.py | 3 --- backend/StudentManagement/urls.py | 7 ------- backend/StudentManagement/views.py | 3 --- backend/Transcripts/settings.py | 6 +++--- backend/Transcripts/urls.py | 1 - backend/accounts/admin.py | 3 +-- backend/accounts/models.py | 15 --------------- 12 files changed, 4 insertions(+), 45 deletions(-) delete mode 100644 backend/StudentManagement/__init__.py delete mode 100644 backend/StudentManagement/admin.py delete mode 100644 backend/StudentManagement/apps.py delete mode 100644 backend/StudentManagement/migrations/__init__.py delete mode 100644 backend/StudentManagement/models.py delete mode 100644 backend/StudentManagement/tests.py delete mode 100644 backend/StudentManagement/urls.py delete mode 100644 backend/StudentManagement/views.py diff --git a/backend/StudentManagement/__init__.py b/backend/StudentManagement/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/StudentManagement/admin.py b/backend/StudentManagement/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/backend/StudentManagement/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/backend/StudentManagement/apps.py b/backend/StudentManagement/apps.py deleted file mode 100644 index a3bedc0..0000000 --- a/backend/StudentManagement/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class StudentmanagementConfig(AppConfig): - name = 'StudentManagement' diff --git a/backend/StudentManagement/migrations/__init__.py b/backend/StudentManagement/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/StudentManagement/models.py b/backend/StudentManagement/models.py deleted file mode 100644 index 71a8362..0000000 --- a/backend/StudentManagement/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/backend/StudentManagement/tests.py b/backend/StudentManagement/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/backend/StudentManagement/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/backend/StudentManagement/urls.py b/backend/StudentManagement/urls.py deleted file mode 100644 index 0d7a858..0000000 --- a/backend/StudentManagement/urls.py +++ /dev/null @@ -1,7 +0,0 @@ - -from django.urls import path,include -from .views import * - -urlpatterns = [ - -] \ No newline at end of file diff --git a/backend/StudentManagement/views.py b/backend/StudentManagement/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/backend/StudentManagement/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index 270b60f..d35fdf9 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -21,7 +21,7 @@ # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'p!ptauvv-$*q(o#_3-xztu6w9c@kqe+dr-gd&e+jhl!4+rey$e' +SECRET_KEY = config('SECRET_KEY') # SECURITY WARNING: don't run with debug turned on in production! @@ -81,8 +81,8 @@ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.gmail.com' EMAIL_PORT = 587 -EMAIL_HOST_USER = 'transcriptbackend@gmail.com' -EMAIL_HOST_PASSWORD = 'okgovzhcycihtrcl' +EMAIL_HOST_USER = config('EMAIL_HOST_USER') +EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') EMAIL_USE_TLS = True # Database diff --git a/backend/Transcripts/urls.py b/backend/Transcripts/urls.py index bbc06e6..61abbd9 100644 --- a/backend/Transcripts/urls.py +++ b/backend/Transcripts/urls.py @@ -20,6 +20,5 @@ urlpatterns = [ path('admin/', admin.site.urls), path('account/', include('accounts.urls')), - path('student/',include('StudentManagement.urls')), ] diff --git a/backend/accounts/admin.py b/backend/accounts/admin.py index a0b3ac6..fcd6041 100644 --- a/backend/accounts/admin.py +++ b/backend/accounts/admin.py @@ -26,5 +26,4 @@ class myUserAdmin(UserAdmin): admin.site.register(AppUser,myUserAdmin) admin.site.register(StudentProfile) -admin.site.register(ManagementProfile) -admin.site.register(Application) \ No newline at end of file +admin.site.register(ManagementProfile) \ No newline at end of file diff --git a/backend/accounts/models.py b/backend/accounts/models.py index 320b341..b4e75ac 100644 --- a/backend/accounts/models.py +++ b/backend/accounts/models.py @@ -102,18 +102,3 @@ class ManagementProfile(models.Model): def __str__(self): return self.name -class Application(models.Model): - Status = ( - ('In Queue', 'In Queue'), - ('In Review', 'In Review'), - ('Rejected', 'Rejected'), - ('Accepted', 'Accepted'), - ) - student = models.ForeignKey(StudentProfile,on_delete=models.CASCADE) - faculty = models.ForeignKey(ManagementProfile,on_delete=models.CASCADE,null=True,blank=True) - application_number = models.AutoField(primary_key=True) - date_of_application = models.DateTimeField(auto_now_add=True) - status = models.CharField(max_length=40, choices=Status) - comment = models.TextField(null=True,blank=True) - in_review = models.BooleanField(default=False) - application_file = models.FileField() \ No newline at end of file From 256e33ae2108ea46affa7ab2e576d9785d6f53b4 Mon Sep 17 00:00:00 2001 From: Chris correia Date: Mon, 22 Feb 2021 12:37:33 +0530 Subject: [PATCH 10/15] removed demo app --- backend/Transcripts/settings.py | 3 +++ backend/Transcripts/urls.py | 4 +++- .../migrations/0003_delete_application.py | 16 ++++++++++++++++ backend/requirements.txt | Bin 661 -> 1424 bytes 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 backend/accounts/migrations/0003_delete_application.py diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index d35fdf9..0e42441 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -44,6 +44,7 @@ 'rest_framework', 'djoser', 'accounts', + 'studentDemo', ] MIDDLEWARE = [ @@ -135,6 +136,8 @@ STATIC_URL = '/static/' + + AUTH_USER_MODEL = 'accounts.AppUser' # to check if user has profile # AUTH_PROFILE_MODEL = 'accounts.modelname' diff --git a/backend/Transcripts/urls.py b/backend/Transcripts/urls.py index 61abbd9..93ded47 100644 --- a/backend/Transcripts/urls.py +++ b/backend/Transcripts/urls.py @@ -16,9 +16,11 @@ from django.contrib import admin from django.urls import path,include - +from django.conf import settings +from django.conf.urls.static import static urlpatterns = [ path('admin/', admin.site.urls), path('account/', include('accounts.urls')), + path('demo/', include('studentDemo.urls')), ] diff --git a/backend/accounts/migrations/0003_delete_application.py b/backend/accounts/migrations/0003_delete_application.py new file mode 100644 index 0000000..2b1b682 --- /dev/null +++ b/backend/accounts/migrations/0003_delete_application.py @@ -0,0 +1,16 @@ +# Generated by Django 3.1.5 on 2021-02-18 07:40 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0002_application'), + ] + + operations = [ + migrations.DeleteModel( + name='Application', + ), + ] diff --git a/backend/requirements.txt b/backend/requirements.txt index 60cdebec206fa56d4cbd89ae008fe2738c9f5637..9d61931f080dbec49584175f1fc4845848bab1b0 100644 GIT binary patch literal 1424 zcmZ{kO>fg+5QO)P#7}V)JEd^p#;FogrB>o>5}UZBwy_=B^5cPLcKsDl6}fTW-JPA8 z-8X-J=hoXdtE{uaO8dxXXB&S~duwlO&v|D986}V6nQi^qSjzXt-i>+XoRM;x+7*AV z#@NwvP!3=Xj-lArpXkwe|J>TSM{wAU^%3u3a|GiNG%*Q;rAucoAPm-^s3WH(`^7V* zNj*c8c$9WVc>$+q@J=({duP?rDf8Z+>;a@2#AZg4zEFKwMHAk8vTFCC0Q2A#%3k+g z@DjL6O-pX>?*~g)EJQ@YYe{(GW#XWv&WHJwng=J|SM0%A4tDNyqS zzI+z@Yo4t;BCN)}>pZG1;u+Bj)%7@^h)m~$Pe-hUsS3p-dPCpZz06ja%83p545GB1 z@#>EE61@{jc%(gQh@9bk#J$SCdWI&yu)F2_#HT@<>OWjWTNv)?pkCUIMW1$F9d60E zu`hP-)xm$7j;hsL8<6;}?|B#F)5tcqP&n({9oY>h1$A;yyp*}P6WPDUp407%4b(J$ z&?UAZyj5k!hEB}>%~8MqoeH<7iAcZhS&gO&R}%ZliF~PCp;)Sq91JF^7+kMzV1w$O zs*V%wsq%GSQ=i7QyD6+^kHiecIDFPU+7rz|NoKBkYx&6Cnhnq#Qe)Rm|0({Ao&-I?F5;PDp&SJXrR literal 661 zcmZuvO>f*F5WVwXKnCw_d-1KONRcWvQqBa%YaH9K10->OeP?i1$)Pvq>&?9Pu=yGg z#$o7Hue5N0C5&MhOlLZ!jXDcAa;%j;sgrP%MGs{dPO4L#a1p^KZl~$Kh1pH8+F_tF z_v4;QyrNCh&O1)D!^2oJ_>Xnrt{GNN7KJ>qM_Gs^t0CUYPZnBN1#S#n+nt*b(lBL|eX} From b1bdb84f5af16011fac8ceea1cd7e028911ce8a5 Mon Sep 17 00:00:00 2001 From: Akshath Date: Mon, 22 Feb 2021 14:26:55 +0530 Subject: [PATCH 11/15] Added management dashboard endpoints Changed auth header to Bearer token which is the standard in JWT Refactored urls so that excluding all api urls will be easy when serving react from django server Changed Management Profile serializer to include accepted and rejected number of applications Created application model and serializers Added management dashboard endpoints --- backend/Transcripts/settings.py | 2 +- backend/Transcripts/urls.py | 5 +- backend/accounts/serializers.py | 2 +- backend/core/admin.py | 4 +- backend/core/migrations/0001_initial.py | 29 +++++++++ .../migrations/0002_auto_20210222_1455.py | 25 ++++++++ backend/core/models.py | 26 +++++++- backend/core/serializers.py | 18 ++++++ backend/core/urls.py | 10 ++- backend/core/views_management.py | 63 +++++++++++++++++++ 10 files changed, 175 insertions(+), 9 deletions(-) create mode 100644 backend/core/migrations/0001_initial.py create mode 100644 backend/core/migrations/0002_auto_20210222_1455.py create mode 100644 backend/core/views_management.py diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index c18ef75..c8c7216 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -143,7 +143,7 @@ # simple jwt setup SIMPLE_JWT = { - 'AUTH_HEADER_TYPES': ('JWT',), + 'AUTH_HEADER_TYPES': ('Bearer',), } diff --git a/backend/Transcripts/urls.py b/backend/Transcripts/urls.py index 6d729f9..2d0b366 100644 --- a/backend/Transcripts/urls.py +++ b/backend/Transcripts/urls.py @@ -19,7 +19,8 @@ urlpatterns = [ path('admin/', admin.site.urls), - path('account/', include('accounts.urls')), - path('core/', include('core.urls')), + path('api/', include('accounts.urls')), + path('api/', include('core.urls')), ] +# urlpatterns += [re_path(r'^(?!api/).*', TemplateView.as_view(template_name="index.html"))] for react in prod if deploying on same server diff --git a/backend/accounts/serializers.py b/backend/accounts/serializers.py index 5fd442b..c6fbd24 100644 --- a/backend/accounts/serializers.py +++ b/backend/accounts/serializers.py @@ -14,7 +14,7 @@ class Meta(UserCreateSerializer.Meta): class ManagementProfileSerializer(serializers.ModelSerializer): class Meta: model = ManagementProfile - fields = ('user', 'staff_id', 'name', 'contact_no') + fields = ('user', 'staff_id', 'name', 'contact_no', 'accepted', 'rejected') class StudentProfileSerializer(serializers.ModelSerializer): class Meta: diff --git a/backend/core/admin.py b/backend/core/admin.py index 8c38f3f..2dfe672 100644 --- a/backend/core/admin.py +++ b/backend/core/admin.py @@ -1,3 +1,5 @@ from django.contrib import admin - +from .models import Application # Register your models here. + +admin.site.register(Application) diff --git a/backend/core/migrations/0001_initial.py b/backend/core/migrations/0001_initial.py new file mode 100644 index 0000000..22b6846 --- /dev/null +++ b/backend/core/migrations/0001_initial.py @@ -0,0 +1,29 @@ +# Generated by Django 3.1.5 on 2021-02-22 07:38 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('accounts', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Application', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('review_time', models.DateTimeField(blank=True, default=None, null=True)), + ('comment', models.TextField(blank=True, null=True)), + ('in_review', models.BooleanField(default=None, null=True)), + ('accepted', models.BooleanField(default=False)), + ('faculty', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.managementprofile')), + ('student', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.studentprofile')), + ], + ), + ] diff --git a/backend/core/migrations/0002_auto_20210222_1455.py b/backend/core/migrations/0002_auto_20210222_1455.py new file mode 100644 index 0000000..240d1a3 --- /dev/null +++ b/backend/core/migrations/0002_auto_20210222_1455.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1.5 on 2021-02-22 09:25 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0001_initial'), + ('core', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='application', + name='faculty', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.managementprofile'), + ), + migrations.AlterField( + model_name='application', + name='student', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.studentprofile'), + ), + ] diff --git a/backend/core/models.py b/backend/core/models.py index 71a8362..f43ec85 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1,3 +1,27 @@ from django.db import models +from django.db.models.signals import post_save -# Create your models here. +from accounts.models import StudentProfile, ManagementProfile + +class Application(models.Model): + #ID will be application number + student = models.ForeignKey(StudentProfile, null=True, blank=True, on_delete=models.SET_NULL, default=None) #Student applies + faculty = models.ForeignKey(ManagementProfile,null=True, blank=True, on_delete=models.SET_NULL, default=None) #Faculty checks + created_at = models.DateTimeField(auto_now_add=True) + review_time = models.DateTimeField(null=True, blank=True, default=None) #To track when something went in review + comment = models.TextField(blank=True, null=True) + in_review = models.BooleanField(null=True, default=None) + accepted = models.BooleanField(default=False) + + # in_review None => Created but hasn't been sent for review + # in_review True => Sent for (and in) review + # in_review False => Finished Reviewing + +def save_application(instance, created, **kwargs): + if (not created) and (instance.in_review is False): #instance.in_review is None has a different meaning so can't use not instance.in_review + if instance.accepted: + instance.faculty.accepted+=1 + else: + instance.faculty.rejected+=1 + instance.faculty.save() +post_save.connect(save_application, sender=Application) \ No newline at end of file diff --git a/backend/core/serializers.py b/backend/core/serializers.py index 68d110b..1f75c55 100644 --- a/backend/core/serializers.py +++ b/backend/core/serializers.py @@ -1,5 +1,6 @@ from rest_framework import serializers from accounts.models import StudentProfile, ManagementProfile, AppUser +from .models import Application # delete this serializer if everthing works fine # class UpdateStudentProfileSerializer(serializers.ModelSerializer): @@ -17,3 +18,20 @@ class UserSerializer(serializers.ModelSerializer): class Meta: model = AppUser fields = ('email',) + +class ApplicationSerializer(serializers.ModelSerializer): + marksheet = serializers.SerializerMethodField('get_marks') + def get_marks(self, obj): + return obj.student.marksheet + class Meta: + model = Application + fields = ('id', 'student','faculty', 'in_review', 'created_at','accepted', 'comment', 'marksheet') + +class AcceptedSerializer(serializers.ModelSerializer): + ''' + Since we are sending a list of many accepted applications, there may potentially be + redundant data sent over network if we use ApplicationSerializer for it + ''' + class Meta: + model = Application + fields = ('id', 'student','faculty') diff --git a/backend/core/urls.py b/backend/core/urls.py index 313deac..05b2cce 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -1,8 +1,12 @@ from django.urls import path,include from .views import * - +from .views_management import ManagementApplication, AcceptedApplications urlpatterns = [ - path('studentdata/', StudentUpdateProfile.as_view(), name='studentprofile'), - path('managementdata/', ManagementUpdateProfile.as_view(), name='managementprofile'), + path('student/profile/', StudentUpdateProfile.as_view(), name='studentprofile'), + path('management/profile/', ManagementUpdateProfile.as_view(), name='managementprofile'), + #Management Dashboard: + path('management/applications/', ManagementApplication.as_view()), + path('management/accepted/', AcceptedApplications.as_view()), + ] \ No newline at end of file diff --git a/backend/core/views_management.py b/backend/core/views_management.py new file mode 100644 index 0000000..33bd8f0 --- /dev/null +++ b/backend/core/views_management.py @@ -0,0 +1,63 @@ +from django.shortcuts import render +from django.db.models import Q +from django.utils import timezone +from django.http import Http404 + +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated +from rest_framework import status + +from datetime import timedelta +from .serializers import ApplicationSerializer, AcceptedSerializer +from .models import Application + +class ManagementApplication(APIView): + permisison_classes = [IsAuthenticated] + def get(self, request): + ''' + Get next application to review from queue + ''' + if not request.user.is_management: + return Response(status=status.HTTP_403_FORBIDDEN) + last_hour = timezone.now() - timedelta(hours=1) #Tested properly with seconds=10 + query = Q(in_review=None) | Q(in_review=True, review_time__lt=last_hour) + obj = Application.objects.filter(query).order_by('created_at').first() + if obj is None: + return Response(status=status.HTTP_204_NO_CONTENT) + obj.in_review = True + obj.review_time = timezone.now() + obj.save() + ser = ApplicationSerializer(obj) + return Response(ser.data, status=status.HTTP_200_OK) + def get_object(self, pk): + try: + return Application.objects.get(pk=pk) + except: + raise Http404 + def put(self, request): + ''' + Update existing application - accept or reject with comment + ''' + if not request.user.is_management: + return Response(status=status.HTTP_403_FORBIDDEN) + application = self.get_object(request.data.get('pk', None)) + request.data['in_review'] = False + request.data['faculty'] = request.user.managementprofile.staff_id + serializer = ApplicationSerializer(application, data=request.data, partial=True) + if serializer.is_valid(): + serializer.save() + return Response(status=status.HTTP_200_OK) + return Response(status=status.HTTP_400_BAD_REQUEST) + +class AcceptedApplications(APIView): + permission_classes = [IsAuthenticated] + def get(self, request): + ''' + Get list of accepted applications to verify + ''' + if not request.user.is_management: + return Response(status=status.HTTP_403_FORBIDDEN) + qs = Application.objects.filter(accepted=True) + ser = AcceptedSerializer(qs, many=True) + return Response(ser.data, status=status.HTTP_200_OK) From 5e7c6cdc40f1cf752dba4ed6e9cd6f03b6e0107c Mon Sep 17 00:00:00 2001 From: Jinay01 Date: Tue, 2 Mar 2021 15:27:38 +0530 Subject: [PATCH 12/15] swagger setup --- backend/Transcripts/settings.py | 15 +++++++++++++++ backend/Transcripts/urls.py | 22 ++++++++++++++++++++++ backend/accounts/serializers.py | 2 +- backend/core/views.py | 3 +++ backend/requirements.txt | 11 ++++++++++- 5 files changed, 51 insertions(+), 2 deletions(-) diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index c8c7216..c9e1ae9 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -45,6 +45,10 @@ 'djoser', 'accounts', 'core', + + # swagger + 'corsheaders', + 'drf_yasg', ] MIDDLEWARE = [ @@ -183,3 +187,14 @@ } } +# swagger +SWAGGER_SETTINGS = { + 'SECURITY_DEFINITIONS': { + 'Bearer': { + 'type': 'apiKey', + 'name': 'Authorization', + 'in': 'header' + } + }, +'REFETCH_SCHEMA_WITH_AUTH': True, +} diff --git a/backend/Transcripts/urls.py b/backend/Transcripts/urls.py index 2d0b366..8d49574 100644 --- a/backend/Transcripts/urls.py +++ b/backend/Transcripts/urls.py @@ -16,11 +16,33 @@ from django.contrib import admin from django.urls import path,include +# swagger +from rest_framework import permissions +from drf_yasg.views import get_schema_view +from drf_yasg import openapi + +schema_view = get_schema_view( + openapi.Info( + title="APP API", + default_version='v1', + description="Test description", + terms_of_service="https://www.ourapp.com/policies/terms/", + contact=openapi.Contact(email="contact@me.local"), + license=openapi.License(name="Test License"), + ), + public=True, + permission_classes=(permissions.AllowAny,), +) urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('accounts.urls')), path('api/', include('core.urls')), + # swagger + path('', schema_view.with_ui('swagger',cache_timeout=0), name='schema-swagger-ui'), + path('api/api.json/', schema_view.without_ui(cache_timeout=0),name='schema-swagger-ui'), + path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), ] # urlpatterns += [re_path(r'^(?!api/).*', TemplateView.as_view(template_name="index.html"))] for react in prod if deploying on same server + diff --git a/backend/accounts/serializers.py b/backend/accounts/serializers.py index c6fbd24..ea183ef 100644 --- a/backend/accounts/serializers.py +++ b/backend/accounts/serializers.py @@ -9,7 +9,7 @@ class UserCreateSerializer(UserCreateSerializer): class Meta(UserCreateSerializer.Meta): model = User - fields = ('id','email','username','is_management','password') + fields = ('id','email','is_management','password') class ManagementProfileSerializer(serializers.ModelSerializer): class Meta: diff --git a/backend/core/views.py b/backend/core/views.py index 9f7c0ba..894ada1 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -22,6 +22,9 @@ def put(self, request): return Response(status=status.HTTP_404_NOT_FOUND) def get(self, request): + ''' + abc + ''' user = request.user # objects users_data = StudentProfile.objects.get(user= user) diff --git a/backend/requirements.txt b/backend/requirements.txt index 60cdebe..2bb2753 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -7,29 +7,38 @@ coreschema==0.0.4 cryptography==3.4.1 defusedxml==0.7.0rc2 Django==3.1.5 +django-cors-headers==3.7.0 django-jsonfield==1.4.1 django-templated-mail==1.1.1 djangorestframework==3.12.2 djangorestframework-simplejwt==4.6.0 djoser==2.1.0 +drf-yasg==1.20.0 idna==2.10 +inflection==0.5.1 itypes==1.2.0 Jinja2==2.11.3 MarkupSafe==1.1.1 oauthlib==3.1.0 +openapi-codec==1.3.2 +packaging==20.9 pycparser==2.20 PyJWT==2.0.1 +pyparsing==2.4.7 python-decouple==3.4 python3-openid==3.2.0 pytz==2020.5 requests==2.25.1 requests-oauthlib==1.3.0 +ruamel.yaml==0.16.12 +ruamel.yaml.clib==0.2.2 semantic-version==2.8.5 setuptools-rust==0.11.6 +simplejson==3.17.2 six==1.15.0 social-auth-app-django==4.0.0 social-auth-core==4.0.3 sqlparse==0.4.1 toml==0.10.2 uritemplate==3.0.1 -urllib3==1.26.3 +urllib3==1.26.3 \ No newline at end of file From ed9863b49d4c3b4badbaf4bdfa99479cf82b9797 Mon Sep 17 00:00:00 2001 From: Chris correia Date: Tue, 2 Mar 2021 16:50:59 +0530 Subject: [PATCH 13/15] changed student urls and add get request to marks --- backend/core/urls.py | 4 ++-- backend/core/views_student.py | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/backend/core/urls.py b/backend/core/urls.py index b768720..aedd34d 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -12,7 +12,7 @@ #Student Dashboard : path('student/applications/', StudentApplication.as_view()), - path('scan_marksheet/', ScanMarksheet.as_view(),name="scan_marksheer"), - path('Enter_marks/',EnterMarks.as_view(),name="Enter marks"), + path('student/scan_marksheet/', ScanMarksheet.as_view(),name="scan_marksheer"), + path('student/marks/',EnterMarks.as_view(),name="Enter marks"), ] \ No newline at end of file diff --git a/backend/core/views_student.py b/backend/core/views_student.py index d6cc1c5..dd16d21 100644 --- a/backend/core/views_student.py +++ b/backend/core/views_student.py @@ -78,6 +78,7 @@ class EnterMarks(APIView): permission_classes = [IsAuthenticated] parser_classes=[JSONParser] serializer_class = EnterResultSerializer + def post(self,request): user = request.user if(StudentProfile.objects.all().filter(user=user).exists()): @@ -106,6 +107,16 @@ def post(self,request): else: return Response({"message": "Profile not created"},status=HTTP_400_BAD_REQUEST) + def get(self,request): + user = request.user + if(StudentProfile.objects.all().filter(user=user).exists()): + student = StudentProfile.objects.get(user=user) + old_marksheet = student.marksheet + return Response(old_marksheet,status=HTTP_200_OK) + else: + return Response({"message": "Profile not created"},status=HTTP_400_BAD_REQUEST) + + class ScanMarksheet(APIView): permission_classes = [IsAuthenticated] serializer_class = UploadMarksheetSerializer From 350c91c9574cd27d057f69c79e427b048f54b29f Mon Sep 17 00:00:00 2001 From: Jinay01 Date: Tue, 2 Mar 2021 17:43:39 +0530 Subject: [PATCH 14/15] swagger setup --- backend/Transcripts/settings.py | 4 + backend/Transcripts/urls.py | 28 +- backend/accounts/admin.py | 2 +- .../accounts/migrations/0002_application.py | 27 ++ .../migrations/0003_delete_application.py | 16 ++ backend/accounts/models.py | 2 - backend/accounts/views.py | 10 + backend/core/admin.py | 5 +- backend/core/migrations/0003_marksheet.py | 20 ++ backend/core/models.py | 19 +- backend/core/serializers.py | 14 +- backend/core/urls.py | 6 + backend/core/views_student.py | 268 ++++++++++++++++++ backend/requirements.txt | 9 + 14 files changed, 413 insertions(+), 17 deletions(-) create mode 100644 backend/accounts/migrations/0002_application.py create mode 100644 backend/accounts/migrations/0003_delete_application.py create mode 100644 backend/core/migrations/0003_marksheet.py create mode 100644 backend/core/views_student.py diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index c9e1ae9..384df54 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -139,6 +139,10 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static") STATIC_URL = '/static/' +MEDIA_URL='/' +MEDIA_ROOT = os.path.join(BASE_DIR,'Images') + + AUTH_USER_MODEL = 'accounts.AppUser' # to check if user has profile diff --git a/backend/Transcripts/urls.py b/backend/Transcripts/urls.py index 8d49574..b735f39 100644 --- a/backend/Transcripts/urls.py +++ b/backend/Transcripts/urls.py @@ -15,7 +15,8 @@ """ from django.contrib import admin -from django.urls import path,include +from django.urls import path, include + # swagger from rest_framework import permissions from drf_yasg.views import get_schema_view @@ -24,7 +25,7 @@ schema_view = get_schema_view( openapi.Info( title="APP API", - default_version='v1', + default_version="v1", description="Test description", terms_of_service="https://www.ourapp.com/policies/terms/", contact=openapi.Contact(email="contact@me.local"), @@ -34,15 +35,24 @@ permission_classes=(permissions.AllowAny,), ) +from django.conf import settings +from django.conf.urls.static import static + urlpatterns = [ - path('admin/', admin.site.urls), - path('api/', include('accounts.urls')), - path('api/', include('core.urls')), + path("admin/", admin.site.urls), + path("api/", include("accounts.urls")), + path("api/", include("core.urls")), # swagger - path('', schema_view.with_ui('swagger',cache_timeout=0), name='schema-swagger-ui'), - path('api/api.json/', schema_view.without_ui(cache_timeout=0),name='schema-swagger-ui'), - path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), + path("", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"), + path( + "api/api.json/", + schema_view.without_ui(cache_timeout=0), + name="schema-swagger-ui", + ), + path("redoc/", schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc"), ] +urlpatterns = urlpatterns + static( + settings.MEDIA_URL, document_root=settings.MEDIA_ROOT +) # urlpatterns += [re_path(r'^(?!api/).*', TemplateView.as_view(template_name="index.html"))] for react in prod if deploying on same server - diff --git a/backend/accounts/admin.py b/backend/accounts/admin.py index c9fc3dd..fcd6041 100644 --- a/backend/accounts/admin.py +++ b/backend/accounts/admin.py @@ -26,4 +26,4 @@ class myUserAdmin(UserAdmin): admin.site.register(AppUser,myUserAdmin) admin.site.register(StudentProfile) -admin.site.register(ManagementProfile) +admin.site.register(ManagementProfile) \ No newline at end of file diff --git a/backend/accounts/migrations/0002_application.py b/backend/accounts/migrations/0002_application.py new file mode 100644 index 0000000..173d467 --- /dev/null +++ b/backend/accounts/migrations/0002_application.py @@ -0,0 +1,27 @@ +# Generated by Django 3.1.5 on 2021-02-18 04:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Application', + fields=[ + ('application_number', models.AutoField(primary_key=True, serialize=False)), + ('date_of_application', models.DateTimeField(auto_now_add=True)), + ('status', models.CharField(choices=[('In Queue', 'In Queue'), ('In Review', 'In Review'), ('Rejected', 'Rejected'), ('Accepted', 'Accepted')], max_length=40)), + ('comment', models.TextField(blank=True, null=True)), + ('in_review', models.BooleanField(default=False)), + ('application_file', models.FileField(upload_to='')), + ('faculty', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='accounts.managementprofile')), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.studentprofile')), + ], + ), + ] diff --git a/backend/accounts/migrations/0003_delete_application.py b/backend/accounts/migrations/0003_delete_application.py new file mode 100644 index 0000000..2b1b682 --- /dev/null +++ b/backend/accounts/migrations/0003_delete_application.py @@ -0,0 +1,16 @@ +# Generated by Django 3.1.5 on 2021-02-18 07:40 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0002_application'), + ] + + operations = [ + migrations.DeleteModel( + name='Application', + ), + ] diff --git a/backend/accounts/models.py b/backend/accounts/models.py index 47d4892..b4e75ac 100644 --- a/backend/accounts/models.py +++ b/backend/accounts/models.py @@ -102,5 +102,3 @@ class ManagementProfile(models.Model): def __str__(self): return self.name - -# USER ENDED # \ No newline at end of file diff --git a/backend/accounts/views.py b/backend/accounts/views.py index ee88568..56f3dd2 100644 --- a/backend/accounts/views.py +++ b/backend/accounts/views.py @@ -10,6 +10,10 @@ from djoser import signals from djoser.compat import get_user_email from djoser.conf import settings as DjoserSettings + +# swagger +# from drf_yasg.utils import swagger_auto_schema +# from drf_yasg import openapi # Create your views here. class UserInfoApi(APIView): @@ -24,6 +28,12 @@ def get(self,request): return Response(data) class UserViewSet(UserViewSet): + # @swagger_auto_schema(method='post', request_body=openapi.Schema( + # type=openapi.TYPE_OBJECT, + # properties={ + # 'x': openapi.Schema(type=openapi.TYPE_STRING, description='string'), + # 'y': openapi.Schema(type=openapi.TYPE_STRING, description='string'), + # })) def perform_create(self, serializer): user = serializer.save() signals.user_registered.send( diff --git a/backend/core/admin.py b/backend/core/admin.py index 2dfe672..cc2fb49 100644 --- a/backend/core/admin.py +++ b/backend/core/admin.py @@ -1,5 +1,8 @@ from django.contrib import admin -from .models import Application +from .models import * # Register your models here. admin.site.register(Application) + +admin.site.register(Marksheet) + diff --git a/backend/core/migrations/0003_marksheet.py b/backend/core/migrations/0003_marksheet.py new file mode 100644 index 0000000..d8b3181 --- /dev/null +++ b/backend/core/migrations/0003_marksheet.py @@ -0,0 +1,20 @@ +# Generated by Django 3.1.5 on 2021-02-22 14:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_auto_20210222_1455'), + ] + + operations = [ + migrations.CreateModel( + name='Marksheet', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file', models.ImageField(blank=True, null=True, upload_to='images')), + ], + ), + ] diff --git a/backend/core/models.py b/backend/core/models.py index f43ec85..19e75ca 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1,7 +1,19 @@ from django.db import models -from django.db.models.signals import post_save - from accounts.models import StudentProfile, ManagementProfile + +from django.db.models.signals import post_delete,post_save +from django.dispatch import receiver + + +class Marksheet(models.Model): + file = models.ImageField( upload_to='marks' ,null=True,blank=True) + +@receiver(post_delete, sender=Marksheet) +def submission_delete(sender, instance, **kwargs): + instance.file.delete(False) + + + class Application(models.Model): #ID will be application number @@ -24,4 +36,5 @@ def save_application(instance, created, **kwargs): else: instance.faculty.rejected+=1 instance.faculty.save() -post_save.connect(save_application, sender=Application) \ No newline at end of file +post_save.connect(save_application, sender=Application) + diff --git a/backend/core/serializers.py b/backend/core/serializers.py index 1f75c55..95fcddf 100644 --- a/backend/core/serializers.py +++ b/backend/core/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers from accounts.models import StudentProfile, ManagementProfile, AppUser -from .models import Application +from .models import Application,Marksheet # delete this serializer if everthing works fine # class UpdateStudentProfileSerializer(serializers.ModelSerializer): @@ -19,6 +19,9 @@ class Meta: model = AppUser fields = ('email',) + + + class ApplicationSerializer(serializers.ModelSerializer): marksheet = serializers.SerializerMethodField('get_marks') def get_marks(self, obj): @@ -35,3 +38,12 @@ class AcceptedSerializer(serializers.ModelSerializer): class Meta: model = Application fields = ('id', 'student','faculty') + +class EnterResultSerializer(serializers.Serializer): + marksheet = serializers.JSONField() + + +class UploadMarksheetSerializer(serializers.ModelSerializer): + class Meta: + model = Marksheet + fields = ['file'] diff --git a/backend/core/urls.py b/backend/core/urls.py index 05b2cce..aedd34d 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -1,5 +1,6 @@ from django.urls import path,include from .views import * +from .views_student import * from .views_management import ManagementApplication, AcceptedApplications urlpatterns = [ @@ -9,4 +10,9 @@ path('management/applications/', ManagementApplication.as_view()), path('management/accepted/', AcceptedApplications.as_view()), + #Student Dashboard : + path('student/applications/', StudentApplication.as_view()), + path('student/scan_marksheet/', ScanMarksheet.as_view(),name="scan_marksheer"), + path('student/marks/',EnterMarks.as_view(),name="Enter marks"), + ] \ No newline at end of file diff --git a/backend/core/views_student.py b/backend/core/views_student.py new file mode 100644 index 0000000..dd16d21 --- /dev/null +++ b/backend/core/views_student.py @@ -0,0 +1,268 @@ +from accounts.models import * +from .models import * +from rest_framework.views import APIView +from rest_framework.status import * +from rest_framework.renderers import JSONRenderer +from rest_framework.response import Response +from rest_framework import authentication, permissions +from rest_framework.permissions import IsAuthenticated +from rest_framework.parsers import FormParser,MultiPartParser,JSONParser,FileUploadParser +from .serializers import * + + +class StudentApplication(APIView): + permission_classes = [IsAuthenticated] + def get(self,request): + user = request.user + if(user.is_management==False): + if(StudentProfile.objects.all().filter(user=user).exists()): + student = StudentProfile.objects.get(user=user) + if(Application.objects.all().filter(student=student).exists()): + Old_application = Application.objects.all().filter(student=student) + d = {"application" : []} + for app in Old_application : + data={} + if(app.in_review==False): + + data = { + "Student Name":student.name, + "Faculty":app.faculty.name, + "created_at" :app.created_at, + "review_time":app.review_time, + "comment":app.comment, + "accepted":app.accepted + } + else: + data = { + "Student Name":student.name, + "created_at" :app.created_at, + } + d["application"].append(data) + return Response({"application": d},status=HTTP_200_OK) + else: + return Response({"message": "Applications Not Created"},status=HTTP_404_NOT_FOUND) + else: + return Response({"message": "Profile not created"},status=HTTP_400_BAD_REQUEST) + else: + return Response({"message": "You are not a student"},status=HTTP_400_BAD_REQUEST) + + def post(self,request): + user = request.user + if(user.is_management==False): + if(StudentProfile.objects.all().filter(user=user).exists()): + new_application=None + student = StudentProfile.objects.get(user=user) + if(Application.objects.all().filter(student=student).exists()): + old_application = Application.objects.all().filter(student=student) + last =len(old_application)-1 + application = old_application[last] + if application.in_review==False : + new_application = Application.objects.create(student=student) + + else: + return Response({"message": "Your Apllication is in Queue . You can't create 2 applications at time "},status=HTTP_208_ALREADY_REPORTED) + else: + new_application=Application.objects.create(student=student) + data = { + "Student Name":student.name, + "created_at" :new_application.created_at, + } + return Response({"message": "Application created","Application":data},status=HTTP_201_CREATED) + else: + return Response({"message": "Profile not created"},status=HTTP_400_BAD_REQUEST) + else: + return Response({"message": "You are not a student"},status=HTTP_400_BAD_REQUEST) + + +class EnterMarks(APIView): + permission_classes = [IsAuthenticated] + parser_classes=[JSONParser] + serializer_class = EnterResultSerializer + + def post(self,request): + user = request.user + if(StudentProfile.objects.all().filter(user=user).exists()): + student = StudentProfile.objects.get(user=user) + old_marksheet = student.marksheet + new_marksheet = {} + serializer=self.serializer_class(data=request.data) + if(serializer.is_valid()): + marks = serializer.data.get("marksheet") + + if(len(old_marksheet)>0): + if(len(old_marksheet)==8): + return Response({"message": "Marksheet Must contain atmost 8 semester"},status=HTTP_400_BAD_REQUEST) + new_marksheet = old_marksheet.copy() + sem_no = str(marks["sem"]) + new_marksheet[sem_no]=marks + new_marksheet = sort_dict(new_marksheet) + else: + sem_no = marks["sem"] + new_marksheet[sem_no]=marks + student.marksheet = new_marksheet + student.save() + return Response({"result":new_marksheet},status=HTTP_200_OK) + else: + return Response({"message": serializer.error_messages},status=HTTP_400_BAD_REQUEST) + else: + return Response({"message": "Profile not created"},status=HTTP_400_BAD_REQUEST) + + def get(self,request): + user = request.user + if(StudentProfile.objects.all().filter(user=user).exists()): + student = StudentProfile.objects.get(user=user) + old_marksheet = student.marksheet + return Response(old_marksheet,status=HTTP_200_OK) + else: + return Response({"message": "Profile not created"},status=HTTP_400_BAD_REQUEST) + + +class ScanMarksheet(APIView): + permission_classes = [IsAuthenticated] + serializer_class = UploadMarksheetSerializer + parser_classes=[MultiPartParser,JSONParser] + def post(self,request): + serializer = self.serializer_class(data=request.data) + if(serializer.is_valid()): + marks = serializer.save() + print(marks.file) + result_data = fetchResult() + #scan(file) + marks.delete() + return Response(result_data,status=HTTP_202_ACCEPTED) + else: + return Response({"message": "File Missing"},status=HTTP_406_NOT_ACCEPTABLE) + + +def sort_dict(dic): + s_dic = sorted(dic.items(), key=lambda x: x[0].lower()) + new_dic={} + for item in s_dic: + new_dic[item[0]]=item[1] + return new_dic + + + + +def fetchResult(): + sem_result = { + "sem":1, + "credits_earned":20, + "cgpa":10, + "courses":[ + { + "couse_code":"DJ19FEC101", + "course_name":"MATHS-1", + "marks":99, + "pointer":10, + "credits_earned":4, + "grade":"O" + }, + { + + "couse_code":"DJ19FET101", + "course_name":"MATHS-1-tut", + "marks":25, + "pointer":10, + "credits_earned":1, + "grade":"O" + }, + { + "couse_code":"DJ19FEC102", + "course_name":"Phy-1", + "marks":91, + "pointer":10, + "credits_earned":2, + "grade":"O" + }, + { + + "couse_code":"DJ19FEL102", + "course_name":"Phy-1-Tut&Lab", + "marks":25, + "pointer":10, + "credits_earned":1.5, + "grade":"O" + }, + { + "couse_code":"DJ19FEC103", + "course_name":"Chem-1", + "marks":95, + "pointer":10, + "credits_earned":2, + "grade":"O" + }, + { + + "couse_code":"DJ19FEL103", + "course_name":"Chem-1-Tut&Lab", + "marks":25, + "pointer":10, + "credits_earned":1.5, + "grade":"O" + }, + { + "couse_code":"DJ19FEC104", + "course_name":"Mech", + "marks":96, + "pointer":10, + "credits_earned":3, + "grade":"O" + }, + { + + "couse_code":"DJ19FEL104", + "course_name":"Mech-Lab", + "marks":25, + "pointer":10, + "credits_earned":1, + "grade":"O" + }, + { + "couse_code":"DJ19FEC105", + "course_name":"BEE", + "marks":97, + "pointer":10, + "credits_earned":3, + "grade":"O" + }, + { + + "couse_code":"DJ19FEL105", + "course_name":"BEE-Lab", + "marks":25, + "pointer":10, + "credits_earned":1, + "grade":"O" + } + + ] + + } + + return sem_result + + + +''' +Json formate +result_Json ={ + sem_number:{ + "sem":"", + "credits_earned":"", + "cgpa":"", + "courses":[ + { + "couse_code":"", + "course_name":"", + "marks":"", + "pointer":"", + "credits_earned":"", + "grade":"", + } + ] + + }, + ... +} +''' diff --git a/backend/requirements.txt b/backend/requirements.txt index 2bb2753..6cdfcc5 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,7 +1,10 @@ +appdirs==1.4.4 asgiref==3.3.1 +black==20.8b1 certifi==2020.12.5 cffi==1.14.4 chardet==4.0.0 +click==7.1.2 coreapi==2.3.3 coreschema==0.0.4 cryptography==3.4.1 @@ -19,15 +22,19 @@ inflection==0.5.1 itypes==1.2.0 Jinja2==2.11.3 MarkupSafe==1.1.1 +mypy-extensions==0.4.3 oauthlib==3.1.0 openapi-codec==1.3.2 packaging==20.9 +pathspec==0.8.1 +Pillow==8.1.1 pycparser==2.20 PyJWT==2.0.1 pyparsing==2.4.7 python-decouple==3.4 python3-openid==3.2.0 pytz==2020.5 +regex==2020.11.13 requests==2.25.1 requests-oauthlib==1.3.0 ruamel.yaml==0.16.12 @@ -40,5 +47,7 @@ social-auth-app-django==4.0.0 social-auth-core==4.0.3 sqlparse==0.4.1 toml==0.10.2 +typed-ast==1.4.2 +typing-extensions==3.7.4.3 uritemplate==3.0.1 urllib3==1.26.3 \ No newline at end of file From 1be9ef2e9a2da345daf4c79fccde383bfd56e2bf Mon Sep 17 00:00:00 2001 From: Jinay01 Date: Sun, 11 Apr 2021 18:21:10 +0530 Subject: [PATCH 15/15] heroku settings --- Procfile | 1 + backend/Transcripts/settings.py | 155 ++++++++++++++++---------------- backend/Transcripts/wsgi.py | 2 + runtime.txt | 1 + 4 files changed, 80 insertions(+), 79 deletions(-) create mode 100644 Procfile create mode 100644 runtime.txt diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..e0e4336 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn Transcripts.wsgi --log-file - \ No newline at end of file diff --git a/backend/Transcripts/settings.py b/backend/Transcripts/settings.py index 384df54..b090b0f 100644 --- a/backend/Transcripts/settings.py +++ b/backend/Transcripts/settings.py @@ -13,15 +13,16 @@ from pathlib import Path from decouple import config import os + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent -config.encoding = 'cp1251' +config.encoding = "cp1251" # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = config('SECRET_KEY') +SECRET_KEY = config("SECRET_KEY") # SECURITY WARNING: don't run with debug turned on in production! @@ -29,74 +30,73 @@ ALLOWED_HOSTS = [] if DEBUG: - ALLOWED_HOSTS = ['*'] + ALLOWED_HOSTS = ["*"] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", # apps - 'rest_framework', - 'djoser', - 'accounts', - 'core', - + "rest_framework", + "djoser", + "accounts", + "core", # swagger - 'corsheaders', - 'drf_yasg', + "corsheaders", + "drf_yasg", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'Transcripts.urls' +ROOT_URLCONF = "Transcripts.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', + "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 = 'Transcripts.wsgi.application' +WSGI_APPLICATION = "Transcripts.wsgi.application" -#Email Setup +# Email Setup -EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_HOST = 'smtp.gmail.com' +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +EMAIL_HOST = "smtp.gmail.com" EMAIL_PORT = 587 -EMAIL_HOST_USER = config('EMAIL_HOST_USER') -EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') +EMAIL_HOST_USER = config("EMAIL_HOST_USER") +EMAIL_HOST_PASSWORD = config("EMAIL_HOST_PASSWORD") EMAIL_USE_TLS = True # Database # https://docs.djangoproject.com/en/3.1/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", } } @@ -106,16 +106,16 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -123,9 +123,9 @@ # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -137,49 +137,48 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.1/howto/static-files/ STATIC_ROOT = os.path.join(BASE_DIR, "static") -STATIC_URL = '/static/' - -MEDIA_URL='/' -MEDIA_ROOT = os.path.join(BASE_DIR,'Images') +STATIC_URL = "/static/" +MEDIA_URL = "/" +MEDIA_ROOT = os.path.join(BASE_DIR, "Images") -AUTH_USER_MODEL = 'accounts.AppUser' +AUTH_USER_MODEL = "accounts.AppUser" # to check if user has profile # AUTH_PROFILE_MODEL = 'accounts.modelname' # simple jwt setup SIMPLE_JWT = { - 'AUTH_HEADER_TYPES': ('Bearer',), + "AUTH_HEADER_TYPES": ("Bearer",), } # REST FRAMEWORK SETUP REST_FRAMEWORK = { - 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework_simplejwt.authentication.JWTAuthentication', + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework_simplejwt.authentication.JWTAuthentication", ), } FRONTEND_DOMAIN = "www.abc.com" # DJOSER SETUP DJOSER = { - 'LOGIN_FIELD':'email', - 'PASSWORD_RESET_CONFIRM_URL': 'password/reset/confirm/{uid}/{token}', - 'ACTIVATION_URL': 'activate/{uid}/{token}', - 'SEND_ACTIVATION_EMAIL': True, - 'SEND_CONFIRMATION_EMAIL':True, - 'PASSWORD_CHANGED_EMAIL_CONFIRMATION':True, - 'PASSWORD_RESET_CONFIRM_RETYPE':True, - 'SEND_CONFIRMATION_EMAIL': True, - 'USER_CREATE_PASSWORD_RETYPE':True, - 'SET_PASSWORD_RETYPE':True, - 'SERIALIZERS': { - 'user_create':'accounts.serializers.UserCreateSerializer', - 'user':'accounts.serializers.UserCreateSerializer', - 'user_delete':'djoser.serializers.UserDeleteSerializer', + "LOGIN_FIELD": "email", + "PASSWORD_RESET_CONFIRM_URL": "password/reset/confirm/{uid}/{token}", + "ACTIVATION_URL": "activate/{uid}/{token}", + "SEND_ACTIVATION_EMAIL": True, + "SEND_CONFIRMATION_EMAIL": True, + "PASSWORD_CHANGED_EMAIL_CONFIRMATION": True, + "PASSWORD_RESET_CONFIRM_RETYPE": True, + "SEND_CONFIRMATION_EMAIL": True, + "USER_CREATE_PASSWORD_RETYPE": True, + "SET_PASSWORD_RETYPE": True, + "SERIALIZERS": { + "user_create": "accounts.serializers.UserCreateSerializer", + "user": "accounts.serializers.UserCreateSerializer", + "user_delete": "djoser.serializers.UserDeleteSerializer", }, "EMAIL": { "activation": "accounts.email.ActivationEmail", @@ -187,18 +186,16 @@ "password_reset": "accounts.email.PasswordResetEmail", "password_changed_confirmation": "accounts.email.PasswordChangedConfirmationEmail", "username_changed_confirmation": "accounts.email.UsernameChangedConfirmationEmail", - "username_reset": "accounts.email.UsernameResetEmail" - } + "username_reset": "accounts.email.UsernameResetEmail", + }, } # swagger SWAGGER_SETTINGS = { - 'SECURITY_DEFINITIONS': { - 'Bearer': { - 'type': 'apiKey', - 'name': 'Authorization', - 'in': 'header' - } + "SECURITY_DEFINITIONS": { + "Bearer": {"type": "apiKey", "name": "Authorization", "in": "header"} }, -'REFETCH_SCHEMA_WITH_AUTH': True, + "REFETCH_SCHEMA_WITH_AUTH": True, } + +STATICFILES_STORAGE = "whitenoise.django.GzipManifestStaticFilesStorage" diff --git a/backend/Transcripts/wsgi.py b/backend/Transcripts/wsgi.py index 460ed12..b704578 100644 --- a/backend/Transcripts/wsgi.py +++ b/backend/Transcripts/wsgi.py @@ -10,7 +10,9 @@ import os from django.core.wsgi import get_wsgi_application +from whitenoise.django import DjangoWhiteNoise os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Transcripts.settings') application = get_wsgi_application() +application = DjangoWhiteNoise(application) \ No newline at end of file diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..1124509 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.8.5 \ No newline at end of file