diff --git a/.gitignore b/.gitignore index b6e4761..d2e39e7 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/CDC_Backend/APIs/__init__.py b/CDC_Backend/APIs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/CDC_Backend/APIs/admin.py b/CDC_Backend/APIs/admin.py new file mode 100644 index 0000000..bc5b427 --- /dev/null +++ b/CDC_Backend/APIs/admin.py @@ -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) diff --git a/CDC_Backend/APIs/adminUrls.py b/CDC_Backend/APIs/adminUrls.py new file mode 100644 index 0000000..203c0dc --- /dev/null +++ b/CDC_Backend/APIs/adminUrls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import companyViews + + +urlpatterns = [ + +] diff --git a/CDC_Backend/APIs/adminViews.py b/CDC_Backend/APIs/adminViews.py new file mode 100644 index 0000000..e69de29 diff --git a/CDC_Backend/APIs/apps.py b/CDC_Backend/APIs/apps.py new file mode 100644 index 0000000..e704049 --- /dev/null +++ b/CDC_Backend/APIs/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ApisConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'APIs' diff --git a/CDC_Backend/APIs/companyUrls.py b/CDC_Backend/APIs/companyUrls.py new file mode 100644 index 0000000..f175144 --- /dev/null +++ b/CDC_Backend/APIs/companyUrls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import companyViews + + +urlpatterns = [ + path('addOpening/', companyViews.addOpening, name="Add Opening"), +] diff --git a/CDC_Backend/APIs/companyViews.py b/CDC_Backend/APIs/companyViews.py new file mode 100644 index 0000000..c31f135 --- /dev/null +++ b/CDC_Backend/APIs/companyViews.py @@ -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) diff --git a/CDC_Backend/APIs/constants.py b/CDC_Backend/APIs/constants.py new file mode 100644 index 0000000..2ea330b --- /dev/null +++ b/CDC_Backend/APIs/constants.py @@ -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' diff --git a/CDC_Backend/APIs/models.py b/CDC_Backend/APIs/models.py new file mode 100644 index 0000000..e844dc2 --- /dev/null +++ b/CDC_Backend/APIs/models.py @@ -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) diff --git a/CDC_Backend/APIs/serializers.py b/CDC_Backend/APIs/serializers.py new file mode 100644 index 0000000..62f9ef7 --- /dev/null +++ b/CDC_Backend/APIs/serializers.py @@ -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'] diff --git a/CDC_Backend/APIs/studentUrls.py b/CDC_Backend/APIs/studentUrls.py new file mode 100644 index 0000000..1d6d76a --- /dev/null +++ b/CDC_Backend/APIs/studentUrls.py @@ -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"), +] diff --git a/CDC_Backend/APIs/studentViews.py b/CDC_Backend/APIs/studentViews.py new file mode 100644 index 0000000..6abee88 --- /dev/null +++ b/CDC_Backend/APIs/studentViews.py @@ -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) diff --git a/CDC_Backend/APIs/tests.py b/CDC_Backend/APIs/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/CDC_Backend/APIs/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/CDC_Backend/APIs/urls.py b/CDC_Backend/APIs/urls.py new file mode 100644 index 0000000..bdb06c8 --- /dev/null +++ b/CDC_Backend/APIs/urls.py @@ -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)), + +] diff --git a/CDC_Backend/APIs/utils.py b/CDC_Backend/APIs/utils.py new file mode 100644 index 0000000..422cec1 --- /dev/null +++ b/CDC_Backend/APIs/utils.py @@ -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, "_" diff --git a/CDC_Backend/CDC_Backend/__init__.py b/CDC_Backend/CDC_Backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/CDC_Backend/CDC_Backend/settings.py b/CDC_Backend/CDC_Backend/settings.py new file mode 100644 index 0000000..e2b812b --- /dev/null +++ b/CDC_Backend/CDC_Backend/settings.py @@ -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()) diff --git a/CDC_Backend/CDC_Backend/urls.py b/CDC_Backend/CDC_Backend/urls.py new file mode 100644 index 0000000..6a3f873 --- /dev/null +++ b/CDC_Backend/CDC_Backend/urls.py @@ -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')) +] diff --git a/CDC_Backend/CDC_Backend/wsgi.py b/CDC_Backend/CDC_Backend/wsgi.py new file mode 100644 index 0000000..057007a --- /dev/null +++ b/CDC_Backend/CDC_Backend/wsgi.py @@ -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() diff --git a/CDC_Backend/README.md b/CDC_Backend/README.md new file mode 100644 index 0000000..95398ea --- /dev/null +++ b/CDC_Backend/README.md @@ -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/`
+Request_Body: + +```json +{} +``` + +> Headers
+> 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
+- 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`
+Request_Body: + +```json +{} +``` + +> Headers
+> 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
+- 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`
+ +Request_Body: + +```json +{} +``` + +> Headers
+> 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
+- 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/`
+ +> Only users with `student` role can access this Api. + +Request_Body: + +```json +{ + "file": "__FILE_OBJECT__" +} +``` + +> Excepted to send Form Data + +> Headers
+> 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
+- 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/`
+ +> 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
+> Authorization: "Bearer {tokenID}" + +- action: Tells us about the message creator
+- 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/`
+ +> 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
+> 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
+- 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. | diff --git a/CDC_Backend/client_secret_956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleusercontent.com (2).json b/CDC_Backend/client_secret_956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleusercontent.com (2).json new file mode 100644 index 0000000..42f9eb6 --- /dev/null +++ b/CDC_Backend/client_secret_956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleusercontent.com (2).json @@ -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"] + } +} diff --git a/CDC_Backend/manage.py b/CDC_Backend/manage.py new file mode 100644 index 0000000..dc415b9 --- /dev/null +++ b/CDC_Backend/manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', '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() diff --git a/CDC_Backend/templates/favicon.ico b/CDC_Backend/templates/favicon.ico new file mode 100644 index 0000000..9b2402e Binary files /dev/null and b/CDC_Backend/templates/favicon.ico differ diff --git a/CDC_Backend/templates/image.png b/CDC_Backend/templates/image.png new file mode 100644 index 0000000..ce9d82e Binary files /dev/null and b/CDC_Backend/templates/image.png differ diff --git a/CDC_Backend/templates/student_application_submitted.html b/CDC_Backend/templates/student_application_submitted.html new file mode 100644 index 0000000..805edee --- /dev/null +++ b/CDC_Backend/templates/student_application_submitted.html @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ +
+ + + + + + + +
+

Hello there, {{ name }}

+

+ We have received your application for a {{ applicaton_type }} offer at + {{ company_name }} + . We received these additional details
+

+ + + + {% for i,j in additional_info.items %} + + + + + + {% endfor %} +
{{ i }}:{{ j }}
+ +

+

+

+ We will keep you informed with the updates. If you have any queries, please + feel to + write to + cdc.support@iitdh.ac.in +

+
+ + + + +
+ + +
+
+
+ + + + + + + +
+

+ ® CDC,IIT Dharwad,2021
+

+
+
+
+ + \ No newline at end of file diff --git a/README.md b/README.md index 9bce313..d812996 100644 --- a/README.md +++ b/README.md @@ -1 +1,39 @@ -# cdc-placement-website-backend \ No newline at end of file +# CDC - Backend + +--- + +### Setup + +1. Download the Repository to your local machine
+2. Create a Virtual Environment in the [CDC_Backend](./) folder with this command below
+ `python -m venv venv` +3. Activate the environment with this command
+ `.\venv\Scripts\activate` +4. Install the dependencies
+ `pip install -r requirements.txt ` +5. Ensure that you have the PostgreSQL installed on your machine and is running on PORT **5432**
+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.
+ `.\venv\Scripts\activate` +2. Start the application by running this command (_Run the command where [manage.py](./CDC_Backend/manage.py) is + located_)
+ ` python manage.py runserver` + +### Accessing the Admin Panel + +1. You can access the admin panel by running the server and opening +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 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..eb6c7a4 --- /dev/null +++ b/requirements.txt @@ -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