diff --git a/CDC_Backend/APIs/adminUrls.py b/CDC_Backend/APIs/adminUrls.py index 587c1bb..da8acd6 100644 --- a/CDC_Backend/APIs/adminUrls.py +++ b/CDC_Backend/APIs/adminUrls.py @@ -15,4 +15,5 @@ urlpatterns = [ 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"), ] diff --git a/CDC_Backend/APIs/adminViews.py b/CDC_Backend/APIs/adminViews.py index e4f0cc2..e935e8d 100644 --- a/CDC_Backend/APIs/adminViews.py +++ b/CDC_Backend/APIs/adminViews.py @@ -429,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) diff --git a/CDC_Backend/APIs/models.py b/CDC_Backend/APIs/models.py index 9b15724..77d8c6a 100644 --- a/CDC_Backend/APIs/models.py +++ b/CDC_Backend/APIs/models.py @@ -276,3 +276,16 @@ class PrePlacementOffer(models.Model): 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 \ No newline at end of file diff --git a/CDC_Backend/APIs/serializers.py b/CDC_Backend/APIs/serializers.py index 1447d6d..bc8cbaa 100644 --- a/CDC_Backend/APIs/serializers.py +++ b/CDC_Backend/APIs/serializers.py @@ -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(str(obj.student.roll_no) + "/" + 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__' diff --git a/CDC_Backend/APIs/studentUrls.py b/CDC_Backend/APIs/studentUrls.py index 45b3619..8a1acce 100644 --- a/CDC_Backend/APIs/studentUrls.py +++ b/CDC_Backend/APIs/studentUrls.py @@ -10,4 +10,5 @@ urlpatterns = [ 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"), ] diff --git a/CDC_Backend/APIs/studentViews.py b/CDC_Backend/APIs/studentViews.py index 45993a5..c68d846 100644 --- a/CDC_Backend/APIs/studentViews.py +++ b/CDC_Backend/APIs/studentViews.py @@ -230,4 +230,20 @@ def deleteApplication(request, id, email, user_type): 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) \ No newline at end of file diff --git a/CDC_Backend/APIs/utils.py b/CDC_Backend/APIs/utils.py index 2f5c6fc..be57175 100644 --- a/CDC_Backend/APIs/utils.py +++ b/CDC_Backend/APIs/utils.py @@ -172,12 +172,17 @@ 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) + 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" @@ -186,7 +191,7 @@ def PlacementApplicationConditions(student, placement): return False, "Can't apply for this tier" for i in PPO: - if int(i.placement.tier) < int(placement.tier): + if int(i.tier) < int(placement.tier): return False, "Can't apply for this tier" if student.degree != 'bTech' and not placement.rs_eligible: diff --git a/CDC_Backend/CDC_Backend/settings.py b/CDC_Backend/CDC_Backend/settings.py index 154e430..9b2ee68 100644 --- a/CDC_Backend/CDC_Backend/settings.py +++ b/CDC_Backend/CDC_Backend/settings.py @@ -30,6 +30,7 @@ DEBUG = os.environ.get('DEBUG') == "True" ALLOWED_HOSTS = ['cdc.iitdh.ac.in', 'localhost'] +ADMINS = [('Gowtham Sai', '190010036@iitdh.ac.in'), ('Karthik Mv', '200010030@iitdh.ac.in')] # Application definition INSTALLED_APPS = [ @@ -47,6 +48,8 @@ INSTALLED_APPS = [ 'simple_history', 'import_export', 'django_extensions', + "django_extensions" + ] MIDDLEWARE = [ @@ -185,14 +188,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' } } diff --git a/CDC_Backend/scripts/__init__.py b/CDC_Backend/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/CDC_Backend/scripts/get_contributor_stats.py b/CDC_Backend/scripts/get_contributor_stats.py new file mode 100644 index 0000000..ef98c88 --- /dev/null +++ b/CDC_Backend/scripts/get_contributor_stats.py @@ -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() \ No newline at end of file diff --git a/README.md b/README.md index bae49a2..1c41ba8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ python# CDC - Backend ### Setup 1. Download the Repository to your local machine
-2. Create a Virtual Environment in the [CDC_Backend](./) folder with this command below
+2. Make Sure u have downloaded python from python.org or windows store. +3. Create a Virtual Environment in the [CDC_Backend](./) folder with this command below
`python -m venv venv` 3. Activate the environment with this command
`.\venv\Scripts\activate` (for WINDOWS)
@@ -14,6 +15,17 @@ python# CDC - Backend `pip install -r requirements.txt ` 5. Ensure that you have the PostgreSQL installed on your machine and is running on PORT **5432**
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 +python manage.py makemigrations +python manage.py migrate +python manage.py collectstatic --noinput +mkdir Storage +python manage.py makemigrations APIs +``` + + ### Running the Application @@ -28,13 +40,20 @@ python# CDC - Backend 1. You can access the admin panel by running the server and opening 2. Run `python manage.py createsuperuser` to create a user to access the admin panel. -3. Set up the Username and Password -4. You can log in and change the database values anytime. +3. if there is an error due to time then sync your machine time . +4. Set up the Username and Password +5. You can log in and change the database values anytime. +6. Create your id as insitute Roll No for both admin and student . +7. if you are still getting an error ,open inspect and see in network +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) diff --git a/doc/setup/postgres.md b/doc/setup/postgres.md index d3dba5e..8941fa6 100644 --- a/doc/setup/postgres.md +++ b/doc/setup/postgres.md @@ -1,6 +1,6 @@ typical conf file for pg_hba.conf for dev work. - +``` # TYPE DATABASE USER ADDRESS METHOD # "local" is for Unix domain socket connections only @@ -13,4 +13,5 @@ host all all ::1/128 md5 # replication privilege. local replication all peer host replication all 127.0.0.1/32 ident -host replication all ::1/128 ident \ No newline at end of file +host replication all ::1/128 ident +```