Initial Commit

This commit is contained in:
gowtham3105 2021-10-15 20:47:23 +05:30
parent 6d811c6313
commit 2e11681173
28 changed files with 1657 additions and 2 deletions

10
.gitignore vendored
View File

@ -1,4 +1,3 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
@ -127,3 +126,12 @@ dmypy.json
# Pyre type checker
.pyre/
/venv/
/.github/
/CDC_Backend/CDC_Backend/__pycache__/
/CDC_Backend/APIs/__pycache__/
/CDC_Backend/APIs/migrations/
.idea
*.pyc

View File

12
CDC_Backend/APIs/admin.py Normal file
View File

@ -0,0 +1,12 @@
from django.contrib import admin
from .models import *
admin.site.register(User)
admin.site.register(Student)
admin.site.register(PR)
admin.site.register(Company)
admin.site.register(Placement)
admin.site.register(Internship)
admin.site.register(PlacementApplication)
admin.site.register(InternshipApplication)
admin.site.register(PrePlacementOffer)

View File

@ -0,0 +1,7 @@
from django.urls import path
from . import companyViews
urlpatterns = [
]

View File

6
CDC_Backend/APIs/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ApisConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'APIs'

View File

@ -0,0 +1,7 @@
from django.urls import path
from . import companyViews
urlpatterns = [
path('addOpening/', companyViews.addOpening, name="Add Opening"),
]

View File

@ -0,0 +1,120 @@
import json
from datetime import datetime
from django.utils.timezone import make_aware
from rest_framework.decorators import api_view
from .utils import *
logger = logging.getLogger('db')
@api_view(['POST'])
@precheck([OPENING_DESIGNATION, OPENING_DESCRIPTION, OPENING_TYPE, OPENING_CITY, OPENING_CITY_TYPE,
OPENING_COMPENSATION, OPENING_COMPENSATION_DETAILS, OPENING_ALLOWED_BATCH, OPENING_ALLOWED_BRANCH,
OPENING_ROUNDS, OPENING_CO_OP, OPENING_START_DATE, OPENING_ADDITIONAL_INFO,
OPENING_DURATION, OPENING_ROUND_DETAILS])
def addOpening(request):
try:
data = request.data
if data[OPENING_TYPE] == "Placement":
opening = Placement()
else:
raise ValueError("Invalid Opening Type")
opening.id = generateRandomString()
# Create Company object here for every Opening
# Some new code above
if data[OPENING_DESIGNATION] != "":
opening.designation = data[OPENING_DESIGNATION]
else:
raise ValueError(OPENING_DESIGNATION + " Not Found")
opening.description = data[OPENING_DESCRIPTION]
if data[OPENING_START_DATE] != "":
opening.description = data[OPENING_START_DATE]
else:
raise ValueError(OPENING_START_DATE + " Not Found")
if data[OPENING_START_DATE] != "":
opening.start_date = datetime.strptime(data[OPENING_START_DATE], '%d-%m-%Y')
else:
raise ValueError(OPENING_START_DATE + " Not Found")
if data[OPENING_CITY] != "":
opening.city = data[OPENING_CITY]
else:
raise ValueError(OPENING_CITY + " Not Found")
if data[OPENING_CITY_TYPE] != "":
opening.city_type = data[OPENING_CITY_TYPE]
else:
raise ValueError(OPENING_CITY_TYPE + " Not Found")
if data[OPENING_COMPENSATION] != "":
opening.compensation = data[OPENING_COMPENSATION]
else:
raise ValueError(OPENING_COMPENSATION + " Not Found")
opening.compensation_details = data[OPENING_COMPENSATION_DETAILS]
if data[OPENING_ALLOWED_BATCH] != "":
if set(json.loads(data[OPENING_ALLOWED_BATCH])).issubset(BATCHES):
opening.allowed_batch = json.loads(data[OPENING_ALLOWED_BATCH])
else:
raise ValueError(OPENING_ALLOWED_BATCH + " is Invalid")
else:
raise ValueError(OPENING_ALLOWED_BATCH + " Not Found")
if data[OPENING_ALLOWED_BRANCH] != "":
if set(json.loads(data[OPENING_ALLOWED_BRANCH])).issubset(BRANCHES):
opening.allowed_branch = json.loads(data[OPENING_ALLOWED_BRANCH])
else:
raise ValueError(OPENING_ALLOWED_BATCH + " is Invalid")
else:
raise ValueError(OPENING_ALLOWED_BRANCH + " Not Found")
opening.rounds = json.loads(data[OPENING_ROUNDS])
opening.additional_info = json.loads(data[OPENING_ADDITIONAL_INFO])
opening.status = STATUS_ACCEPTING_APPLICATIONS
opening.rounds_details = json.loads(data[OPENING_ROUND_DETAILS])
opening.created_at = make_aware(datetime.now())
files = request.FILES.getlist(OPENING_ATTACHMENTS)
attachments = []
for file in files:
attachments.append(saveFile(file, STORAGE_DESTINATION_COMPANY_ATTACHMENTS))
opening.attachments = attachments
opening.save()
data = {
"designation": opening.designation,
"opening_type": data[OPENING_TYPE],
"opening_link": "google.com", # Some Changes here too
"company_name": opening.company.name
}
# Needs some edits here
email = 'This is temporary'
# Delete the above var when done
stat = sendEmail(email, COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT.format(id=opening.id), data,
COMPANY_OPENING_SUBMITTED_TEMPLATE)
if stat is not True:
logger.warning("Add New Opening: Unable to send email - " + stat)
return Response({'action': "Add Opening", 'message': "Opening Added"},
status=status.HTTP_200_OK)
except ValueError as e:
return Response({'action': "Add Opening", 'message': str(e)},
status=status.HTTP_400_BAD_REQUEST)
except:
logger.warning("Add New Opening: " + str(sys.exc_info()))
return Response({'action': "Add Opening", 'message': "Error Occurred {0}".format(
str(sys.exc_info()[1]))},
status=status.HTTP_400_BAD_REQUEST)

View File

@ -0,0 +1,92 @@
BRANCH_CHOICES = [
["CSE", "CSE"],
["EE", "EE"],
["ME", "ME"]
]
BATCH_CHOICES = [
["FIRST", "First"],
["SECOND", "Second"],
["THIRD", "Third"],
["FOURTH", "Fourth"]
]
OFFER_CITY_TYPE = [
['Domestic', 'Domestic'],
['International', 'International']
]
TIERS = [
['psu', 'PSU'],
['1', 'Tier 1'],
['2', 'Tier 2'],
['3', 'Tier 3'],
['4', 'Tier 4'],
['5', 'Tier 5'],
['6', 'Tier 6']
]
TOTAL_BRANCHES = 3 # Total No of Branches
TOTAL_BATCHES = 4 # Total No of Batches
CLIENT_ID = "956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleusercontent.com" # Google Login Client ID
TOKEN = "token_id"
EMAIL = "email"
STUDENT = 'student'
ADMIN = 'Admin'
COMPANY = ''
STORAGE_DESTINATION = "./Storage/Resumes/"
STORAGE_DESTINATION_COMPANY_ATTACHMENTS = './Storage/Company_Attachments/'
RESUME_FILE_NAME = 'resume_file_name'
APPLICATION_ID = "application_id"
APPLICATION_OPENING_TYPE = "opening_type"
APPLICATION_OPENING_ID = "opening_id"
APPLICATION_ADDITIONAL_INFO = "additional_info"
STATUS_ACCEPTING_APPLICATIONS = "Accepting Applications"
PLACEMENT = "Placement"
COMPANY_WEBSITE = 'website'
COMPANY_ADDRESS = 'address'
COMPANY_PHONE_NUMBER = 'phone_number'
COMPANY_CONTACT_PERSON_NAME = 'contact_person_name'
OPENING_DESIGNATION = 'designation'
OPENING_DESCRIPTION = 'description'
OPENING_TYPE = 'opening_type'
OPENING_CITY = 'city'
OPENING_CITY_TYPE = 'city_type'
OPENING_COMPENSATION = 'compensation'
OPENING_COMPENSATION_DETAILS = 'compensation_details'
OPENING_ALLOWED_BATCH = 'allowed_batch'
OPENING_ALLOWED_BRANCH = 'allowed_branch'
OPENING_ATTACHMENTS = 'attachments'
OPENING_ROUNDS = 'rounds'
OPENING_ADDITIONAL_INFO = 'additional_info'
OPENING_ROUND_DETAILS = 'round_details'
OPENING_DURATION = 'duration'
OPENING_CO_OP = 'co_op'
OPENING_START_DATE = 'start_date'
BRANCHES = [
"CSE",
"EE",
"ME"
]
BATCHES = [
"FIRST",
"SECOND",
"THIRD",
"FOURTH"
]
COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT = "Notification Submitted - {id} - CDC IIT Dharwad"
STUDENT_APPLICATION_SUBMITTED_TEMPLATE = 'student_application_submitted.html'
COMPANY_OPENING_SUBMITTED_TEMPLATE = 'company_opening_submitted.html'

103
CDC_Backend/APIs/models.py Normal file
View File

@ -0,0 +1,103 @@
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils import timezone
from .constants import *
class User(models.Model):
email = models.CharField(primary_key=True, blank=False, max_length=50)
id = models.CharField(blank=False, max_length=25)
user_type = ArrayField(models.CharField(blank=False, max_length=10), size=4, default=list, blank=False)
class Student(models.Model):
id = models.CharField(blank=False, max_length=15, primary_key=True)
roll_no = models.IntegerField(blank=False)
name = models.CharField(blank=False, max_length=50)
batch = models.CharField(max_length=10, choices=BATCH_CHOICES, blank=False)
branch = models.CharField(choices=BRANCH_CHOICES, blank=False, max_length=10)
phone_number = models.PositiveBigIntegerField(blank=True, default=None, null=True)
resumes = ArrayField(models.CharField(null=True, default=None, max_length=100), size=10, default=list, blank=True)
cpi = models.DecimalField(decimal_places=2, max_digits=4)
class Admin(models.Model):
id = models.CharField(blank=False, max_length=15, primary_key=True)
name = models.CharField(blank=False, max_length=50)
class Placement(models.Model):
id = models.CharField(blank=False, primary_key=True, max_length=15)
name = models.CharField(blank=False, max_length=50)
address = models.CharField(blank=False, max_length=150)
companyType = models.CharField(blank=False, max_length=50)
website = models.CharField(blank=True, max_length=50)
contact_person_name = models.CharField(blank=False, max_length=50)
phone_number = models.PositiveBigIntegerField(blank=False)
designation = models.CharField(blank=False, max_length=25, default=None, null=True)
description = models.CharField(blank=False, max_length=200)
start_date = models.DateField(blank=False, verbose_name="Start Date")
city = models.CharField(blank=False, max_length=100, default="")
city_type = models.CharField(blank=False, max_length=15, choices=OFFER_CITY_TYPE)
compensation = models.IntegerField(blank=False) # Job - Per Year
compensation_details = models.CharField(blank=True, max_length=200)
tier = models.CharField(blank=False, choices=TIERS, max_length=10, default=None, null=True)
allowed_batch = ArrayField(
models.CharField(max_length=10, choices=BATCH_CHOICES),
size=TOTAL_BATCHES,
default=list
)
allowed_branch = ArrayField(
models.CharField(choices=BRANCH_CHOICES, blank=False, max_length=10),
size=TOTAL_BRANCHES,
default=list
)
attachments = ArrayField(
models.CharField(max_length=100, blank=True),
size=10,
blank=True
)
rounds = ArrayField(
models.CharField(max_length=25, blank=True),
size=10,
)
additional_info = ArrayField(
models.CharField(max_length=25, blank=True),
size=10,
blank=True
)
status = models.CharField(max_length=50, blank=False)
rounds_details = models.JSONField(blank=True, default=dict)
created_at = models.DateTimeField(blank=False, default=None, null=True)
class PlacementApplication(models.Model):
id = models.CharField(blank=False, primary_key=True, max_length=15)
placement = models.ForeignKey(Placement, blank=False, on_delete=models.RESTRICT, default=None, null=True)
student = models.ForeignKey(Student, blank=False, on_delete=models.CASCADE)
resume = models.CharField(max_length=100, blank=False, null=True, default=None)
status = models.CharField(max_length=50, null=True, blank=True, default=None)
additional_info = models.JSONField(blank=True, default=None, null=True)
selected = models.BooleanField(null=True, default=None, blank=True)
applied_at = models.DateTimeField(blank=False, default=None, null=True)
def save(self, *args, **kwargs):
''' On save, add timestamps '''
if not self.applied_at:
self.applied_at = timezone.now()
return super(PlacementApplication, self).save(*args, **kwargs)
class Meta:
verbose_name_plural = "Placement Applications"
class PrePlacementOffer(models.Model):
id = models.AutoField(primary_key=True)
student = models.ForeignKey(Student, on_delete=models.CASCADE, blank=False)
company = models.CharField(max_length=50, blank=False)
compensation = models.IntegerField(blank=False) # Job - Per Year
compensation_details = models.CharField(blank=True, max_length=200)
tier = models.CharField(blank=False, choices=TIERS, max_length=10)
designation = models.CharField(blank=False, max_length=25, default=None, null=True)
accepted = models.BooleanField(default=None, null=True)

View File

@ -0,0 +1,56 @@
from rest_framework import serializers
from .models import *
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = '__all__'
# exclude = ['id']
class PlacementSerializer(serializers.ModelSerializer):
company_details = serializers.SerializerMethodField()
def get_company_details(self, obj):
data = {
"id": obj.company.id,
"name": obj.company.name,
"address": obj.company.address,
"companyType": obj.company.companyType,
"website": obj.company.website,
}
return data
class Meta:
model = Placement
exclude=[COMPANY]
depth = 1
class PlacementApplicationSerializer(serializers.ModelSerializer):
application_status = serializers.SerializerMethodField()
company_details = serializers.SerializerMethodField()
def get_application_status(self, obj):
if obj.status is None:
return obj.placement.status
else:
return obj.status
def get_company_details(self, obj):
data = {
"id": obj.placement.company.id,
"name": obj.placement.company.name,
"address": obj.placement.company.address,
"companyType": obj.placement.company.companyType,
"website": obj.placement.company.website,
}
return data
class Meta:
model = PlacementApplication
exclude = ['status', 'student']

View File

@ -0,0 +1,12 @@
from django.urls import path, include
from . import studentViews
urlpatterns = [
path('login/', studentViews.login, name="Login"),
path('profile/', studentViews.studentProfile, name="Student Profile"),
path('getDashboard/', studentViews.getDashboard, name="Dashboard"),
path("addResume/", studentViews.addResume, name="Upload Resume"),
path("deleteResume/", studentViews.deleteResume, name="Upload Resume"),
path("submitApplication/", studentViews.submitApplication, name="Submit Application"),
]

View File

@ -0,0 +1,190 @@
import logging
from os import path, remove
from rest_framework.decorators import api_view
from .serializers import *
from .utils import *
logger = logging.getLogger('db')
@api_view(['POST'])
@isAuthorized(allowed_users='*')
def login(request, id, email, user_type):
try:
return Response({'action': "Login", 'message': "Verified", "user_type": user_type},
status=status.HTTP_200_OK)
except:
return Response({'action': "Login", 'message': "Error Occurred {0}".format(
str(sys.exc_info()[1]))},
status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET'])
@isAuthorized(allowed_users=[STUDENT])
def studentProfile(request, id, email, user_type):
try:
studentDetails = get_object_or_404(Student, id=id)
data = StudentSerializer(studentDetails).data
return Response({'action': "Student Profile", 'message': "Details Found", "details": data},
status=status.HTTP_200_OK)
except:
return Response({'action': "Student Profile", 'message': "Error Occurred {0}".format(str(sys.exc_info()[1]))},
status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@isAuthorized(allowed_users=[STUDENT])
def addResume(request, id, email, user_type):
destination_path = ""
try:
student = get_object_or_404(Student, id=id)
prefix = generateRandomString()
files = request.FILES
file_name = prefix + "_" + files['file'].name
print(file_name)
student.resumes.append(file_name)
file = files['file']
destination_path = STORAGE_DESTINATION + str(file_name)
if path.exists(destination_path):
remove(destination_path)
with open(destination_path, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
student.save()
return Response({'action': "Upload Resume", 'message': "Resume Added"},
status=status.HTTP_200_OK)
except Http404:
return Response({'action': "Upload Resume", 'message': 'Student Not Found'},
status=status.HTTP_404_NOT_FOUND)
except:
if path.exists(destination_path):
logger.error("Upload Resume: Error in Saving Resume")
remove(destination_path)
else:
logger.warning("Upload Resume: " + str(sys.exc_info()))
return Response({'action': "Upload Resume", 'message': "Error Occurred {0}".format(
str(sys.exc_info()[1]))},
status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET'])
@isAuthorized(allowed_users=[STUDENT])
def getDashboard(request, id, email, user_type):
try:
studentDetails = get_object_or_404(Student, id=id)
placements = Placement.objects.filter(allowed_batch__contains=[studentDetails.batch],
allowed_branch__contains=[studentDetails.branch],
status=STATUS_ACCEPTING_APPLICATIONS)
placementsdata = PlacementSerializer(placements, many=True).data
placementApplications = PlacementApplication.objects.filter(student_id=id)
placementApplications = PlacementApplicationSerializer(placementApplications, many=True).data
return Response(
{'action': "Placement and Internships", 'message': "Data Found", "placements": placementsdata,
'placementApplication': placementApplications},
status=status.HTTP_200_OK)
except Http404:
return Response({'action': "Placements and Internships", 'message': 'Student Not Found'},
status=status.HTTP_404_NOT_FOUND)
except:
logger.warning("Placements and Internships: " + str(sys.exc_info()))
return Response({'action': "Placements and Internships", 'message': "Error Occurred {0}".format(
str(sys.exc_info()[1]))},
status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@isAuthorized(allowed_users=[STUDENT])
@precheck(required_data=[RESUME_FILE_NAME])
def deleteResume(request, id, email, user_type):
try:
student = get_object_or_404(Student, id=id)
file_name = request.data[RESUME_FILE_NAME]
destination_path = STORAGE_DESTINATION + str(file_name)
if path.exists(destination_path):
remove(destination_path)
student.resumes.remove(file_name)
student.save()
return Response({'action': "Delete Resume", 'message': "Resume Deleted"},
status=status.HTTP_200_OK)
else:
raise FileNotFoundError("File Not Found")
except Http404:
return Response({'action': "Delete Resume", 'message': 'Student Not Found'},
status=status.HTTP_404_NOT_FOUND)
except FileNotFoundError as e:
return Response({'action': "Delete Resume", 'message': str(e)},
status=status.HTTP_404_NOT_FOUND)
except:
logger.warning("Delete Resume: " + str(sys.exc_info()))
return Response({'action': "Delete Resume", 'message': "Error Occurred {0}".format(
str(sys.exc_info()))},
status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@isAuthorized(allowed_users=[STUDENT])
@precheck(required_data=[APPLICATION_OPENING_TYPE, APPLICATION_OPENING_ID, RESUME_FILE_NAME,
APPLICATION_ADDITIONAL_INFO])
def submitApplication(request, id, email, user_type):
try:
data = request.data
student = get_object_or_404(Student, id=id)
if data[APPLICATION_OPENING_TYPE] == PLACEMENT:
if not len(PlacementApplication.objects.filter(
student_id=id, placement_id=data[APPLICATION_OPENING_ID])):
application = PlacementApplication()
opening = get_object_or_404(Placement, id=data[APPLICATION_OPENING_ID],
status=STATUS_ACCEPTING_APPLICATIONS)
cond_stat, cond_msg = PlacementApplicationConditions(student, opening)
print(cond_stat, cond_msg)
if not cond_stat:
raise PermissionError(cond_msg)
application.placement = opening
else:
raise PermissionError("Application is already Submitted")
else:
raise ValueError(APPLICATION_OPENING_TYPE + " is Invalid")
if data[RESUME_FILE_NAME] in student.resumes:
application.resume = data[RESUME_FILE_NAME]
else:
raise FileNotFoundError(RESUME_FILE_NAME + " Not Found")
application.student = student
application.id = generateRandomString()
for i in opening.additional_info:
if i not in data[APPLICATION_ADDITIONAL_INFO]:
print(i)
raise AttributeError(i + " not found in Additional Info")
application.additional_info = data[APPLICATION_ADDITIONAL_INFO]
if not sendApplicationEmail(email, student.name, opening.company.name, data[APPLICATION_OPENING_TYPE],
data[APPLICATION_ADDITIONAL_INFO]):
logger.error("Submit Application: Unable to Send Email")
# raise RuntimeError("Unable to Send Email")
application.save()
return Response({'action': "Submit Application", 'message': "Application Submitted"},
status=status.HTTP_200_OK)
except PermissionError as e:
return Response({'action': "Submit Application", 'message': str(e)},
status=status.HTTP_403_FORBIDDEN)
except FileNotFoundError as e:
return Response({'action': "Submit Application", 'message': str(e)},
status=status.HTTP_404_NOT_FOUND)
except:
logger.warning("Submit Application: " + str(sys.exc_info()))
return Response({'action': "Submit Application", 'message': "Error Occurred {0}".format(
str(sys.exc_info()[1]))},
status=status.HTTP_400_BAD_REQUEST)

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
CDC_Backend/APIs/urls.py Normal file
View File

@ -0,0 +1,10 @@
from django.urls import path, include
from . import studentViews, studentUrls, companyUrls, adminUrls
urlpatterns = [
path('login/', studentViews.login, name="Login"),
path('student/', include(studentUrls)),
path('company/', include(companyUrls)),
path('admin/', include(adminUrls)),
]

197
CDC_Backend/APIs/utils.py Normal file
View File

@ -0,0 +1,197 @@
import logging
import os
import random
import string
import sys
from os import path, remove
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from google.auth.transport import requests
from google.oauth2 import id_token
from rest_framework import status
from rest_framework.response import Response
from .models import *
logger = logging.getLogger('db')
def precheck(required_data=None):
if required_data is None:
required_data = []
def decorator(view_func):
def wrapper_func(request, *args, **kwargs):
try:
request_data = None
if request.method == 'GET':
request_data = request.GET
elif request.method == 'POST':
request_data = request.data
if not len(request_data):
request_data = request.POST
if len(request_data):
for i in required_data:
if i not in request_data:
return Response({'action': "Pre check", 'message': str(i) + " Not Found"},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response({'action': "Pre check", 'message': "Message Data not Found"},
status=status.HTTP_400_BAD_REQUEST)
return view_func(request, *args, **kwargs)
except:
return Response({'action': "Pre check", 'message': "Error Occurred " + str(sys.exc_info())},
status=status.HTTP_400_BAD_REQUEST)
return wrapper_func
return decorator
def isAuthorized(allowed_users=None):
if allowed_users is None:
allowed_users = []
def decorator(view_func):
def wrapper_func(request, *args, **kwargs):
try:
headers = request.META
if 'HTTP_AUTHORIZATION' in headers:
token_id = headers['HTTP_AUTHORIZATION'][7:]
idinfo = id_token.verify_oauth2_token(token_id, requests.Request(), CLIENT_ID)
email = idinfo[EMAIL]
print(email)
user = get_object_or_404(User, email=email)
if user:
if len(set(user.user_type).intersection(set(allowed_users))) or allowed_users == '*':
return view_func(request, user.id, user.email, user.user_type, *args, **kwargs)
else:
raise PermissionError("Access Denied. You are not allowed to use this service")
else:
raise PermissionError("Authorization Header Not Found")
except PermissionError as e:
print(e)
return Response({'action': "Is Authorized?", 'message': str(e)},
status=status.HTTP_401_UNAUTHORIZED)
except Http404:
print('http404')
return Response({'action': "Is Authorized?", 'message': "User Not Found. Contact CDC for more details"},
status=status.HTTP_404_NOT_FOUND)
except ValueError as e:
logger.warning("Problem with Google Oauth2.0 " + str(e))
return Response({'action': "Is Authorized?", 'message': str(e)},
status=status.HTTP_401_UNAUTHORIZED)
except:
return Response({'action': "Is Authorized?", 'message': "Error Occurred {0}".format(
str(sys.exc_info()[1]))},
status=status.HTTP_400_BAD_REQUEST)
return wrapper_func
return decorator
def generateRandomString():
try:
N = 15
res = ''.join(random.choices(string.ascii_uppercase + string.digits, k=N))
return res
except:
return False
def sendApplicationEmail(email, name, company_name, applicaton_type, additional_info):
try:
subject = 'CDC - Application Submitted - ' + str(company_name)
data = {
"name": name,
"company_name": company_name,
"applicaton_type": applicaton_type,
"additional_info": additional_info
}
html_content = render_to_string('student_application_submited.html', data) # render with dynamic value
text_content = strip_tags(html_content)
email_from = settings.EMAIL_HOST_USER
recipient_list = [str(email), ]
msg = EmailMultiAlternatives(subject, text_content, email_from, recipient_list)
msg.attach_alternative(html_content, "text/html")
msg.send()
return True
except:
return False
def saveFile(file, location):
prefix = generateRandomString()
file_name = prefix + "_" + file.name
if not path.isdir(location):
os.mkdir(location)
destination_path = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + str(file_name)
if path.exists(destination_path):
remove(destination_path)
with open(destination_path, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
return file_name
def sendEmail(email_to, subject, data, template):
try:
html_content = render_to_string(template, data) # render with dynamic value
text_content = strip_tags(html_content)
email_from = settings.EMAIL_HOST_USER
recipient_list = [str(email_to), ]
msg = EmailMultiAlternatives(subject, text_content, email_from, recipient_list)
msg.attach_alternative(html_content, "text/html")
msg.send()
return True
except:
print(str(sys.exc_info()[1]))
return str(sys.exc_info()[1])
def PlacementApplicationConditions(student, placement):
try:
selected_companies = PlacementApplication.objects.filter(student=student, selected=True)
selected_companies_PSU = [i for i in selected_companies if i.placement.tier == 'psu']
PPO = PrePlacementOffer.objects.filter(internship_application__student=student, accepted=True)
if len(selected_companies) + len(PPO) >= 2:
raise PermissionError("Max Applications Reached for the Season")
if len(selected_companies_PSU) > 0:
raise PermissionError('Selected for PSU Can\'t apply anymore')
if placement.tier == 'psu':
return True, "Conditions Satisfied"
for i in selected_companies:
print(int(i.placement.tier) < int(placement.tier), int(i.placement.tier), int(placement.tier))
if int(i.placement.tier) < int(placement.tier):
return False, "Can't apply for this tier"
return True, "Conditions Satisfied"
except PermissionError as e:
return False, e
except:
print(sys.exc_info())
logger.warning("Utils - PlacementApplicationConditions: " + str(sys.exc_info()))
return False, "_"

View File

View File

@ -0,0 +1,191 @@
"""
Django settings for CDC_Backend project.
Generated by 'django-admin startproject' using Django 2.2.5.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""
import os
# import django_heroku
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'e_i2g3z!y4+p3dwm%k9k=zmsot@aya-0$mmetgxz4mp#8_oy#*'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['cdc-iitdh.herokuapp.com/', 'localhost', '192.168.29.199']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'APIs',
'rest_framework',
'corsheaders',
'django_db_logger',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'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 = 'CDC_Backend.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['templates'],
'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 = 'CDC_Backend.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'cdc',
'USER': 'postgres',
'PASSWORD': 'root',
'HOST': 'localhost',
'PORT': '5432',
},
# 'default': {
# 'ENGINE': 'django.db.backends.postgresql_psycopg2',
# 'NAME': 'd84i5cbjig5rrf',
# 'USER': 'hbkullcdjbxuwh',
# 'PASSWORD': '45d990da00e2cc96d7d4e2e5e308d4b07a387883f70c40e090a6252175cb634e',
# 'HOST': 'ec2-54-163-97-228.compute-1.amazonaws.com',
# 'PORT': '5432',
# }
}
# Password validation
# https://docs.djangoproject.com/en/2.2/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/2.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Kolkata'
USE_I18N = True
USE_L10N = True
USE_TZ = True
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_DIR = (
os.path.join(BASE_DIR, 'static'),
)
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = [
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://localhost:8000",
"http://127.0.0.1:8000"
]
# EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = './emails'
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_USER = 'saisurya3127@gmail.com'#'email here'
EMAIL_HOST_PASSWORD = 'ehwkqmryyqjiifcz'#'password here'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(asctime)s %(message)s'
},
},
'handlers': {
'db_log': {
'level': 'DEBUG',
'class': 'django_db_logger.db_log_handler.DatabaseLogHandler'
},
},
'loggers': {
'db': {
'handlers': ['db_log'],
'level': 'DEBUG'
}
}
}
# django_heroku.settings(locals())

View File

@ -0,0 +1,7 @@
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('APIs.urls'))
]

View File

@ -0,0 +1,16 @@
"""
WSGI config for CDC_Backend 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/2.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'CDC_Backend.settings')
application = get_wsgi_application()

403
CDC_Backend/README.md Normal file
View File

@ -0,0 +1,403 @@
# API References
1. [**Common APIs**](#common-apis)
1. [**api/login/**](#apilogin)
2[**Student APIs**](#student-portal-apis)
2. [**api/student/profile/**](#apistudentprofile)
3. [**api/student/getDashboard/**](#apistudentgetdashboard)
4. [**api/student/addResume/**](#apistudentaddresume)
5. [**api/student/deleteResume/**](#apistudentdeleteresume)
6. [**api/student/submitApplication/**](#apistudentsubmitapplication)
3[**Common Errors**](#common-errors)
---
# Common APIs
## `api/login/`
This Api is used to Verify the user and find out the role he/she has
### How to Use?
Send a `POST` request to `api/login/`<br>
Request_Body:
```json
{}
```
> Headers <br>
> Authorization: "Bearer {tokenID}"
### Response
Response is a Json with these fields
```json
{
"action": "Login",
"message": "Verified",
"user_type": [
"student"
]
}
```
- action: Tells us about the message creator<Br>
- message: Tells us what happened with our Request.
- user_type: Tells us about the role the user possess. Can have these values
- student
- Admin
### Status Codes
The possible responses for this api request are as follows
| Status Codes | Possible Messages |
| ------------ | ----------------- |
| 200 OK | `Verified` |
You may see some different errors which can be seen [here](#common-errors)
---
# Student Portal APIs
## `api/student/profile`
This Api is used to get the profile of the student.
### How to Use?
Send a `GET` request to `api/student/profile`<br>
Request_Body:
```json
{}
```
> Headers <br>
> Authorization: "Bearer {tokenID}"
### Response
Response is a Json with these fields
```json
{
"action": "Student Profile",
"message": "Details Found",
"details": {
"id": "fdgdb",
"roll_no": 190010036,
"name": "Gowtham Sai",
"batch": "THIRD",
"branch": "CSE",
"phone_number": 9390291911,
"resumes": [
"XB85F4RIGBF5VJN_Cv-Gowtham.pdf"
],
"cpi": "9.02"
}
}
```
- action: Tells us about the message creator<Br>
- message: Tells us what happened with our Request.
- details: Has the student data.
### Status Codes
The possible responses for this api request are as follows
| Status Codes | Possible Messages |
| --------------- | ------------------------ |
| 200 OK | `Details Found` |
| 400 BAD_REQUEST | `Error Occurred {error}` |
You may see some different errors which can be seen [here](#common-errors)
---
## `api/student/getDashboard`
This Api is used to get all the placements applicable to the student.
### How to Use?
Send a `GET` request to `api/student/Dashboard`<br>
Request_Body:
```json
{}
```
> Headers <br>
> Authorization: "Bearer {tokenID}"
### Response
Response is a Json with these fields
```json
{
"action": "Placement and Internships",
"message": "Data Found",
"placements": [
{
"id": "fdgdb121",
"designation": "Software Developer",
"description": "nice job",
"start_date": "2021-06-17",
"city": "Mumbai",
"city_type": "Domestic",
"compensation": 1200000,
"compensation_details": "",
"allowed_batch": [
"THIRD",
"FOURTH"
],
"allowed_branch": [
"CSE",
"EE",
"ME"
],
"attachments": [],
"rounds": [
"Resume Shortlisting",
"Technical Test",
"Interview"
],
"additional_info": [
"school",
"place of study",
"language"
],
"status": "Resume Shortlisting",
"rounds_details": {
"Interview": "One -to-One interview",
"Technical Test": "Online Technical test which will be monitored remotely",
"Resume Shortlisting": "Resume will be seen"
},
"company_details": {
"id": "fdgdb",
"name": "Apple",
"address": "California",
"companyType": "Technology",
"website": ""
}
}
],
"placementApplication": [
{
"id": "dsdads",
"application_status": "Resume Shortlisting",
"resume": "XB85F4RIGBF5VJN_Cv-Gowtham.pdf",
"additional_info": {
"school": "Delhi Public School",
"language": "Telugu",
"place of study": "Visakhapatnam"
},
"selected": null,
"placement": "fdgdb121"
}
]
}
```
- action: Tells us about the message creator<Br>
- message: Tells us what happened with our Request.
- placements: Has the placements data.
- internships: Has the internships data.
- application_status: Can have many names
- Accepting Applications
- One of the Round Names
- Completed
- selected: Can take three Values
- null: Student is still in the Selection process
- true: Student is Selected
- false: Student is not selected
### Status Codes
The possible responses for this api request are as follows
| Status Codes | Possible Messages |
| --------------- | ------------------------ |
| 200 OK | `Resume Added` |
| 400 BAD_REQUEST | `Error Occurred {error}` |
You can see some common errors [here](#common-errors)
---
## `api/student/addResume/`
This Api is used to add resumes by a student.
### How to Use?
Send a `POST` request to `api/student/addResume/`<br>
> Only users with `student` role can access this Api.
Request_Body:
```json
{
"file": "__FILE_OBJECT__"
}
```
> Excepted to send Form Data
> Headers <br>
> Authorization: "Bearer {tokenID}"
### Response
Response is a Json with these fields
```json
{
"action": "Upload Resume",
"message": "Resume Added"
}
```
- action: Tells us about the message creator<Br>
- message: Tells us what happened with our Request.
### Status Codes
The possible responses for this api request are as follows
| Status Codes | Possible Messages |
| ------------ | ----------------- |
| 200 OK | `Resume Added` |
You can see some common errors [here](#common-errors)
---
## `api/student/deleteResume/`
This Api is used to delete resumes added by a student.
### How to Use?
Send a `POST` request to `api/student/deleteResume/`<br>
> Only users with `student` role can access this Api.
Request_Body:
```json
{
"resume_file_name": "8ZJ44RIS9914SO4_Resume for Google STEP.pdf"
}
```
### Response
Response is a Json with these fields
```json
{
"action": "Delete Resume",
"message": "Resume Deleted"
}
```
> Headers <br>
> Authorization: "Bearer {tokenID}"
- action: Tells us about the message creator<Br>
- message: Tells us what happened with our Request.
### Status Codes
The possible responses for this api request are as follows
| Status Codes | Possible Messages |
| ------------- | ----------------- |
| 200 OK | `Resume Deleted` |
| 404 NOT FOUND | `File Not Found` |
You can see some common errors [here](#common-errors)
---
## `api/student/submitApplication/`
This Api is used to submit application to Internships/Placements.
### How to Use?
Send a `POST` request to `api/student/submitApplication/`<br>
> Only users with `student` role can access this Api.
Request_Body:
```json
{
"opening_type": "Placement",
"opening_id": "fgervsdgdsf",
"resume_file_name": "1FYE0PQZZ508HR6_Resume for Google STEP.pdf",
"additional_info": {
"school": "Narayana English Medium",
"place of study": "Vizag",
"language": "Telugu"
}
}
```
> Headers <br>
> Authorization: "Bearer {tokenID}"
- opening_type: Can be Placement/Internship
- opening_id: Opening Id unique to each opening.
- additional_info: This is the info which the Internship/Placement demands besides the normal user data which has to
asked and sent. These fields can be found in the Internship Details.
### Response
Response is a Json with these fields
```json
{
"action": "Submit Application",
"message": "Application Submitted"
}
```
- action: Tells us about the message creator<Br>
- message: Tells us what happened with our Request.
### Status Codes
The possible responses for this api request are as follows
| Status Codes | Possible Messages |
| ------------- | ---------------------------------- |
| 200 OK | `Application Submitted` |
| 403 FORBIDDEN | `Application is already Submitted` |
| 404 NOT FOUND | `RESUME_FILE_NAME Not Found` |
You can see some common errors [here](#common-errors)
---
## `Common Errors`
Some common errors that you may see while accessing the Apis
| Status Codes | Possible Messages | Possible Reasons |
| ---------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| 401 UNAUTHORIZED | `Authorization Header Not Found` | Check for the authorization header in you request and the prefix( Should use `Bearer`) used. |
| 401 UNAUTHORIZED | `Access Denied. You are not allowed to use this service` | Your may not have required access to those access those Apis. |
| 401 UNAUTHORIZED | `Token has wrong audience` | You may be using wrong credentials for Google OAuth2.0. |
| 404 NOT FOUND | `User Not Found. Contact CDC for more details` | You may not be a user at CDC, IIT Dharwad. Please contact us to get your user account |
| 400 BAD_REQUEST | `Error Occurred {error}` | Any random Error which can be seen in the {error} string. |

View File

@ -0,0 +1,12 @@
{
"web": {
"client_id": "956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleusercontent.com",
"project_id": "cdc-automation",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "ZzvcweJylL1IDLUnYOi1ws2W",
"redirect_uris": ["https://www.getpostman.com/oauth2/callback"],
"javascript_origins": ["http://localhost:3000"]
}
}

21
CDC_Backend/manage.py Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'CDC_Backend.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()

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -0,0 +1,113 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="x-apple-disable-message-reformatting">
<title></title>
<!--[if mso]>
<noscript>
<xml>
<o:OfficeDocumentSettings>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
</noscript>
<![endif]-->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link rel="shortcut icon" href="favicon.ico"/>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap" rel="stylesheet">
<style>
table, td, div, h1, p {
font-family: 'Roboto', sans-serif;
}
</style>
</head>
<body style="margin:0;padding:0;">
<table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;background:#ffffff;">
<tr>
<td align="center" style="padding:0;">
<table role="presentation"
style="width:602px;border-collapse:collapse;border:1px solid #334878;border-spacing:0;text-align:left;">
<tr>
<td align="center" style="padding:40px 0 30px 0;background:#334878;">
<img src="./image.png" alt="" width="200" style="height:auto;display:block;"/>
</td>
</tr>
<tr>
<td style="padding:36px 30px 42px 30px;">
<table role="presentation"
style="width:100%;border-collapse:collapse;border:0;border-spacing:0;">
<tr>
<td style="padding:0 0 36px 0;color:#153643;">
<h1 style="font-size:24px;margin:0 0 20px 0;font-family: 'Roboto', sans-serif;
">Hello there, {{ name }}</h1>
<p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family: 'Roboto', sans-serif;">
We have received your application for a <b>{{ applicaton_type }}</b> offer at <b>
{{ company_name }}</b>
. We received these additional details<br>
<p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family:
'Roboto', sans-serif;text-align: center">
<table style="border:solid 1px; margin: auto; text-align: center;width: 80%;
border-radius:15px; background-color: #e0e3ee">
{% for i,j in additional_info.items %}
<tr>
<td style="padding:8px 10px;color:#153643; ">{{ i }}:</td>
<td style="padding:8px 10px;color:#153643;">{{ j }}</td>
</tr>
{% endfor %}
</table>
</p>
</p>
<p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family: 'Roboto', sans-serif;">
We will keep you informed with the updates. If you have any queries, please
feel to
write to
<nobr><u>cdc.support@iitdh.ac.in</u></nobr>
</p>
</td>
</tr>
<tr>
<td style="padding:0;">
<table role="presentation"
style="width:100%;border-collapse:collapse;border:0;border-spacing:0;">
<tr>
<td style="width:260px;padding:0;vertical-align:top;color:#334878;">
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="padding:30px;background:#334878;">
<table role="presentation"
style="width:100%;border-collapse:collapse;border:0;border-spacing:0;font-size:9px;font-family: 'Roboto', sans-serif;">
<tr>
<td style="padding:0;width:50%;" align="left">
<p style="margin:0;font-size:14px;line-height:16px;font-family: 'Roboto', sans-serif;color:#ffffff;">
&reg; CDC,IIT Dharwad,2021<br/>
</p>
</td>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -1 +1,39 @@
# cdc-placement-website-backend
# CDC - Backend
---
### Setup
1. Download the Repository to your local machine <br>
2. Create a Virtual Environment in the [CDC_Backend](./) folder with this command below <br>
`python -m venv venv`
3. Activate the environment with this command <br>
`.\venv\Scripts\activate`
4. Install the dependencies <br>
`pip install -r requirements.txt `
5. Ensure that you have the PostgreSQL installed on your machine and is running on PORT **5432** <br>
6. Make sure to give the correct database credentials in [settings.py](./CDC_Backend/CDC_Backend/settings.py)
### Running the Application
1. Activate the environment with this command. <br>
`.\venv\Scripts\activate`
2. Start the application by running this command (_Run the command where [manage.py](./CDC_Backend/manage.py) is
located_) <br>
` python manage.py runserver`
### Accessing the Admin Panel
1. You can access the admin panel by running the server and opening <http://localhost:8000/admin>
2. Run `python manage.py createsuperuser` to create a user to access the admin panel.
3. Set up the Username and Password
4. You can log in and change the database values anytime.
### Deploying
1. Add the hosted domain name in `ALLOWED_HOSTS` in [settings.py](./CDC_Backend/CDC_Backend/settings.py)
2. Update the `CORS_ORIGIN_WHITELIST` list and `CORS_ORIGIN_ALLOW_ALL` variable
### API Reference
Check [here](./CDC_Backend/README.md) for Api Reference

31
requirements.txt Normal file
View File

@ -0,0 +1,31 @@
asgiref==3.4.1
astroid==2.7.1
cachetools==4.2.2
certifi==2021.5.30
chardet==4.0.0
colorama==0.4.4
dj-database-url==0.5.0
Django==3.2.6
django-cors-headers==3.8.0
django-db-logger==0.1.10
djangorestframework==3.12.4
google-auth==2.0.0
idna==3.2
isort==5.9.3
jsonfield==3.1.0
lazy-object-proxy==1.6.0
Markdown==3.3.4
mccabe==0.6.1
psycopg2==2.9.1
pyasn1==0.4.8
pyasn1-modules==0.2.8
pylint==2.9.6
pytz==2021.1
requests==2.26.0
rsa==4.7.2
six==1.16.0
sqlparse==0.4.1
toml==0.10.2
urllib3==1.26.6
whitenoise==5.3.0
wrapt==1.12.1