diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..4808ac2 Binary files /dev/null and b/.DS_Store differ diff --git a/CDC_Backend.zip b/CDC_Backend.zip new file mode 100644 index 0000000..c170b6a Binary files /dev/null and b/CDC_Backend.zip differ diff --git a/CDC_Backend/.DS_Store b/CDC_Backend/.DS_Store new file mode 100644 index 0000000..e1c3358 Binary files /dev/null and b/CDC_Backend/.DS_Store differ diff --git a/CDC_Backend/APIs/admin.py b/CDC_Backend/APIs/admin.py index 29bfefd..31b5b81 100644 --- a/CDC_Backend/APIs/admin.py +++ b/CDC_Backend/APIs/admin.py @@ -10,11 +10,44 @@ from import_export import resources from .models import * -class UserAdmin(ImportExportMixin, SimpleHistoryAdmin): - pass -admin.site.register(User,UserAdmin) -admin.site.register(Admin, SimpleHistoryAdmin) +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" @@ -54,6 +87,17 @@ 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(AdminAdmin): list_display = (COMPANY_NAME, CONTACT_PERSON_NAME, PHONE_NUMBER, 'tier', 'compensation_CTC') @@ -70,6 +114,7 @@ class PlacementApplicationResources(resources.ModelResource): class PlacementAdmin(ExportMixin, SimpleHistoryAdmin): resource_class = PlacementApplicationResources + @admin.register(PlacementApplication) class PlacementApplication(PlacementAdmin): list_display = ('id', 'Placement', 'Student', 'selected') @@ -92,6 +137,7 @@ class PrePlacementResources(resources.ModelResource): class PrePlacementOfferAdmin(ExportMixin, SimpleHistoryAdmin): resource_class = PrePlacementResources + @admin.register(PrePlacementOffer) class PrePlacementOffer(PrePlacementOfferAdmin): list_display = ('company', 'Student', 'accepted') diff --git a/CDC_Backend/APIs/adminViews.py b/CDC_Backend/APIs/adminViews.py index 88261d1..889fdcf 100644 --- a/CDC_Backend/APIs/adminViews.py +++ b/CDC_Backend/APIs/adminViews.py @@ -104,22 +104,29 @@ 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 + offer_accepted = data[OFFER_ACCEPTED] opening = get_object_or_404(Placement, pk=data[OPENING_ID]) - if not opening.offer_accepted: - opening.offer_accepted = True + if opening.offer_accepted is None: + opening.offer_accepted = offer_accepted == "true" opening.changed_by = get_object_or_404(User, id=id) opening.save() 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"}, diff --git a/CDC_Backend/APIs/companyViews.py b/CDC_Backend/APIs/companyViews.py index 2b27a9b..77d228e 100644 --- a/CDC_Backend/APIs/companyViews.py +++ b/CDC_Backend/APIs/companyViews.py @@ -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) @@ -278,7 +283,7 @@ def verifyEmail(request): "opening_type": PLACEMENT, "company_name": opening.company_name, } - sendEmail(opening.email, COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT.format(id=opening.id), 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"}, diff --git a/CDC_Backend/APIs/constants.py b/CDC_Backend/APIs/constants.py index ed40d44..5696feb 100644 --- a/CDC_Backend/APIs/constants.py +++ b/CDC_Backend/APIs/constants.py @@ -1,23 +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 = [ @@ -32,32 +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 = os.environ.get("GOOGLE_OAUTH_CLIENT_ID") # "client_id" +CLIENT_ID = os.environ.get('GOOGLE_OAUTH_CLIENT_ID') # Google Login Client ID # To be Configured Properly -PLACEMENT_OPENING_URL = "http://localhost:3000/student/dashboard/placements/{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 +MAX_RESUMES_PER_STUDENT = 3 EMAIL_VERIFICATION_TOKEN_TTL = 48 # in hours JNF_TEXT_MAX_CHARACTER_COUNT = 100 JNF_TEXTMEDIUM_MAX_CHARACTER_COUNT = 200 @@ -140,6 +151,7 @@ SPECIAL_FORMAT_IN_PDF = ['website', 'company_details_pdf_names', 'description_pd 'compensation_details_pdf_names', 'selection_procedure_details_pdf_names'] +COMPANY_OPENING_ERROR_TEMPLATE = "Alert! Error submitting opening for {company_name}." COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT = "Notification Submitted - {id} - Career Development Cell, IIT Dharwad" STUDENT_APPLICATION_STATUS_TEMPLATE_SUBJECT = 'Application Status - {company_name} - {id}' STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT = 'CDC - Application Submitted - {company_name}' @@ -159,4 +171,3 @@ 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 diff --git a/CDC_Backend/APIs/models.py b/CDC_Backend/APIs/models.py index 8580029..a7b50a1 100644 --- a/CDC_Backend/APIs/models.py +++ b/CDC_Backend/APIs/models.py @@ -33,6 +33,7 @@ class Student(models.Model): 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): @@ -82,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) diff --git a/CDC_Backend/APIs/utils.py b/CDC_Backend/APIs/utils.py index 7fcf318..244e63c 100644 --- a/CDC_Backend/APIs/utils.py +++ b/CDC_Backend/APIs/utils.py @@ -57,6 +57,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) @@ -146,7 +147,10 @@ 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") @@ -180,6 +184,9 @@ def PlacementApplicationConditions(student, placement): if int(i.placement.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: @@ -250,6 +257,8 @@ def verify_recaptcha(request): } r = rq.post('https://www.google.com/recaptcha/api/siteverify', data=data) result = r.json() + if not result['success']: + logger.warning("Utils - verify_recaptcha: " + str(result)) return result['success'] except: # get exception line number @@ -258,8 +267,13 @@ def verify_recaptcha(request): def opening_description_table_html(opening): - details = model_to_dict(opening, fields=[field.name for field in Placement._meta.fields], + # 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: @@ -269,7 +283,7 @@ def opening_description_table_html(opening): if key == 'website': details[key] = {"details": details[key], "type": ["link"]} else: - details[key] = {"details": [item[16:] for item in 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'): @@ -326,3 +340,41 @@ def send_opening_notifications(placement_id): 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) \ No newline at end of file diff --git a/CDC_Backend/CDC_Backend/settings.py b/CDC_Backend/CDC_Backend/settings.py index 6eda075..c5667cf 100644 --- a/CDC_Backend/CDC_Backend/settings.py +++ b/CDC_Backend/CDC_Backend/settings.py @@ -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,12 +23,12 @@ 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 = os.getenv("GOOGLE_OAUTH_SECRET_KEY") +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', '127.0.0.1'] +ALLOWED_HOSTS = ['cdc.iitdh.ac.in', 'localhost'] # Application definition @@ -56,6 +55,7 @@ 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', @@ -146,13 +146,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' diff --git a/CDC_Backend/CDC_Backend/wsgi.py b/CDC_Backend/CDC_Backend/wsgi.py index 057007a..0537202 100644 --- a/CDC_Backend/CDC_Backend/wsgi.py +++ b/CDC_Backend/CDC_Backend/wsgi.py @@ -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 diff --git a/CDC_Backend/run_prod.sh b/CDC_Backend/run_prod.sh new file mode 100644 index 0000000..3ad9b30 --- /dev/null +++ b/CDC_Backend/run_prod.sh @@ -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 & diff --git a/CDC_Backend/templates/company_jnf_response.html b/CDC_Backend/templates/company_jnf_response.html index 6e7191c..33484c1 100644 --- a/CDC_Backend/templates/company_jnf_response.html +++ b/CDC_Backend/templates/company_jnf_response.html @@ -44,7 +44,7 @@ {% for item in value.details %}
  • {% if 'link' in value.type and value.link %} - {{ item }} + {{ item|slice:"16:" }} {% elif 'link' in value.type %} {{ item }} {% else %} diff --git a/Email_service_README.md b/Email_service_README.md new file mode 100644 index 0000000..26f134e --- /dev/null +++ b/Email_service_README.md @@ -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 diff --git a/nginx.conf b/nginx.conf index 544da1f..27b6f3d 100755 --- a/nginx.conf +++ b/nginx.conf @@ -34,21 +34,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 +71,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"; diff --git a/start_email_service.sh b/start_email_service.sh new file mode 100755 index 0000000..048b305 --- /dev/null +++ b/start_email_service.sh @@ -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