Merge branch 'main' into Abhishek28112002-patch-1

This commit is contained in:
karthik mv 2023-05-30 15:03:39 +05:30 committed by GitHub
commit b06455cf5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 995 additions and 131 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

1
.gitignore vendored
View File

@ -139,3 +139,4 @@ dmypy.json
/CDC_Backend/Storage/
.idea
*.pyc
dev.env

BIN
CDC_Backend.zip Normal file

Binary file not shown.

BIN
CDC_Backend/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -4,10 +4,50 @@ from django.shortcuts import resolve_url
from django.utils.html import format_html
from django.utils.safestring import SafeText
from simple_history.admin import SimpleHistoryAdmin
from import_export.admin import ImportExportMixin, ExportMixin
from import_export import resources
from .models import *
admin.site.register(User)
admin.site.register(Admin)
class ArrayFieldListFilter(admin.SimpleListFilter):
"""This is a list filter based on the values
from a model's `keywords` ArrayField. """
title = 'Roles'
parameter_name = 'user_type'
def lookups(self, request, model_admin):
# Very similar to our code above, but this method must return a
# list of tuples: (lookup_value, human-readable value). These
# appear in the admin's right sidebar
keywords = User.objects.values_list("user_type", flat=True)
keywords = [(kw, kw) for sublist in keywords for kw in sublist if kw]
keywords = sorted(set(keywords))
return keywords
def queryset(self, request, queryset):
# when a user clicks on a filter, this method gets called. The
# provided queryset with be a queryset of Items, so we need to
# filter that based on the clicked keyword.
lookup_value = self.value() # The clicked keyword. It can be None!
if lookup_value:
# the __contains lookup expects a list, so...
queryset = queryset.filter(user_type__contains=[lookup_value])
return queryset
class UserAdmin(ImportExportMixin, SimpleHistoryAdmin):
list_display = ('email', 'user_type', 'last_login_time')
list_filter = (ArrayFieldListFilter, 'last_login_time')
search_fields = ('email', 'user_type')
ordering = ('email', 'user_type')
admin.site.register(User, UserAdmin)
admin.site.site_header = "CDC Recruitment Portal"
@ -17,8 +57,12 @@ def model_admin_url(obj, name=None) -> str:
return format_html('<a href="{}">{}</a>', url, name or str(obj))
class StudentAdmin(ImportExportMixin, SimpleHistoryAdmin):
pass
@admin.register(Student)
class Student(admin.ModelAdmin):
class Student(StudentAdmin):
list_display = ("roll_no", "name", "batch", "branch", "phone_number", 'can_apply')
search_fields = ("roll_no", "name", "phone_number")
ordering = ("roll_no", "name", "batch", "branch", "phone_number")
@ -35,17 +79,57 @@ class Student(admin.ModelAdmin):
queryset.update(can_apply=True)
self.message_user(request, "Registered the users")
class PlacementResources(resources.ModelResource):
class Meta:
model = Placement
exclude = ('id','changed_by', 'is_company_details_pdf', 'is_description_pdf',
'is_compensation_details_pdf', 'is_selection_procedure_details_pdf')
class AdminAdmin(ExportMixin, SimpleHistoryAdmin):
resource_class = PlacementResources
class PlacementResources(resources.ModelResource):
class Meta:
model = Placement
exclude = ('id', 'changed_by', 'is_company_details_pdf', 'is_description_pdf',
'is_compensation_details_pdf', 'is_selection_procedure_details_pdf')
class AdminAdmin(ExportMixin, SimpleHistoryAdmin):
resource_class = PlacementResources
class PlacementResources(resources.ModelResource):
class Meta:
model = Placement
exclude = ('id', 'changed_by', 'is_company_details_pdf', 'is_description_pdf',
'is_compensation_details_pdf', 'is_selection_procedure_details_pdf')
class AdminAdmin(ExportMixin, SimpleHistoryAdmin):
resource_class = PlacementResources
@admin.register(Placement)
class Placement(admin.ModelAdmin):
class Placement(AdminAdmin):
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',)
class PlacementApplicationResources(resources.ModelResource):
class Meta:
model = PlacementApplication
exclude = ('id', 'changed_by')
class PlacementAdmin(ExportMixin, SimpleHistoryAdmin):
resource_class = PlacementApplicationResources
@admin.register(PlacementApplication)
class PlacementApplication(admin.ModelAdmin):
class PlacementApplication(PlacementAdmin):
list_display = ('id', 'Placement', 'Student', 'selected')
search_fields = ('id',)
ordering = ('id',)
@ -58,8 +142,18 @@ class PlacementApplication(admin.ModelAdmin):
return model_admin_url(obj.student)
class PrePlacementResources(resources.ModelResource):
class Meta:
model = PrePlacementOffer
exclude = ('id', 'changed_by')
class PrePlacementOfferAdmin(ExportMixin, SimpleHistoryAdmin):
resource_class = PrePlacementResources
@admin.register(PrePlacementOffer)
class PrePlacementOffer(admin.ModelAdmin):
class PrePlacementOffer(PrePlacementOfferAdmin):
list_display = ('company', 'Student', 'accepted')
search_fields = ('company',)
ordering = ('company',)

View File

@ -8,10 +8,12 @@ urlpatterns = [
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"),
path('deleteAdditionalInfo/', adminViews.deleteAdditionalInfo, name="Delete Additional Info"),
path('addAdditionalInfo/', adminViews.addAdditionalInfo, name="Add Additional Info"),
path('getApplications/', adminViews.getApplications, name="Get Applications"),
path("submitApplication/", adminViews.submitApplication, name="Submit Application"),
path('generateCSV/', adminViews.generateCSV, name="Generate CSV"),
path('addPPO/', adminViews.addPPO, name="Add PPO"),
path('getStudentApplication/', adminViews.getStudentApplication, name="Get student application"),
path('getStats/', adminViews.getStats, name="Get Stats"),
]

View File

@ -39,6 +39,7 @@ def markStatus(request, id, email, user_type):
sendEmail(email, subject, data, STUDENT_APPLICATION_STATUS_SELECTED_TEMPLATE)
else:
sendEmail(email, subject, data, STUDENT_APPLICATION_STATUS_NOT_SELECTED_TEMPLATE)
application.chaged_by = get_object_or_404(User, id=id)
application.save()
else:
raise ValueError("Student - " + i[STUDENT_ID] + " didn't apply for this opening")
@ -89,6 +90,7 @@ def updateDeadline(request, id, email, user_type):
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)
opening.save()
return Response({'action': "Update Deadline", 'message': "Deadline Updated"},
status=status.HTTP_200_OK)
@ -102,21 +104,30 @@ def updateDeadline(request, id, email, user_type):
@api_view(['POST'])
@isAuthorized([ADMIN])
@isAuthorized([SUPER_ADMIN])
@precheck([OPENING_ID, OFFER_ACCEPTED])
def updateOfferAccepted(request, id, email, user_type):
try:
data = request.data
print(data)
offer_accepted = data[OFFER_ACCEPTED]
opening = get_object_or_404(Placement, pk=data[OPENING_ID])
opening.offer_accepted = True if data[OFFER_ACCEPTED] == True else False
print(opening.offer_accepted)
opening.save()
if opening.offer_accepted is None:
opening.offer_accepted = offer_accepted == "true"
opening.changed_by = get_object_or_404(User, id=id)
opening.save()
if opening.offer_accepted:
send_opening_notifications(opening.id)
else:
raise ValueError("Offer Status already updated")
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 ValueError as e:
return Response({'action': "Update Offer Accepted", 'message': str(e)},
status=status.HTTP_400_BAD_REQUEST)
except:
logger.warning("Update Offer Accepted: " + str(sys.exc_info()))
return Response({'action': "Update Offer Accepted", 'message': "Something went wrong"},
@ -131,6 +142,7 @@ def updateEmailVerified(request, id, email, user_type):
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.changed_by = get_object_or_404(User, id=id)
opening.save()
return Response({'action': "Update Email Verified", 'message': "Email Verified Updated"},
status=status.HTTP_200_OK)
@ -145,29 +157,55 @@ def updateEmailVerified(request, id, email, user_type):
@api_view(['POST'])
@isAuthorized([ADMIN])
@precheck([OPENING_ID, ADDITIONAL_INFO])
def updateAdditionalInfo(request, id, email, user_type):
@precheck([OPENING_ID, FIELD])
def deleteAdditionalInfo(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]
if data[FIELD] in opening.additional_info:
opening.additional_info.remove(data[FIELD])
opening.changed_by = get_object_or_404(User, id=id)
opening.save()
return Response({'action': "Delete Additional Info", 'message': "Additional Info Deleted"},
status=status.HTTP_200_OK)
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)
raise ValueError("Additional Info Not Found")
except Http404:
return Response({'action': "Update Additional Info", 'message': 'Opening Not Found'},
return Response({'action': "Delete 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"},
return Response({'action': "Delete Additional Info", 'message': "Additional Info not found"},
status=status.HTTP_404_NOT_FOUND)
except Exception as e:
logger.warning("Delete Additional Info: " + str(e))
return Response({'action': "Delete Additional Info", 'message': "Something went wrong"},
status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@isAuthorized([ADMIN])
@precheck([OPENING_ID, FIELD])
def addAdditionalInfo(request, id, email, user_type):
try:
data = request.data
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()
return Response({'action': "Add Additional Info", 'message': "Additional Info Added"},
status=status.HTTP_200_OK)
else:
raise ValueError("Additional Info Found")
except Http404:
return Response({'action': "Add Additional Info", 'message': 'Opening Not Found'},
status=status.HTTP_404_NOT_FOUND)
except ValueError:
return Response({'action': "Add Additional Info", 'message': "Additional Info already found"},
status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
logger.warning("Update Additional Info: " + str(e))
return Response({'action': "Update Additional Info", 'message': "Something went wrong"},
logger.warning("Add Additional Info: " + str(e))
return Response({'action': "Add Additional Info", 'message': "Something went wrong"},
status=status.HTTP_400_BAD_REQUEST)
@ -199,7 +237,7 @@ def submitApplication(request, id, email, user_type):
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()
@ -216,7 +254,16 @@ def submitApplication(request, id, email, user_type):
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)
else:
@ -235,7 +282,17 @@ def submitApplication(request, id, email, user_type):
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:
@ -300,7 +357,6 @@ def generateCSV(request, id, email, user_type):
status=status.HTTP_200_OK)
except:
logger.warning("Create csv: " + str(sys.exc_info()))
print(sys.exc_info())
return Response({'action': "Create csv", 'message': "Something Went Wrong"},
status=status.HTTP_400_BAD_REQUEST)
@ -313,7 +369,7 @@ def addPPO(request, id, email, user_type):
data = request.data
PPO = PrePlacementOffer()
PPO.company = data[COMPANY_NAME]
PPO.compensation = data[COMPENSATION_GROSS]
PPO.compensation = int(data[COMPENSATION_GROSS])
if data[OFFER_ACCEPTED] == "true":
PPO.accepted = True
elif data[OFFER_ACCEPTED] == "false":
@ -322,15 +378,15 @@ def addPPO(request, id, email, user_type):
PPO.accepted = None
PPO.student = get_object_or_404(Student, id=data[STUDENT_ID])
PPO.designation = data[DESIGNATION]
PPO.tier = data[TIER]
PPO.tier = int(data[TIER])
if COMPENSATION_DETAILS in data:
PPO.compensation_details = data[COMPENSATION_DETAILS]
PPO.changed_by = get_object_or_404(User, id=id)
PPO.save()
return Response({'action': "Add PPO", 'message': "PPO added"},
status=status.HTTP_200_OK)
except:
logger.warning("Add PPO: " + str(sys.exc_info()))
print(sys.exc_info())
return Response({'action': "Add PPO", 'message': "Something Went Wrong"},
status=status.HTTP_400_BAD_REQUEST)
@ -373,3 +429,205 @@ def getStudentApplication(request, id, email, user_type):
logger.warning("Get Student Application: " + str(sys.exc_info()))
return Response({'action': "Get Student Application", 'message': "Something Went Wrong"},
status.HTTP_400_BAD_REQUEST)
@api_view(['GET'])
@isAuthorized(allowed_users=[ADMIN])
def getStats(request, id, email, user_type):
try:
stats = []
placement_ids = {}
tier_count = {
"CSE": {
"1":0,
"2":0,
"3":0,
"4":0,
"5":0,
"6":0,
"7":0,
"psu":0,
},
"EE": {
"1":0,
"2":0,
"3":0,
"4":0,
"5":0,
"6":0,
"7":0,
"psu":0,
},
"MMAE": {
"1":0,
"2":0,
"3":0,
"4":0,
"5":0,
"6":0,
"7":0,
"psu":0,
},
"Total": {
"1":0,
"2":0,
"3":0,
"4":0,
"5":0,
"6":0,
"7":0,
"psu":0,
},
}
number_of_students_placed = {
"CSE": 0,
"EE": 0,
"MMAE": 0,
"Total": 0,
}
number_of_students_with_multiple_offers = 0
number_of_students_with_no_offers = {
"CSE": 0,
"EE": 0,
"MMAE": 0,
"Total": 0,
}
max_CTC = {
"CSE": 0,
"EE": 0,
"MMAE": 0
}
average_CTC = {
"CSE": 0,
"EE": 0,
"MMAE": 0
}
count = {
"CSE": 0,
"EE": 0,
"MMAE": 0
}
students = Student.objects.all().order_by("roll_no")
for student in students.iterator():
applications = PlacementApplication.objects.filter(student=student, selected=True)
ppos = PrePlacementOffer.objects.filter(student=student, accepted=True)
first_offer_data = None
second_offer_data = None
# get the first and second offer
offers = []
offers.extend(applications)
offers.extend(ppos)
if len(offers) == 0:
number_of_students_with_no_offers[student.branch] += 1
number_of_students_with_no_offers["Total"] += 1
else:
number_of_students_placed[student.branch] += 1
number_of_students_placed["Total"] += 1
if len(offers) > 1:
number_of_students_with_multiple_offers += 1
for offer in offers:
if type(offer) == PrePlacementOffer:
if first_offer_data is None:
first_offer_data = {
"id": offer.id,
"company": offer.company,
"compensation": offer.compensation,
"tier": offer.tier,
"type": "PPO",
}
elif second_offer_data is None:
second_offer_data = {
"id": offer.id,
"company": offer.company,
"compensation": offer.compensation,
"tier": offer.tier,
"type": "PPO",
}
elif type(offer) == PlacementApplication:
if first_offer_data is None:
first_offer_data = {
"id": offer.placement.id,
"company": offer.placement.company_name,
"compensation": offer.placement.compensation_CTC,
"tier": offer.placement.tier,
"type": "Placement",
}
elif second_offer_data is None:
second_offer_data = {
"id": offer.placement.id,
"company": offer.placement.company_name,
"compensation": offer.placement.compensation_CTC,
"tier": offer.placement.tier,
"type": "Placement",
}
data = {
"id": student.id,
"name": student.name,
"roll_no": student.roll_no,
"batch": student.batch,
"branch": student.branch,
"cpi": student.cpi,
"first_offer": first_offer_data['company'] if first_offer_data is not None else None,
"first_offer_tier": first_offer_data['tier'] if first_offer_data is not None else None,
"first_offer_compensation": first_offer_data['compensation'] if first_offer_data is not None else None,
"second_offer": second_offer_data['company'] if second_offer_data is not None else None,
"second_offer_tier": second_offer_data['tier'] if second_offer_data is not None else None,
"second_offer_compensation": second_offer_data['compensation'] if second_offer_data is not None else None,
}
if first_offer_data is not None:
tier_count[student.branch][first_offer_data['tier']] += 1
tier_count['Total'][first_offer_data['tier']] += 1
max_CTC[student.branch] = max(max_CTC[student.branch], first_offer_data['compensation'])
average_CTC[student.branch] += first_offer_data['compensation']
count[student.branch] += 1
if first_offer_data['type'] == "Placement":
placement_ids[first_offer_data['company']] = first_offer_data['id']
if second_offer_data is not None:
tier_count[student.branch][second_offer_data['tier']] += 1
tier_count['Total'][second_offer_data['tier']] += 1
max_CTC[student.branch] = max(max_CTC[student.branch], second_offer_data['compensation'])
average_CTC[student.branch] += second_offer_data['compensation']
count[student.branch] += 1
if second_offer_data['type'] == "Placement":
placement_ids[second_offer_data['company']] = second_offer_data['id']
stats.append(data)
for branch in average_CTC:
if count[branch] > 0:
average_CTC[branch] /= count[branch]
# round off to 2 decimal places
average_CTC[branch] = round(average_CTC[branch], 2)
else:
average_CTC[branch] = 0
return Response({'action': "Get Stats", 'message': "Stats fetched", 'stats': stats, 'placement_ids': placement_ids,
"tier_count": {br: tier_count[br].values() for br in tier_count},
"number_of_students_placed": number_of_students_placed,
"number_of_students_with_multiple_offers": number_of_students_with_multiple_offers,
"number_of_students_with_no_offers": number_of_students_with_no_offers,
"max_CTC": max_CTC,
"average_CTC": average_CTC,
},
status=status.HTTP_200_OK)
except:
logger.warning("Get Stats: " + str(sys.exc_info()))
print(sys.exc_info())
return Response({'action': "Get Stats", 'message': "Something Went Wrong"},
status=status.HTTP_400_BAD_REQUEST)

View File

@ -194,7 +194,7 @@ def addPlacement(request):
'%d-%m-%Y').date()
# Only Allowing Fourth Year for Placement
opening.allowed_batch = [FOURTH_YEAR, ]
opening.allowed_batch = [2017, 2018, 2019, 2020, 2021]
# Check if allowed_branch are valid
if data[ALLOWED_BRANCH] is None:
raise ValueError('Allowed Branch cannot be empty')
@ -230,9 +230,14 @@ def addPlacement(request):
status=status.HTTP_200_OK)
except ValueError as e:
store_all_files(request)
exception_email(data)
logger.info("ValueError in addPlacement: " + str(e))
return Response({'action': "Add Placement", 'message': str(e)},
status=status.HTTP_400_BAD_REQUEST)
except:
store_all_files(request)
exception_email(data)
logger.warning("Add New Placement: " + str(sys.exc_info()))
return Response({'action': "Add Placement", 'message': "Something went wrong"},
status=status.HTTP_400_BAD_REQUEST)
@ -276,11 +281,9 @@ def verifyEmail(request):
data = {
"designation": opening.designation,
"opening_type": PLACEMENT,
"opening_link": PLACEMENT_OPENING_URL.format(id=opening.id), # Some Changes here too
"company_name": opening.company_name,
}
json_data = json.dumps(data, default=str)
sendEmail(opening.email, COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT.format(id=opening.id), json_data,
sendEmail([opening.email, CDC_MAIl_ADDRESS], COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT.format(id=opening.id), data,
COMPANY_OPENING_SUBMITTED_TEMPLATE, attachment_jnf_respone)
return Response({'action': "Verify Email", 'message': "Email Verified Successfully"},

View File

@ -1,20 +1,24 @@
import os
BRANCH_CHOICES = [
["CSE", "CSE"],
["EE", "EE"],
["ME", "ME"],
['MMAE', 'MMAE'],
['EP', 'EP'],
]
BRANCHES = [
"CSE",
"EE",
"ME",
"MMAE",
"EP"
]
BATCH_CHOICES = [
["2021", "2021"],
["2020", "2020"],
["2019", "2019"],
["2018", "2018"]
["2018", "2018"],
["2017", "2017"],
]
OFFER_CITY_TYPE = [
@ -29,31 +33,42 @@ TIERS = [
['3', 'Tier 3'],
['4', 'Tier 4'],
['5', 'Tier 5'],
['6', 'Tier 6']
['6', 'Tier 6'],
['7', 'Tier 7'],
]
DEGREE_CHOICES = [
['bTech', 'B.Tech'],
['ms/phd', 'MS/ PhD'],
]
TOTAL_BRANCHES = 4 # Total No of Branches
TOTAL_BATCHES = 4 # Total No of Batches
TOTAL_BATCHES = 5 # Total No of Batches
CDC_MAIl_ADDRESS = 'cdc@iitdh.ac.in'
# To be Configured Properly
CLIENT_ID = "956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleusercontent.com" # Google Login Client ID
CLIENT_ID = os.environ.get('GOOGLE_OAUTH_CLIENT_ID') # Google Login Client ID
# To be Configured Properly
PLACEMENT_OPENING_URL = "https://www.googleapis.com/auth/adwords/{id}" # On frontend, this is the URL to be opened
LINK_TO_STORAGE_COMPANY_ATTACHMENT = "http://localhost/storage/Company_Attachments/"
LINK_TO_STORAGE_RESUME = "http://localhost/storage/Resumes/"
LINK_TO_APPLICATIONS_CSV = "http://localhost/storage/Application_CSV/"
LINK_TO_EMAIl_VERIFICATION_API = "http://localhost:3000/company/verifyEmail?token={token}"
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'
COMPANY = ''
SUPER_ADMIN = 's_admin'
COMPANY = 'company'
TIER = 'tier'
# To be Configured Properly
FOURTH_YEAR = '2019'
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
@ -70,6 +85,7 @@ 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"
@ -133,21 +149,25 @@ EXCLUDE_IN_PDF = ['id', 'is_company_details_pdf', 'offer_accepted', 'is_descript
'email_verified', 'created_at']
SPECIAL_FORMAT_IN_PDF = ['website', 'company_details_pdf_names', 'description_pdf_names',
'compensation_details_pdf_names',
'selection_procedure_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_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', ]
PDF_FILES_SERVING_ENDPOINT = 'http://localhost/storage/Company_Attachments/' # TODO: Change this to actual URL

View File

@ -1,6 +1,7 @@
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 *
@ -10,9 +11,10 @@ from .constants import *
class User(models.Model):
email = models.EmailField(primary_key=True, blank=False, max_length=JNF_TEXT_MAX_CHARACTER_COUNT)
id = models.CharField(blank=False, max_length=25)
id = models.CharField(blank=False, max_length=25, db_index=True)
user_type = ArrayField(models.CharField(blank=False, max_length=10), size=4, default=list, blank=False)
last_login_time = models.DateTimeField(default=timezone.now)
history = HistoricalRecords()
class Meta:
verbose_name_plural = "User"
@ -30,18 +32,46 @@ 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')
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)
def __str__(self):
return str(self.roll_no)
@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 Admin(models.Model):
id = models.CharField(blank=False, max_length=15, primary_key=True)
name = models.CharField(blank=False, max_length=JNF_TEXT_MAX_CHARACTER_COUNT)
changed_by = models.ForeignKey(User, blank=True, on_delete=models.RESTRICT, default=None, null=True)
history = HistoricalRecords(user_model=User)
@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 two_day_after_today():
return timezone.now() + timezone.timedelta(days=2)
# round off to nearest day
return timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) + timezone.timedelta(days=2)
class Placement(models.Model):
@ -53,7 +83,7 @@ class Placement(models.Model):
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)
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)
@ -114,6 +144,8 @@ class Placement(models.Model):
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)
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:
@ -156,7 +188,19 @@ class Placement(models.Model):
if self.other_requirements is not None:
self.other_requirements = self.other_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)]
self.additional_info = [info.strip()[:JNF_TEXTMEDIUM_MAX_CHARACTER_COUNT] for info in
list(self.additional_info)]
@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 '''
@ -179,6 +223,8 @@ class PlacementApplication(models.Model):
selected = models.BooleanField(null=True, default=None, blank=True)
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 '''
@ -188,6 +234,17 @@ class PlacementApplication(models.Model):
return super(PlacementApplication, 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 = "Placement Applications"
unique_together = ('placement_id', 'student_id')
@ -204,5 +261,31 @@ class PrePlacementOffer(models.Model):
compensation = models.IntegerField(blank=False) # Job - Per Year
compensation_details = models.CharField(blank=True, max_length=200)
tier = models.CharField(blank=False, choices=TIERS, max_length=10)
designation = models.CharField(blank=False, max_length=25, default=None, null=True)
designation = models.CharField(blank=False, max_length=100, default=None, null=True)
accepted = models.BooleanField(default=None, null=True)
changed_by = models.ForeignKey(User, blank=False, on_delete=models.RESTRICT, default=None, null=True)
history = HistoricalRecords(user_model=User)
@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 Contributor(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, blank=False, default="")
email = models.EmailField(max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, blank=False, default="", unique=True)
github_id = models.CharField(max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, blank=False, default="", unique=True)
linkedin = models.CharField(max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, unique=True, null=True)
commits = models.IntegerField(blank=False, default=0)
image = models.CharField(max_length=JNF_SMALLTEXT_MAX_CHARACTER_COUNT, blank=False, default="", null=True)
def __str__(self):
return self.name

View File

@ -162,7 +162,8 @@ class PlacementApplicationSerializer(serializers.ModelSerializer):
return data
def get_resume_link(self, obj):
ele = {'link': LINK_TO_STORAGE_RESUME + urllib.parse.quote(obj.id + "/" + obj.resume), 'name': obj.resume}
ele = {'link': LINK_TO_STORAGE_RESUME + urllib.parse.quote(str(obj.student.roll_no) + "/" + obj.resume),
'name': obj.resume}
return ele
class Meta:
@ -185,3 +186,9 @@ class PlacementApplicationSerializerForAdmin(serializers.ModelSerializer):
class Meta:
model = PlacementApplication
exclude = ['placement', 'resume']
class ContributorSerializer(serializers.ModelSerializer):
class Meta:
model = Contributor
fields = '__all__'

View File

@ -9,4 +9,6 @@ urlpatterns = [
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("getContributorStats/", studentViews.getContributorStats, name="Get Contributor Stats"),
]

View File

@ -40,17 +40,23 @@ def addResume(request, id, email, user_type):
student = get_object_or_404(Student, id=id)
files = request.FILES
if len(student.resumes) >= MAX_RESUMES_PER_STUDENT:
raise PermissionError('Max Number of Resumes limit reached')
file = files['file']
destination_path = STORAGE_DESTINATION_RESUMES + str(student.roll_no) + "/"
file_name = saveFile(file, destination_path)
student.resumes.append(file_name)
student.changed_by = get_object_or_404(User, id=id)
student.save()
return Response({'action': "Upload Resume", 'message': "Resume Added"},
status=status.HTTP_200_OK)
except Http404:
return Response({'action': "Upload Resume", 'message': 'Student Not Found'},
status=status.HTTP_404_NOT_FOUND)
except PermissionError:
return Response({'action': "Upload Resume", 'message': 'Max Number of Resumes limit reached'},
status=status.HTTP_400_BAD_REQUEST)
except:
if path.exists(destination_path):
logger.error("Upload Resume: Error in Saving Resume")
@ -71,7 +77,10 @@ def getDashboard(request, id, email, user_type):
allowed_branch__contains=[studentDetails.branch],
deadline_datetime__gte=datetime.datetime.now(),
offer_accepted=True, email_verified=True).order_by('deadline_datetime')
placementsdata = PlacementSerializerForStudent(placements, many=True).data
filtered_placements = placement_eligibility_filters(studentDetails, placements)
placementsdata = PlacementSerializerForStudent(filtered_placements, many=True).data
placementApplications = PlacementApplication.objects.filter(student_id=id)
placementApplications = PlacementApplicationSerializer(placementApplications, many=True).data
return Response(
@ -83,7 +92,6 @@ def getDashboard(request, id, email, user_type):
status=status.HTTP_404_NOT_FOUND)
except:
logger.warning("Get Dashboard -Student: " + str(sys.exc_info()))
print(sys.exc_info())
return Response({'action': "Get Dashboard - Student", 'message': "Something Went Wrong"},
status=status.HTTP_400_BAD_REQUEST)
@ -104,6 +112,7 @@ def deleteResume(request, id, email, user_type):
if path.exists(destination_path):
# remove(destination_path)
student.resumes.remove(file_name)
student.changed_by = get_object_or_404(User, id=id)
student.save()
return Response({'action': "Delete Resume", 'message': "Resume Deleted"},
status=status.HTTP_200_OK)
@ -177,7 +186,7 @@ def submitApplication(request, id, email, user_type):
}
subject = STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT.format(company_name=opening.company_name)
sendEmail(email, subject, data, STUDENT_APPLICATION_SUBMITTED_TEMPLATE)
application.changed_by = get_object_or_404(User, id=id)
application.save()
return Response({'action': "Submit Application", 'message': "Application Submitted"},
status=status.HTTP_200_OK)
@ -192,8 +201,49 @@ def submitApplication(request, id, email, user_type):
status=status.HTTP_404_NOT_FOUND)
except:
logger.warning("Submit Application: " + str(sys.exc_info()))
print(traceback.format_exc())
print(sys.exc_info())
return Response({'action': "Submit Application", 'message': "Something Went Wrong"},
status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@isAuthorized(allowed_users=[STUDENT])
@precheck(required_data=[APPLICATION_ID])
def deleteApplication(request, id, email, user_type):
try:
data = request.data
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"},
status=status.HTTP_200_OK)
except Http404 as e:
return Response({'action': "Delete Application", 'message': str(e)},
status=status.HTTP_404_NOT_FOUND)
except PermissionError as e:
return Response({'action': "Delete Application", 'message': str(e)},
status=status.HTTP_403_FORBIDDEN)
except:
logger.warning("Delete Application: " + str(sys.exc_info()))
return Response({'action': "Delete Application", 'message': "Something Went Wrong"},
status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET'])
@isAuthorized(allowed_users='*')
def getContributorStats(request, id, email, user_type):
try:
contributors = Contributor.objects.all()
serialized_data = ContributorSerializer(contributors, many=True).data
return Response({'action': "Get Contributor Stats", 'message': "Contributor Stats Fetched",
'data': serialized_data},
status=status.HTTP_200_OK)
except:
logger.warning("Get Contributor Stats: " + str(sys.exc_info()))
return Response({'action': "Get Contributor Stats", 'message': "Something Went Wrong"},
status=status.HTTP_400_BAD_REQUEST)

View File

@ -12,6 +12,7 @@ from os import path, remove
import background_task
import jwt
import pdfkit
import pytz
import requests as rq
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
@ -27,7 +28,7 @@ from rest_framework import status
from rest_framework.response import Response
from .constants import *
from .models import User, PrePlacementOffer, PlacementApplication, Placement
from .models import User, PrePlacementOffer, PlacementApplication, Placement, Student
logger = logging.getLogger('db')
@ -57,6 +58,7 @@ def precheck(required_data=None):
return view_func(request, *args, **kwargs)
except:
logger.warning("Pre check: " + str(sys.exc_info()))
return Response({'action': "Pre check", 'message': "Something went wrong"},
status=status.HTTP_400_BAD_REQUEST)
@ -77,7 +79,6 @@ def isAuthorized(allowed_users=None):
token_id = headers['HTTP_AUTHORIZATION'][7:]
idinfo = id_token.verify_oauth2_token(token_id, requests.Request(), CLIENT_ID)
email = idinfo[EMAIL]
print(email)
user = get_object_or_404(User, email=email)
if user:
user.last_login_time = timezone.now()
@ -138,7 +139,7 @@ def saveFile(file, location):
return file_name
@background_task.background(schedule=5)
@background_task.background(schedule=2)
def sendEmail(email_to, subject, data, template, attachment_jnf_response=None):
try:
if not isinstance(data, dict):
@ -147,12 +148,15 @@ def sendEmail(email_to, subject, data, template, attachment_jnf_response=None):
text_content = strip_tags(html_content)
email_from = settings.EMAIL_HOST_USER
recipient_list = [str(email_to), ]
if type(email_to) is list:
recipient_list = [str(email) for email in email_to]
else:
recipient_list = [str(email_to), ]
msg = EmailMultiAlternatives(subject, text_content, email_from, recipient_list)
msg.attach_alternative(html_content, "text/html")
if attachment_jnf_response:
logger.info(attachment_jnf_response)
# logger.info(attachment_jnf_response)
pdf = pdfkit.from_string(attachment_jnf_response['html'], False,
options={"--enable-local-file-access": "", '--dpi': '96'})
msg.attach(attachment_jnf_response['name'], pdf, 'application/pdf')
@ -160,7 +164,6 @@ def sendEmail(email_to, subject, data, template, attachment_jnf_response=None):
return True
except:
logger.error("Send Email: " + str(sys.exc_info()))
print(str(sys.exc_info()[1]))
return False
@ -169,31 +172,36 @@ def PlacementApplicationConditions(student, placement):
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(student=student, accepted=True)
# find lenght of PPO
print(PPO)
print(len(PPO), "ere")
PPO_PSU = [i for i in PPO if i.tier == 'psu']
# find length of PPO
if len(selected_companies) + len(PPO) >= MAX_OFFERS_PER_STUDENT:
raise PermissionError("Max Applications Reached for the Season")
if len(selected_companies_PSU) > 0:
raise PermissionError('Selected for PSU Can\'t apply anymore')
if len(PPO_PSU) > 0:
raise PermissionError('Selected for PSU Can\'t apply anymore')
if placement.tier == 'psu':
return True, "Conditions Satisfied"
for i in selected_companies:
print(int(i.placement.tier) < int(placement.tier), int(i.placement.tier), int(placement.tier))
if int(i.placement.tier) < int(placement.tier):
return False, "Can't apply for this tier"
for i in PPO:
if int(i.tier) < int(placement.tier):
return False, "Can't apply for this tier"
if student.degree != 'bTech' and not placement.rs_eligible:
raise PermissionError("Can't apply for this placement")
return True, "Conditions Satisfied"
except PermissionError as e:
return False, e
except:
print(sys.exc_info())
print(traceback.format_exc())
logger.warning("Utils - PlacementApplicationConditions: " + str(sys.exc_info()))
return False, "_"
@ -231,7 +239,6 @@ def getTier(compensation_gross, is_psu=False):
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, "_"
@ -248,36 +255,35 @@ def generateOneTimeVerificationLink(email, opening_id, opening_type):
link = LINK_TO_EMAIl_VERIFICATION_API.format(token=token)
return True, link
except:
print(sys.exc_info())
logger.warning("Utils - generateOneTimeVerificationLink: " + str(sys.exc_info()))
return False, "_"
def verify_recaptcha(request):
try:
print(settings.RECAPTCHA_SECRET_KEY)
data = {
'secret': settings.RECAPTCHA_SECRET_KEY,
'response': request
}
print(data)
r = rq.post('https://www.google.com/recaptcha/api/siteverify', data=data)
result = r.json()
# logger.info("Recaptcha Response: " + str(result)+"request: "+str(data))
print(result, "Result")
if not result['success']:
logger.warning("Utils - verify_recaptcha: " + str(result))
return result['success']
except:
# get exception line number
print(sys.exc_info())
print(traceback.format_exc())
logger.warning("Utils - verify_recaptcha: " + str(sys.exc_info()))
return False, "_"
def opening_description_table_html(opening):
details = model_to_dict(opening, fields=[field.name for field in Placement._meta.fields],
exclude=EXCLUDE_IN_PDF)
# check typing of opening
if isinstance(opening, Placement):
details = model_to_dict(opening, fields=[field.name for field in Placement._meta.fields],
exclude=EXCLUDE_IN_PDF)
# check typing of opening is query dict
else: # if isinstance(opening, QueryDict):
details = opening
keys = list(details.keys())
newdetails = {}
for key in keys:
@ -287,7 +293,7 @@ def opening_description_table_html(opening):
if key == 'website':
details[key] = {"details": details[key], "type": ["link"]}
else:
details[key] = {"details": details[key]["details"], "type": ["list", "link"],
details[key] = {"details": [item for item in details[key]["details"]], "type": ["list", "link"],
"link": PDF_FILES_SERVING_ENDPOINT + opening.id + "/"}
new_key = key.replace('_', ' ')
if new_key.endswith(' names'):
@ -295,9 +301,95 @@ def opening_description_table_html(opening):
new_key = new_key.capitalize()
newdetails[new_key] = details[key]
imagepath = os.path.abspath('./templates/image.png')
print(imagepath)
data = {
"data": newdetails,
"imgpath": imagepath
}
return render_to_string(COMPANY_JNF_RESPONSE_TEMPLATE, data)
def placement_eligibility_filters(student, placements):
try:
filtered_placements = []
for placement in placements.iterator():
if PlacementApplicationConditions(student, placement)[0]:
filtered_placements.append(placement)
return filtered_placements
except:
logger.warning("Utils - placement_eligibility_filters: " + str(sys.exc_info()))
return placements
@background_task.background(schedule=2)
def send_opening_notifications(placement_id):
try:
placement = get_object_or_404(Placement, id=placement_id)
students = Student.objects.all()
for student in students.iterator():
if student.branch in placement.allowed_branch:
if student.degree == 'bTech' or placement.rs_eligible is True:
if PlacementApplicationConditions(student, placement)[0]:
try:
student_user = get_object_or_404(User, id=student.id)
subject = NOTIFY_STUDENTS_OPENING_TEMPLATE_SUBJECT.format(
company_name=placement.company_name)
deadline_datetime = placement.deadline_datetime.astimezone(pytz.timezone('Asia/Kolkata'))
data = {
"company_name": placement.company_name,
"opening_type": 'Placement',
"designation": placement.designation,
"deadline": deadline_datetime.strftime("%A, %-d %B %Y, %-I:%M %p"),
"link": PLACEMENT_OPENING_URL.format(id=placement.id)
}
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))
except:
logger.warning('Utils - send_opening_notifications: ' + str(sys.exc_info()))
return False
def exception_email(opening):
opening = opening.dict()
data = {
"designation": opening["designation"],
"opening_type": PLACEMENT,
"company_name": opening["company_name"],
}
pdfhtml = opening_description_table_html(opening)
name = opening["company_name"] + '_jnf_response.pdf'
attachment_jnf_respone = {
"name": name,
"html": pdfhtml,
}
sendEmail(CDC_MAIl_ADDRESS, COMPANY_OPENING_ERROR_TEMPLATE.format(company_name=opening["company_name"]), data,
COMPANY_OPENING_SUBMITTED_TEMPLATE, attachment_jnf_respone)
def store_all_files(request):
files = request.FILES
data = request.data
# save all the files
if files:
# company details pdf
for file in files.getlist(COMPANY_DETAILS_PDF):
file_location = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + "temp" + '/'
saveFile(file, file_location)
# compensation details pdf
for file in files.getlist(COMPENSATION_DETAILS_PDF):
file_location = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + "temp" + '/'
saveFile(file, file_location)
# selection procedure details pdf
for file in files.getlist(SELECTION_PROCEDURE_DETAILS_PDF):
file_location = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + "temp" + '/'
saveFile(file, file_location)
# description pdf
for file in files.getlist(DESCRIPTION_PDF):
file_location = STORAGE_DESTINATION_COMPANY_ATTACHMENTS + "temp" + '/'
saveFile(file, file_location)

View File

@ -15,7 +15,6 @@ 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, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -24,13 +23,14 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'e_i2g3z!y4+p3dwm%k9k=zmsot@aya-0$mmetgxz4mp#8_oy#*'
SECRET_KEY = os.environ.get("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = os.environ.get('DEBUG') == "True"
ALLOWED_HOSTS = ['cdc-iitdh.herokuapp.com/', 'localhost', '192.168.29.199']
ALLOWED_HOSTS = ['cdc.iitdh.ac.in', 'localhost']
ADMINS = [('Gowtham Sai', '190010036@iitdh.ac.in'), ('Karthik Mv', '200010030@iitdh.ac.in')]
# Application definition
INSTALLED_APPS = [
@ -44,7 +44,10 @@ INSTALLED_APPS = [
'rest_framework',
'corsheaders',
'django_db_logger',
'background_task'
'background_task',
'simple_history',
'import_export',
"django_extensions"
]
MIDDLEWARE = [
@ -54,10 +57,11 @@ MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'corsheaders.middleware.CorsPostCsrfMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'simple_history.middleware.HistoryRequestMiddleware',
]
ROOT_URLCONF = 'CDC_Backend.urls'
@ -144,13 +148,14 @@ STATICFILES_DIR = (
os.path.join(BASE_DIR, 'static'),
)
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = [
"https://cdc.iitdh.ac.in",
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://localhost:8000",
"http://127.0.0.1:8000"
"https://localhost:3000"
]
CORS_REPLACE_HTTPS_REFERER = True
CSRF_TRUSTED_ORIGINS = [ "https://cdc.iitdh.ac.in", "http://cdc.iitdh.ac.in"]
# EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = './emails'
@ -181,14 +186,13 @@ LOGGING = {
'class': 'django_db_logger.db_log_handler.DatabaseLogHandler'
},
'mail_admins': {
'level': 'ERROR',
'level': 'WARNING',
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
}
},
'loggers': {
'db': {
'handlers': ['db_log'],
'handlers': ['db_log', 'mail_admins'],
'level': 'DEBUG'
}
}

View File

@ -6,7 +6,6 @@ It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application

1
CDC_Backend/run_prod.sh Normal file
View File

@ -0,0 +1 @@
gunicorn --certfile=/home/cdc/Desktop/1f9476e3959ebe60.crt --keyfile=/home/cdc/Desktop/star_iitdh_key.key --bind localhost:8000 CDC_Backend.wsgi --access-logfile access.log --error-logfile error.log &

View File

View File

@ -0,0 +1,70 @@
from APIs.models import Contributor
from django.shortcuts import get_object_or_404
import time
from dotenv import load_dotenv
import requests
import os
load_dotenv("../dev.env")
owner = 'CDC-IITDH'
access_token = os.environ.get("GITHUB_ACCESS_TOKEN")
headers = {'Authorization': "Token " + access_token}
maxRetires = 10
REPEAT_AFTER = 60 * 15 # 15 minutes
def getStats():
try:
stats = {}
repos = ['cdc-placement-website-backend', 'cdc-placement-website-frontend']
for i in repos:
try:
repo_name = i
print(repo_name)
url = f"https://api.github.com/repos/{owner}/{repo_name}/stats/contributors"
retry = 0
contributors = []
while True:
if retry > maxRetires:
break
req = requests.get(url, headers=headers)
contributors = req.json()
if req.status_code != 200:
print("ERROR:", req.json())
retry += 1
elif len(contributors):
break
retry += 1
time.sleep(1)
for contributor in contributors:
if contributor['author']['login'] not in stats:
stats[contributor['author']['login']] = 0
stats[contributor['author']['login']] += contributor['total']
except Exception as e:
print(e)
for i in stats:
try:
contributor = get_object_or_404(Contributor, github_id=i)
contributor.commits = stats[i]
contributor.save()
except:
pass
stats = sorted(stats.items(), key=lambda x: x[1], reverse=True)
for i in stats:
print(i)
except Exception as e:
print(e)
return stats
def run():
while True:
getStats()
print("Sleeping for", REPEAT_AFTER, "seconds")
time.sleep(REPEAT_AFTER)
print("Running send_reminder_mails()")
run()

View File

@ -44,7 +44,7 @@
{% for item in value.details %}
<li>
{% if 'link' in value.type and value.link %}
<a href="{{ value.link|add:item}}">{{ item }}</a>
<a href="{{ value.link|add:item}}">{{ item|slice:"16:" }}</a>
{% elif 'link' in value.type %}
<a href="{{ item }}">{{ item }}</a>
{% else %}

View File

@ -65,8 +65,7 @@
<p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family: 'Roboto', sans-serif;">
We have received your <b>{{ opening_type }}</b> notification for a
<b>{{ designation }}</b> offer at <b>
{{ company_name }}</b>. Click <a href="{{ opening_link }}">here</a> to view your
notification.
{{ company_name }}</b>.
</p>
<p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family: 'Roboto', sans-serif;">
We will keep you informed with the updates. If you have any queries, please

View File

@ -50,12 +50,12 @@
<p style="text-indent: 4ch; margin:0 0 12px 0;font-size:16px;line-height:24px;font-family: 'Roboto', sans-serif;">
CDC is excited to announce that <b>{{ company_name }}</b> is interested in
recruiting <b>{{ designation }}</b> from IIT Dharwad.
More details can be found in the CDC-webportal.
More details can be found in the CDC Web Portal.
</p>
<p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family: 'Roboto', sans-serif;">
Interested students can apply before <b>{{ deadline }}</b> in the <a
href="{{ link }}">CDC-webportal</a>.
href="{{ link }}">CDC-Web Portal</a>.
</p>
</td>
</tr>

View File

@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="x-apple-disable-message-reformatting">
<title></title>
<!--[if mso]>
<noscript>
<xml>
<o:OfficeDocumentSettings>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
</noscript>
<![endif]-->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link rel="shortcut icon" href="favicon.ico"/>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap" rel="stylesheet">
<style>
table, td, div, h1, p {
font-family: 'Roboto', sans-serif;
}
</style>
</head>
<body style="margin:0;padding:0;">
<table role="presentation" style="width:100%;border-collapse:collapse;border:0;border-spacing:0;background:#ffffff;">
<tr>
<td align="center" style="padding:0;">
<table role="presentation"
style="width:602px;border-collapse:collapse;border:1px solid #334878;border-spacing:0;text-align:left;">
<tr>
<td align="center" style="padding:40px 0 30px 0;background:#334878;">
<img src="https://drive.google.com/uc?id=1QTA6dB7jnsZfU1kzyUqfD_2V5xODpWFt" alt="" width="200"
style="height:auto;display:block;"/>
</td>
</tr>
<tr>
<td style="padding:36px 30px 42px 30px;">
<table role="presentation"
style="width:100%;border-collapse:collapse;border:0;border-spacing:0;">
<tr>
<td style="padding:0 0 36px 0;color:#153643;">
<h1 style="font-size:24px;margin:0 0 20px 0;font-family: 'Roboto', sans-serif;
">Hello, {{ name }}</h1>
<p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family: 'Roboto', sans-serif;">
We have received some update in your application for a <b>{{ application_type }}</b> offer at
<b>
{{ company_name }}</b>.
<table style="border:solid 1px; margin: auto; text-align: center;width: 80%;
border-radius:15px; background-color: #e0e3ee">
<tr>
<td style="padding:8px 10px;color:#153643; "> resume:</td>
<td style="padding:8px 10px;color:#153643;">{{ resume }}</td>
</tr>
</table>
{% if additional_info_items %}
We received these additional details
<br>
<table style="border:solid 1px; margin: auto; text-align: center;width: 80%;
border-radius:15px; background-color: #e0e3ee">
<p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family:
'Roboto', sans-serif;text-align: center">
{% for i,j in additional_info_items.items %}
<tr>
<td style="padding:8px 10px;color:#153643; ">{{ i }}:</td>
<td style="padding:8px 10px;color:#153643;">{{ j }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
</p>
</p>
<p style="margin:0 0 12px 0;font-size:16px;line-height:24px;font-family: 'Roboto', sans-serif;">
We will keep you informed with the updates. If you have any queries, please
feel to
write to
<nobr><u>cdc.support@iitdh.ac.in</u></nobr>
</p>
</td>
</tr>
<tr>
<td style="padding:0;">
<table role="presentation"
style="width:100%;border-collapse:collapse;border:0;border-spacing:0;">
<tr>
<td style="width:260px;padding:0;vertical-align:top;color:#334878;">
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="padding:30px;background:#334878;">
<table role="presentation"
style="width:100%;border-collapse:collapse;border:0;border-spacing:0;font-size:9px;font-family: 'Roboto', sans-serif;">
<tr>
<td style="padding:0;width:50%;" align="left">
<p style="margin:0;font-size:14px;line-height:16px;font-family: 'Roboto', sans-serif;color:#ffffff;">
&reg; CDC,IIT Dharwad,2021<br/>
</p>
</td>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

13
Email_service_README.md Normal file
View File

@ -0,0 +1,13 @@
we have defined a service for running the `start_email_service.sh` file continusouly on the server. Use the following commands for doing anything to the email
service.
## Enable the Service
`sudo systemctl enable cdc-email-sender.service`
## Start the Service
`sudo systemctl start cdc-email-sender.service`
## Check Status of the Service
`sudo systemctl status cdc-email-sender.service`
Any Doubts contact Gowtham Sai - 190010036

View File

@ -9,11 +9,12 @@ python# CDC - Backend
3. Create a Virtual Environment in the [CDC_Backend](./) folder with this command below <br>
`python -m venv venv`
3. Activate the environment with this command <br>
`.\venv\Scripts\activate`
`.\venv\Scripts\activate` (for WINDOWS) <br>
`source ./venv/bin/activate` (for LINUX)
4. Install the dependencies <br>
`pip install -r requirements.txt `
5. Ensure that you have the PostgreSQL installed on your machine and is running on PORT **5432** <br>
6. Make sure to give the correct database credentials in [settings.py](./CDC_Backend/CDC_Backend/settings.py)
6. Make sure to give the correct database credentials in [settings.py](./CDC_Backend/CDC_Backend/settings.py)(https://www.youtube.com/watch?v=bE9h6aAky4s&t=193s)
7. Run these following commands below. (The same are there in setup.sh for linux users and setup.bat for windows users)
```cd CDC_Backend
python manage.py flush --no-input
@ -25,10 +26,12 @@ python manage.py makemigrations APIs
```
### Running the Application
1. Activate the environment with this command. <br>
`.\venv\Scripts\activate`
`.\venv\Scripts\activate` (for WINDOWS) <br>
`source ./venv/bin/activate` (for LINUX)
2. Start the application by running this command (_Run the command where [manage.py](./CDC_Backend/manage.py) is
located_) <br>
` python manage.py runserver`
@ -45,6 +48,12 @@ python manage.py makemigrations APIs
And then recognize it
8.Check the client link in dev.env in backend and .env in frontend is the same
# Error
1.make sure that your machine time and google time are same ,if not go to setting of date and time and sync this
2.make sure u have used same id for both student and Admin that is your iitfh roll_no
3. same client link in .env of frontend or constants.py of bakcend
### Deploying
1. Add the hosted domain name in `ALLOWED_HOSTS` in [settings.py](./CDC_Backend/CDC_Backend/settings.py)

12
dev.env
View File

@ -1,12 +0,0 @@
HOSTING_URL=http://localhost:8000/
DEBUG=True
EMAIL=saisurya3127@gmail.com
EMAIL_PASSWORD=lvryxwieedpervtv
SECRET_KEY=%2e!&f6(ib^690y48z=)&w6fczhwukzzp@3y*^*7u+7%4s-mie
EMAIL_VERIFICATION_SECRET_KEY=b'<\xa3\xaf&(*|\x0e\xbces\x07P\xf7\xd6\xa9sf\x19$\x96\xb7\x90\x8b\x88\x84\x0e\x191\xde,M\x90\x17(\xf7\nG\x13"\x8d$\x9f&\xb0\xcd\xa4\xaf\xa9\x1b\x15\x02B\x8a\xaf\xff\x0c\x1e\xd5\xb3\x06\xb8\xa6\x9bQ\xa0TR\xe8\x98\x9ae\xe0n}\xcc/[\xdaFz\x18\xfeX\xaf\xbd\xd0\x88\xeal\xe3\xd2\xe3\xb8\x8c\x199{\xf3<\xb0\xc5\xd0\xe7*Rv\xda\xbb \x1d\x85~\xff%>\x1e\xb8\xa7\xbf\xbc\xb2\x06\x86X\xc3\x9f\x13<\x9fd\xea\xb5"\\5&\x01\xa4\x7f=\xa0\x1b\x8bO\x01h\xe8\xfd\x1f\xfe\xba\xbeg\\\xc2\xcb\xc3\xd1~\xff\xd5/9d\xa8\xa7x{\x16\xdb\\\xbb\x08\rI\xcd\x9e7\x8c~\x0f\x1d\x81rXZD\xf0\xf7\x87K\x8f\xfb,\xf4\xf0\xa5\x9e\xde^\xca\xae\x80|9b\x9b\xaaE"\xba\xfb\xdf\x80\xb1\x99\x83e[\xf8\xce&Rq\x99\xdb}\xeeO\xd5\x18\x8d\x0bv\xe7\xab\xf9\xb9{\xb5u\xce\xcf\x90\xa6HE\xc5\x92p\x00\x158\xdf\x1d'
DB_NAME=cdc
DB_USER=postgres
DB_PASSWORD=postgres
DB_HOST=localhost
DB_PORT=5432
RECAPTCHA_SECRET_KEY=6Lcv-mEfAAAAAOxM3pzPc-9W96yPlkWnn6v41fLl

16
doc/setup/postgres.md Normal file
View File

@ -0,0 +1,16 @@
typical conf file for pg_hba.conf for dev work.
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all md5
# IPv4 local connections:
host all all 127.0.0.1/32 md5
# IPv6 local connections:
host all all ::1/128 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all peer
host replication all 127.0.0.1/32 ident
host replication all ::1/128 ident

View File

@ -27,6 +27,8 @@ http {
sendfile on;
#tcp_nopush on;
client_max_body_size 50M;
#keepalive_timeout 0;
keepalive_timeout 65;
@ -34,21 +36,27 @@ http {
server {
listen 80;
server_name localhost;
server_name cdc.iitdh.ac.in;
# listen 443 ssl;
listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
ssl_certificate /home/cdc/Desktop/1f9476e3959ebe60.pem;
ssl_certificate_key /home/cdc/Desktop/star_iitdh_key.key;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /portal;
}
location /portal {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /portal/index.html;
}
# Server static files /storage
@ -65,14 +73,14 @@ http {
}
location /api/ {
proxy_pass http://localhost:8000;
proxy_pass https://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_pass https://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";

View File

@ -5,13 +5,19 @@ certifi==2021.10.8
chardet==4.0.0
charset-normalizer==2.0.12
colorama==0.4.4
defusedxml==0.7.1
diff-match-patch==20200713
dill==0.3.5.1
dj-database-url==0.5.0
Django==3.2.13
django-background-tasks==1.2.5
django-compat==1.0.15
django-cors-headers==3.11.0
django-db-logger==0.1.12
django-import-export==2.8.0
django-simple-history==3.1.1
djangorestframework==3.13.1
et-xmlfile==1.1.0
google-auth==2.6.6
gunicorn==20.1.0
idna==3.3
@ -20,23 +26,31 @@ isort==5.10.1
jsonfield==3.1.0
lazy-object-proxy==1.7.1
Markdown==3.3.6
MarkupPy==1.14
mccabe==0.7.0
odfpy==1.4.1
openpyxl==3.0.10
pdfkit==1.0.0
platformdirs==2.5.1
psycopg2-binary==2.9.3
pyasn1==0.4.8
pyasn1-modules==0.2.8
PyJWT==2.4.0
pylint==2.13.5
python-dotenv==0.20.0
pytz==2022.1
PyJWT==2.4.0
PyYAML==6.0
requests==2.27.1
rsa==4.8
six==1.16.0
sqlparse==0.4.2
tablib==3.2.1
toml==0.10.2
tomli==2.0.1
typing_extensions==4.1.1
urllib3==1.26.9
whitenoise==6.0.0
wrapt==1.14.0
xlrd==2.0.1
xlwt==1.3.0
zipp==3.8.0
wrapt==1.14.0

7
start_email_service.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
source /home/cdc/Desktop/CDC_Web_Portal_Backend/cdc-placement-website-backend/venv/bin/activate
cd /home/cdc/Desktop/CDC_Web_Portal_Backend/cdc-placement-website-backend/CDC_Backend
python manage.py process_tasks