From c988bf66f510af2e83bdb011ed4eb36ae14974f0 Mon Sep 17 00:00:00 2001 From: gowtham3105 <66207607+gowtham3105@users.noreply.github.com> Date: Fri, 3 Dec 2021 01:04:49 +0530 Subject: [PATCH 1/6] Many Issues Resolved - Placement Modal Resolved - Add Placement API Resolved - Update Deadline API - Update Offer Accepted API - Update Email Verified API - Update Additional Info API - Profile API Changes: Added Offers - Serializers are tested and changed to fit the new modals - Latest Documentation -All APIs Tested and Checked --- CDC_Backend/APIs/adminUrls.py | 8 +- CDC_Backend/APIs/adminViews.py | 155 +++++++++++ CDC_Backend/APIs/companyUrls.py | 2 +- CDC_Backend/APIs/companyViews.py | 250 ++++++++++++------ CDC_Backend/APIs/constants.py | 106 +++++--- CDC_Backend/APIs/models.py | 82 ++++-- CDC_Backend/APIs/serializers.py | 172 +++++++++--- CDC_Backend/APIs/studentViews.py | 84 +++--- CDC_Backend/APIs/utils.py | 85 +++--- CDC_Backend/CDC_Backend/settings.py | 3 +- .../templates/company_opening_submitted.html | 85 ++++++ ...udent_application_status_not_selected.html | 84 ++++++ .../student_application_status_selected.html | 83 ++++++ .../student_application_submitted.html | 2 +- 14 files changed, 952 insertions(+), 249 deletions(-) create mode 100644 CDC_Backend/templates/company_opening_submitted.html create mode 100644 CDC_Backend/templates/student_application_status_not_selected.html create mode 100644 CDC_Backend/templates/student_application_status_selected.html diff --git a/CDC_Backend/APIs/adminUrls.py b/CDC_Backend/APIs/adminUrls.py index 203c0dc..0dbe3b3 100644 --- a/CDC_Backend/APIs/adminUrls.py +++ b/CDC_Backend/APIs/adminUrls.py @@ -1,7 +1,13 @@ from django.urls import path -from . import companyViews +from . import adminViews urlpatterns = [ + path('markStatus/', adminViews.markStatus, name="Mark Status"), + path('getDashboard/', adminViews.getDashboard, name="Get Dashboard"), + path('updateDeadline/', adminViews.updateDeadline, name="Update Deadline"), + path('updateOfferAccepted/', adminViews.updateOfferAccepted, name="Update Offer Accepted"), + path('updateEmailVerified', adminViews.updateEmailVerified, name="Update Email Verified"), + path('updateAdditionalInfo/', adminViews.updateAdditionalInfo, name="Update Additional Info"), ] diff --git a/CDC_Backend/APIs/adminViews.py b/CDC_Backend/APIs/adminViews.py index e69de29..374266a 100644 --- a/CDC_Backend/APIs/adminViews.py +++ b/CDC_Backend/APIs/adminViews.py @@ -0,0 +1,155 @@ +from datetime import datetime + +from .utils import * +from rest_framework.decorators import api_view + +from .serializers import * + + +@api_view(['POST']) +@isAuthorized([ADMIN]) +@precheck([OPENING_ID, STUDENT_LIST]) +def markStatus(request, id, email, user_type): + try: + data = request.data + # Getting all application from db for this opening + applications = PlacementApplication.objects.filter(placement_id=data[OPENING_ID]) + for i in data[STUDENT_LIST]: + application = applications.filter(student_id=i[STUDENT_ID]) # Filtering student's application + if len(application) > 0: + application = application[0] + application.selected = True if i[STUDENT_SELECTED] == "true" else False + + email = str(application.student.roll_no) + "@iitdh.ac.in" # Only allowing for IITDh emails + subject = STUDENT_APPLICATION_STATUS_TEMPLATE_SUBJECT.format(company_name=application.placement.company_name, + id=application.id) + data = { + "company_name": application.placement.company_name, + "designation": application.placement.designation, + "student_name": application.student.name + } + if application.selected: # Sending corresponding email to students + sendEmail(email, subject, data, STUDENT_APPLICATION_STATUS_SELECTED_TEMPLATE) + else: + sendEmail(email, subject, data, STUDENT_APPLICATION_STATUS_NOT_SELECTED_TEMPLATE) + application.save() + else: + raise ValueError("Student - " + i[STUDENT_ID] + " didn't apply for this opening") + return Response({'action': "Mark Status", 'message': "Marked Status"}, + status=status.HTTP_200_OK) + + except ValueError as e: + return Response({'action': "Mark Status", 'message': str(e)}, + status=status.HTTP_400_BAD_REQUEST) + except: + logger.warning("Mark Status: " + str(sys.exc_info())) + return Response({'action': "Mark Status", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) + + +@api_view(['GET']) +@isAuthorized([ADMIN]) +def getDashboard(request, id, email, user_type): + try: + placements = Placement.objects.all().order_by('-created_at') + ongoing = placements.filter(deadline_datetime__gt=datetime.now()) + previous = placements.exclude(deadline_datetime__gt=datetime.now()) + ongoing = PlacementSerializerForAdmin(ongoing, many=True).data + previous = PlacementSerializerForAdmin(previous, many=True).data + + return Response( + {'action': "Get Dashboard - Admin", 'message': "Data Found", "ongoing": ongoing, "previous": previous}, + status=status.HTTP_200_OK) + except Http404: + return Response({'action': "Get Dashboard - Admin", 'message': 'Student Not Found'}, + status=status.HTTP_404_NOT_FOUND) + except: + logger.warning("Get Dashboard - Admin: " + str(sys.exc_info())) + return Response({'action': "Get Dashboard - Admin", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) + + +@api_view(['POST']) +@isAuthorized([ADMIN]) +@precheck([OPENING_ID, DEADLINE_DATETIME]) +def updateDeadline(request, id, email, user_type): + try: + data = request.data + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + # Updating deadline date with correct format in datetime field + opening.deadline_datetime = datetime.strptime(data[DEADLINE_DATETIME], '%Y-%m-%d %H:%M:%S %z') + opening.save() + return Response({'action': "Update Deadline", 'message': "Deadline Updated"}, + status=status.HTTP_200_OK) + except Http404: + return Response({'action': "Update Deadline", 'message': 'Opening Not Found'}, + status=status.HTTP_404_NOT_FOUND) + except: + logger.warning("Update Deadline: " + str(sys.exc_info())) + return Response({'action': "Update Deadline", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) + +@api_view(['POST']) +@isAuthorized([ADMIN]) +@precheck([OPENING_ID, OFFER_ACCEPTED]) +def updateOfferAccepted(request, id, email, user_type): + try: + data = request.data + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + opening.offer_accepted = True if data[OFFER_ACCEPTED] == "true" else False + opening.save() + return Response({'action': "Update Offer Accepted", 'message': "Offer Accepted Updated"}, + status=status.HTTP_200_OK) + except Http404: + return Response({'action': "Update Offer Accepted", 'message': 'Opening Not Found'}, + status=status.HTTP_404_NOT_FOUND) + except: + logger.warning("Update Offer Accepted: " + str(sys.exc_info())) + return Response({'action': "Update Offer Accepted", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) + +@api_view(['POST']) +@isAuthorized([ADMIN]) +@precheck([OPENING_ID, EMAIL_VERIFIED]) +def updateEmailVerified(request, id, email, user_type): + try: + data = request.data + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + opening.email_verified = True if data[EMAIL_VERIFIED] == "true" else False + opening.save() + return Response({'action': "Update Email Verified", 'message': "Email Verified Updated"}, + status=status.HTTP_200_OK) + except Http404: + return Response({'action': "Update Email Verified", 'message': 'Opening Not Found'}, + status=status.HTTP_404_NOT_FOUND) + except: + logger.warning("Update Email Verified: " + str(sys.exc_info())) + return Response({'action': "Update Email Verified", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) + +@api_view(['POST']) +@isAuthorized([ADMIN]) +@precheck([OPENING_ID, ADDITIONAL_INFO]) +def updateAdditionalInfo(request, id, email, user_type): + try: + data = request.data + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + if data[ADDITIONAL_INFO] == "": + opening.additional_info = [] + elif isinstance(data[ADDITIONAL_INFO], list): + opening.additional_info = data[ADDITIONAL_INFO] + else: + raise ValueError("Additional Info must be a list") + opening.save() + return Response({'action': "Update Additional Info", 'message': "Additional Info Updated"}, + status=status.HTTP_200_OK) + except Http404: + return Response({'action': "Update Additional Info", 'message': 'Opening Not Found'}, + status=status.HTTP_404_NOT_FOUND) + except ValueError: + return Response({'action': "Update Additional Info", 'message': "Additional Info must be a list"}, + status=status.HTTP_400_BAD_REQUEST) + except: + logger.warning("Update Additional Info: " + str(sys.exc_info())) + return Response({'action': "Update Additional Info", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/CDC_Backend/APIs/companyUrls.py b/CDC_Backend/APIs/companyUrls.py index f175144..5cda889 100644 --- a/CDC_Backend/APIs/companyUrls.py +++ b/CDC_Backend/APIs/companyUrls.py @@ -3,5 +3,5 @@ from . import companyViews urlpatterns = [ - path('addOpening/', companyViews.addOpening, name="Add Opening"), + path('addPlacement/', companyViews.addPlacement, name="Add Placement"), ] diff --git a/CDC_Backend/APIs/companyViews.py b/CDC_Backend/APIs/companyViews.py index 92c7eac..7d7d50b 100644 --- a/CDC_Backend/APIs/companyViews.py +++ b/CDC_Backend/APIs/companyViews.py @@ -1,120 +1,222 @@ import json from datetime import datetime -from django.utils.timezone import make_aware from rest_framework.decorators import api_view +from .models import * from .utils import * logger = logging.getLogger('db') @api_view(['POST']) -@precheck([DESIGNATION, DESCRIPTION, OPENING_TYPE, CITY, CITY_TYPE, - COMPENSATION, COMPENSATION_DETAILS, ALLOWED_BATCH, ALLOWED_BRANCH, - ROUNDS, CO_OP, START_DATE, ADDITIONAL_INFO, - DURATION, ROUND_DETAILS]) -def addOpening(request): +@precheck([COMPANY_NAME, ADDRESS, COMPANY_TYPE, NATURE_OF_BUSINESS, WEBSITE, COMPANY_DETAILS, IS_COMPANY_DETAILS_PDF, + CONTACT_PERSON_NAME, PHONE_NUMBER, EMAIL, CITY, STATE, COUNTRY, PINCODE, DESIGNATION, DESCRIPTION, + IS_DESCRIPTION_PDF, + COMPENSATION_CTC, COMPENSATION_GROSS, COMPENSATION_TAKE_HOME, COMPENSATION_BONUS, COMPENSATION_DETAILS, + IS_COMPENSATION_DETAILS_PDF, + ALLOWED_BRANCH, SELECTION_PROCEDURE_ROUNDS, SELECTION_PROCEDURE_DETAILS, IS_SELECTION_PROCEDURE_DETAILS_PDF, + TENTATIVE_DATE_OF_JOINING, + TENTATIVE_NO_OF_OFFERS, OTHER_REQUIREMENTS + ]) +def addPlacement(request): try: data = request.data - if data[OPENING_TYPE] == "Placement": - opening = Placement() - else: - raise ValueError("Invalid Opening Type") + files = request.FILES + opening = Placement() opening.id = generateRandomString() - # Create Company object here for every Opening - - - # Some new code above - - if data[DESIGNATION] != "": - opening.designation = data[DESIGNATION] + # Add a company details in the opening + opening.company_name = data[COMPANY_NAME] + opening.address = data[ADDRESS] + opening.company_type = data[COMPANY_TYPE] + opening.nature_of_business = data[NATURE_OF_BUSINESS] + opening.website = data[WEBSITE] + opening.company_details = data[COMPANY_DETAILS] + if data[IS_COMPANY_DETAILS_PDF] == "true": + opening.is_company_details_pdf = True + elif data[IS_COMPANY_DETAILS_PDF] == "false": + opening.is_company_details_pdf = False else: - raise ValueError(DESIGNATION + " Not Found") + raise ValueError('Invalid value for is_company_details_pdf') + if opening.is_company_details_pdf: + company_details_pdf = [] + for file in files.getlist(COMPANY_DETAILS_PDF): + file_location = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + opening.id + '/' + company_details_pdf.append(saveFile(file, file_location)) + + opening.company_details_pdf_names = company_details_pdf + + # Add a contact person details in the opening + opening.contact_person_name = data[CONTACT_PERSON_NAME] + # Check if Phone number is Integer + if data[PHONE_NUMBER].isdigit(): + opening.phone_number = int(data[PHONE_NUMBER]) + else: + raise ValueError('Phone number should be integer') + + opening.email = data[EMAIL] + + # Add a company location in the opening + opening.city = data[CITY] + opening.state = data[STATE] + opening.country = data[COUNTRY] + + # Check if Pincode is Integer + if data[PINCODE].isdigit(): + opening.pin_code = int(data[PINCODE]) + else: + raise ValueError('Pincode should be integer') + + # If India then set city_type as Domestic else International + if opening.country == 'India': + opening.city_type = 'Domestic' + else: + opening.city_type = 'International' + + # Add a designation details in the opening + opening.designation = data[DESIGNATION] opening.description = data[DESCRIPTION] - if data[START_DATE] != "": - opening.description = data[START_DATE] + # Check if is_description_pdf is boolean + if data[IS_DESCRIPTION_PDF] == "true": + opening.is_description_pdf = True + elif data[IS_DESCRIPTION_PDF] == "false": + opening.is_description_pdf = False else: - raise ValueError(START_DATE + " Not Found") - if data[START_DATE] != "": - opening.start_date = datetime.strptime(data[START_DATE], '%d-%m-%Y') + raise ValueError('Invalid value for is_description_pdf') + + if opening.is_description_pdf: + description_pdf = [] + for file in files.getlist(DESCRIPTION_PDF): + file_location = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + opening.id + '/' + description_pdf.append(saveFile(file, file_location)) + + opening.description_pdf_names = description_pdf + + # Add a compensation details in the opening + # Check if compensation_ctc is integer + if data[COMPENSATION_CTC].isdigit(): + opening.compensation_CTC = int(data[COMPENSATION_CTC]) + elif data[COMPENSATION_CTC] is None: + opening.compensation_CTC = None else: - raise ValueError(START_DATE + " Not Found") - if data[CITY] != "": - opening.city = data[CITY] + raise ValueError('Compensation CTC must be an integer') + + # Check if compensation_gross is integer + if data[COMPENSATION_GROSS].isdigit(): + opening.compensation_gross = int(data[COMPENSATION_GROSS]) + elif data[COMPENSATION_GROSS] is None: + opening.compensation_gross = None else: - raise ValueError(CITY + " Not Found") - if data[CITY_TYPE] != "": - opening.city_type = data[CITY_TYPE] + raise ValueError('Compensation Gross must be an integer') + + # Check if compensation_take_home is integer + if data[COMPENSATION_TAKE_HOME].isdigit(): + opening.compensation_take_home = int(data[COMPENSATION_TAKE_HOME]) + elif data[COMPENSATION_TAKE_HOME] is None: + opening.compensation_take_home = None else: - raise ValueError(CITY_TYPE + " Not Found") - if data[COMPENSATION] != "": - opening.compensation = data[COMPENSATION] + raise ValueError('Compensation Take Home must be an integer') + + # Check if compensation_bonus is integer + if data[COMPENSATION_BONUS].isdigit(): + opening.compensation_bonus = int(data[COMPENSATION_BONUS]) + elif data[COMPENSATION_BONUS] is None: + opening.compensation_bonus = None else: - raise ValueError(COMPENSATION + " Not Found") + raise ValueError('Compensation Bonus must be an integer') opening.compensation_details = data[COMPENSATION_DETAILS] - - if data[ALLOWED_BATCH] != "": - if set(json.loads(data[ALLOWED_BATCH])).issubset(BATCHES): - opening.allowed_batch = json.loads(data[ALLOWED_BATCH]) - else: - raise ValueError(ALLOWED_BATCH + " is Invalid") + # Check if is_compensation_details_pdf is boolean + if data[IS_COMPENSATION_DETAILS_PDF] == "true": + opening.is_compensation_details_pdf = True + elif data[IS_COMPENSATION_DETAILS_PDF] == "false": + opening.is_compensation_details_pdf = False else: - raise ValueError(ALLOWED_BATCH + " Not Found") - if data[ALLOWED_BRANCH] != "": - if set(json.loads(data[ALLOWED_BRANCH])).issubset(BRANCHES): - opening.allowed_branch = json.loads(data[ALLOWED_BRANCH]) - else: - raise ValueError(ALLOWED_BATCH + " is Invalid") + raise ValueError('Invalid value for is_compensation_details_pdf') + + if opening.is_compensation_details_pdf: + compensation_details_pdf = [] + for file in files.getlist(COMPENSATION_DETAILS_PDF): + file_location = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + opening.id + '/' + compensation_details_pdf.append(saveFile(file, file_location)) + + opening.compensation_details_pdf_names = compensation_details_pdf + + opening.bond_details = data[BOND_DETAILS] + + # Check if selection_procedure_rounds is list + if data[SELECTION_PROCEDURE_ROUNDS] is None: + raise ValueError('Selection Procedure Rounds cannot be empty') else: - raise ValueError(ALLOWED_BRANCH + " Not Found") + try: + opening.selection_procedure_rounds = json.loads(data[SELECTION_PROCEDURE_ROUNDS]) + except: + raise ValueError('Selection Procedure Rounds must be a list') + opening.selection_procedure_details = data[SELECTION_PROCEDURE_DETAILS] + # Check if is_selection_procedure_details_pdf is boolean + if data[IS_SELECTION_PROCEDURE_DETAILS_PDF] == "true": + opening.is_selection_procedure_details_pdf = True + elif data[IS_SELECTION_PROCEDURE_DETAILS_PDF] == "false": + opening.is_selection_procedure_details_pdf = False + else: + raise ValueError('Invalid value for is_selection_procedure_pdf') - opening.rounds = json.loads(data[ROUNDS]) + if opening.is_selection_procedure_details_pdf: + selection_procedure_details_pdf = [] + for file in files.getlist(SELECTION_PROCEDURE_DETAILS_PDF): + file_location = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + opening.id + '/' + selection_procedure_details_pdf.append(saveFile(file, file_location)) - opening.additional_info = json.loads(data[ADDITIONAL_INFO]) + opening.selection_procedure_details_pdf_names = selection_procedure_details_pdf - opening.status = STATUS_ACCEPTING_APPLICATIONS + stat, tier = getTier(opening.compensation_gross) + if stat: + opening.tier = tier + else: + raise ValueError('Invalid compensation gross') + # Convert to date object + opening.tentative_date_of_joining = datetime.strptime(data[TENTATIVE_DATE_OF_JOINING], '%d-%m-%Y').date() - opening.rounds_details = json.loads(data[ROUND_DETAILS]) + # Only Allowing Fourth Year for Placement + opening.allowed_batch = [FOURTH_YEAR, ] + # Check if allowed_branch are valid + if data[ALLOWED_BRANCH] is None: + raise ValueError('Allowed Branch cannot be empty') + elif set(json.loads(data[ALLOWED_BRANCH])).issubset(BRANCHES): + opening.allowed_branch = json.loads(data[ALLOWED_BRANCH]) + else: + raise ValueError('Allowed Branch must be a subset of ' + str(BRANCHES)) - opening.created_at = make_aware(datetime.now()) - files = request.FILES.getlist(ATTACHMENTS) - attachments = [] - for file in files: - attachments.append(saveFile(file, STORAGE_DESTINATION_COMPANY_ATTACHMENTS)) + # Check if tentative_no_of_offers is integer + if data[TENTATIVE_NO_OF_OFFERS].isdigit(): + opening.tentative_no_of_offers = int(data[TENTATIVE_NO_OF_OFFERS]) + else: + raise ValueError('Tentative No Of Offers must be an integer') + + opening.other_requirements = data[OTHER_REQUIREMENTS] - 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 + "opening_type": PLACEMENT, + "opening_link": PLACEMENT_OPENING_URL.format(id=opening.id), # Some Changes here too + "company_name": opening.company_name } - # Needs some edits here + sendEmail(opening.email, COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT.format(id=opening.id), data, + COMPANY_OPENING_SUBMITTED_TEMPLATE) - 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"}, + return Response({'action': "Add Placement", 'message': "Placement Added Successfully"}, status=status.HTTP_200_OK) except ValueError as e: - return Response({'action': "Add Opening", 'message': str(e)}, + return Response({'action': "Add Placement", '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]))}, + logger.warning("Add New Placement: " + str(sys.exc_info())) + return Response({'action': "Add Placement", 'message': "Something went wrong"}, status=status.HTTP_400_BAD_REQUEST) diff --git a/CDC_Backend/APIs/constants.py b/CDC_Backend/APIs/constants.py index 7a4b25b..fa9790e 100644 --- a/CDC_Backend/APIs/constants.py +++ b/CDC_Backend/APIs/constants.py @@ -1,14 +1,20 @@ BRANCH_CHOICES = [ ["CSE", "CSE"], ["EE", "EE"], - ["ME", "ME"] + ["ME", "ME"], + ['EP', 'EP'], +] +BRANCHES = [ + "CSE", + "EE", + "ME", + "EP" ] - BATCH_CHOICES = [ - ["FIRST", "First"], - ["SECOND", "Second"], - ["THIRD", "Third"], - ["FOURTH", "Fourth"] + ["2021", "2021"], + ["2020", "2020"], + ["2019", "2019"], + ["2018", "2018"] ] OFFER_CITY_TYPE = [ @@ -26,70 +32,102 @@ TIERS = [ ['6', 'Tier 6'] ] - -TOTAL_BRANCHES = 3 # Total No of Branches +TOTAL_BRANCHES = 4 # Total No of Branches TOTAL_BATCHES = 4 # Total No of Batches +# To be Configured Properly CLIENT_ID = "956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleusercontent.com" # Google Login Client ID +# To be Configured Properly +PLACEMENT_OPENING_URL = "https://www.googleapis.com/auth/adwords/{id}" +LINK_TO_STORAGE_COMPANY_ATTACHMENT = "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/" +LINK_TO_STORAGE_RESUME = "https://storage.googleapis.com/cdc-backend-attachments/resume/" + + TOKEN = "token_id" EMAIL = "email" STUDENT = 'student' -ADMIN = 'Admin' +ADMIN = 'admin' COMPANY = '' -STORAGE_DESTINATION = "./Storage/Resumes/" +# To be Configured Properly +FOURTH_YEAR = '2018' +MAX_OFFERS_PER_STUDENT = 2 + + +STORAGE_DESTINATION_RESUMES = "./Storage/Resumes/" STORAGE_DESTINATION_COMPANY_ATTACHMENTS = './Storage/Company_Attachments/' RESUME_FILE_NAME = 'resume_file_name' APPLICATION_ID = "application_id" OPENING_ID = "opening_id" -STUDENT_ID = "student_id" ADDITIONAL_INFO = "additional_info" STATUS_ACCEPTING_APPLICATIONS = "Accepting Applications" PLACEMENT = "Placement" -COMPANY_WEBSITE = 'website' -COMPANY_ADDRESS = 'address' +COMPANY_NAME = "company_name" +ADDRESS = "address" +COMPANY_TYPE = "company_type" +NATURE_OF_BUSINESS = "nature_of_business" +WEBSITE = 'website' +COMPANY_DETAILS = "company_details" +COMPANY_DETAILS_PDF = "company_details_pdf" +IS_COMPANY_DETAILS_PDF = "is_company_details_pdf" +COMPANY_DETAILS_PDF_NAMES = "company_details_pdf_names" PHONE_NUMBER = 'phone_number' CONTACT_PERSON_NAME = 'contact_person_name' +CITY = 'city' +STATE = 'state' +COUNTRY = 'country' +PINCODE = 'pincode' + DESIGNATION = 'designation' DESCRIPTION = 'description' +DESCRIPTION_PDF = 'description_pdf' +DESCRIPTION_PDF_NAMES = 'description_pdf_names' +IS_DESCRIPTION_PDF = 'is_description_pdf' OPENING_TYPE = 'opening_type' -CITY = 'city' -CITY_TYPE = 'city_type' -COMPENSATION = 'compensation' +COMPENSATION_CTC = 'compensation_ctc' +COMPENSATION_GROSS = 'compensation_gross' +COMPENSATION_TAKE_HOME = 'compensation_take_home' +COMPENSATION_BONUS = 'compensation_bonus' COMPENSATION_DETAILS = 'compensation_details' +COMPENSATION_DETAILS_PDF = 'compensation_details_pdf' +COMPENSATION_DETAILS_PDF_NAMES = 'compensation_details_pdf_names' +IS_COMPENSATION_DETAILS_PDF = 'is_compensation_details_pdf' ALLOWED_BATCH = 'allowed_batch' ALLOWED_BRANCH = 'allowed_branch' -ATTACHMENTS = 'attachments' -ROUNDS = 'rounds' -ROUND_DETAILS = 'round_details' -DURATION = 'duration' -CO_OP = 'co_op' -START_DATE = "start_date" +BOND_DETAILS = 'bond_details' +SELECTION_PROCEDURE_ROUNDS = 'selection_procedure_rounds' +SELECTION_PROCEDURE_DETAILS = 'selection_procedure_details' +SELECTION_PROCEDURE_DETAILS_PDF = 'selection_procedure_details_pdf' +SELECTION_PROCEDURE_DETAILS_PDF_NAMES = 'selection_procedure_details_pdf_names' +IS_SELECTION_PROCEDURE_DETAILS_PDF = 'is_selection_procedure_details_pdf' +TENTATIVE_DATE_OF_JOINING = 'tentative_date_of_joining' +TENTATIVE_NO_OF_OFFERS = 'tentative_no_of_offers' +OTHER_REQUIREMENTS = 'other_requirements' +DEADLINE_DATETIME = 'deadline_datetime' +OFFER_ACCEPTED = 'offer_accepted' +EMAIL_VERIFIED = 'email_verified' STUDENT_LIST = "student_list" -STUDENT_STATUS = "student_status" +STUDENT_ID = "student_id" +STUDENT_SELECTED = "student_selected" + + -BRANCHES = [ - "CSE", - "EE", - "ME" -] -BATCHES = [ - "FIRST", - "SECOND", - "THIRD", - "FOURTH" -] COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT = "Notification Submitted - {id} - CDC IIT Dharwad" +STUDENT_APPLICATION_STATUS_TEMPLATE_SUBJECT = 'Application Status : {company_name} - {id}' +STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT = 'CDC - Application Submitted - {company_name}' STUDENT_APPLICATION_SUBMITTED_TEMPLATE = 'student_application_submitted.html' COMPANY_OPENING_SUBMITTED_TEMPLATE = 'company_opening_submitted.html' +STUDENT_APPLICATION_STATUS_SELECTED_TEMPLATE = 'student_application_status_selected.html' +STUDENT_APPLICATION_STATUS_NOT_SELECTED_TEMPLATE = 'student_application_status_not_selected.html' + diff --git a/CDC_Backend/APIs/models.py b/CDC_Backend/APIs/models.py index 7203738..d8c9080 100644 --- a/CDC_Backend/APIs/models.py +++ b/CDC_Backend/APIs/models.py @@ -2,6 +2,8 @@ from django.contrib.postgres.fields import ArrayField from django.db import models from django.utils import timezone from .constants import * +# from .utils import * + class User(models.Model): @@ -26,58 +28,80 @@ class Admin(models.Model): name = models.CharField(blank=False, max_length=50) +def two_day_after_today(): + return timezone.now() + timezone.timedelta(days=2) + + class Placement(models.Model): id = models.CharField(blank=False, primary_key=True, max_length=15) - name = models.CharField(blank=False, max_length=50, default="") - address = models.CharField(blank=False, max_length=150, default="") - companyType = models.CharField(blank=False, max_length=50, default="") - website = models.CharField(blank=True, max_length=50, default="") - contact_person_name = models.CharField(blank=False, max_length=50, default="") - phone_number = models.PositiveBigIntegerField(blank=False, default=0) - 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") + # Company Details + company_name = models.CharField(blank=False, max_length=50) + address = models.CharField(blank=False, max_length=150) + company_type = models.CharField(blank=False, max_length=50) + nature_of_business = models.CharField(blank=False, max_length=50, default="") + website = models.CharField(blank=True, max_length=50) + company_details = models.CharField(blank=False, max_length=500, default=None, null=True) + company_details_pdf_names = ArrayField(models.CharField(null=True, default=None, max_length=100), size=5, default=list, blank=True) + is_company_details_pdf = models.BooleanField(blank=False, default=False) + contact_person_name = models.CharField(blank=False, max_length=50) + phone_number = models.PositiveBigIntegerField(blank=False) + email = models.CharField(blank=False, max_length=50, default="") city = models.CharField(blank=False, max_length=100, default="") + state = models.CharField(blank=False, max_length=100, default="") + country = models.CharField(blank=False, max_length=100, default="") + pin_code = models.IntegerField(blank=False, default=None,null=True) 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) + # Job Details + designation = models.CharField(blank=False, max_length=25, default=None, null=True) + description = models.CharField(blank=False, max_length=200, default=None, null=True) + description_pdf_names = ArrayField(models.CharField(null=True, default=None, max_length=100), size=5, default=list, blank=True) + is_description_pdf = models.BooleanField(blank=False, default=False) + compensation_CTC = models.IntegerField(blank=False, default=None, null=True ) # Job - Per Year + compensation_gross = models.IntegerField(blank=False, default=None, null=True) + compensation_take_home = models.IntegerField(blank=False, default=None, null=True) + compensation_bonus = models.IntegerField(blank=True, default=None, null=True) + compensation_details = models.CharField(blank=True, max_length=200, default=None, null=True) + compensation_details_pdf_names = ArrayField(models.CharField(null=True, default=None, max_length=100), size=5, default=list, blank=True) + is_compensation_details_pdf = models.BooleanField(blank=False, default=False) + bond_details = models.CharField(blank=True, max_length=200) + selection_procedure_rounds = ArrayField(models.CharField(null=True, default=None, max_length=100), size=10, default=list, blank=True) + selection_procedure_details = models.CharField(blank=True, max_length=200) + selection_procedure_details_pdf_names = ArrayField(models.CharField(null=True, default=None, max_length=100), size=5, default=list, blank=True) + is_selection_procedure_details_pdf = models.BooleanField(blank=False, default=False) tier = models.CharField(blank=False, choices=TIERS, max_length=10, default=None, null=True) + tentative_date_of_joining = models.DateField(blank=False, verbose_name="Tentative Date", default=timezone.now) 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) + tentative_no_of_offers = models.IntegerField(blank=False, default=1) + other_requirements = models.CharField(blank=True, max_length=200, default="") + additional_info = ArrayField(models.CharField(blank=True, max_length=200), size=15, default=list, blank=True) + email_verified = models.BooleanField(blank=False, default=False) + offer_accepted = models.BooleanField(blank=False, default=None, null=True) + deadline_datetime = models.DateTimeField(blank=False, verbose_name="Deadline Date", default=two_day_after_today) created_at = models.DateTimeField(blank=False, default=None, null=True) + def save(self, *args, **kwargs): + ''' On save, add timestamps ''' + if not self.created_at: + self.created_at = timezone.now() + + return super(Placement, self).save(*args, **kwargs) 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) + additional_info = models.JSONField(blank=True, null=True, default=None) selected = models.BooleanField(null=True, default=None, blank=True) applied_at = models.DateTimeField(blank=False, default=None, null=True) diff --git a/CDC_Backend/APIs/serializers.py b/CDC_Backend/APIs/serializers.py index 62f9ef7..787de1e 100644 --- a/CDC_Backend/APIs/serializers.py +++ b/CDC_Backend/APIs/serializers.py @@ -1,56 +1,162 @@ +import urllib + from rest_framework import serializers from .models import * class StudentSerializer(serializers.ModelSerializer): + resume_list = serializers.SerializerMethodField() + offers = serializers.SerializerMethodField() + + def get_resume_list(self, obj): + links = [] + for i in obj.resumes: + ele = {} + ele['link'] = LINK_TO_STORAGE_RESUME + urllib.parse.quote_plus(obj.id + "/" + i) + ele['name'] = i + links.append(ele) + return links + + def get_offers(self, obj): + selected_companies = PlacementApplication.objects.filter(student_id=obj.id, selected=True) + companies = [] + + for i in selected_companies: + ele = {} + ele['designation'] = i.placement.designation + ele['company_name'] = i.placement.company_name + ele['application_id'] = i.id + companies.append(ele) + + return companies + class Meta: model = Student - fields = '__all__' - # exclude = ['id'] + exclude = ['resumes'] + +class PlacementSerializerForStudent(serializers.ModelSerializer): + company_details_pdf_links = serializers.SerializerMethodField() + description_pdf_links = serializers.SerializerMethodField() + compensation_pdf_links = serializers.SerializerMethodField() + selection_procedure_details_pdf_links = serializers.SerializerMethodField() -class PlacementSerializer(serializers.ModelSerializer): - company_details = serializers.SerializerMethodField() + def get_company_details_pdf_links(self, obj): + links =[] + for pdf_name in obj.company_details_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote_plus(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links + + def get_description_pdf_links(self, obj): + links =[] + for pdf_name in obj.description_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote_plus(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links + + def get_compensation_pdf_links(self, obj): + links =[] + for pdf_name in obj.compensation_details_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote_plus(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links + + def get_selection_procedure_details_pdf_links(self, obj): + links =[] + for pdf_name in obj.selection_procedure_details_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote_plus(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links - 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] + exclude = [CONTACT_PERSON_NAME, PHONE_NUMBER, EMAIL, COMPANY_DETAILS_PDF_NAMES, DESCRIPTION_PDF_NAMES, + COMPENSATION_DETAILS_PDF_NAMES, SELECTION_PROCEDURE_DETAILS_PDF_NAMES, OFFER_ACCEPTED, EMAIL_VERIFIED] + depth = 1 + +class PlacementSerializerForAdmin(serializers.ModelSerializer): + company_details_pdf_links = serializers.SerializerMethodField() + description_pdf_links = serializers.SerializerMethodField() + compensation_pdf_links = serializers.SerializerMethodField() + selection_procedure_details_pdf_links = serializers.SerializerMethodField() + + + def get_company_details_pdf_links(self, obj): + links =[] + for pdf_name in obj.company_details_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote_plus(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links + + def get_description_pdf_links(self, obj): + links =[] + for pdf_name in obj.description_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote_plus(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links + + def get_compensation_pdf_links(self, obj): + links =[] + for pdf_name in obj.compensation_details_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote_plus(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links + + def get_selection_procedure_details_pdf_links(self, obj): + links =[] + for pdf_name in obj.selection_procedure_details_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote_plus(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links + + + + class Meta: + model = Placement + exclude = [COMPANY_DETAILS_PDF_NAMES, DESCRIPTION_PDF_NAMES, + COMPENSATION_DETAILS_PDF_NAMES, SELECTION_PROCEDURE_DETAILS_PDF_NAMES] depth = 1 class PlacementApplicationSerializer(serializers.ModelSerializer): - application_status = serializers.SerializerMethodField() - company_details = serializers.SerializerMethodField() + placement = serializers.SerializerMethodField() + resume_link = 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, - } + def get_placement(self, obj): + data = PlacementSerializerForStudent(obj.placement).data return data + def get_resume_link(self, obj): + link = LINK_TO_STORAGE_RESUME + urllib.parse.quote_plus(obj.id + "/" + obj.resume) + return link + class Meta: model = PlacementApplication - exclude = ['status', 'student'] + exclude = [STUDENT, 'resume'] diff --git a/CDC_Backend/APIs/studentViews.py b/CDC_Backend/APIs/studentViews.py index d709576..329f5c3 100644 --- a/CDC_Backend/APIs/studentViews.py +++ b/CDC_Backend/APIs/studentViews.py @@ -1,5 +1,5 @@ -import logging -from os import path, remove +import json +from datetime import datetime from rest_framework.decorators import api_view @@ -16,8 +16,7 @@ def login(request, id, email, user_type): 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]))}, + return Response({'action': "Login", 'message': "Something Went Wrong"}, status=status.HTTP_400_BAD_REQUEST) @@ -31,7 +30,8 @@ def studentProfile(request, id, email, user_type): 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]))}, + logger.warning("Student Profile: " + str(sys.exc_info())) + return Response({'action': "Student Profile", 'message': "Something Went Wrong"}, status=status.HTTP_400_BAD_REQUEST) @@ -41,20 +41,12 @@ 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) + destination_path = STORAGE_DESTINATION_RESUMES + id + "/" + file_name = saveFile(file, destination_path) + student.resumes.append(file_name) student.save() return Response({'action': "Upload Resume", 'message': "Resume Added"}, @@ -68,8 +60,7 @@ def addResume(request, id, email, user_type): 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]))}, + return Response({'action': "Upload Resume", 'message': "Something Went Wrong"}, status=status.HTTP_400_BAD_REQUEST) @@ -81,23 +72,23 @@ def getDashboard(request, id, email, user_type): placements = Placement.objects.filter(allowed_batch__contains=[studentDetails.batch], allowed_branch__contains=[studentDetails.branch], - status=STATUS_ACCEPTING_APPLICATIONS) - placementsdata = PlacementSerializer(placements, many=True).data + deadline_datetime__gte=datetime.now(), + offer_accepted=True, email_verified=True).order_by('deadline_datetime') + placementsdata = PlacementSerializerForStudent(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, + {'action': "Get Dashboard - Student", 'message': "Data Found", "placements": placementsdata, 'placementApplication': placementApplications}, status=status.HTTP_200_OK) except Http404: - return Response({'action': "Placements and Internships", 'message': 'Student Not Found'}, + return Response({'action': "Get Dashboard - Student", '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]))}, + logger.warning("Get Dashboard -Student: " + str(sys.exc_info())) + return Response({'action': "Get Dashboard - Student", 'message': "Something Went Wrong"}, status=status.HTTP_400_BAD_REQUEST) @@ -108,7 +99,7 @@ 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) + destination_path = STORAGE_DESTINATION_RESUMES + id + "/" + str(file_name) if path.exists(destination_path): remove(destination_path) student.resumes.remove(file_name) @@ -125,28 +116,32 @@ def deleteResume(request, id, email, user_type): 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()))}, + return Response({'action': "Delete Resume", 'message': "Something Went Wrong"}, status=status.HTTP_400_BAD_REQUEST) @api_view(['POST']) @isAuthorized(allowed_users=[STUDENT]) @precheck(required_data=[OPENING_TYPE, OPENING_ID, RESUME_FILE_NAME, - ADDITIONAL_INFO]) + ]) def submitApplication(request, id, email, user_type): try: data = request.data student = get_object_or_404(Student, id=id) - + # Only Allowing Applications for Placements if data[OPENING_TYPE] == PLACEMENT: if not len(PlacementApplication.objects.filter( student_id=id, placement_id=data[OPENING_ID])): application = PlacementApplication() opening = get_object_or_404(Placement, id=data[OPENING_ID], - status=STATUS_ACCEPTING_APPLICATIONS) + allowed_batch__contains=[student.batch], + allowed_branch__contains=[student.branch], + deadline_datetime__gte=datetime.now().date() + ) + if not opening.offer_accepted or not opening.email_verified: + raise PermissionError("Placement Not Approved") + cond_stat, cond_msg = PlacementApplicationConditions(student, opening) - print(cond_stat, cond_msg) if not cond_stat: raise PermissionError(cond_msg) application.placement = opening @@ -162,21 +157,29 @@ def submitApplication(request, id, email, user_type): application.student = student application.id = generateRandomString() + additional_info = {} for i in opening.additional_info: if i not in data[ADDITIONAL_INFO]: - print(i) raise AttributeError(i + " not found in Additional Info") + else: + additional_info[i] = data[ADDITIONAL_INFO][i] - application.additional_info = data[ADDITIONAL_INFO] - if not sendApplicationEmail(email, student.name, opening.company.name, data[OPENING_TYPE], - data[ADDITIONAL_INFO]): - logger.error("Submit Application: Unable to Send Email") - # raise RuntimeError("Unable to Send Email") + application.additional_info = json.dumps(additional_info) + data = { + "name": student.name, + "company_name": opening.company_name, + "application_type": data[OPENING_TYPE], + "additional_info": dict(json.loads(application.additional_info)), + } + subject = STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT.format(company_name=opening.company_name) + sendEmail(email, subject, data, STUDENT_APPLICATION_SUBMITTED_TEMPLATE) application.save() return Response({'action': "Submit Application", 'message': "Application Submitted"}, status=status.HTTP_200_OK) - + except Http404 as e: + return Response({'action': "Submit Application", 'message': "Student Not Found"}, + status=status.HTTP_404_NOT_FOUND) except PermissionError as e: return Response({'action': "Submit Application", 'message': str(e)}, status=status.HTTP_403_FORBIDDEN) @@ -185,6 +188,5 @@ def submitApplication(request, id, email, user_type): 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]))}, + return Response({'action': "Submit Application", 'message': "Something Went Wrong"}, status=status.HTTP_400_BAD_REQUEST) diff --git a/CDC_Backend/APIs/utils.py b/CDC_Backend/APIs/utils.py index 422cec1..d7ea9fb 100644 --- a/CDC_Backend/APIs/utils.py +++ b/CDC_Backend/APIs/utils.py @@ -5,6 +5,7 @@ import string import sys from os import path, remove +import background_task from django.conf import settings from django.core.mail import EmailMultiAlternatives from django.http import Http404 @@ -16,7 +17,8 @@ from google.oauth2 import id_token from rest_framework import status from rest_framework.response import Response -from .models import * +from .constants import * +from .models import User, PrePlacementOffer, PlacementApplication logger = logging.getLogger('db') @@ -78,11 +80,9 @@ def isAuthorized(allowed_users=None): 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: @@ -90,9 +90,10 @@ def isAuthorized(allowed_users=None): 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) + logger.warning("Is Authorized? " + str(sys.exc_info())) + return Response( + {'action': "Is Authorized?", 'message': "Something went wrong. Contact CDC for more details"}, + status=status.HTTP_400_BAD_REQUEST) return wrapper_func @@ -108,30 +109,6 @@ def generateRandomString(): 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 @@ -139,7 +116,7 @@ def saveFile(file, location): if not path.isdir(location): os.mkdir(location) - destination_path = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + str(file_name) + destination_path = location + str(file_name) if path.exists(destination_path): remove(destination_path) @@ -150,6 +127,7 @@ def saveFile(file, location): return file_name +@background_task.background(schedule=10) def sendEmail(email_to, subject, data, template): try: html_content = render_to_string(template, data) # render with dynamic value @@ -163,17 +141,18 @@ def sendEmail(email_to, subject, data, template): msg.send() return True except: + logger.error("Send Email: " + str(sys.exc_info())) print(str(sys.exc_info()[1])) - return str(sys.exc_info()[1]) + return False 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) + PPO = PrePlacementOffer.objects.filter(student=student, accepted=True) - if len(selected_companies) + len(PPO) >= 2: + if len(selected_companies) + len(PPO) >= MAX_OFFERS_PER_STUDENT: raise PermissionError("Max Applications Reached for the Season") if len(selected_companies_PSU) > 0: @@ -195,3 +174,41 @@ def PlacementApplicationConditions(student, placement): print(sys.exc_info()) logger.warning("Utils - PlacementApplicationConditions: " + str(sys.exc_info())) return False, "_" + + +def getTier(compensation_gross, is_psu=False): + try: + if is_psu: + return True, 'psu' + if compensation_gross < 0: + raise ValueError("Negative Compensation") + elif compensation_gross < 600000: # Tier 7 If less than 600,000 + return True, "7" + # Tier 6 If less than 800,000 and greater than or equal to 600,000 + elif compensation_gross < 800000: + return True, "6" + # Tier 5 If less than 1,000,000 and greater than or equal to 800,000 + elif compensation_gross < 1000000: + return True, "5" + # Tier 4 If less than 1,200,000 and greater than or equal to 1,000,000 + elif compensation_gross < 1200000: + return True, "4" + # Tier 3 If less than 1,500,000 and greater than or equal to 1,200,000 + elif compensation_gross < 1500000: + return True, "3" + # Tier 2 If less than 1,800,000 and greater than or equal to 1,500,000 + elif compensation_gross < 1800000: + return True, "2" + # Tier 1 If greater than or equal to 1,800,000 + elif compensation_gross >= 1800000: + return True, "1" + else: + raise ValueError("Invalid Compensation") + + except ValueError as e: + logger.warning("Utils - getTier: " + str(sys.exc_info())) + return False, e + except: + print(sys.exc_info()) + logger.warning("Utils - getTier: " + str(sys.exc_info())) + return False, "_" diff --git a/CDC_Backend/CDC_Backend/settings.py b/CDC_Backend/CDC_Backend/settings.py index e2b812b..7c0a6f1 100644 --- a/CDC_Backend/CDC_Backend/settings.py +++ b/CDC_Backend/CDC_Backend/settings.py @@ -41,6 +41,7 @@ INSTALLED_APPS = [ 'rest_framework', 'corsheaders', 'django_db_logger', + 'background_task' ] MIDDLEWARE = [ @@ -160,7 +161,7 @@ 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' +EMAIL_HOST_PASSWORD = 'yeylqcnsyjfpzsew'#'password here' LOGGING = { 'version': 1, diff --git a/CDC_Backend/templates/company_opening_submitted.html b/CDC_Backend/templates/company_opening_submitted.html new file mode 100644 index 0000000..88147e0 --- /dev/null +++ b/CDC_Backend/templates/company_opening_submitted.html @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ +
+ + + + + +
+

Thank You for filling the form

+

+ We have received your {{ opening_type }} notification for a {{ designation }} offer at + {{ company_name }}. Click here to view your notification. +

+ +

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

+
+
+ + + + + + + +
+

+ ® CDC,IIT Dharwad,2021
+

+
+
+
+ + \ No newline at end of file diff --git a/CDC_Backend/templates/student_application_status_not_selected.html b/CDC_Backend/templates/student_application_status_not_selected.html new file mode 100644 index 0000000..ee01642 --- /dev/null +++ b/CDC_Backend/templates/student_application_status_not_selected.html @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ +
+ + + + + + + +
+

Hey, {{ student_name }}

+

+ We regret to inform you that you have not been selected for {{ designation }} role at {{ company_name }}. + CDC will keep bringing more such opportunities for you in the future. +

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

+ ® CDC,IIT Dharwad,2021
+

+
+
+
+ + diff --git a/CDC_Backend/templates/student_application_status_selected.html b/CDC_Backend/templates/student_application_status_selected.html new file mode 100644 index 0000000..0cd439f --- /dev/null +++ b/CDC_Backend/templates/student_application_status_selected.html @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ +
+ + + + + + + +
+

Hey, {{ student_name }}

+

+ Congratulations, You have been selected for the {{ designation }} at {{ company_name }}.
+

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

+ ® CDC,IIT Dharwad,2021
+

+
+
+
+ + diff --git a/CDC_Backend/templates/student_application_submitted.html b/CDC_Backend/templates/student_application_submitted.html index 805edee..eaeb123 100644 --- a/CDC_Backend/templates/student_application_submitted.html +++ b/CDC_Backend/templates/student_application_submitted.html @@ -44,7 +44,7 @@

Hello there, {{ name }}

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

- message: Tells us what happened with our Request. - user_type: Tells us about the role the user possess. Can have these values - - student - - Admin + - student + - Admin ### Status Codes @@ -88,16 +98,26 @@ Response is a Json with these fields "action": "Student Profile", "message": "Details Found", "details": { - "id": "fdgdb", + "id": "190010036", + "resume_list": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/resume/190010036%2F8KIOT3PW1JIS718_CSE-V-SEM.pdf", + "name": "8KIOT3PW1JIS718_CSE-V-SEM.pdf" + } + ], + "offers": [ + { + "designation": "Software Developer", + "company_name": "Make My Trip", + "application_id": "LLW4STE76GEJYOR" + } + ], "roll_no": 190010036, "name": "Gowtham Sai", - "batch": "THIRD", + "batch": "2019", "branch": "CSE", "phone_number": 9390291911, - "resumes": [ - "XB85F4RIGBF5VJN_Cv-Gowtham.pdf" - ], - "cpi": "9.02" + "cpi": "9.15" } } ``` @@ -113,7 +133,7 @@ The possible responses for this api request are as follows | Status Codes | Possible Messages | | --------------- | ------------------------ | | 200 OK | `Details Found` | -| 400 BAD_REQUEST | `Error Occurred {error}` | +| 400 BAD_REQUEST | `Something Went Wrong` | You may see some different errors which can be seen [here](#common-errors) @@ -121,11 +141,11 @@ 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. +This Api is used to get all the placements applicable to the student and his applications which he has already applied. ### How to Use? -Send a `GET` request to `api/student/Dashboard`
+Send a `GET` request to `api/student/getDashboard`
Request_Body: @@ -136,88 +156,215 @@ Request_Body: > Headers
> Authorization: "Bearer {tokenID}" +> Only users with `student` role can access this Api. + + ### Response Response is a Json with these fields ```json { - "action": "Placement and Internships", + "action": "Get Dashboard - Student", "message": "Data Found", "placements": [ { - "id": "fdgdb121", - "designation": "Software Developer", - "description": "nice job", - "start_date": "2021-06-17", + "id": "Q54IRZZMC3RP8F6", + "company_details_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FI5U4RDTV0OP0EM0_2019+Student+Details+-+Total%28State+Sort%29+-+Copy.pdf", + "name": "I5U4RDTV0OP0EM0_2019 Student Details - Total(State Sort) - Copy.pdf" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FW04JWWNNMIBX0JX_2019+Student+Details+-+Total%28State+Sort%29.pdf", + "name": "W04JWWNNMIBX0JX_2019 Student Details - Total(State Sort).pdf" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FT1BXP98WBT9BHOR_AP0313017732021LL-Driving+Licence+-+Copy.pdf", + "name": "T1BXP98WBT9BHOR_AP0313017732021LL-Driving Licence - Copy.pdf" + } + ], + "description_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FC78TE2Z67BPZ41O_CSE-V-SEM.pdf", + "name": "C78TE2Z67BPZ41O_CSE-V-SEM.pdf" + } + ], + "compensation_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2F8D5OFQ46H43DD3S_module5And6Attendance.pdf", + "name": "8D5OFQ46H43DD3S_module5And6Attendance.pdf" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FG8OU2PE919PFKSR_Print+Application11.pdf", + "name": "G8OU2PE919PFKSR_Print Application11.pdf" + } + ], + "selection_procedure_details_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FDZTQQ6YBGBQ47PY_screencapture-onlinesbi-sbi-sbicollect-fsssuccessresponseredirect-htm-2021-07-19-18_", + "name": "DZTQQ6YBGBQ47PY_screencapture-onlinesbi-sbi-sbicollect-fsssuccessresponseredirect-htm-2021-07-19-18_" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FN490PUXJEEN4JZ9_screencapture-onlinesbi-sbi-sbicollect-payment-suvidhapayment-htm-2021-07-19-23_12_3", + "name": "N490PUXJEEN4JZ9_screencapture-onlinesbi-sbi-sbicollect-payment-suvidhapayment-htm-2021-07-19-23_12_3" + } + ], + "company_name": "Make My Trip", + "address": "MakeMyTrip India Pvt. Ltd.5, Awagarh House, MG Road(next to Bachoomal collections)Agra (UP), - 282002India", + "company_type": "Private Sector", + "nature_of_business": "Technology", + "website": "www.makemytrip.com", + "company_details": "This s a very nice company", + "is_company_details_pdf": true, "city": "Mumbai", + "state": "Maharashtra", + "country": "India", + "pin_code": 530013, "city_type": "Domestic", - "compensation": 1200000, - "compensation_details": "", + "designation": "Software Developer", + "description": "very nice job", + "is_description_pdf": true, + "compensation_CTC": 1200000, + "compensation_gross": 1100000, + "compensation_take_home": 1000000, + "compensation_bonus": 10000, + "compensation_details": "very good compensation", + "is_compensation_details_pdf": true, + "bond_details": "nil", + "selection_procedure_rounds": [ + "Resume Shortlisting", + "Technical Interview", + "HR Interview" + ], + "selection_procedure_details": "All rounds are complusory", + "is_selection_procedure_details_pdf": true, + "tier": "4", + "tentative_date_of_joining": "2022-01-15", "allowed_batch": [ - "THIRD", - "FOURTH" + "2018", + "2019" ], "allowed_branch": [ "CSE", - "EE", - "ME" - ], - "attachments": [], - "rounds": [ - "Resume Shortlisting", - "Technical Test", - "Interview" + "EE" ], + "tentative_no_of_offers": 5, + "other_requirements": "above 8 cpi", "additional_info": [ - "school", - "place of study", - "language" + "School", + "Home Town" ], - "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": "" - } + "deadline_date": "2021-12-04", + "created_at": "2021-12-02T20:12:21+05:30" } ], "placementApplication": [ { - "id": "dsdads", - "application_status": "Resume Shortlisting", - "resume": "XB85F4RIGBF5VJN_Cv-Gowtham.pdf", - "additional_info": { - "school": "Delhi Public School", - "language": "Telugu", - "place of study": "Visakhapatnam" + "id": "LLW4STE76GEJYOR", + "placement": { + "id": "Q54IRZZMC3RP8F6", + "company_details_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FI5U4RDTV0OP0EM0_2019+Student+Details+-+Total%28State+Sort%29+-+Copy.pdf", + "name": "I5U4RDTV0OP0EM0_2019 Student Details - Total(State Sort) - Copy.pdf" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FW04JWWNNMIBX0JX_2019+Student+Details+-+Total%28State+Sort%29.pdf", + "name": "W04JWWNNMIBX0JX_2019 Student Details - Total(State Sort).pdf" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FT1BXP98WBT9BHOR_AP0313017732021LL-Driving+Licence+-+Copy.pdf", + "name": "T1BXP98WBT9BHOR_AP0313017732021LL-Driving Licence - Copy.pdf" + } + ], + "description_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FC78TE2Z67BPZ41O_CSE-V-SEM.pdf", + "name": "C78TE2Z67BPZ41O_CSE-V-SEM.pdf" + } + ], + "compensation_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2F8D5OFQ46H43DD3S_module5And6Attendance.pdf", + "name": "8D5OFQ46H43DD3S_module5And6Attendance.pdf" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FG8OU2PE919PFKSR_Print+Application11.pdf", + "name": "G8OU2PE919PFKSR_Print Application11.pdf" + } + ], + "selection_procedure_details_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FDZTQQ6YBGBQ47PY_screencapture-onlinesbi-sbi-sbicollect-fsssuccessresponseredirect-htm-2021-07-19-18_", + "name": "DZTQQ6YBGBQ47PY_screencapture-onlinesbi-sbi-sbicollect-fsssuccessresponseredirect-htm-2021-07-19-18_" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FN490PUXJEEN4JZ9_screencapture-onlinesbi-sbi-sbicollect-payment-suvidhapayment-htm-2021-07-19-23_12_3", + "name": "N490PUXJEEN4JZ9_screencapture-onlinesbi-sbi-sbicollect-payment-suvidhapayment-htm-2021-07-19-23_12_3" + } + ], + "company_name": "Make My Trip", + "address": "MakeMyTrip India Pvt. Ltd.5, Awagarh House, MG Road(next to Bachoomal collections)Agra (UP), - 282002India", + "company_type": "Private Sector", + "nature_of_business": "Technology", + "website": "www.makemytrip.com", + "company_details": "This s a very nice company", + "is_company_details_pdf": true, + "city": "Mumbai", + "state": "Maharashtra", + "country": "India", + "pin_code": 530013, + "city_type": "Domestic", + "designation": "Software Developer", + "description": "very nice job", + "is_description_pdf": true, + "compensation_CTC": 1200000, + "compensation_gross": 1100000, + "compensation_take_home": 1000000, + "compensation_bonus": 10000, + "compensation_details": "very good compensation", + "is_compensation_details_pdf": true, + "bond_details": "nil", + "selection_procedure_rounds": [ + "Resume Shortlisting", + "Technical Interview", + "HR Interview" + ], + "selection_procedure_details": "All rounds are complusory", + "is_selection_procedure_details_pdf": true, + "tier": "4", + "tentative_date_of_joining": "2022-01-15", + "allowed_batch": [ + "2018", + "2019" + ], + "allowed_branch": [ + "CSE", + "EE" + ], + "tentative_no_of_offers": 5, + "other_requirements": "above 8 cpi", + "additional_info": [ + "School", + "Home Town" + ], + "deadline_date": "2021-12-04", + "created_at": "2021-12-02T20:12:21+05:30" }, + "resume_link": "https://storage.googleapis.com/cdc-backend-attachments/resume/LLW4STE76GEJYOR%2F8KIOT3PW1JIS718_CSE-V-SEM.pdf", + "additional_info": "{\"School\": \"Narayana English Medium High School\", \"Home Town\": \"Vizag\"}", "selected": null, - "placement": "fdgdb121" + "applied_at": "2021-12-02T21:58:18.032466+05:30" } - ] + ] } ``` - 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 +- placements: Array of Placement Objects +- placementApplication: Array of Placement Application Objects ### Status Codes @@ -225,8 +372,9 @@ The possible responses for this api request are as follows | Status Codes | Possible Messages | | --------------- | ------------------------ | -| 200 OK | `Resume Added` | -| 400 BAD_REQUEST | `Error Occurred {error}` | +| 200 OK | `Data Found` | +| 404 Not Found | `Student Not Found` | +| 400 BAD_REQUEST | `Something Went Wrong` | You can see some common errors [here](#common-errors) @@ -331,7 +479,7 @@ You can see some common errors [here](#common-errors) ## `api/student/submitApplication/` -This Api is used to submit application to Internships/Placements. +This Api is used to submit application to Placements. ### How to Use? @@ -343,24 +491,23 @@ 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" - } + "opening_type": "Placement", + "opening_id": "Q54IRZZMC3RP8F6", + "resume_file_name": "8KIOT3PW1JIS718_CSE-V-SEM.pdf", + "additional_info": { + "School": "Narayana English Medium High School", + "Home Town": "Vizag" + } } ``` > Headers
> Authorization: "Bearer {tokenID}" -- opening_type: Can be Placement/Internship +- opening_type: Can be Placement - 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. +- additional_info: This is the info which the Placement demands besides the normal user data which has to + asked and sent. These fields can be found in the Placement Details. ### Response @@ -384,12 +531,559 @@ The possible responses for this api request are as follows | ------------- | ---------------------------------- | | 200 OK | `Application Submitted` | | 403 FORBIDDEN | `Application is already Submitted` | +| 403 FORBIDDEN | `Placement Not Approved` | | 404 NOT FOUND | `RESUME_FILE_NAME Not Found` | You can see some common errors [here](#common-errors) --- +# Admin Portal APIs + +## `api/admin/markStatus` + +This Api is used to mark the status for application for a specific placement. + +### How to Use? + +Send a `POST` request to `api/admin/markStatus`
+Request_Body: + +```json +{ + "opening_id": "Q54IRZZMC3RP8F6", + "student_list": [ + { + "student_id":"190010036", + "student_selected":"true" + }, + { + "student_id":"190050022", + "student_selected":"false" + } + ] +} +``` + + + +> Headers
+> Authorization: "Bearer {tokenID}" + +### Response + +Response is a Json with these fields + +```json +{ + "action": "Mark Status", + "message": "Marked Status" +} +``` + +- 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 | `Marked Status` | +| 400 BAD_REQUEST | `Something Went Wrong` | + +You may see some different errors which can be seen [here](#common-errors) + +--- + +## `api/admin/getDashboard` + +This Api is used to get all the placements. + +### How to Use? + +Send a `GET` request to `api/admin/getdashboard`
+ +Request_Body: + +```json +{} +``` + +> Headers
+> Authorization: "Bearer {tokenID}" + +### Response + +Response is a Json with these fields + +```json +{ + "action": "Get Dashboard - Admin", + "message": "Data Found", + "ongoing": [ + { + "id": "Q54IRZZMC3RP8F6", + "company_details_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FI5U4RDTV0OP0EM0_2019+Student+Details+-+Total%28State+Sort%29+-+Copy.pdf", + "name": "I5U4RDTV0OP0EM0_2019 Student Details - Total(State Sort) - Copy.pdf" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FW04JWWNNMIBX0JX_2019+Student+Details+-+Total%28State+Sort%29.pdf", + "name": "W04JWWNNMIBX0JX_2019 Student Details - Total(State Sort).pdf" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FT1BXP98WBT9BHOR_AP0313017732021LL-Driving+Licence+-+Copy.pdf", + "name": "T1BXP98WBT9BHOR_AP0313017732021LL-Driving Licence - Copy.pdf" + } + ], + "description_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FC78TE2Z67BPZ41O_CSE-V-SEM.pdf", + "name": "C78TE2Z67BPZ41O_CSE-V-SEM.pdf" + } + ], + "compensation_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2F8D5OFQ46H43DD3S_module5And6Attendance.pdf", + "name": "8D5OFQ46H43DD3S_module5And6Attendance.pdf" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FG8OU2PE919PFKSR_Print+Application11.pdf", + "name": "G8OU2PE919PFKSR_Print Application11.pdf" + } + ], + "selection_procedure_details_pdf_links": [ + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FDZTQQ6YBGBQ47PY_screencapture-onlinesbi-sbi-sbicollect-fsssuccessresponseredirect-htm-2021-07-19-18_", + "name": "DZTQQ6YBGBQ47PY_screencapture-onlinesbi-sbi-sbicollect-fsssuccessresponseredirect-htm-2021-07-19-18_" + }, + { + "link": "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/Q54IRZZMC3RP8F6%2FN490PUXJEEN4JZ9_screencapture-onlinesbi-sbi-sbicollect-payment-suvidhapayment-htm-2021-07-19-23_12_3", + "name": "N490PUXJEEN4JZ9_screencapture-onlinesbi-sbi-sbicollect-payment-suvidhapayment-htm-2021-07-19-23_12_3" + } + ], + "company_name": "Make My Trip", + "address": "MakeMyTrip India Pvt. Ltd.5, Awagarh House, MG Road(next to Bachoomal collections)Agra (UP), - 282002India", + "company_type": "Private Sector", + "nature_of_business": "Technology", + "website": "www.makemytrip.com", + "company_details": "This s a very nice company", + "is_company_details_pdf": true, + "contact_person_name": "Gowtham", + "phone_number": 9390291911, + "email": "saisurya3127@gmail.com", + "city": "Mumbai", + "state": "Maharashtra", + "country": "India", + "pin_code": 530013, + "city_type": "Domestic", + "designation": "Software Developer", + "description": "very nice job", + "is_description_pdf": true, + "compensation_CTC": 1200000, + "compensation_gross": 1100000, + "compensation_take_home": 1000000, + "compensation_bonus": 10000, + "compensation_details": "very good compensation", + "is_compensation_details_pdf": true, + "bond_details": "nil", + "selection_procedure_rounds": [ + "Resume Shortlisting", + "Technical Interview", + "HR Interview" + ], + "selection_procedure_details": "All rounds are complusory", + "is_selection_procedure_details_pdf": true, + "tier": "4", + "tentative_date_of_joining": "2022-01-15", + "allowed_batch": [ + "2018", + "2019" + ], + "allowed_branch": [ + "CSE", + "EE" + ], + "tentative_no_of_offers": 5, + "other_requirements": "above 8 cpi", + "additional_info": [ + "School", + "Home Town" + ], + "email_verified": false, + "offer_accepted": null, + "deadline_date": "2021-12-04", + "created_at": "2021-12-02T20:12:21+05:30" + } + ], + "previous": [] +} +``` + +- action: Tells us about the message creator
+- message: Tells us what happened with our Request. +- ongoing: Gives us the list of placements that are accepting applications. +- previous: Gives us the list of placements that stopped accepting applications. + + +### Status Codes + +The possible responses for this api request are as follows + +| Status Codes | Possible Messages | +| --------------- | ------------------------ | +| 200 OK | `Data Found` | +| 400 BAD_REQUEST | `Something Went Wrong` | + +You can see some common errors [here](#common-errors) + +--- + +## `api/admin/updateDeadline` + +This Api is used to update deadline for a specific placement. + +### How to Use? + +Send a `POST` request to `api/admin/updateDeadline`
+Request_Body: + +```json +{ + "opening_id": "Q54IRZZMC3RP8F6", + "deadline_datetime": "2021-12-06 16:28:18 +0530" +} +``` + +> Headers
+> Authorization: "Bearer {tokenID}" + +### Response + +Response is a Json with these fields + +```json +{ + "action": "Update Deadline", + "message": "Deadline Updated" +} +``` + +- 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 | `Deadline Updated` | +| 400 BAD_REQUEST | `Something Went Wrong` | + +You may see some different errors which can be seen [here](#common-errors) + +--- + +## `api/admin/updateOfferAccepted` + +This Api is used to update offer verification state for a specific placement. + +### How to Use? + +Send a `POST` request to `api/admin/updateOfferAccepted`
+Request_Body: + +```json +{ + "opening_id": "Q54IRZZMC3RP8F6", + "offer_accepted": "true" +} +``` + +> Headers
+> Authorization: "Bearer {tokenID}" + +### Response + +Response is a Json with these fields + +```json +{ + "action": "Update Offer Accepted", + "message": "Offer Accepted Updated" +} +``` + +- 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 | `Update Offer Accepted` | +| 400 BAD_REQUEST | `Something Went Wrong` | + +You may see some different errors which can be seen [here](#common-errors) + +--- + +## `api/admin/updateEmailVerified` + +This Api is used to update email verification state for a specific placement. + +### How to Use? + +Send a `POST` request to `api/admin/updateEmailVerified`
+Request_Body: + +```json +{ + "opening_id": "Q54IRZZMC3RP8F6", + "email_verified": "false" +} +``` + +> Headers
+> Authorization: "Bearer {tokenID}" + +### Response + +Response is a Json with these fields + +```json +{ + "action": "Update Email Verified", + "message": "Email Verified Updated" +} +``` + +- 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 | `Email Verified Updated` | +| 400 BAD_REQUEST | `Something Went Wrong` | + +You may see some different errors which can be seen [here](#common-errors) + +--- + +## `api/admin/updateAdditionalInfo` + +This Api is used to update additional_info for a specific placement. + +### How to Use? + +Send a `POST` request to `api/admin/updateAdditionalInfo`
+Request_Body: + +```json +{ + "opening_id": "Q54IRZZMC3RP8F6", + "additional_info": [ + "School", + "Place of Living", + "Research Interests" + ] +} +``` + +> Headers
+> Authorization: "Bearer {tokenID}" + +### Response + +Response is a Json with these fields + +```json +{ + "action": "Update Additional Info", + "message": "Additional Info Updated" +} +``` + +- 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 | `Additional Info Updated` | +| 400 BAD_REQUEST | `Something Went Wrong` | + +You may see some different errors which can be seen [here](#common-errors) + +--- + +# Company Portal APIs + +## `api/company/addPlacement/` + +This Api is used to add placements. + +### How to Use? + +Send a `POST` request to `api/company/addPlacement`
+ +Request_Body: + +```json +{ + "company_name": [ + "Make My Trip" + ], + "address": [ + "MakeMyTrip India Pvt. Ltd.\n5, Awagarh House, MG Road\n(next to Bachoomal collections)\nAgra (UP), - 282002\nIndia" + ], + "company_type": [ + "Private Sector" + ], + "nature_of_business": [ + "Technology" + ], + "website": [ + "www.makemytrip.com" + ], + "company_details": [ + "This s a very nice company" + ], + "is_company_details_pdf": [ + "true" + ], + "contact_person_name": [ + "Gowtham" + ], + "phone_number": [ + "9390291911" + ], + "email": [ + "saisurya3127@gmail.com" + ], + "city": [ + "Mumbai" + ], + "state": [ + "Maharashtra" + ], + "country": [ + "India" + ], + "pincode": [ + "530013" + ], + "designation": [ + "Software Developer" + ], + "description": [ + "very nice job" + ], + "is_description_pdf": [ + "true" + ], + "compensation_ctc": [ + "1200000" + ], + "compensation_gross": [ + "1100000" + ], + "compensation_take_home": [ + "1000000" + ], + "compensation_bonus": [ + "10000" + ], + "compensation_details": [ + "very good compensation" + ], + "is_compensation_details_pdf": [ + "true" + ], + "bond_details": [ + "nil" + ], + "selection_procedure_rounds": [ + "['Resume Shortlisting', 'Technical Interview', 'HR Interview']" + ], + "selection_procedure_details": [ + "All rounds are complusory" + ], + "is_selection_procedure_details_pdf": [ + "true" + ], + "tentative_date_of_joining": [ + "15-01-2022" + ], + "allowed_branch": [ + "['CSE', 'EE']" + ], + "tentative_no_of_offers": [ + "5" + ], + "other_requirements": [ + "above 8 cpi" + ], + "company_details_pdf": [ + "__FILE_OBJECT__", + "__FILE_OBJECT__" + ], + "description_pdf": [ + "__FILE_OBJECT__" + ], + "compensation_details_pdf": [ + "__FILE_OBJECT__" + ], + "selection_procedure_details_pdf": [ + "__FILE_OBJECT__", + "__FILE_OBJECT__" + ] +} +``` + +### Response + +Response is a Json with these fields + +```json +{ + "action": "Add Placement", + "message": "Placement Added Successfully" +} +``` + +> 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 | `Placement Added Successfully` | +| 404 NOT FOUND | `Something went wrong` | + +You can see some common errors [here](#common-errors) + +--- + ## `Common Errors` Some common errors that you may see while accessing the Apis @@ -400,4 +1094,9 @@ Some common errors that you may see while accessing the Apis | 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. | +| 400 BAD_REQUEST | `Error Occurred` | Any random Error which can be seen in the {error} string. | +| 400 BAD_REQUEST | `Something went wrong` | Any random Error which can be seen in the {error} string. | + + + + From bb3d57a33705eec20c455ea5cfc3fac3de7f9939 Mon Sep 17 00:00:00 2001 From: gowtham Date: Sun, 12 Dec 2021 19:14:27 +0530 Subject: [PATCH 4/6] Added Few More Apis and corrected a view --- CDC_Backend/APIs/admin.py | 36 ++++++- CDC_Backend/APIs/adminUrls.py | 4 +- CDC_Backend/APIs/adminViews.py | 146 +++++++++++++++++++++++++++- CDC_Backend/APIs/constants.py | 7 +- CDC_Backend/APIs/serializers.py | 16 +++ CDC_Backend/APIs/studentViews.py | 2 +- CDC_Backend/APIs/utils.py | 2 +- CDC_Backend/CDC_Backend/settings.py | 30 +++--- nginx.conf | 92 ++++++++++++++++++ requirements.txt | 76 ++++++++------- setup.sh | 22 +++++ 11 files changed, 375 insertions(+), 58 deletions(-) create mode 100755 nginx.conf create mode 100644 setup.sh diff --git a/CDC_Backend/APIs/admin.py b/CDC_Backend/APIs/admin.py index 4c26bd9..eb4711c 100644 --- a/CDC_Backend/APIs/admin.py +++ b/CDC_Backend/APIs/admin.py @@ -1,9 +1,41 @@ from django.contrib import admin from .models import * +from django.contrib import admin +from django.contrib.admin.templatetags.admin_urls import admin_urlname +from django.shortcuts import resolve_url +from django.utils.html import format_html +from django.utils.safestring import SafeText +from .models import * admin.site.register(User) -admin.site.register(Student) +# admin.site.register(Student) admin.site.register(Admin) -admin.site.register(Placement) +# admin.site.register(Placement) admin.site.register(PlacementApplication) admin.site.register(PrePlacementOffer) + +admin.site.site_header = "CDC Recruitment Portal" + + +def model_admin_url(obj, name=None) -> str: + url = resolve_url(admin_urlname(obj._meta, SafeText("change")), obj.pk) + return format_html('{}', url, name or str(obj)) + + +@admin.register(Student) +class Student(admin.ModelAdmin): + list_display = ("roll_no", "name", "batch", "branch", "phone_number") + search_fields = ("roll_no", "name","phone_number") + ordering = ("roll_no", "name", "batch", "branch", "phone_number") + list_filter = ("batch", "branch") + + +@admin.register(Placement) +class Placement(admin.ModelAdmin): + list_display = (COMPANY_NAME, CONTACT_PERSON_NAME, PHONE_NUMBER, 'tier', 'compensation_CTC') + search_fields = (COMPANY_NAME, CONTACT_PERSON_NAME) + ordering = (COMPANY_NAME, CONTACT_PERSON_NAME, 'tier', 'compensation_CTC') + list_filter = ('tier',) + + + diff --git a/CDC_Backend/APIs/adminUrls.py b/CDC_Backend/APIs/adminUrls.py index 0dbe3b3..bd3d6e5 100644 --- a/CDC_Backend/APIs/adminUrls.py +++ b/CDC_Backend/APIs/adminUrls.py @@ -9,5 +9,7 @@ urlpatterns = [ path('updateOfferAccepted/', adminViews.updateOfferAccepted, name="Update Offer Accepted"), path('updateEmailVerified', adminViews.updateEmailVerified, name="Update Email Verified"), path('updateAdditionalInfo/', adminViews.updateAdditionalInfo, name="Update Additional Info"), - + path('getApplications/', adminViews.getApplications, name="Get Applications"), + path("submitApplication/", adminViews.submitApplication, name="Submit Application"), + path('generateCSV/', adminViews.generateCSV, name="Generate CSV"), ] diff --git a/CDC_Backend/APIs/adminViews.py b/CDC_Backend/APIs/adminViews.py index 374266a..845ecef 100644 --- a/CDC_Backend/APIs/adminViews.py +++ b/CDC_Backend/APIs/adminViews.py @@ -1,8 +1,9 @@ +import json from datetime import datetime from .utils import * from rest_framework.decorators import api_view - +import csv from .serializers import * @@ -152,4 +153,147 @@ def updateAdditionalInfo(request, id, email, user_type): except: logger.warning("Update Additional Info: " + str(sys.exc_info())) return Response({'action': "Update Additional Info", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) + + +@api_view(['GET']) +@isAuthorized([ADMIN]) +@precheck([OPENING_ID]) +def getApplications(request, id, email, user_type): + try: + data = request.GET + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + applications = PlacementApplication.objects.filter(placement=opening) + serializer = PlacementApplicationSerializerForAdmin(applications, many=True) + return Response({'action': "Get Applications", 'message': 'Data Found', 'applications':serializer.data}, + status=status.HTTP_200_OK) + except Http404: + return Response({'action': "Get Applications", 'message': 'Opening Not Found'}, + status=status.HTTP_404_NOT_FOUND) + except: + logger.warning("Get Applications: " + str(sys.exc_info())) + return Response({'action': "Get Applications", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) + + + +@api_view(['POST']) +@isAuthorized(allowed_users=[ADMIN]) +@precheck(required_data=[OPENING_TYPE, OPENING_ID, RESUME_FILE_NAME, + STUDENT_ID]) +def submitApplication(request, id, email, user_type): + try: + data = request.data + student = get_object_or_404(Student, roll_no=data[STUDENT_ID]) + # Only Allowing Applications for Placements + if data[OPENING_TYPE] == PLACEMENT: + if not len(PlacementApplication.objects.filter( + student_id=student.id, placement_id=data[OPENING_ID])): + application = PlacementApplication() + opening = get_object_or_404(Placement, id=data[OPENING_ID], + allowed_batch__contains=[student.batch], + allowed_branch__contains=[student.branch], + deadline_datetime__gte=datetime.now().date() + ) + if not opening.offer_accepted or not opening.email_verified: + raise PermissionError("Placement Not Approved") + + cond_stat, cond_msg = PlacementApplicationConditions(student, opening) + if not cond_stat: + raise PermissionError(cond_msg) + application.placement = opening + else: + raise PermissionError("Application is already Submitted") + else: + raise ValueError(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() + additional_info = {} + for i in opening.additional_info: + if i not in data[ADDITIONAL_INFO]: + raise AttributeError(i + " not found in Additional Info") + else: + additional_info[i] = data[ADDITIONAL_INFO][i] + + application.additional_info = json.dumps(additional_info) + data = { + "name": student.name, + "company_name": opening.company_name, + "application_type": data[OPENING_TYPE], + "additional_info": dict(json.loads(application.additional_info)), + } + subject = STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT.format(company_name=opening.company_name) + student_email = str(student.roll_no)+"@iitdh.ac.in" + sendEmail(student_email, subject, data, STUDENT_APPLICATION_SUBMITTED_TEMPLATE) + + application.save() + return Response({'action': "Submit Application", 'message': "Application Submitted"}, + status=status.HTTP_200_OK) + except Http404 as e: + return Response({'action': "Submit Application", 'message': str(e)}, + status=status.HTTP_404_NOT_FOUND) + 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': "Something Went Wrong"}, + status=status.HTTP_400_BAD_REQUEST) + + + +@api_view(['POST']) +@isAuthorized(allowed_users=[ADMIN]) +@precheck(required_data=[OPENING_ID]) +def generateCSV(request, id, email, user_type): + try: + data = request.data + placement = get_object_or_404(Placement, id=data[OPENING_ID]) + applications = PlacementApplication.objects.filter(placement=placement) + filename = generateRandomString() + if not os.path.isdir(STORAGE_DESTINATION_APPLICATION_CSV): + os.mkdir(STORAGE_DESTINATION_APPLICATION_CSV) + destination_path = STORAGE_DESTINATION_APPLICATION_CSV + filename + ".csv" + f = open(destination_path, 'w') + writer = csv.writer(f) + header_row = APPLICATION_CSV_COL_NAMES.copy() + + header_row.extend(placement.additional_info) + writer.writerow(header_row) + for apl in applications: + row_details=[] + + row_details.append(apl.applied_at) + row_details.append(apl.student.roll_no) + row_details.append(apl.student.name) + row_details.append(str(apl.student.roll_no)+"@iitdh.ac.in") + row_details.append(apl.student.phone_number) + row_details.append(apl.student.branch) + row_details.append(apl.student.batch) + row_details.append(apl.student.cpi) + link = LINK_TO_STORAGE_RESUME + urllib.parse.quote_plus(apl.student.id + "/" + apl.resume) + row_details.append(link) + row_details.append(apl.selected) + + for i in placement.additional_info: + row_details.append(json.loads(apl.additional_info)[i]) + + writer.writerow(row_details) + f.close() + file_path = LINK_TO_APPLICATIONS_CSV + urllib.parse.quote_plus(filename+".csv") + return Response({'action': "Create csv", 'message': "CSV created", 'file': file_path}, + status=status.HTTP_200_OK) + except: + logger.warning("Create csv: " + str(sys.exc_info())) + print(sys.exc_info()) + return Response({'action': "Create csv", 'message': "Error Occurred"}, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/CDC_Backend/APIs/constants.py b/CDC_Backend/APIs/constants.py index fa9790e..4201ce9 100644 --- a/CDC_Backend/APIs/constants.py +++ b/CDC_Backend/APIs/constants.py @@ -42,6 +42,7 @@ CLIENT_ID = "956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleuserconten PLACEMENT_OPENING_URL = "https://www.googleapis.com/auth/adwords/{id}" LINK_TO_STORAGE_COMPANY_ATTACHMENT = "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/" LINK_TO_STORAGE_RESUME = "https://storage.googleapis.com/cdc-backend-attachments/resume/" +LINK_TO_APPLICATIONS_CSV = "https://storage.googleapis.com/cdc-backend-attachments/applications-csv/" TOKEN = "token_id" @@ -58,6 +59,8 @@ MAX_OFFERS_PER_STUDENT = 2 STORAGE_DESTINATION_RESUMES = "./Storage/Resumes/" STORAGE_DESTINATION_COMPANY_ATTACHMENTS = './Storage/Company_Attachments/' +STORAGE_DESTINATION_APPLICATION_CSV = './Storage/Application_CSV/' + RESUME_FILE_NAME = 'resume_file_name' @@ -120,8 +123,6 @@ STUDENT_ID = "student_id" STUDENT_SELECTED = "student_selected" - - COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT = "Notification Submitted - {id} - CDC IIT Dharwad" STUDENT_APPLICATION_STATUS_TEMPLATE_SUBJECT = 'Application Status : {company_name} - {id}' STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT = 'CDC - Application Submitted - {company_name}' @@ -131,3 +132,5 @@ COMPANY_OPENING_SUBMITTED_TEMPLATE = 'company_opening_submitted.html' STUDENT_APPLICATION_STATUS_SELECTED_TEMPLATE = 'student_application_status_selected.html' STUDENT_APPLICATION_STATUS_NOT_SELECTED_TEMPLATE = 'student_application_status_not_selected.html' +APPLICATION_CSV_COL_NAMES = ['Applied At', 'Roll No.', 'Name', 'Email', 'Phone Number', 'Branch', 'Batch', 'CPI', + 'Resume', 'Selected', ] diff --git a/CDC_Backend/APIs/serializers.py b/CDC_Backend/APIs/serializers.py index 787de1e..a7c1228 100644 --- a/CDC_Backend/APIs/serializers.py +++ b/CDC_Backend/APIs/serializers.py @@ -160,3 +160,19 @@ class PlacementApplicationSerializer(serializers.ModelSerializer): class Meta: model = PlacementApplication exclude = [STUDENT, 'resume'] + +class PlacementApplicationSerializerForAdmin(serializers.ModelSerializer): + student_details = serializers.SerializerMethodField() + resume_link = serializers.SerializerMethodField() + + def get_student_details(self, obj): + data = StudentSerializer(obj.student).data + return data + + def get_resume_link(self, obj): + link = LINK_TO_STORAGE_RESUME + urllib.parse.quote_plus(obj.id + "/" + obj.resume) + return link + + class Meta: + model = PlacementApplication + exclude = ['placement', 'resume'] \ No newline at end of file diff --git a/CDC_Backend/APIs/studentViews.py b/CDC_Backend/APIs/studentViews.py index 329f5c3..0ea1824 100644 --- a/CDC_Backend/APIs/studentViews.py +++ b/CDC_Backend/APIs/studentViews.py @@ -178,7 +178,7 @@ def submitApplication(request, id, email, user_type): return Response({'action': "Submit Application", 'message': "Application Submitted"}, status=status.HTTP_200_OK) except Http404 as e: - return Response({'action': "Submit Application", 'message': "Student Not Found"}, + return Response({'action': "Submit Application", 'message': str(e)}, status=status.HTTP_404_NOT_FOUND) except PermissionError as e: return Response({'action': "Submit Application", 'message': str(e)}, diff --git a/CDC_Backend/APIs/utils.py b/CDC_Backend/APIs/utils.py index d7ea9fb..496d52c 100644 --- a/CDC_Backend/APIs/utils.py +++ b/CDC_Backend/APIs/utils.py @@ -103,7 +103,7 @@ def isAuthorized(allowed_users=None): def generateRandomString(): try: N = 15 - res = ''.join(random.choices(string.ascii_uppercase + string.digits, k=N)) + res = ''.join(random.choices(string.ascii_uppercase +string.ascii_lowercase+ string.digits, k=N)) return res except: return False diff --git a/CDC_Backend/CDC_Backend/settings.py b/CDC_Backend/CDC_Backend/settings.py index 7c0a6f1..8219a0f 100644 --- a/CDC_Backend/CDC_Backend/settings.py +++ b/CDC_Backend/CDC_Backend/settings.py @@ -11,7 +11,9 @@ https://docs.djangoproject.com/en/2.2/ref/settings/ """ import os +from dotenv import load_dotenv +load_dotenv("../dev.env") # import django_heroku # Build paths inside the project like this: os.path.join(BASE_DIR, ...) @@ -81,19 +83,15 @@ WSGI_APPLICATION = 'CDC_Backend.wsgi.application' # 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', + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.environ.get("DB_NAME"), + 'USER': os.environ.get("DB_USER"), + 'PASSWORD': os.environ.get("DB_PASSWORD"), + 'HOST': os.environ.get("DB_HOST"), + 'PORT': os.environ.get("DB_PORT"), }, + # 'default': { # 'ENGINE': 'django.db.backends.postgresql_psycopg2', # 'NAME': 'd84i5cbjig5rrf', @@ -160,8 +158,8 @@ 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 = 'yeylqcnsyjfpzsew'#'password here' +EMAIL_HOST_USER = os.environ.get("EMAIL") # 'email here' +EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_PASSWORD") # 'password here' LOGGING = { 'version': 1, @@ -179,6 +177,11 @@ LOGGING = { 'level': 'DEBUG', 'class': 'django_db_logger.db_log_handler.DatabaseLogHandler' }, + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler', + 'include_html': True, + } }, 'loggers': { 'db': { @@ -188,5 +191,4 @@ LOGGING = { } } - # django_heroku.settings(locals()) diff --git a/nginx.conf b/nginx.conf new file mode 100755 index 0000000..2bf79c2 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,92 @@ + +#user nobody; +worker_processes 1; + +error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' + # '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log logs/access.log; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #gzip on; + + server { + listen 80; + server_name localhost; + + # listen 443 ssl; + # server_name localhost; + + # ssl_certificate cert.pem; + # ssl_certificate_key cert.key; + + #charset koi8-r; + + #access_log logs/host.access.log main; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + # Server static files /storage + location /storage { + alias '/home/gowtham/Gowtham/Shared Projects/cdc-placement-website-backend/CDC_Backend/Storage'; +# autoindex on; + } + + location /static { +# autoindex on; + alias '/home/gowtham/Gowtham/Shared Projects/cdc-placement-website-backend/CDC_Backend/static'; + } + + location /api/ { + proxy_pass http://localhost:8000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + } + location /admin/ { + proxy_pass http://localhost:8000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + } + + + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } +} + +} diff --git a/requirements.txt b/requirements.txt index f17d5c0..0d32630 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,36 +1,40 @@ -asgiref==3.4.1 -astroid==2.9.0 -cachetools==4.2.4 -certifi==2021.10.8 -chardet==4.0.0 -charset-normalizer==2.0.8 -colorama==0.4.4 -dj-database-url==0.5.0 -Django==3.2.9 -django-background-tasks==1.2.5 -django-compat==1.0.15 -django-cors-headers==3.10.0 -django-db-logger==0.1.11 -djangorestframework==3.12.4 -google-auth==2.3.3 -idna==3.3 -isort==5.10.1 -jsonfield==3.1.0 -lazy-object-proxy==1.6.0 -Markdown==3.3.6 -mccabe==0.6.1 -platformdirs==2.4.0 -psycopg2==2.9.2 -pyasn1==0.4.8 -pyasn1-modules==0.2.8 -pylint==2.12.1 -pytz==2021.3 -requests==2.26.0 -rsa==4.8 -six==1.16.0 -sqlparse==0.4.2 -toml==0.10.2 -typing-extensions==4.0.0 -urllib3==1.26.7 -whitenoise==5.3.0 -wrapt==1.13.3 +asgiref==3.4.1 +astroid==2.9.0 +cachetools==4.2.4 +certifi==2021.10.8 +chardet==4.0.0 +charset-normalizer==2.0.8 +colorama==0.4.4 +dj-database-url==0.5.0 +Django==3.2.9 +django-background-tasks==1.2.5 +django-compat==1.0.15 +django-cors-headers==3.10.0 +django-db-logger==0.1.11 +djangorestframework==3.12.4 +google-auth==2.3.3 +gunicorn==20.1.0 +idna==3.3 +importlib-metadata==4.8.2 +isort==5.10.1 +jsonfield==3.1.0 +lazy-object-proxy==1.6.0 +Markdown==3.3.6 +mccabe==0.6.1 +platformdirs==2.4.0 +psycopg2-binary==2.9.2 +pyasn1==0.4.8 +pyasn1-modules==0.2.8 +pylint==2.12.1 +python-dotenv==0.19.2 +pytz==2021.3 +requests==2.26.0 +rsa==4.8 +six==1.16.0 +sqlparse==0.4.2 +toml==0.10.2 +typing_extensions==4.0.0 +urllib3==1.26.7 +whitenoise==5.3.0 +wrapt==1.13.3 +zipp==3.6.0 diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..86a2bc3 --- /dev/null +++ b/setup.sh @@ -0,0 +1,22 @@ +python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt +echo "Environment setup complete" +cd CDC_Backend + +python3 manage.py flush --no-input +python3 manage.py makemigrations +python3 manage.py migrate +echo "Migrations complete" + +python3 manage.py collectstatic --noinput + +DIR="./Storage" +if [ -d "$DIR" ]; then + ### Take action if $DIR exists ### + echo "${DIR} Directory already exists" +else + ### Control will jump here if $DIR does NOT exists ### + echo "Creating ${DIR} Directory..." + mkdir ${DIR} + echo "${DIR} Directory Created" +fi + From 0792becea5f6066170f0e728a3db5c80e6f0bd9b Mon Sep 17 00:00:00 2001 From: gowtham Date: Sun, 12 Dec 2021 19:19:50 +0530 Subject: [PATCH 5/6] Updated requirements.txt --- requirements.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0d32630..674266d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,13 +3,13 @@ astroid==2.9.0 cachetools==4.2.4 certifi==2021.10.8 chardet==4.0.0 -charset-normalizer==2.0.8 +charset-normalizer==2.0.9 colorama==0.4.4 dj-database-url==0.5.0 -Django==3.2.9 +Django==4.0 django-background-tasks==1.2.5 django-compat==1.0.15 -django-cors-headers==3.10.0 +django-cors-headers==3.10.1 django-db-logger==0.1.11 djangorestframework==3.12.4 google-auth==2.3.3 @@ -25,7 +25,7 @@ platformdirs==2.4.0 psycopg2-binary==2.9.2 pyasn1==0.4.8 pyasn1-modules==0.2.8 -pylint==2.12.1 +pylint==2.12.2 python-dotenv==0.19.2 pytz==2021.3 requests==2.26.0 @@ -33,7 +33,7 @@ rsa==4.8 six==1.16.0 sqlparse==0.4.2 toml==0.10.2 -typing_extensions==4.0.0 +typing_extensions==4.0.1 urllib3==1.26.7 whitenoise==5.3.0 wrapt==1.13.3 From d3a6e5a63a307600beb0576c0f82d8101095b412 Mon Sep 17 00:00:00 2001 From: gowtham Date: Sun, 12 Dec 2021 19:40:52 +0530 Subject: [PATCH 6/6] Updated requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 674266d..7771af7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ chardet==4.0.0 charset-normalizer==2.0.9 colorama==0.4.4 dj-database-url==0.5.0 -Django==4.0 +Django==3.2.10 django-background-tasks==1.2.5 django-compat==1.0.15 django-cors-headers==3.10.1