diff --git a/CDC_Backend/APIs/admin.py b/CDC_Backend/APIs/admin.py index 4c26bd9..eb4711c 100644 --- a/CDC_Backend/APIs/admin.py +++ b/CDC_Backend/APIs/admin.py @@ -1,9 +1,41 @@ from django.contrib import admin from .models import * +from django.contrib import admin +from django.contrib.admin.templatetags.admin_urls import admin_urlname +from django.shortcuts import resolve_url +from django.utils.html import format_html +from django.utils.safestring import SafeText +from .models import * admin.site.register(User) -admin.site.register(Student) +# admin.site.register(Student) admin.site.register(Admin) -admin.site.register(Placement) +# admin.site.register(Placement) admin.site.register(PlacementApplication) admin.site.register(PrePlacementOffer) + +admin.site.site_header = "CDC Recruitment Portal" + + +def model_admin_url(obj, name=None) -> str: + url = resolve_url(admin_urlname(obj._meta, SafeText("change")), obj.pk) + return format_html('{}', url, name or str(obj)) + + +@admin.register(Student) +class Student(admin.ModelAdmin): + list_display = ("roll_no", "name", "batch", "branch", "phone_number") + search_fields = ("roll_no", "name","phone_number") + ordering = ("roll_no", "name", "batch", "branch", "phone_number") + list_filter = ("batch", "branch") + + +@admin.register(Placement) +class Placement(admin.ModelAdmin): + list_display = (COMPANY_NAME, CONTACT_PERSON_NAME, PHONE_NUMBER, 'tier', 'compensation_CTC') + search_fields = (COMPANY_NAME, CONTACT_PERSON_NAME) + ordering = (COMPANY_NAME, CONTACT_PERSON_NAME, 'tier', 'compensation_CTC') + list_filter = ('tier',) + + + diff --git a/CDC_Backend/APIs/adminUrls.py b/CDC_Backend/APIs/adminUrls.py index 0dbe3b3..bd3d6e5 100644 --- a/CDC_Backend/APIs/adminUrls.py +++ b/CDC_Backend/APIs/adminUrls.py @@ -9,5 +9,7 @@ urlpatterns = [ path('updateOfferAccepted/', adminViews.updateOfferAccepted, name="Update Offer Accepted"), path('updateEmailVerified', adminViews.updateEmailVerified, name="Update Email Verified"), path('updateAdditionalInfo/', adminViews.updateAdditionalInfo, name="Update Additional Info"), - + path('getApplications/', adminViews.getApplications, name="Get Applications"), + path("submitApplication/", adminViews.submitApplication, name="Submit Application"), + path('generateCSV/', adminViews.generateCSV, name="Generate CSV"), ] diff --git a/CDC_Backend/APIs/adminViews.py b/CDC_Backend/APIs/adminViews.py index 374266a..845ecef 100644 --- a/CDC_Backend/APIs/adminViews.py +++ b/CDC_Backend/APIs/adminViews.py @@ -1,8 +1,9 @@ +import json from datetime import datetime from .utils import * from rest_framework.decorators import api_view - +import csv from .serializers import * @@ -152,4 +153,147 @@ def updateAdditionalInfo(request, id, email, user_type): except: logger.warning("Update Additional Info: " + str(sys.exc_info())) return Response({'action': "Update Additional Info", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) + + +@api_view(['GET']) +@isAuthorized([ADMIN]) +@precheck([OPENING_ID]) +def getApplications(request, id, email, user_type): + try: + data = request.GET + opening = get_object_or_404(Placement, pk=data[OPENING_ID]) + applications = PlacementApplication.objects.filter(placement=opening) + serializer = PlacementApplicationSerializerForAdmin(applications, many=True) + return Response({'action': "Get Applications", 'message': 'Data Found', 'applications':serializer.data}, + status=status.HTTP_200_OK) + except Http404: + return Response({'action': "Get Applications", 'message': 'Opening Not Found'}, + status=status.HTTP_404_NOT_FOUND) + except: + logger.warning("Get Applications: " + str(sys.exc_info())) + return Response({'action': "Get Applications", 'message': "Something went wrong"}, + status=status.HTTP_400_BAD_REQUEST) + + + +@api_view(['POST']) +@isAuthorized(allowed_users=[ADMIN]) +@precheck(required_data=[OPENING_TYPE, OPENING_ID, RESUME_FILE_NAME, + STUDENT_ID]) +def submitApplication(request, id, email, user_type): + try: + data = request.data + student = get_object_or_404(Student, roll_no=data[STUDENT_ID]) + # Only Allowing Applications for Placements + if data[OPENING_TYPE] == PLACEMENT: + if not len(PlacementApplication.objects.filter( + student_id=student.id, placement_id=data[OPENING_ID])): + application = PlacementApplication() + opening = get_object_or_404(Placement, id=data[OPENING_ID], + allowed_batch__contains=[student.batch], + allowed_branch__contains=[student.branch], + deadline_datetime__gte=datetime.now().date() + ) + if not opening.offer_accepted or not opening.email_verified: + raise PermissionError("Placement Not Approved") + + cond_stat, cond_msg = PlacementApplicationConditions(student, opening) + if not cond_stat: + raise PermissionError(cond_msg) + application.placement = opening + else: + raise PermissionError("Application is already Submitted") + else: + raise ValueError(OPENING_TYPE + " is Invalid") + + if data[RESUME_FILE_NAME] in student.resumes: + application.resume = data[RESUME_FILE_NAME] + else: + raise FileNotFoundError(RESUME_FILE_NAME + " Not Found") + + application.student = student + application.id = generateRandomString() + additional_info = {} + for i in opening.additional_info: + if i not in data[ADDITIONAL_INFO]: + raise AttributeError(i + " not found in Additional Info") + else: + additional_info[i] = data[ADDITIONAL_INFO][i] + + application.additional_info = json.dumps(additional_info) + data = { + "name": student.name, + "company_name": opening.company_name, + "application_type": data[OPENING_TYPE], + "additional_info": dict(json.loads(application.additional_info)), + } + subject = STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT.format(company_name=opening.company_name) + student_email = str(student.roll_no)+"@iitdh.ac.in" + sendEmail(student_email, subject, data, STUDENT_APPLICATION_SUBMITTED_TEMPLATE) + + application.save() + return Response({'action': "Submit Application", 'message': "Application Submitted"}, + status=status.HTTP_200_OK) + except Http404 as e: + return Response({'action': "Submit Application", 'message': str(e)}, + status=status.HTTP_404_NOT_FOUND) + except PermissionError as e: + return Response({'action': "Submit Application", 'message': str(e)}, + status=status.HTTP_403_FORBIDDEN) + except FileNotFoundError as e: + return Response({'action': "Submit Application", 'message': str(e)}, + status=status.HTTP_404_NOT_FOUND) + except: + logger.warning("Submit Application: " + str(sys.exc_info())) + return Response({'action': "Submit Application", 'message': "Something Went Wrong"}, + status=status.HTTP_400_BAD_REQUEST) + + + +@api_view(['POST']) +@isAuthorized(allowed_users=[ADMIN]) +@precheck(required_data=[OPENING_ID]) +def generateCSV(request, id, email, user_type): + try: + data = request.data + placement = get_object_or_404(Placement, id=data[OPENING_ID]) + applications = PlacementApplication.objects.filter(placement=placement) + filename = generateRandomString() + if not os.path.isdir(STORAGE_DESTINATION_APPLICATION_CSV): + os.mkdir(STORAGE_DESTINATION_APPLICATION_CSV) + destination_path = STORAGE_DESTINATION_APPLICATION_CSV + filename + ".csv" + f = open(destination_path, 'w') + writer = csv.writer(f) + header_row = APPLICATION_CSV_COL_NAMES.copy() + + header_row.extend(placement.additional_info) + writer.writerow(header_row) + for apl in applications: + row_details=[] + + row_details.append(apl.applied_at) + row_details.append(apl.student.roll_no) + row_details.append(apl.student.name) + row_details.append(str(apl.student.roll_no)+"@iitdh.ac.in") + row_details.append(apl.student.phone_number) + row_details.append(apl.student.branch) + row_details.append(apl.student.batch) + row_details.append(apl.student.cpi) + link = LINK_TO_STORAGE_RESUME + urllib.parse.quote_plus(apl.student.id + "/" + apl.resume) + row_details.append(link) + row_details.append(apl.selected) + + for i in placement.additional_info: + row_details.append(json.loads(apl.additional_info)[i]) + + writer.writerow(row_details) + f.close() + file_path = LINK_TO_APPLICATIONS_CSV + urllib.parse.quote_plus(filename+".csv") + return Response({'action': "Create csv", 'message': "CSV created", 'file': file_path}, + status=status.HTTP_200_OK) + except: + logger.warning("Create csv: " + str(sys.exc_info())) + print(sys.exc_info()) + return Response({'action': "Create csv", 'message': "Error Occurred"}, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/CDC_Backend/APIs/constants.py b/CDC_Backend/APIs/constants.py index fa9790e..4201ce9 100644 --- a/CDC_Backend/APIs/constants.py +++ b/CDC_Backend/APIs/constants.py @@ -42,6 +42,7 @@ CLIENT_ID = "956830229554-290mirc16pdhd5j7ph7v7ukibo4t1qcp.apps.googleuserconten PLACEMENT_OPENING_URL = "https://www.googleapis.com/auth/adwords/{id}" LINK_TO_STORAGE_COMPANY_ATTACHMENT = "https://storage.googleapis.com/cdc-backend-attachments/company_attachments/" LINK_TO_STORAGE_RESUME = "https://storage.googleapis.com/cdc-backend-attachments/resume/" +LINK_TO_APPLICATIONS_CSV = "https://storage.googleapis.com/cdc-backend-attachments/applications-csv/" TOKEN = "token_id" @@ -58,6 +59,8 @@ MAX_OFFERS_PER_STUDENT = 2 STORAGE_DESTINATION_RESUMES = "./Storage/Resumes/" STORAGE_DESTINATION_COMPANY_ATTACHMENTS = './Storage/Company_Attachments/' +STORAGE_DESTINATION_APPLICATION_CSV = './Storage/Application_CSV/' + RESUME_FILE_NAME = 'resume_file_name' @@ -120,8 +123,6 @@ STUDENT_ID = "student_id" STUDENT_SELECTED = "student_selected" - - COMPANY_OPENING_SUBMITTED_TEMPLATE_SUBJECT = "Notification Submitted - {id} - CDC IIT Dharwad" STUDENT_APPLICATION_STATUS_TEMPLATE_SUBJECT = 'Application Status : {company_name} - {id}' STUDENT_APPLICATION_SUBMITTED_TEMPLATE_SUBJECT = 'CDC - Application Submitted - {company_name}' @@ -131,3 +132,5 @@ COMPANY_OPENING_SUBMITTED_TEMPLATE = 'company_opening_submitted.html' STUDENT_APPLICATION_STATUS_SELECTED_TEMPLATE = 'student_application_status_selected.html' STUDENT_APPLICATION_STATUS_NOT_SELECTED_TEMPLATE = 'student_application_status_not_selected.html' +APPLICATION_CSV_COL_NAMES = ['Applied At', 'Roll No.', 'Name', 'Email', 'Phone Number', 'Branch', 'Batch', 'CPI', + 'Resume', 'Selected', ] diff --git a/CDC_Backend/APIs/serializers.py b/CDC_Backend/APIs/serializers.py index 787de1e..a7c1228 100644 --- a/CDC_Backend/APIs/serializers.py +++ b/CDC_Backend/APIs/serializers.py @@ -160,3 +160,19 @@ class PlacementApplicationSerializer(serializers.ModelSerializer): class Meta: model = PlacementApplication exclude = [STUDENT, 'resume'] + +class PlacementApplicationSerializerForAdmin(serializers.ModelSerializer): + student_details = serializers.SerializerMethodField() + resume_link = serializers.SerializerMethodField() + + def get_student_details(self, obj): + data = StudentSerializer(obj.student).data + return data + + def get_resume_link(self, obj): + link = LINK_TO_STORAGE_RESUME + urllib.parse.quote_plus(obj.id + "/" + obj.resume) + return link + + class Meta: + model = PlacementApplication + exclude = ['placement', 'resume'] \ No newline at end of file diff --git a/CDC_Backend/APIs/studentViews.py b/CDC_Backend/APIs/studentViews.py index 329f5c3..0ea1824 100644 --- a/CDC_Backend/APIs/studentViews.py +++ b/CDC_Backend/APIs/studentViews.py @@ -178,7 +178,7 @@ def submitApplication(request, id, email, user_type): return Response({'action': "Submit Application", 'message': "Application Submitted"}, status=status.HTTP_200_OK) except Http404 as e: - return Response({'action': "Submit Application", 'message': "Student Not Found"}, + return Response({'action': "Submit Application", 'message': str(e)}, status=status.HTTP_404_NOT_FOUND) except PermissionError as e: return Response({'action': "Submit Application", 'message': str(e)}, diff --git a/CDC_Backend/APIs/utils.py b/CDC_Backend/APIs/utils.py index d7ea9fb..496d52c 100644 --- a/CDC_Backend/APIs/utils.py +++ b/CDC_Backend/APIs/utils.py @@ -103,7 +103,7 @@ def isAuthorized(allowed_users=None): def generateRandomString(): try: N = 15 - res = ''.join(random.choices(string.ascii_uppercase + string.digits, k=N)) + res = ''.join(random.choices(string.ascii_uppercase +string.ascii_lowercase+ string.digits, k=N)) return res except: return False diff --git a/CDC_Backend/CDC_Backend/settings.py b/CDC_Backend/CDC_Backend/settings.py index 7c0a6f1..8219a0f 100644 --- a/CDC_Backend/CDC_Backend/settings.py +++ b/CDC_Backend/CDC_Backend/settings.py @@ -11,7 +11,9 @@ https://docs.djangoproject.com/en/2.2/ref/settings/ """ import os +from dotenv import load_dotenv +load_dotenv("../dev.env") # import django_heroku # Build paths inside the project like this: os.path.join(BASE_DIR, ...) @@ -81,19 +83,15 @@ WSGI_APPLICATION = 'CDC_Backend.wsgi.application' # https://docs.djangoproject.com/en/2.2/ref/settings/#databases DATABASES = { - # 'default': { - # 'ENGINE': 'django.db.backends.sqlite3', - # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - # } - 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'cdc', - 'USER': 'postgres', - 'PASSWORD': 'root', - 'HOST': 'localhost', - 'PORT': '5432', + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.environ.get("DB_NAME"), + 'USER': os.environ.get("DB_USER"), + 'PASSWORD': os.environ.get("DB_PASSWORD"), + 'HOST': os.environ.get("DB_HOST"), + 'PORT': os.environ.get("DB_PORT"), }, + # 'default': { # 'ENGINE': 'django.db.backends.postgresql_psycopg2', # 'NAME': 'd84i5cbjig5rrf', @@ -160,8 +158,8 @@ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.gmail.com' EMAIL_USE_TLS = True EMAIL_PORT = 587 -EMAIL_HOST_USER = 'saisurya3127@gmail.com'#'email here' -EMAIL_HOST_PASSWORD = 'yeylqcnsyjfpzsew'#'password here' +EMAIL_HOST_USER = os.environ.get("EMAIL") # 'email here' +EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_PASSWORD") # 'password here' LOGGING = { 'version': 1, @@ -179,6 +177,11 @@ LOGGING = { 'level': 'DEBUG', 'class': 'django_db_logger.db_log_handler.DatabaseLogHandler' }, + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler', + 'include_html': True, + } }, 'loggers': { 'db': { @@ -188,5 +191,4 @@ LOGGING = { } } - # django_heroku.settings(locals()) diff --git a/dev.env b/dev.env new file mode 100644 index 0000000..8695ea6 --- /dev/null +++ b/dev.env @@ -0,0 +1,10 @@ +HOSTING_URL=http://localhost:8000/ +DEBUG=True +EMAIL=saisurya3127@gmail.com +EMAIL_PASSWORD=yeylqcnsyjfpzsew +SECRET_KEY=%2e!&f6(ib^690y48z=)&w6fczhwukzzp@3y*^*7u+7%4s-mie +DB_NAME=cdc +DB_USER=postgres +DB_PASSWORD=root +DB_HOST=localhost +DB_PORT=5432 diff --git a/nginx.conf b/nginx.conf new file mode 100755 index 0000000..2bf79c2 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,92 @@ + +#user nobody; +worker_processes 1; + +error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' + # '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log logs/access.log; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + #gzip on; + + server { + listen 80; + server_name localhost; + + # listen 443 ssl; + # server_name localhost; + + # ssl_certificate cert.pem; + # ssl_certificate_key cert.key; + + #charset koi8-r; + + #access_log logs/host.access.log main; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + # Server static files /storage + location /storage { + alias '/home/gowtham/Gowtham/Shared Projects/cdc-placement-website-backend/CDC_Backend/Storage'; +# autoindex on; + } + + location /static { +# autoindex on; + alias '/home/gowtham/Gowtham/Shared Projects/cdc-placement-website-backend/CDC_Backend/static'; + } + + location /api/ { + proxy_pass http://localhost:8000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + } + location /admin/ { + proxy_pass http://localhost:8000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + } + + + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } +} + +} diff --git a/requirements.txt b/requirements.txt index f17d5c0..0d32630 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,36 +1,40 @@ -asgiref==3.4.1 -astroid==2.9.0 -cachetools==4.2.4 -certifi==2021.10.8 -chardet==4.0.0 -charset-normalizer==2.0.8 -colorama==0.4.4 -dj-database-url==0.5.0 -Django==3.2.9 -django-background-tasks==1.2.5 -django-compat==1.0.15 -django-cors-headers==3.10.0 -django-db-logger==0.1.11 -djangorestframework==3.12.4 -google-auth==2.3.3 -idna==3.3 -isort==5.10.1 -jsonfield==3.1.0 -lazy-object-proxy==1.6.0 -Markdown==3.3.6 -mccabe==0.6.1 -platformdirs==2.4.0 -psycopg2==2.9.2 -pyasn1==0.4.8 -pyasn1-modules==0.2.8 -pylint==2.12.1 -pytz==2021.3 -requests==2.26.0 -rsa==4.8 -six==1.16.0 -sqlparse==0.4.2 -toml==0.10.2 -typing-extensions==4.0.0 -urllib3==1.26.7 -whitenoise==5.3.0 -wrapt==1.13.3 +asgiref==3.4.1 +astroid==2.9.0 +cachetools==4.2.4 +certifi==2021.10.8 +chardet==4.0.0 +charset-normalizer==2.0.8 +colorama==0.4.4 +dj-database-url==0.5.0 +Django==3.2.9 +django-background-tasks==1.2.5 +django-compat==1.0.15 +django-cors-headers==3.10.0 +django-db-logger==0.1.11 +djangorestframework==3.12.4 +google-auth==2.3.3 +gunicorn==20.1.0 +idna==3.3 +importlib-metadata==4.8.2 +isort==5.10.1 +jsonfield==3.1.0 +lazy-object-proxy==1.6.0 +Markdown==3.3.6 +mccabe==0.6.1 +platformdirs==2.4.0 +psycopg2-binary==2.9.2 +pyasn1==0.4.8 +pyasn1-modules==0.2.8 +pylint==2.12.1 +python-dotenv==0.19.2 +pytz==2021.3 +requests==2.26.0 +rsa==4.8 +six==1.16.0 +sqlparse==0.4.2 +toml==0.10.2 +typing_extensions==4.0.0 +urllib3==1.26.7 +whitenoise==5.3.0 +wrapt==1.13.3 +zipp==3.6.0 diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..86a2bc3 --- /dev/null +++ b/setup.sh @@ -0,0 +1,22 @@ +python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt +echo "Environment setup complete" +cd CDC_Backend + +python3 manage.py flush --no-input +python3 manage.py makemigrations +python3 manage.py migrate +echo "Migrations complete" + +python3 manage.py collectstatic --noinput + +DIR="./Storage" +if [ -d "$DIR" ]; then + ### Take action if $DIR exists ### + echo "${DIR} Directory already exists" +else + ### Control will jump here if $DIR does NOT exists ### + echo "Creating ${DIR} Directory..." + mkdir ${DIR} + echo "${DIR} Directory Created" +fi +