From 1378898d371e4bc0c4cae8c5caa065630b7e1679 Mon Sep 17 00:00:00 2001 From: uttamthummala Date: Tue, 3 Oct 2023 01:47:26 +0530 Subject: [PATCH] Updated Backend To Accomodate Internships --- CDC_Backend/APIs/admin.py | 20 +- CDC_Backend/APIs/adminViews.py | 308 ++++++++++++++++++++++++------- CDC_Backend/APIs/companyViews.py | 31 ++++ CDC_Backend/APIs/constants.py | 7 + CDC_Backend/APIs/models.py | 14 ++ CDC_Backend/APIs/serializers.py | 141 ++++++++++++++ CDC_Backend/APIs/studentUrls.py | 17 +- CDC_Backend/APIs/studentViews.py | 76 ++++++-- CDC_Backend/APIs/utils.py | 42 ++++- setup.sh | 2 +- 10 files changed, 562 insertions(+), 96 deletions(-) mode change 100644 => 100755 setup.sh diff --git a/CDC_Backend/APIs/admin.py b/CDC_Backend/APIs/admin.py index 3cb49a0..7e72dc7 100644 --- a/CDC_Backend/APIs/admin.py +++ b/CDC_Backend/APIs/admin.py @@ -138,6 +138,13 @@ class PlacementApplicationResources(resources.ModelResource): class PlacementAdmin(ExportMixin, SimpleHistoryAdmin): resource_class = PlacementApplicationResources +class InternshipApplicationResources(resources.ModelResource): + class Meta: + model = InternshipApplication + exclude = ('id', 'changed_by') +class InternshipApplicationAdmin(ExportMixin, SimpleHistoryAdmin): + resource_class = InternshipApplicationResources + @admin.register(PlacementApplication) class PlacementApplication(PlacementAdmin): @@ -151,7 +158,18 @@ class PlacementApplication(PlacementAdmin): def Student(self, obj): return model_admin_url(obj.student) +@admin.register(InternshipApplication) +class InternshipApplication(InternshipApplicationAdmin): + list_display = ('id', 'Internship', 'Student', 'selected') + search_fields = ('id',) + ordering = ('id',) + list_filter = ('selected',) + def Internship(self, obj): + return model_admin_url(obj.internship) + + def Student(self, obj): + return model_admin_url(obj.student) class PrePlacementResources(resources.ModelResource): class Meta: @@ -178,7 +196,7 @@ class InternshipResources(resources.ModelResource): class Meta: model = Internship exclude = ('id', 'changed_by', 'is_company_details_pdf', 'is_description_pdf', - 'is_compensation_details_pdf', 'is_selection_procedure_details_pdf') + 'is_stipend_details_pdf', 'is_selection_procedure_details_pdf') class InternAdmin(ExportMixin, SimpleHistoryAdmin): diff --git a/CDC_Backend/APIs/adminViews.py b/CDC_Backend/APIs/adminViews.py index e935e8d..a834b01 100644 --- a/CDC_Backend/APIs/adminViews.py +++ b/CDC_Backend/APIs/adminViews.py @@ -12,9 +12,18 @@ from .utils import * def markStatus(request, id, email, user_type): try: data = request.data + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] #not to break the code + else: + opening_type= "Placement" + if opening_type == "Internship": + applications = InternshipApplication.objects.filter(internship_id=data[OPENING_ID]) + else: + applications = PlacementApplication.objects.filter(placement_id=data[OPENING_ID]) # Getting all application from db for this opening - applications = PlacementApplication.objects.filter(placement_id=data[OPENING_ID]) + # applications = PlacementApplication.objects.filter(placement_id=data[OPENING_ID]) for i in data[STUDENT_LIST]: + # print(i[STUDENT_ID]) issue is using student id instead of roll no both may not be same application = applications.filter(student__roll_no=i[STUDENT_ID]) # Filtering student's application if len(application) > 0: application = application[0] @@ -27,14 +36,23 @@ def markStatus(request, id, email, user_type): raise ValueError("Student already selected") 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 opening_type == "Internship": + subject = STUDENT_APPLICATION_STATUS_TEMPLATE_SUBJECT.format( + company_name=application.internship.company_name,id=application.id) + data = { + "company_name": application.internship.company_name, + "designation": application.internship.designation, + "student_name": application.student.name + } + else: + 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: @@ -64,13 +82,22 @@ def getDashboard(request, id, email, user_type): previous = placements.exclude(deadline_datetime__gt=timezone.now()).filter( offer_accepted=True, email_verified=True) new = placements.filter(offer_accepted__isnull=True, email_verified=True) + internships=Internship.objects.all().order_by('-created_at') + ongoing_internships = internships.filter(deadline_datetime__gt=timezone.now(), offer_accepted=True, email_verified=True) + previous_internships = internships.exclude(deadline_datetime__gt=timezone.now()).filter( + offer_accepted=True, email_verified=True) + new_internships = internships.filter(offer_accepted__isnull=True, email_verified=True) ongoing = PlacementSerializerForAdmin(ongoing, many=True).data previous = PlacementSerializerForAdmin(previous, many=True).data new = PlacementSerializerForAdmin(new, many=True).data + ongoing_internships = InternshipSerializerForAdmin(ongoing_internships, many=True).data + previous_internships = InternshipSerializerForAdmin(previous_internships, many=True).data + new_internships = InternshipSerializerForAdmin(new_internships, many=True).data return Response( {'action': "Get Dashboard - Admin", 'message': "Data Found", "ongoing": ongoing, "previous": previous, - "new": new}, + "new": new, "ongoing_internships": ongoing_internships, "previous_internships": previous_internships, + "new_internships": new_internships}, status=status.HTTP_200_OK) except Http404: return Response({'action': "Get Dashboard - Admin", 'message': 'Student Not Found'}, @@ -87,7 +114,15 @@ def getDashboard(request, id, email, user_type): def updateDeadline(request, id, email, user_type): try: data = request.data - opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] + else: + opening_type= "Placement" + if opening_type == "Internship": + opening = get_object_or_404(Internship, pk=data[OPENING_ID]) + else: + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + # Updating deadline date with correct format in datetime field opening.deadline_datetime = datetime.datetime.strptime(data[DEADLINE_DATETIME], '%Y-%m-%d %H:%M:%S %z') opening.changed_by = get_object_or_404(User, id=id) @@ -110,7 +145,14 @@ def updateOfferAccepted(request, id, email, user_type): try: data = request.data offer_accepted = data[OFFER_ACCEPTED] - opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] + else: + opening_type= "Placement" + if opening_type == "Internship": + opening = get_object_or_404(Internship, pk=data[OPENING_ID]) + else: + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) if opening.offer_accepted is None: opening.offer_accepted = offer_accepted == "true" opening.changed_by = get_object_or_404(User, id=id) @@ -140,7 +182,14 @@ def updateOfferAccepted(request, id, email, user_type): def updateEmailVerified(request, id, email, user_type): try: data = request.data - opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] + else: + opening_type= "Placement" + if opening_type == "Internship": + opening = get_object_or_404(Internship, pk=data[OPENING_ID]) + else: + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) opening.email_verified = True if data[EMAIL_VERIFIED] == "true" else False opening.changed_by = get_object_or_404(User, id=id) opening.save() @@ -161,7 +210,14 @@ def updateEmailVerified(request, id, email, user_type): def deleteAdditionalInfo(request, id, email, user_type): try: data = request.data - opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] + else: + opening_type= "Placement" + if opening_type == "Internship": + opening = get_object_or_404(Internship, pk=data[OPENING_ID]) + else: + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) if data[FIELD] in opening.additional_info: opening.additional_info.remove(data[FIELD]) opening.changed_by = get_object_or_404(User, id=id) @@ -188,7 +244,14 @@ def deleteAdditionalInfo(request, id, email, user_type): def addAdditionalInfo(request, id, email, user_type): try: data = request.data - opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] + else: + opening_type= "Placement" + if opening_type == "Internship": + opening = get_object_or_404(Internship, pk=data[OPENING_ID]) + else: + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) if data[FIELD] not in opening.additional_info: opening.additional_info.append(data[FIELD]) opening.save() @@ -215,11 +278,20 @@ def addAdditionalInfo(request, id, email, user_type): 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) + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] + else: + opening_type= "Placement" + if opening_type == "Internship": + opening = get_object_or_404(Internship, pk=data[OPENING_ID]) + applications = InternshipApplication.objects.filter(internship=opening) + serializer = InternshipApplicationSerializerForAdmin(applications, many=True) + else: + 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) + status=status.HTTP_200_OK) except Http404: return Response({'action': "Get Applications", 'message': 'Opening Not Found'}, status=status.HTTP_404_NOT_FOUND) @@ -235,68 +307,136 @@ def getApplications(request, id, email, user_type): def submitApplication(request, id, email, user_type): try: data = request.data - student = get_object_or_404(Student, pk=data[STUDENT_ID]) - opening = get_object_or_404(Placement, pk=data[OPENING_ID]) - student_user = get_object_or_404(User, id=student.id) - if data[APPLICATION_ID] == "": - application = PlacementApplication() - application.id = generateRandomString() - application.placement = opening - application.student = student - if data[RESUME_FILE_NAME] in student.resumes: - application.resume = data[RESUME_FILE_NAME] - else: - raise FileNotFoundError(RESUME_FILE_NAME + " Not Found") - 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": "Placement", - "additional_info": dict(json.loads(application.additional_info)), - } - subject = STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT.format(company_name=opening.company_name) - application.changed_by = get_object_or_404(User, id=id) - application.save() - sendEmail(student_user.email, subject, data, STUDENT_APPLICATION_SUBMITTED_TEMPLATE) - return Response({'action': "Add Student Application", 'message': "Application added"}, - status=status.HTTP_200_OK) + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] else: - application = get_object_or_404(PlacementApplication, id=data[APPLICATION_ID]) - if application: + opening_type= "Placement" + student = get_object_or_404(Student, pk=data[STUDENT_ID]) + if opening_type == "Internship": + opening = get_object_or_404(Internship, pk=data[OPENING_ID]) + student_user = get_object_or_404(User, id=student.id) + if data[APPLICATION_ID] == "": + application = InternshipApplication() + application.id = generateRandomString() + application.internship = opening + application.student = student if data[RESUME_FILE_NAME] in student.resumes: application.resume = data[RESUME_FILE_NAME] else: raise FileNotFoundError(RESUME_FILE_NAME + " Not Found") - application.resume = data[RESUME_FILE_NAME] 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": "Internship", + "additional_info": dict(json.loads(application.additional_info)), + } + subject = STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT.format(company_name=opening.company_name) + application.changed_by = get_object_or_404(User, id=id) + application.save() + sendEmail(student_user.email, subject, data, STUDENT_APPLICATION_SUBMITTED_TEMPLATE) + return Response({'action': "Add Student Application", 'message': "Application added For Internship"}, + status=status.HTTP_200_OK) + else: + application = get_object_or_404(InternshipApplication, id=data[APPLICATION_ID]) + if application: + if data[RESUME_FILE_NAME] in student.resumes: + application.resume = data[RESUME_FILE_NAME] + else: + raise FileNotFoundError(RESUME_FILE_NAME + " Not Found") + application.resume = data[RESUME_FILE_NAME] + 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": "Internship", + "resume": application.resume[16:], + "additional_info_items": dict(json.loads(application.additional_info)), + } + subject = STUDENT_APPLICATION_UPDATED_TEMPLATE_SUBJECT.format(company_name=opening.company_name) + application.changed_by = get_object_or_404(User, id=id) + application.save() + sendEmail(student_user.email, subject, data, STUDENT_APPLICATION_UPDATED_TEMPLATE) + return Response({'action': "Add Student Application", 'message': "Application updated For Internship"}, + status=status.HTTP_200_OK) + else: + return Response({'action': "Edit Student Application", 'message': "No Application Found For Internship"}, + status=status.HTTP_400_BAD_REQUEST) + else: + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + student_user = get_object_or_404(User, id=student.id) + if data[APPLICATION_ID] == "": + application = PlacementApplication() + application.id = generateRandomString() + application.placement = opening + application.student = student + if data[RESUME_FILE_NAME] in student.resumes: + application.resume = data[RESUME_FILE_NAME] + else: + raise FileNotFoundError(RESUME_FILE_NAME + " Not Found") + 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": "Placement", - "resume": application.resume[16:], - "additional_info_items": dict(json.loads(application.additional_info)), + "additional_info": dict(json.loads(application.additional_info)), } - subject = STUDENT_APPLICATION_UPDATED_TEMPLATE_SUBJECT.format(company_name=opening.company_name) + subject = STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT.format(company_name=opening.company_name) application.changed_by = get_object_or_404(User, id=id) application.save() - sendEmail(student_user.email, subject, data, STUDENT_APPLICATION_UPDATED_TEMPLATE) - return Response({'action': "Add Student Application", 'message': "Application updated"}, - status=status.HTTP_200_OK) + sendEmail(student_user.email, subject, data, STUDENT_APPLICATION_SUBMITTED_TEMPLATE) + return Response({'action': "Add Student Application", 'message': "Application added"}, + status=status.HTTP_200_OK) else: - return Response({'action': "Edit Student Application", 'message': "No Application Found"}, + application = get_object_or_404(PlacementApplication, id=data[APPLICATION_ID]) + if application: + if data[RESUME_FILE_NAME] in student.resumes: + application.resume = data[RESUME_FILE_NAME] + else: + raise FileNotFoundError(RESUME_FILE_NAME + " Not Found") + application.resume = data[RESUME_FILE_NAME] + 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": "Placement", + "resume": application.resume[16:], + "additional_info_items": dict(json.loads(application.additional_info)), + } + subject = STUDENT_APPLICATION_UPDATED_TEMPLATE_SUBJECT.format(company_name=opening.company_name) + application.changed_by = get_object_or_404(User, id=id) + application.save() + sendEmail(student_user.email, subject, data, STUDENT_APPLICATION_UPDATED_TEMPLATE) + return Response({'action': "Add Student Application", 'message': "Application updated"}, + status=status.HTTP_200_OK) + else: + return Response({'action': "Edit Student Application", 'message': "No Application Found"}, status=status.HTTP_400_BAD_REQUEST) except Http404 as e: @@ -320,8 +460,16 @@ def submitApplication(request, id, email, user_type): 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) + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] + else: + opening_type= "Placement" + if opening_type == "Internship": + opening = get_object_or_404(Internship, id=data[OPENING_ID]) + applications = InternshipApplication.objects.filter(internship=opening) + else: + opening = get_object_or_404(Placement, id=data[OPENING_ID]) + applications = PlacementApplication.objects.filter(placement=opening) filename = generateRandomString() if not os.path.isdir(STORAGE_DESTINATION_APPLICATION_CSV): os.mkdir(STORAGE_DESTINATION_APPLICATION_CSV) @@ -330,7 +478,7 @@ def generateCSV(request, id, email, user_type): writer = csv.writer(f) header_row = APPLICATION_CSV_COL_NAMES.copy() - header_row.extend(placement.additional_info) + header_row.extend(opening.additional_info) writer.writerow(header_row) for apl in applications: row_details = [] @@ -347,7 +495,7 @@ def generateCSV(request, id, email, user_type): row_details.append(link) row_details.append(apl.selected) - for i in placement.additional_info: + for i in opening.additional_info: row_details.append(json.loads(apl.additional_info)[i]) writer.writerow(row_details) @@ -397,6 +545,10 @@ def addPPO(request, id, email, user_type): def getStudentApplication(request, id, email, user_type): try: data = request.data + if OPENING_TYPE in data: + opening_type= data[OPENING_TYPE] + else: + opening_type= "Placement" student = get_object_or_404(Student, id=data[STUDENT_ID]) student_serializer = StudentSerializer(student) student_details = { @@ -406,15 +558,29 @@ def getStudentApplication(request, id, email, user_type): "resume_list": student_serializer.data['resume_list'], } # search for the application if there or not - application = PlacementApplication.objects.filter(student=student, + if opening_type == "Internship": + application = InternshipApplication.objects.filter(student=student, + internship=get_object_or_404(Internship, + id=data[OPENING_ID])) + else: + application = PlacementApplication.objects.filter(student=student, placement=get_object_or_404(Placement, id=data[OPENING_ID])) + if application: - serializer = PlacementApplicationSerializer(application[0]) - application_info = { - "id": serializer.data['id'], - "additional_info": serializer.data['additional_info'], - "resume": serializer.data['resume_link'], - } + if opening_type == "Internship": + serializer = InternshipApplicationSerializer(application[0]) + application_info = { + "id": serializer.data['id'], + "additional_info": serializer.data['additional_info'], + "resume": serializer.data['resume_link'], + } + else: + serializer = PlacementApplicationSerializer(application[0]) + application_info = { + "id": serializer.data['id'], + "additional_info": serializer.data['additional_info'], + "resume": serializer.data['resume_link'], + } return Response( {'action': "Get Student Application", 'application_found': "true", "application_info": application_info, "student_details": student_details}, status=status.HTTP_200_OK) diff --git a/CDC_Backend/APIs/companyViews.py b/CDC_Backend/APIs/companyViews.py index bfc9ecc..67fad0b 100644 --- a/CDC_Backend/APIs/companyViews.py +++ b/CDC_Backend/APIs/companyViews.py @@ -321,6 +321,24 @@ def autoFillJnf(request): return Response({'action': "Get AutoFill", 'message': "Something went wrong"}, status=status.HTTP_400_BAD_REQUEST) +@api_view(['GET']) +@precheck([PLACEMENT_ID]) +def autoFillInf(request): + try: + data = request.GET + internship_id = data.get(INTERNSHIP_ID) + opening = get_object_or_404(Internship, id=internship_id) + serializer = AutofillSerializersInternship(opening) + return Response({'action': "Get AutoFill ", 'message': 'Data Found', 'internship_data': serializer.data}, + status=status.HTTP_200_OK) + except Http404: + return Response({'action': "Get AutoFill", 'message': 'Internship Not Found'}, + status=status.HTTP_404_NOT_FOUND) + except Exception as e: + traceback_str = traceback.format_exc() + logger.warning("Get AutoFill: " + traceback_str) + return Response({'action': "Get AutoFill", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) ## Internships ## @@ -397,6 +415,14 @@ def addInternship(request): internship.is_work_from_home = True else: internship.is_work_from_home = False + + if data[ALLOWED_BATCH] is None or json.loads(data[ALLOWED_BATCH]) == "": + raise ValueError('Allowed Branch cannot be empty') + elif set(json.loads(data[ALLOWED_BATCH])).issubset(BATCHES): + internship.allowed_batch = json.loads(data[ALLOWED_BATCH]) + else: + raise ValueError('Allowed Batch must be a subset of ' + str(BATCHES)) + if data[ALLOWED_BRANCH] is None or json.loads(data[ALLOWED_BRANCH]) == "": raise ValueError('Allowed Branch cannot be empty') elif set(json.loads(data[ALLOWED_BRANCH])).issubset(BRANCHES): @@ -468,6 +494,11 @@ def addInternship(request): internship.selection_procedure_details_pdf_names = selection_procedure_details_pdf internship.additional_facilities = data[OTHER_FACILITIES] + #add additional info + # Only Allowing Fourth Year for Placement + + + internship.academic_requirements = data[OTHER_REQUIREMENTS] diff --git a/CDC_Backend/APIs/constants.py b/CDC_Backend/APIs/constants.py index 92d869e..0c8e21a 100644 --- a/CDC_Backend/APIs/constants.py +++ b/CDC_Backend/APIs/constants.py @@ -21,6 +21,12 @@ BRANCHES = [ "CHEMICAL", "BSMS", ] +BATCHES = [ #change it accordingly + "2023", + "2022", + "2021", + "2020", +] BATCH_CHOICES = [ ["2022", "2022"], ["2021", "2021"], @@ -218,6 +224,7 @@ STIPEND = 'stipend' FACILITIES = 'facilities' OTHER_FACILITIES = 'other_facilities' STIPEND_DETAILS_PDF = 'compensation_details_pdf' +STIPEND_DETAILS_PDF_NAMES = 'stipend_description_pdf_names' SEASONS = ( 'Summer', diff --git a/CDC_Backend/APIs/models.py b/CDC_Backend/APIs/models.py index 75867a8..f5dbc57 100644 --- a/CDC_Backend/APIs/models.py +++ b/CDC_Backend/APIs/models.py @@ -32,6 +32,7 @@ class Student(models.Model): default=list, blank=True) cpi = models.DecimalField(decimal_places=2, max_digits=4) can_apply = models.BooleanField(default=True, verbose_name='Registered') + can_apply_internship = models.BooleanField(default=True, verbose_name='Internship Registered') #added for internship changed_by = models.ForeignKey(User, blank=True, on_delete=models.RESTRICT, default=None, null=True) degree = models.CharField(choices=DEGREE_CHOICES, blank=False, max_length=10, default=DEGREE_CHOICES[0][0]) history = HistoricalRecords(user_model=User) @@ -318,6 +319,11 @@ class Internship(models.Model): size=TOTAL_BRANCHES, default=list ) + allowed_batch = ArrayField( + models.CharField(max_length=10, choices=BATCH_CHOICES), + size=TOTAL_BATCHES, + default=list + ) sophomore_eligible = models.BooleanField(blank=False, default=False) rs_eligible = models.BooleanField(blank=False, default=False) tentative_no_of_offers = models.IntegerField(blank=False, default=None, null=True) @@ -332,6 +338,11 @@ class Internship(models.Model): default=list, blank=True ) + additional_info = ArrayField(models.CharField(blank=True, max_length=JNF_TEXTMEDIUM_MAX_CHARACTER_COUNT), size=15, + default=list, blank=True) + 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) + 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) # selection process @@ -388,6 +399,9 @@ class Internship(models.Model): 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.additional_info is not None: + self.additional_info = [info.strip()[:JNF_TEXTMEDIUM_MAX_CHARACTER_COUNT] for info in + list(self.additional_info)] # if self.contact_person_designation is not None: # self.contact_person_designation = self.contact_person_designation.strip()[:JNF_SMALLTEXT_MAX_CHARACTER_COUNT] diff --git a/CDC_Backend/APIs/serializers.py b/CDC_Backend/APIs/serializers.py index 19d94bc..39750df 100644 --- a/CDC_Backend/APIs/serializers.py +++ b/CDC_Backend/APIs/serializers.py @@ -99,6 +99,59 @@ class PlacementSerializerForStudent(serializers.ModelSerializer): ] depth = 1 +class InternshipSerializerForStudent(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(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(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.stipend_description_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote(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(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links + + class Meta: + model = Internship + exclude = [CONTACT_PERSON_NAME, PHONE_NUMBER, EMAIL, COMPANY_DETAILS_PDF_NAMES, DESCRIPTION_PDF_NAMES, + STIPEND_DETAILS_PDF_NAMES, SELECTION_PROCEDURE_DETAILS_PDF_NAMES, OFFER_ACCEPTED, + EMAIL_VERIFIED, + ] + depth = 1 + class PlacementSerializerForAdmin(serializers.ModelSerializer): company_details_pdf_links = serializers.SerializerMethodField() @@ -152,6 +205,58 @@ class PlacementSerializerForAdmin(serializers.ModelSerializer): COMPENSATION_DETAILS_PDF_NAMES, SELECTION_PROCEDURE_DETAILS_PDF_NAMES] depth = 1 +class InternshipSerializerForAdmin(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(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(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.stipend_description_pdf_names: + ele = {} + link = LINK_TO_STORAGE_COMPANY_ATTACHMENT + urllib.parse.quote(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(obj.id + "/" + pdf_name) + ele['link'] = link + ele['name'] = pdf_name + links.append(ele) + return links + + class Meta: + model = Internship + exclude = [COMPANY_DETAILS_PDF_NAMES, DESCRIPTION_PDF_NAMES, SELECTION_PROCEDURE_DETAILS_PDF_NAMES, + STIPEND_DETAILS_PDF_NAMES] + depth = 1 + class PlacementApplicationSerializer(serializers.ModelSerializer): placement = serializers.SerializerMethodField() @@ -168,6 +273,21 @@ class PlacementApplicationSerializer(serializers.ModelSerializer): class Meta: model = PlacementApplication exclude = [STUDENT, 'resume'] +class InternshipApplicationSerializer(serializers.ModelSerializer): + internship = serializers.SerializerMethodField() + resume_link = serializers.SerializerMethodField() + + def get_internship(self, obj): + data = InternshipSerializerForStudent(obj.internship).data + return data + + def get_resume_link(self, obj): + ele = {'link': LINK_TO_STORAGE_RESUME + urllib.parse.quote(str(obj.student.roll_no) + "/" + obj.resume), 'name': obj.resume} + return ele + + class Meta: + model = InternshipApplication + exclude = [STUDENT, 'resume'] class PlacementApplicationSerializerForAdmin(serializers.ModelSerializer): @@ -186,6 +306,22 @@ class PlacementApplicationSerializerForAdmin(serializers.ModelSerializer): model = PlacementApplication exclude = ['placement', 'resume'] +class InternshipApplicationSerializerForAdmin(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): + ele = {'link': LINK_TO_STORAGE_RESUME + urllib.parse.quote(obj.id + "/" + obj.resume), 'name': obj.resume} + return ele + + class Meta: + model = InternshipApplication + exclude = ['internship', 'resume'] + class ContributorSerializer(serializers.ModelSerializer): class Meta: model = Contributor @@ -195,4 +331,9 @@ class ContributorSerializer(serializers.ModelSerializer): class AutofillSerializers(serializers.ModelSerializer): class Meta: model = Placement + fields = '__all__' + +class AutofillSerializersInternship(serializers.ModelSerializer): + class Meta: + model = Internship fields = '__all__' \ No newline at end of file diff --git a/CDC_Backend/APIs/studentUrls.py b/CDC_Backend/APIs/studentUrls.py index f9dee52..07c4710 100644 --- a/CDC_Backend/APIs/studentUrls.py +++ b/CDC_Backend/APIs/studentUrls.py @@ -3,13 +3,14 @@ from django.urls import path from . import studentViews urlpatterns = [ - path('login/', studentViews.login, name="Login"), - path('profile/', studentViews.studentProfile, name="Student Profile"), - path('getDashboard/', studentViews.getDashboard, name="Dashboard"), - path("addResume/", studentViews.addResume, name="Upload Resume"), - path("deleteResume/", studentViews.deleteResume, name="Upload Resume"), - path("submitApplication/", studentViews.submitApplication, name="Submit Application"), - path("deleteApplication/", studentViews.deleteApplication, name="Delete Application"), + path('login/', studentViews.login, name="Login"), #done for intern + path('profile/', studentViews.studentProfile, name="Student Profile"), #done for intern + path('getDashboard/', studentViews.getDashboard, name="Dashboard"), # customised dashboard.. check are we checking registedred check allowed branch/batches filter in + path("addResume/", studentViews.addResume, name="Upload Resume"), #done for intern + path("deleteResume/", studentViews.deleteResume, name="Upload Resume"),#done for intern + path("submitApplication/", studentViews.submitApplication, name="Submit Application"), #done for intern + path("deleteApplication/", studentViews.deleteApplication, name="Delete Application"), #done for intern check for opening type data in headers path("getContributorStats/", studentViews.getContributorStats, name="Get Contributor Stats"), - path("studentAcceptOffer/", studentViews.studentAcceptOffer, name="Student Accept Offer"), + path("studentAcceptOffer/", studentViews.studentAcceptOffer, name="Student Accept Offer"), #same as above check header ] +#store all files.. \ No newline at end of file diff --git a/CDC_Backend/APIs/studentViews.py b/CDC_Backend/APIs/studentViews.py index 62d36b4..a9fb2aa 100644 --- a/CDC_Backend/APIs/studentViews.py +++ b/CDC_Backend/APIs/studentViews.py @@ -48,6 +48,7 @@ def refresh(request): @isAuthorized(allowed_users=[STUDENT]) def studentProfile(request, id, email, user_type): try: + print(id) studentDetails = get_object_or_404(Student, id=id) data = StudentSerializer(studentDetails).data @@ -110,9 +111,20 @@ def getDashboard(request, id, email, user_type): placementApplications = PlacementApplication.objects.filter(student_id=id) placementApplications = PlacementApplicationSerializer(placementApplications, many=True).data + internships = Internship.objects.filter(allowed_batch__contains=[studentDetails.batch], + allowed_branch__contains=[studentDetails.branch], + deadline_datetime__gte=datetime.datetime.now(), + offer_accepted=True, email_verified=True).order_by('deadline_datetime') + + filtered_internships = internship_eligibility_filters(studentDetails, internships) + print(len(filtered_internships)) + internshipsdata = InternshipSerializerForStudent(filtered_internships, many=True).data + + internshipApplications = InternshipApplication.objects.filter(student_id=id) + internshipApplications = InternshipApplicationSerializer(internshipApplications, many=True).data return Response( {'action': "Get Dashboard - Student", 'message': "Data Found", "placements": placementsdata, - 'placementApplication': placementApplications}, + 'placementApplication': placementApplications, 'internships':internshipsdata,'internshipApplication':internshipApplications}, status=status.HTTP_200_OK) except Http404: return Response({'action': "Get Dashboard - Student", 'message': 'Student Not Found'}, @@ -163,13 +175,14 @@ def deleteResume(request, id, email, user_type): ]) def submitApplication(request, id, email, user_type): try: - data = request.data + data = request.data student = get_object_or_404(Student, id=id) - if not student.can_apply: - return Response({'action': "Submit Application", 'message': "Student Can't Apply"}, - status=status.HTTP_400_BAD_REQUEST) + # Only Allowing Applications for Placements if data[OPENING_TYPE] == PLACEMENT: + if not student.can_apply: #why not checking in admin + return Response({'action': "Submit Application", 'message': "Student Can't Apply"}, + status=status.HTTP_400_BAD_REQUEST) if not len(PlacementApplication.objects.filter( student_id=id, placement_id=data[OPENING_ID])): application = PlacementApplication() @@ -187,6 +200,27 @@ def submitApplication(request, id, email, user_type): application.placement = opening else: raise PermissionError("Application is already Submitted") + elif data[OPENING_TYPE] == INTERNSHIP: + if not student.can_apply_internship: + return Response({'action': "Submit Application", 'message': "Student Can't Apply Internship"}, + status=status.HTTP_400_BAD_REQUEST) + if not len(InternshipApplication.objects.filter( + student_id=id, internship_id=data[OPENING_ID])): + application = InternshipApplication() + opening = get_object_or_404(Internship, id=data[OPENING_ID], + allowed_batch__contains=[student.batch], + allowed_branch__contains=[student.branch], + deadline_datetime__gte=datetime.datetime.now().date() + ) + if not opening.offer_accepted or not opening.email_verified: + raise PermissionError("Internship Not Approved") + + cond_stat, cond_msg = InternshipApplicationConditions(student, opening) + if not cond_stat: + raise PermissionError(cond_msg) + application.internship = opening + else: + raise PermissionError("Application is already Submitted") else: raise ValueError(OPENING_TYPE + " is Invalid") @@ -239,10 +273,22 @@ def submitApplication(request, id, email, user_type): def deleteApplication(request, id, email, user_type): try: data = request.data - application = get_object_or_404(PlacementApplication, id=data[APPLICATION_ID], + if OPENING_TYPE in request.data: + opening_type = request.data[OPENING_TYPE] + else: + opening_type = PLACEMENT + if opening_type==INTERNSHIP: #check whether it has header or not + application = get_object_or_404(InternshipApplication, id=data[APPLICATION_ID], student_id=id) - if application.placement.deadline_datetime < timezone.now(): - raise PermissionError("Deadline Passed") + if application.internship.deadline_datetime < timezone.now(): + raise PermissionError("Deadline Passed") + else: + application = get_object_or_404(PlacementApplication, id=data[APPLICATION_ID], + student_id=id) + if application.placement.deadline_datetime < timezone.now(): + raise PermissionError("Deadline Passed") + + application.delete() return Response({'action': "Delete Application", 'message': "Application Deleted"}, @@ -283,9 +329,17 @@ def studentAcceptOffer(request, id, email, user_type): company_id = request.data['id'] student_id=request.data['profileInfo']['id'] offer_status = request.data['offerStatus'] - placement_application=PlacementApplication.objects.get(placement=company_id,student=student_id) - placement_application.offer_accepted=offer_status - placement_application.save() + if OPENING_TYPE in request.data: + opening_type = request.data[OPENING_TYPE] + else: + opening_type = PLACEMENT + if opening_type==INTERNSHIP: + application=InternshipApplication.objects.get(internship=company_id,student=student_id) #check syntax + else: + application=PlacementApplication.objects.get(placement=company_id,student=student_id) + + application.offer_accepted=offer_status + application.save() return Response({'action': "Accept Offer", 'message': "Updated Offer Status"}, status=status.HTTP_200_OK) except: diff --git a/CDC_Backend/APIs/utils.py b/CDC_Backend/APIs/utils.py index cd864d0..53d29a6 100644 --- a/CDC_Backend/APIs/utils.py +++ b/CDC_Backend/APIs/utils.py @@ -28,7 +28,7 @@ from rest_framework import status from rest_framework.response import Response from .constants import * -from .models import User, PrePlacementOffer, PlacementApplication, Placement, Student, Internship +from .models import User, PrePlacementOffer, PlacementApplication, Placement, Student, Internship,InternshipApplication logger = logging.getLogger('db') @@ -112,10 +112,12 @@ def isAuthorized(allowed_users=None): def wrapper_func(request, *args, **kwargs): try: headers = request.META + #print(headers) if 'HTTP_AUTHORIZATION' in headers: token_id = headers['HTTP_AUTHORIZATION'][7:] idinfo = id_token.verify_oauth2_token(token_id, requests.Request(), CLIENT_ID) email = idinfo[EMAIL] + # print(idinfo) user = get_object_or_404(User, email=email) if user: user.last_login_time = timezone.now() @@ -167,6 +169,8 @@ def saveFile(file, location): file_name = re.sub(r'[\\/:*?"<>|]', '_', file_name) + # print("Inside saveFile: " + str(file_name)) + if not path.isdir(location): os.makedirs(location) @@ -194,7 +198,7 @@ def sendEmail(email_to, subject, data, template, attachment_jnf_response=None): else: recipient_list = [str(email_to), ] - msg = EmailMultiAlternatives(subject, text_content, email_from, recipient_list) + msg = EmailMultiAlternatives(subject, text_content, email_from,"uttamthummala@gmail.com",bcc=recipient_list) msg.attach_alternative(html_content, "text/html") if attachment_jnf_response: # logger.info(attachment_jnf_response) @@ -246,6 +250,20 @@ def PlacementApplicationConditions(student, placement): logger.warning("Utils - PlacementApplicationConditions: " + str(sys.exc_info())) return False, "_" +def InternshipApplicationConditions(student, internship): + try: + selected_companies = InternshipApplication.objects.filter(student=student, selected=True) + if len(selected_companies)>=1: + print("selected companies > 1") + return False, "You have already secured a Internship" + return True, "Conditions Satisfied" + + except PermissionError as e: + return False, e + except: + logger.warning("Utils - InternshipApplicationConditions: " + str(sys.exc_info())) + return False, "_" + def getTier(compensation_gross, is_psu=False): try: @@ -370,12 +388,27 @@ def placement_eligibility_filters(student, placements): except: logger.warning("Utils - placement_eligibility_filters: " + str(sys.exc_info())) return placements +def internship_eligibility_filters(student, internships): + try: + filtered_internships = [] + for internship in internships.iterator(): + + if InternshipApplicationConditions(student, internship)[0]: + filtered_internships.append(internship) + else: + print("Not applicable") + + return filtered_internships + except: + logger.warning("Utils - internship_eligibility_filters: " + str(sys.exc_info())) + return internships @background_task.background(schedule=2) def send_opening_notifications(placement_id): try: placement = get_object_or_404(Placement, id=placement_id) + emails=[] students = Student.objects.all() for student in students.iterator(): if student.branch in placement.allowed_branch: @@ -393,12 +426,13 @@ def send_opening_notifications(placement_id): "deadline": deadline_datetime.strftime("%A, %-d %B %Y, %-I:%M %p"), "link": PLACEMENT_OPENING_URL.format(id=placement.designation) } - sendEmail(student_user.email, subject, data, NOTIFY_STUDENTS_OPENING_TEMPLATE) + emails.append(student_user.email) + #sendEmail(student_user.email, subject, data, NOTIFY_STUDENTS_OPENING_TEMPLATE) except Http404: logger.warning('Utils - send_opening_notifications: user not found : ' + student.id) except Exception as e: logger.warning('Utils - send_opening_notifications: For Loop' + str(e)) - + sendEmail(emails, subject, data, NOTIFY_STUDENTS_OPENING_TEMPLATE) #handled multiple mailings except: logger.warning('Utils - send_opening_notifications: ' + str(sys.exc_info())) return False diff --git a/setup.sh b/setup.sh old mode 100644 new mode 100755 index aaa20b9..1ba33a4 --- a/setup.sh +++ b/setup.sh @@ -3,7 +3,7 @@ echo "Environment setup complete" cd CDC_Backend python3 manage.py flush --no-input -python3 manage.py makemigrations +python3 manage.py makemigrations APIs python3 manage.py migrate echo "Migrations complete"