diff --git a/CDC_Backend/CDC_Backend/settings.py b/CDC_Backend/CDC_Backend/settings.py index 2447ee0..4f04556 100644 --- a/CDC_Backend/CDC_Backend/settings.py +++ b/CDC_Backend/CDC_Backend/settings.py @@ -41,6 +41,7 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'APIs', + 'internAPIs', 'rest_framework', 'corsheaders', 'django_db_logger', diff --git a/CDC_Backend/internAPIs/__init__.py b/CDC_Backend/internAPIs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/CDC_Backend/internAPIs/admin.py b/CDC_Backend/internAPIs/admin.py new file mode 100644 index 0000000..e0f38d1 --- /dev/null +++ b/CDC_Backend/internAPIs/admin.py @@ -0,0 +1,10 @@ +from django.contrib import admin + +# Register your models here. +from .models import * + +admin.site.register(Internship) + +admin.site.register(Season) + +admin.site.register(InternshipApplication) \ No newline at end of file diff --git a/CDC_Backend/internAPIs/apps.py b/CDC_Backend/internAPIs/apps.py new file mode 100644 index 0000000..d8cd54d --- /dev/null +++ b/CDC_Backend/internAPIs/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class InternapisConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'internAPIs' diff --git a/CDC_Backend/internAPIs/constants.py b/CDC_Backend/internAPIs/constants.py new file mode 100644 index 0000000..58e9431 --- /dev/null +++ b/CDC_Backend/internAPIs/constants.py @@ -0,0 +1,189 @@ +import os + +BRANCH_CHOICES = [ + ["CSE", "CSE"], + ["EE", "EE"], + ["ME", "ME"], + ['MMAE', 'MMAE'], + ['EP', 'EP'], +] +BRANCHES = [ + "CSE", + "EE", + "MMAE", + "EP" +] +BATCH_CHOICES = [ + ["2021", "2021"], + ["2020", "2020"], + ["2019", "2019"], + ["2018", "2018"], + ["2017", "2017"], +] + +OFFER_CITY_TYPE = [ + ['Domestic', 'Domestic'], + ['International', 'International'] +] + +FACILITIES_PROVIDED = [ + ['Accommodation', 'Accommodation'], + ['Food', 'Food'], + ['Transport', 'Transport'], + ['Medical', 'Medical'], +] + +TOTAL_FACILITIES = 4 + +TIERS = [ + ['psu', 'PSU'], + ['1', 'Tier 1'], + ['2', 'Tier 2'], + ['3', 'Tier 3'], + ['4', 'Tier 4'], + ['5', 'Tier 5'], + ['6', 'Tier 6'], + ['7', 'Tier 7'], +] + +SEASON_CHOICES = ( + ('summer', 'Summer'), + ('winter', 'Winter'), + ('monsoon', 'Monsoon'), + ('spring', 'Spring'), + ) + +DEGREE_CHOICES = [ + ['bTech', 'B.Tech'], + ['ms/phd', 'MS/ PhD'], +] + +TOTAL_BRANCHES = 4 # Total No of Branches +TOTAL_BATCHES = 5 # Total No of Batches + +CDC_MAIl_ADDRESS = 'cdc@iitdh.ac.in' + +# To be Configured Properly +CLIENT_ID = os.environ.get('GOOGLE_OAUTH_CLIENT_ID') # Google Login Client ID + +# To be Configured Properly +PLACEMENT_OPENING_URL = "https://cdc.iitdh.ac.in/portal/student/dashboard/placements/{id}" # On frontend, this is the URL to be opened +LINK_TO_STORAGE_COMPANY_ATTACHMENT = "https://cdc.iitdh.ac.in/storage/Company_Attachments/" +LINK_TO_STORAGE_RESUME = "https://cdc.iitdh.ac.in/storage/Resumes/" +LINK_TO_APPLICATIONS_CSV = "https://cdc.iitdh.ac.in/storage/Application_CSV/" +LINK_TO_EMAIl_VERIFICATION_API = "https://cdc.iitdh.ac.in/portal/company/verifyEmail?token={token}" +PDF_FILES_SERVING_ENDPOINT = 'https://cdc.iitdh.ac.in/storage/Company_Attachments/' # TODO: Change this to actual URL + +EMAIL = "email" + +STUDENT = 'student' +ADMIN = 'admin' +SUPER_ADMIN = 's_admin' +COMPANY = 'company' +TIER = 'tier' +# To be Configured Properly +FOURTH_YEAR = '2020' +MAX_OFFERS_PER_STUDENT = 2 +MAX_RESUMES_PER_STUDENT = 3 +EMAIL_VERIFICATION_TOKEN_TTL = 48 # in hours +JNF_TEXT_MAX_CHARACTER_COUNT = 100 +JNF_TEXTMEDIUM_MAX_CHARACTER_COUNT = 200 +JNF_TEXTAREA_MAX_CHARACTER_COUNT = 1000 +JNF_SMALLTEXT_MAX_CHARACTER_COUNT = 50 + +STORAGE_DESTINATION_RESUMES = "./Storage/Resumes/" +STORAGE_DESTINATION_COMPANY_ATTACHMENTS = './Storage/Company_Attachments/' +STORAGE_DESTINATION_APPLICATION_CSV = './Storage/Application_CSV/' + +TOKEN = 'token' +RESUME_FILE_NAME = 'resume_file_name' + +APPLICATION_ID = "application_id" +OPENING_ID = "opening_id" +ADDITIONAL_INFO = "additional_info" +FIELD = "field" + +STATUS_ACCEPTING_APPLICATIONS = "Accepting Applications" + +PLACEMENT = "Placement" + +COMPANY_NAME = "company_name" +ADDRESS = "address" +COMPANY_TYPE = "company_type" +NATURE_OF_BUSINESS = "nature_of_business" +TYPE_OF_ORGANISATION = "type_of_organisation" +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' +JOB_LOCATION = 'job_location' +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' +RS_ELIGIBLE = 'rs_eligible' +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' +RECAPTCHA_VALUE = 'recaptchakey' + +STUDENT_LIST = "student_list" +STUDENT_ID = "student_id" +STUDENT_SELECTED = "student_selected" + +EXCLUDE_IN_PDF = ['id', 'is_company_details_pdf', 'offer_accepted', 'is_description_pdf', + 'is_compensation_details_pdf', 'is_selection_procedure_details_pdf', + 'email_verified', 'created_at'] +SPECIAL_FORMAT_IN_PDF = ['website', 'company_details_pdf_names', 'description_pdf_names', + 'compensation_details_pdf_names', + 'selection_procedure_details_pdf_names'] + +COMPANY_OPENING_ERROR_TEMPLATE = "Alert! Error submitting opening for {company_name}." +COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT = "Notification Submitted - {id} - Career Development Cell, IIT Dharwad" +STUDENT_APPLICATION_STATUS_TEMPLATE_SUBJECT = 'Application Status - {company_name} - {id}' +STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT = 'CDC - Application Submitted - {company_name}' +STUDENT_APPLICATION_UPDATED_TEMPLATE_SUBJECT = 'CDC - Application Updated - {company_name}' +COMPANY_EMAIl_VERIFICATION_TEMPLATE_SUBJECT = 'Email Verification - Career Development Cell, IIT Dharwad' +NOTIFY_STUDENTS_OPENING_TEMPLATE_SUBJECT = 'Placement Opportunity at {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' +STUDENT_APPLICATION_UPDATED_TEMPLATE = 'student_application_updated.html' +COMPANY_EMAIL_VERIFICATION_TEMPLATE = 'company_email_verification.html' +COMPANY_JNF_RESPONSE_TEMPLATE = 'company_jnf_response.html' +NOTIFY_STUDENTS_OPENING_TEMPLATE = 'notify_students_new_opening.html' + +APPLICATION_CSV_COL_NAMES = ['Applied At', 'Roll No.', 'Name', 'Email', 'Phone Number', 'Branch', 'Batch', 'CPI', + 'Resume', 'Selected', ] + diff --git a/CDC_Backend/internAPIs/models.py b/CDC_Backend/internAPIs/models.py new file mode 100644 index 0000000..a8da702 --- /dev/null +++ b/CDC_Backend/internAPIs/models.py @@ -0,0 +1,186 @@ +from django.db import models + +# Create your models here. +from django.contrib.postgres.fields import ArrayField +from django.db import models +from django.utils import timezone +from simple_history.models import HistoricalRecords + +from .constants import * + +#import models from other apps +from APIs.models import User,Student + +# Create your models here. +class Internship(models.Model): + id = models.CharField(blank=False, primary_key=True, max_length=15) #unique id for each internship + # Company Details + company_name = models.CharField(blank=False, max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT) + address = models.CharField(blank=False, max_length=JNF_TEXTAREA_MAX_CHARACTER_COUNT) + company_type = models.CharField(blank=False, max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT) + nature_of_business = models.CharField(blank=False, max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, default="") + type_of_organisation = models.CharField(max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, default="", blank=False) + website = models.CharField(blank=True, max_length=JNF_TEXT_MAX_CHARACTER_COUNT) + company_details = models.CharField(max_length=JNF_TEXTAREA_MAX_CHARACTER_COUNT, default=None, null=True, blank=True) + company_details_pdf_names = ArrayField( + models.CharField(null=True, default=None, max_length=JNF_TEXT_MAX_CHARACTER_COUNT), size=5, + default=list, blank=True) + is_company_details_pdf = models.BooleanField(blank=False, default=False) + #Company Address + city = models.CharField(blank=False, max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, default="") + state = models.CharField(blank=False, max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, default="") + country = models.CharField(blank=False, max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, default="") + pin_code = models.IntegerField(blank=False, default=None, null=True) + # selection process + selection_procedure_rounds = ArrayField( + models.CharField(null=True, default=None, max_length=JNF_TEXT_MAX_CHARACTER_COUNT), size=10, + default=list, blank=True) + selection_procedure_details = models.CharField(blank=True, max_length=JNF_TEXTAREA_MAX_CHARACTER_COUNT) + selection_procedure_details_pdf_names = ArrayField( + models.CharField(null=True, default=None, max_length=JNF_TEXT_MAX_CHARACTER_COUNT), + size=5, default=list, blank=True) + is_selection_procedure_details_pdf = models.BooleanField(blank=False, default=False) + #Internship Details + description_pdf_names = ArrayField( + models.CharField(null=True, default=None, max_length=JNF_TEXT_MAX_CHARACTER_COUNT), size=5, default=list, + blank=True) + is_description_pdf = models.BooleanField(blank=False, default=False) + description = models.CharField(blank=False, max_length=JNF_TEXTAREA_MAX_CHARACTER_COUNT, default=None, null=True) + interning_period_from = models.DateField(blank=False, default=None, null=True) + interning_period_to = models.DateField(blank=False, default=None, null=True) + is_work_from_home = models.BooleanField(blank=False, default=False) + sophomore_eligible = models.BooleanField(blank=False, default=False) + tentative_no_of_offers = models.IntegerField(blank=False, default=None, null=True) + stipend_description_pdf_names=ArrayField( + models.CharField(null=True, default=None, max_length=JNF_TEXT_MAX_CHARACTER_COUNT), size=5, default=list, + blank=True) + is_stipend_description_pdf = models.BooleanField(blank=False, default=False) + stipend=models.IntegerField(blank=False, default=None, null=True) + facilities_provided=ArrayField( + models.CharField(choices=FACILITIES_PROVIDED, blank=False, max_length=20), + size=TOTAL_FACILITIES, + default=list + ) + additional_facilities = models.CharField(blank=True, max_length=JNF_TEXTAREA_MAX_CHARACTER_COUNT, default=None, null=True) + academic_requirements = models.CharField(blank=True, max_length=JNF_TEXTAREA_MAX_CHARACTER_COUNT, default=None, null=True) + #contact details of company person + contact_person_name = models.CharField(blank=False, max_length=JNF_TEXT_MAX_CHARACTER_COUNT) + phone_number = models.PositiveBigIntegerField(blank=False) + email = models.EmailField(blank=False) + contact_person_designation = models.CharField(blank=False, max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, default="") + telephone_number = models.PositiveBigIntegerField(blank=True, default=None, null=True) + email_verified = models.BooleanField(blank=False, default=False) + #history + created_at = models.DateTimeField(blank=False, default=None, null=True) + updated_at = models.DateTimeField(blank=False, default=None, null=True) + changed_by = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True) + history = HistoricalRecords(user_model=User) + + + def format(self): + if self.company_name is not None: + self.company_name = self.company_name.strip()[:JNF_SMALLTEXT_MAX_CHARACTER_COUNT] + if self.company_type is not None: + self.company_type = self.company_type.strip()[:JNF_SMALLTEXT_MAX_CHARACTER_COUNT] + if self.company_details is not None: + self.company_details = self.company_details.strip()[:JNF_TEXTAREA_MAX_CHARACTER_COUNT] + if self.address is not None: + self.address = self.address.strip()[:JNF_TEXTAREA_MAX_CHARACTER_COUNT] + if self.nature_of_business is not None: + self.nature_of_business = self.nature_of_business.strip()[:JNF_TEXTAREA_MAX_CHARACTER_COUNT] + if self.type_of_organisation is not None: + self.type_of_organisation = self.type_of_organisation.strip()[:JNF_TEXTAREA_MAX_CHARACTER_COUNT] + if self.website is not None: + self.website = self.website.strip()[:JNF_TEXT_MAX_CHARACTER_COUNT] + if self.contact_person_name is not None: + self.contact_person_name = self.contact_person_name.strip()[:JNF_TEXT_MAX_CHARACTER_COUNT] + if self.city is not None: + self.city = self.city.strip()[:JNF_SMALLTEXT_MAX_CHARACTER_COUNT] + if self.state is not None: + self.state = self.state.strip()[:JNF_SMALLTEXT_MAX_CHARACTER_COUNT] + if self.country is not None: + self.country = self.country.strip()[:JNF_SMALLTEXT_MAX_CHARACTER_COUNT] + if self.city_type is not None: + self.city_type = self.city_type.strip()[:JNF_SMALLTEXT_MAX_CHARACTER_COUNT] + if self.selection_procedure_details is not None: + self.selection_procedure_details = self.selection_procedure_details.strip()[:JNF_TEXTAREA_MAX_CHARACTER_COUNT] + if self.description is not None: + self.description = self.description.strip()[:JNF_TEXTAREA_MAX_CHARACTER_COUNT] + if self.additional_facilities is not None: + self.additional_facilities = self.additional_facilities.strip()[:JNF_TEXTAREA_MAX_CHARACTER_COUNT] + if self.academic_requirements is not None: + self.academic_requirements = self.academic_requirements.strip()[:JNF_TEXTAREA_MAX_CHARACTER_COUNT] + if self.contact_person_designation is not None: + self.contact_person_designation = self.contact_person_designation.strip()[:JNF_SMALLTEXT_MAX_CHARACTER_COUNT] + + @property + def _history_user(self): + return self.changed_by + + @_history_user.setter + def _history_user(self, value): + if isinstance(value, User): + self.changed_by = value + else: + self.changed_by = None + + def save(self, *args, **kwargs): + ''' On save, add timestamps ''' + if not self.created_at: + self.created_at = timezone.now() + self.format() + self.updated_at = timezone.now() + return super(Internship, self).save(*args, **kwargs) + + def __str__(self): + return self.company_name + " - " + self.id + + + +class Season(models.Model): + + season = models.CharField(max_length=10, choices=SEASON_CHOICES, unique=True) + student = models.ForeignKey(Student, on_delete=models.CASCADE, default=None) + + def __str__(self): + return self.season + " Season - " + self.student.id + +class InternshipApplication(models.Model): + id = models.CharField(blank=False, primary_key=True, max_length=15) #unique id for each internship + internship=models.ForeignKey(Internship,blank=False, on_delete=models.CASCADE, default=None) + student=models.ForeignKey(Student,blank=False, on_delete=models.CASCADE, default=None) + resume = models.CharField(max_length=JNF_TEXT_MAX_CHARACTER_COUNT, blank=False, null=True, default=None) + additional_info = models.JSONField(blank=True, null=True, default=None) + selected = models.BooleanField(null=True, default=None, blank=True) + offer_accepted = models.BooleanField(null=True, default=None, blank=True) # True if offer accepted, False if rejected, None if not yet decided + applied_at = models.DateTimeField(blank=False, default=None, null=True) + updated_at = models.DateTimeField(blank=False, default=None, null=True) + changed_by = models.ForeignKey(User, blank=False, on_delete=models.RESTRICT, default=None, null=True) + history = HistoricalRecords(user_model=User) + + def save(self, *args, **kwargs): + ''' On save, add timestamps ''' + if not self.applied_at: + self.applied_at = timezone.now() + self.updated_at = timezone.now() + + return super(InternshipApplication, self).save(*args, **kwargs) + + @property + def _history_user(self): + return self.changed_by + + @_history_user.setter + def _history_user(self, value): + if isinstance(value, User): + self.changed_by = value + else: + self.changed_by = None + + class Meta: + verbose_name_plural = "Internship Applications" + unique_together = ('internship', 'student') + + def __str__(self): + return self.internship.company_name + " - " + self.student.name + \ No newline at end of file diff --git a/CDC_Backend/internAPIs/tests.py b/CDC_Backend/internAPIs/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/CDC_Backend/internAPIs/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/CDC_Backend/internAPIs/views.py b/CDC_Backend/internAPIs/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/CDC_Backend/internAPIs/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here.