From aa9aa40e81bc1f491064b46c84cc7b64d599cd6b Mon Sep 17 00:00:00 2001 From: gowtham3105 <66207607+gowtham3105@users.noreply.github.com> Date: Fri, 15 Oct 2021 20:47:23 +0530 Subject: [PATCH] Initial Commit --- .gitignore | 10 +- CDC_Backend/APIs/__init__.py | 0 CDC_Backend/APIs/admin.py | 12 + CDC_Backend/APIs/adminUrls.py | 7 + CDC_Backend/APIs/adminViews.py | 0 CDC_Backend/APIs/apps.py | 6 + CDC_Backend/APIs/companyUrls.py | 7 + CDC_Backend/APIs/companyViews.py | 120 ++++++ CDC_Backend/APIs/constants.py | 92 ++++ CDC_Backend/APIs/models.py | 103 +++++ CDC_Backend/APIs/serializers.py | 56 +++ CDC_Backend/APIs/studentUrls.py | 12 + CDC_Backend/APIs/studentViews.py | 190 +++++++++ CDC_Backend/APIs/tests.py | 3 + CDC_Backend/APIs/urls.py | 10 + CDC_Backend/APIs/utils.py | 197 +++++++++ CDC_Backend/CDC_Backend/__init__.py | 0 CDC_Backend/CDC_Backend/settings.py | 191 +++++++++ CDC_Backend/CDC_Backend/urls.py | 7 + CDC_Backend/CDC_Backend/wsgi.py | 16 + CDC_Backend/README.md | 403 ++++++++++++++++++ ...4t1qcp.apps.googleusercontent.com (2).json | 12 + CDC_Backend/manage.py | 21 + CDC_Backend/templates/favicon.ico | Bin 0 -> 15086 bytes CDC_Backend/templates/image.png | Bin 0 -> 46082 bytes .../student_application_submitted.html | 113 +++++ README.md | 40 +- requirements.txt | 31 ++ 28 files changed, 1657 insertions(+), 2 deletions(-) create mode 100644 CDC_Backend/APIs/__init__.py create mode 100644 CDC_Backend/APIs/admin.py create mode 100644 CDC_Backend/APIs/adminUrls.py create mode 100644 CDC_Backend/APIs/adminViews.py create mode 100644 CDC_Backend/APIs/apps.py create mode 100644 CDC_Backend/APIs/companyUrls.py create mode 100644 CDC_Backend/APIs/companyViews.py create mode 100644 CDC_Backend/APIs/constants.py create mode 100644 CDC_Backend/APIs/models.py create mode 100644 CDC_Backend/APIs/serializers.py create mode 100644 CDC_Backend/APIs/studentUrls.py create mode 100644 CDC_Backend/APIs/studentViews.py create mode 100644 CDC_Backend/APIs/tests.py create mode 100644 CDC_Backend/APIs/urls.py create mode 100644 CDC_Backend/APIs/utils.py create mode 100644 CDC_Backend/CDC_Backend/__init__.py create mode 100644 CDC_Backend/CDC_Backend/settings.py create mode 100644 CDC_Backend/CDC_Backend/urls.py create mode 100644 CDC_Backend/CDC_Backend/wsgi.py create mode 100644 CDC_Backend/README.md create mode 100644 CDC_Backend/client_secret_956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleusercontent.com (2).json create mode 100644 CDC_Backend/manage.py create mode 100644 CDC_Backend/templates/favicon.ico create mode 100644 CDC_Backend/templates/image.png create mode 100644 CDC_Backend/templates/student_application_submitted.html create mode 100644 requirements.txt 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 0000000000000000000000000000000000000000..9b2402e7d4475d1d941bc0f1698432605803e0ae GIT binary patch literal 15086 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 YDOJNvW?v7b#`gCjaY>VN*1Qt`4}LbD+W-In literal 0 HcmV?d00001 diff --git a/CDC_Backend/templates/image.png b/CDC_Backend/templates/image.png new file mode 100644 index 0000000000000000000000000000000000000000..ce9d82eb3b010ca05a096a66bda359a84c771b26 GIT binary patch literal 46082 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 @MKH9XD;4CgUYZa4=+TFM&Hnn-taJv3>61IsP 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 + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ +
+ + + + + + + +
+

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