From cda3600435c551a4587f6760f3aa6ec9377a5397 Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Wed, 12 Jun 2024 12:45:55 +0530 Subject: [PATCH 01/14] feat: Add Donor model and DonorSerializer for creating donor objects --- api/donate/donate_serializer.py | 20 ++++++++++++++++++++ api/donate/views.py | 32 +++++++++++++++++++++++++------- db/donor.py | 20 ++++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 api/donate/donate_serializer.py create mode 100644 db/donor.py diff --git a/api/donate/donate_serializer.py b/api/donate/donate_serializer.py new file mode 100644 index 00000000..9c24b769 --- /dev/null +++ b/api/donate/donate_serializer.py @@ -0,0 +1,20 @@ +import uuid + +from rest_framework import serializers +from django.conf import settings + +from db.donor import Donor +from utils.utils import DateTimeUtils + + +class DonorSerializer(serializers.ModelSerializer): + + class Meta: + model = Donor + exclude = ['created_by', 'created_at', 'updated_by', 'updated_at', 'id'] + + def create(self, validated_data): + validated_data["created_by_id"] = settings.SYSTEM_ADMIN_ID + validated_data["updated_by_id"] = settings.SYSTEM_ADMIN_ID + validated_data["id"] = uuid.uuid4() + return Donor.objects.create(**validated_data) diff --git a/api/donate/views.py b/api/donate/views.py index f6d2a3b8..6671fe27 100644 --- a/api/donate/views.py +++ b/api/donate/views.py @@ -1,11 +1,11 @@ import razorpay from io import BytesIO -from reportlab.pdfgen import canvas from django.http import HttpResponse from rest_framework.views import APIView from utils.response import CustomResponse +from .donate_serializer import DonorSerializer from mulearnbackend.settings import RAZORPAY_ID, RAZORPAY_SECRET from reportlab.lib.pagesizes import letter @@ -65,14 +65,22 @@ def create_receipt(transaction_details): class RazorPayOrderAPI(APIView): def post(self, request): try: + serializer = DonorSerializer(data=request.data) + if not serializer.is_valid(): + return CustomResponse(general_message=serializer.errors).get_failure_response() + validated_data = serializer.validated_data + data = { - "amount": int(float(request.data.get("amount")) * 100), - "currency": request.data.get("currency", "INR"), + "amount": int(float(validated_data.get("amount")) * 100), + "currency": validated_data.get("currency"), "payment_capture": 1, "notes": { - "email": request.data.get("email"), - "name": request.data.get("name"), - "company": request.data.get("company", None), + "email": validated_data.get("email"), + "name": validated_data.get("name"), + "phone_number": validated_data.get("phone_number", None), + "company": validated_data.get("company", None), + "pan_number": validated_data.get("pan_number", None), + "validated_data": validated_data }, } order = razorpay_client.order.create(data) @@ -101,8 +109,18 @@ def post(self, request): "Currency": data['currency'], "Name": data['notes']['name'], "Email": data['notes']['email'], - "Company": data['notes']['company'] } + if extra_data := data['notes'].get('company', None): + transaction_details["Company"] = extra_data + if extra_data := data['notes'].get('phone_number', None): + transaction_details["Phone Number"] = extra_data + if extra_data := data['notes'].get('pan_number', None): + transaction_details["PAN number"] = extra_data + + serializer = DonorSerializer(data=data['notes']['validated_data']) + if serializer.is_valid(): + serializer.save() + return create_receipt(transaction_details) except razorpay.errors.SignatureVerificationError as e: return CustomResponse(message=str(e)).get_error_response() diff --git a/db/donor.py b/db/donor.py new file mode 100644 index 00000000..98d36275 --- /dev/null +++ b/db/donor.py @@ -0,0 +1,20 @@ +from django.db import models +from db.user import User +from django.conf import settings + + +class Donor(models.Model): + id = models.CharField(primary_key=True, max_length=36) + payment_id = models.CharField(max_length=100) + payment_method = models.CharField(max_length=100) + amount = models.FloatField() + currency = models.CharField(max_length=30) + name = models.CharField(max_length=100) + email = models.EmailField(max_length=200) + company = models.CharField(max_length=100) + phone_number = models.CharField(max_length=20) + pan_number = models.CharField(max_length=10) + updated_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='updated_by', related_name='donor_updated_by') + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='created_by', related_name='donor_created_by') + created_at = models.DateTimeField(auto_now_add=True) \ No newline at end of file From 5950fa9e9bfadbef5def1769ecbe32301a2c20f9 Mon Sep 17 00:00:00 2001 From: jelanmathewjames <72068016+jelanmathewjames@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:51:19 +0530 Subject: [PATCH 02/14] Update donor.py --- db/donor.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/db/donor.py b/db/donor.py index 98d36275..aced03c2 100644 --- a/db/donor.py +++ b/db/donor.py @@ -14,7 +14,5 @@ class Donor(models.Model): company = models.CharField(max_length=100) phone_number = models.CharField(max_length=20) pan_number = models.CharField(max_length=10) - updated_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='updated_by', related_name='donor_updated_by') - updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='created_by', related_name='donor_created_by') - created_at = models.DateTimeField(auto_now_add=True) \ No newline at end of file + created_at = models.DateTimeField(auto_now_add=True) From 210e49a216c98353c82f9e1ff5d5450cd5ed4ba6 Mon Sep 17 00:00:00 2001 From: jelanmathewjames <72068016+jelanmathewjames@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:52:11 +0530 Subject: [PATCH 03/14] Update donate_serializer.py --- api/donate/donate_serializer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/donate/donate_serializer.py b/api/donate/donate_serializer.py index 9c24b769..3ce7e743 100644 --- a/api/donate/donate_serializer.py +++ b/api/donate/donate_serializer.py @@ -11,10 +11,9 @@ class DonorSerializer(serializers.ModelSerializer): class Meta: model = Donor - exclude = ['created_by', 'created_at', 'updated_by', 'updated_at', 'id'] + exclude = ['created_by', 'created_at', 'id'] def create(self, validated_data): validated_data["created_by_id"] = settings.SYSTEM_ADMIN_ID - validated_data["updated_by_id"] = settings.SYSTEM_ADMIN_ID validated_data["id"] = uuid.uuid4() return Donor.objects.create(**validated_data) From 349e0feb519b530908175216555f418c4d8b42b0 Mon Sep 17 00:00:00 2001 From: jelanmathewjames <72068016+jelanmathewjames@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:56:00 +0530 Subject: [PATCH 04/14] Update donor.py --- db/donor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/db/donor.py b/db/donor.py index aced03c2..53a78865 100644 --- a/db/donor.py +++ b/db/donor.py @@ -16,3 +16,7 @@ class Donor(models.Model): pan_number = models.CharField(max_length=10) created_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='created_by', related_name='donor_created_by') created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + managed = False + db_table = 'donor' From 91f7a234f0369b2c1f2791e2c9f15f5b96eacfc5 Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Wed, 12 Jun 2024 13:19:06 +0530 Subject: [PATCH 05/14] refactor: Update Donor model fields to allow null values --- db/donor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/db/donor.py b/db/donor.py index 53a78865..be29d494 100644 --- a/db/donor.py +++ b/db/donor.py @@ -11,9 +11,9 @@ class Donor(models.Model): currency = models.CharField(max_length=30) name = models.CharField(max_length=100) email = models.EmailField(max_length=200) - company = models.CharField(max_length=100) - phone_number = models.CharField(max_length=20) - pan_number = models.CharField(max_length=10) + company = models.CharField(max_length=100, null=True) + phone_number = models.CharField(max_length=20, null=True) + pan_number = models.CharField(max_length=10, null=True) created_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='created_by', related_name='donor_created_by') created_at = models.DateTimeField(auto_now_add=True) From b006f9f66d014b7d31b35214dc17ff9b0fac4fad Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Sun, 16 Jun 2024 21:57:41 +0530 Subject: [PATCH 06/14] commenting donor tables --- api/donate/views.py | 4 ++-- db/donor.py | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/api/donate/views.py b/api/donate/views.py index 6671fe27..a1edda0a 100644 --- a/api/donate/views.py +++ b/api/donate/views.py @@ -118,8 +118,8 @@ def post(self, request): transaction_details["PAN number"] = extra_data serializer = DonorSerializer(data=data['notes']['validated_data']) - if serializer.is_valid(): - serializer.save() + # if serializer.is_valid(): + # serializer.save() return create_receipt(transaction_details) except razorpay.errors.SignatureVerificationError as e: diff --git a/db/donor.py b/db/donor.py index be29d494..07847111 100644 --- a/db/donor.py +++ b/db/donor.py @@ -3,20 +3,20 @@ from django.conf import settings -class Donor(models.Model): - id = models.CharField(primary_key=True, max_length=36) - payment_id = models.CharField(max_length=100) - payment_method = models.CharField(max_length=100) - amount = models.FloatField() - currency = models.CharField(max_length=30) - name = models.CharField(max_length=100) - email = models.EmailField(max_length=200) - company = models.CharField(max_length=100, null=True) - phone_number = models.CharField(max_length=20, null=True) - pan_number = models.CharField(max_length=10, null=True) - created_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='created_by', related_name='donor_created_by') - created_at = models.DateTimeField(auto_now_add=True) +# class Donor(models.Model): +# id = models.CharField(primary_key=True, max_length=36) +# payment_id = models.CharField(max_length=100) +# payment_method = models.CharField(max_length=100) +# amount = models.FloatField() +# currency = models.CharField(max_length=30) +# name = models.CharField(max_length=100) +# email = models.EmailField(max_length=200) +# company = models.CharField(max_length=100, null=True) +# phone_number = models.CharField(max_length=20, null=True) +# pan_number = models.CharField(max_length=10, null=True) +# created_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='created_by', related_name='donor_created_by') +# created_at = models.DateTimeField(auto_now_add=True) - class Meta: - managed = False - db_table = 'donor' +# class Meta: +# managed = False +# db_table = 'donor' From e7ec797f4814175b38044310c40b40462cd1ea97 Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Mon, 17 Jun 2024 00:18:45 +0530 Subject: [PATCH 07/14] feat: Launchpad Dashboard --- api/launchpad/launchpad_views.py | 209 ++++++++++++++++++++++++++----- api/launchpad/serializers.py | 10 ++ api/launchpad/urls.py | 6 +- db/launchpad.py | 25 ++++ utils/types.py | 18 +++ 5 files changed, 239 insertions(+), 29 deletions(-) create mode 100644 db/launchpad.py diff --git a/api/launchpad/launchpad_views.py b/api/launchpad/launchpad_views.py index 3ac8f237..9616dbf6 100644 --- a/api/launchpad/launchpad_views.py +++ b/api/launchpad/launchpad_views.py @@ -2,12 +2,15 @@ from rest_framework.views import APIView -from .serializers import LaunchpadLeaderBoardSerializer, LaunchpadParticipantsSerializer, CollegeDataSerializer +from .serializers import LaunchpadLeaderBoardSerializer, LaunchpadParticipantsSerializer, \ + CollegeDataSerializer, LaunchpadUserSerializer from utils.response import CustomResponse -from utils.utils import CommonUtils +from utils.utils import CommonUtils, ImportCSV +from utils.types import LaunchPadLevels, LaunchPadRoles from db.user import User, UserRoleLink from db.organization import UserOrganizationLink, Organization from db.task import KarmaActivityLog +from db.launchpad import LaunchPadUsers, LaunchPadUserCollegeLink @@ -66,12 +69,7 @@ def get(self, request): class ListParticipantsAPI(APIView): def get(self, request): allowed_org_types = ["College", "School", "Company"] - allowed_levels = [ - "IEEE Launchpad Level 1", - "IEEE Launchpad Level 2", - "IEEE Launchpad Level 3", - "IEEE Launchpad Level 4" - ] + allowed_levels = LaunchPadLevels.get_all_values() intro_task_completed_users = KarmaActivityLog.objects.filter( task__event='launchpad', @@ -123,12 +121,7 @@ def get(self, request): class LaunchpadDetailsCount(APIView): def get(self, request): allowed_org_types = ["College", "School", "Company"] - allowed_levels = [ - "IEEE Launchpad Level 1", - "IEEE Launchpad Level 2", - "IEEE Launchpad Level 3", - "IEEE Launchpad Level 4" - ] + allowed_levels = LaunchPadLevels.get_all_values() intro_task_completed_users = KarmaActivityLog.objects.filter( task__event='launchpad', @@ -167,22 +160,17 @@ def get(self, request): # Count participants at each level level_counts = { "total_participants": users.count(), - "Level_1": users.filter(level="IEEE Launchpad Level 1").count(), - "Level_2": users.filter(level="IEEE Launchpad Level 2").count(), - "Level_3": users.filter(level="IEEE Launchpad Level 3").count(), - "Level_4": users.filter(level="IEEE Launchpad Level 4").count(), + "Level_1": users.filter(level=LaunchPadLevels.LEVEL_1.value).count(), + "Level_2": users.filter(level=LaunchPadLevels.LEVEL_2.value).count(), + "Level_3": users.filter(level=LaunchPadLevels.LEVEL_3.value).count(), + "Level_4": users.filter(level=LaunchPadLevels.LEVEL_4.value).count(), } return CustomResponse(response=level_counts).get_success_response() class CollegeData(APIView): def get(self, request): - allowed_levels = [ - "IEEE Launchpad Level 1", - "IEEE Launchpad Level 2", - "IEEE Launchpad Level 3", - "IEEE Launchpad Level 4" - ] + allowed_levels = LaunchPadLevels.get_all_values() org = Organization.objects.filter( org_type="College", @@ -201,19 +189,19 @@ def get(self, request): total_users=Count("user_organization_link_org__user"), level1 = Count( "user_organization_link_org__user", - filter=Q(user_organization_link_org__user__user_role_link_user__role__title="IEEE Launchpad Level 1") + filter=Q(user_organization_link_org__user__user_role_link_user__role__title=LaunchPadLevels.LEVEL_1.value) ), level2 = Count( "user_organization_link_org__user", - filter=Q(user_organization_link_org__user__user_role_link_user__role__title="IEEE Launchpad Level 2") + filter=Q(user_organization_link_org__user__user_role_link_user__role__title=LaunchPadLevels.LEVEL_2.value) ), level3 = Count( "user_organization_link_org__user", - filter=Q(user_organization_link_org__user__user_role_link_user__role__title="IEEE Launchpad Level 3") + filter=Q(user_organization_link_org__user__user_role_link_user__role__title=LaunchPadLevels.LEVEL_3.value) ), level4 = Count( "user_organization_link_org__user", - filter=Q(user_organization_link_org__user__user_role_link_user__role__title="IEEE Launchpad Level 4") + filter=Q(user_organization_link_org__user__user_role_link_user__role__title=LaunchPadLevels.LEVEL_4.value) ) ).order_by("-total_users") @@ -229,3 +217,168 @@ def get(self, request): return CustomResponse().paginated_response( data=serializer.data, pagination=paginated_queryset.get("pagination") ) + + +class LaunchPadUser(APIView): + + def post(self, request): + data = request.data + auth_mail = data.pop('current_user') + if not (auth_user := LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN).first()): + return CustomResponse(general_message="Unauthorized").get_error_response() + serializer = LaunchpadUserSerializer(data=data) + if not serializer.is_valid(): + return CustomResponse(message=serializer.errors).get_error_response() + + errors = {} + error = False + not_found_colleges = [] + user = LaunchPadUsers.objects.create( + email=data.get('email'), + full_name=data.get('full_name'), + role=data.get('role') + ) + if data.get('college') is None: + return CustomResponse(general_message="Successfully added user").get_error_response() + for college in data.get('college'): + if not Organization.objects.filter(id=college, org_type="College").exists(): + error = True + not_found_colleges.append[college] + LaunchPadUserCollegeLink.objects.create( + user=user, + college_id=college, + created_by=auth_user, + updated_by=auth_user + ) + errors[data.get('email')] = not_found_colleges + if error: + return CustomResponse(message=errors).get_error_response() + return CustomResponse(general_message="Successfully added user").get_success_response() + + def get(self, request): + data = request.data + auth_mail = data.pop('current_user') + if not LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN).exists(): + return CustomResponse(general_message="Unauthorized").get_error_response() + users = LaunchPadUsers.objects.all() + serializer = LaunchpadUserSerializer(users, many=True) + return CustomResponse(data=serializer.data).get_success_response() + + +class UserProfile(APIView): + + def get(self, request): + data = request.data + auth_mail = data.pop('current_user') + if not LaunchPadUsers.objects.filter(email=auth_mail).exists(): + return CustomResponse(general_message="Unauthorized").get_error_response() + user = LaunchPadUsers.objects.get(email=auth_mail) + serializer = LaunchpadUserSerializer(user) + return CustomResponse(data=serializer.data).get_success_response() + + +class UserBasedCollegeData(APIView): + + def get(self, request): + data = request.data + auth_mail = data.pop('current_user') + if not LaunchPadUsers.objects.filter(email=auth_mail).exists(): + return CustomResponse(general_message="Unauthorized").get_error_response() + user = LaunchPadUsers.objects.get(email=auth_mail) + colleges = LaunchPadUserCollegeLink.objects.filter(user=user) + college_ids = [college.college_id for college in colleges] + + allowed_levels = LaunchPadLevels.get_all_values() + + org = Organization.objects.filter( + org_type="College", + id__in=college_ids + ).prefetch_related( + Prefetch( + "user_organization_link_org", + queryset=UserOrganizationLink.objects.filter( + user__user_role_link_user__role__title__in=allowed_levels + ) + ) + ).filter( + user_organization_link_org__user__user_role_link_user__role__title__in=allowed_levels + ).annotate( + district_name=F("district__name"), + state=F("district__zone__state__name"), + total_users=Count("user_organization_link_org__user"), + level1 = Count( + "user_organization_link_org__user", + filter=Q(user_organization_link_org__user__user_role_link_user__role__title=LaunchPadLevels.LEVEL_1.value) + ), + level2 = Count( + "user_organization_link_org__user", + filter=Q(user_organization_link_org__user__user_role_link_user__role__title=LaunchPadLevels.LEVEL_2.value) + ), + level3 = Count( + "user_organization_link_org__user", + filter=Q(user_organization_link_org__user__user_role_link_user__role__title=LaunchPadLevels.LEVEL_3.value) + ), + level4 = Count( + "user_organization_link_org__user", + filter=Q(user_organization_link_org__user__user_role_link_user__role__title=LaunchPadLevels.LEVEL_4.value) + ) + ).order_by("-total_users") + + paginated_queryset = CommonUtils.get_paginated_queryset( + org, + request, + ["title", "district_name", "state"] + ) + + serializer = CollegeDataSerializer( + paginated_queryset.get("queryset"), many=True + ) + return CustomResponse().paginated_response( + data=serializer.data, pagination=paginated_queryset.get("pagination") + ) + + +class BulkLaunchpadUser(APIView): + + def create(self, request): + data = request.data + auth_mail = data.pop('current_user') + if not (auth_user := LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN).first()): + return CustomResponse(general_message="Unauthorized").get_error_response() + try: + file_obj = request.FILES['user_data'] + except KeyError: + return CustomResponse(general_message={'File not found.'}).get_failure_response() + excel_data = ImportCSV() + excel_data = excel_data.read_excel_file(file_obj) + if not excel_data: + return CustomResponse(general_message={'Empty csv file.'}).get_failure_response() + errors = {} + error = False + + for data in excel_data: + not_found_colleges = [] + serializer = LaunchpadUserSerializer(data=data) + if not serializer.is_valid(): + return CustomResponse(message=serializer.errors).get_error_response() + user = LaunchPadUsers.objects.create( + email=data.get('email'), + full_name=data.get('full_name'), + role=data.get('role') + ) + if data.get('college') is None: + continue + for college in data.get('college'): + if not Organization.objects.filter(title=college, org_type="College").exists(): + error = True + not_found_colleges.append[college] + LaunchPadUserCollegeLink.objects.create( + user=user, + college_id=college, + created_by=auth_user, + updated_by=auth_user + ) + errors[data.get('email')] = not_found_colleges + if error: + return CustomResponse(message=errors).get_error_response() + return CustomResponse(general_message="Successfully added users").get_success_response() \ No newline at end of file diff --git a/api/launchpad/serializers.py b/api/launchpad/serializers.py index 6da2dca5..1ea703ee 100644 --- a/api/launchpad/serializers.py +++ b/api/launchpad/serializers.py @@ -5,6 +5,7 @@ from db.user import User from db.organization import UserOrganizationLink, Organization from db.task import KarmaActivityLog +from utils.types import LaunchPadRoles class LaunchpadLeaderBoardSerializer(serializers.ModelSerializer): @@ -95,3 +96,12 @@ class Meta: "level3", "level4" ) + +class LaunchpadUserSerializer(serializers.ModelSerializerSerializer): + role = serializers.ChoiceField(choices=LaunchPadRoles.get_all_values()) + college = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True) + + class Meta: + model = User + fields = ("full_name", "email", "phone_number", "role", "college", "district", "zone") + \ No newline at end of file diff --git a/api/launchpad/urls.py b/api/launchpad/urls.py index 6ac7a4b5..aea0f90f 100644 --- a/api/launchpad/urls.py +++ b/api/launchpad/urls.py @@ -6,5 +6,9 @@ path('leaderboard/', launchpad_views.Leaderboard.as_view()), path('list-participants/', launchpad_views.ListParticipantsAPI.as_view()), path('launchpad-details/', launchpad_views.LaunchpadDetailsCount.as_view()), - path('college-data/', launchpad_views.CollegeData.as_view()) + path('college-data/', launchpad_views.CollegeData.as_view()), + path('user-college-link/', launchpad_views.LaunchPadUser.as_view()), + path('user-profile/', launchpad_views.UserProfile.as_view()), + path('user-college-data/', launchpad_views.UserBasedCollegeData.as_view()), + path('bulk-user-college-link/', launchpad_views.BulkLaunchpadUser.as_view()), ] diff --git a/db/launchpad.py b/db/launchpad.py new file mode 100644 index 00000000..fff8d149 --- /dev/null +++ b/db/launchpad.py @@ -0,0 +1,25 @@ +from django.db import models + +from db.organization import Organization + + +class LaunchPadUsers(models.Model): + id = models.CharField(primary_key=True, max_length=36) + email = models.CharField(max_length=255, unique=True) + phone_number = models.CharField(max_length=15, null=True) + full_name = models.CharField(max_length=255, null=True) + district = models.CharField(max_length=100, null=True) + zone = models.CharField(max_length=100, null=True) + role = models.CharField(max_length=20) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + +class LaunchPadUserCollegeLink(models.Model): + id = models.CharField(primary_key=True, max_length=36) + user = models.ForeignKey(LaunchPadUsers, on_delete=models.CASCADE) + college = models.ForeignKey(Organization, on_delete=models.CASCADE) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey(LaunchPadUsers, on_delete=models.CASCADE, related_name='launchpad_user_college_link_created_by') + updated_by = models.ForeignKey(LaunchPadUsers, on_delete=models.CASCADE, related_name='launchpad_user_college_link_updated_by') + diff --git a/utils/types.py b/utils/types.py index 47f351cf..7d692e8e 100644 --- a/utils/types.py +++ b/utils/types.py @@ -135,6 +135,24 @@ class DiscountTypes(Enum): PERCENTAGE = 'percentage' AMOUNT = 'amount' +class LaunchPadLevels(Enum): + LEVEL_1 = 'IEEE Launchpad Level 1' + LEVEL_2 = 'IEEE Launchpad Level 2' + LEVEL_3 = 'IEEE Launchpad Level 3' + LEVEL_4 = 'IEEE Launchpad Level 4' + + @classmethod + def get_all_values(cls): + return [member.value for member in cls] + +class LaunchPadRoles(Enum): + ADMIN = 'IEEEAdmin' + DC = 'IEEEDC' + + @classmethod + def get_all_values(cls): + return [member.value for member in cls] + DEFAULT_HACKATHON_FORM_FIELDS = { 'name': 'system', 'gender': 'system', From 77c6aa3cb6280f339dcb3bdedd24cca4f45e11e9 Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Mon, 17 Jun 2024 00:29:02 +0530 Subject: [PATCH 08/14] Refactor serializers.py and donor.py --- api/launchpad/serializers.py | 2 +- db/donor.py | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/api/launchpad/serializers.py b/api/launchpad/serializers.py index 1ea703ee..980c3df6 100644 --- a/api/launchpad/serializers.py +++ b/api/launchpad/serializers.py @@ -97,7 +97,7 @@ class Meta: "level4" ) -class LaunchpadUserSerializer(serializers.ModelSerializerSerializer): +class LaunchpadUserSerializer(serializers.ModelSerializer): role = serializers.ChoiceField(choices=LaunchPadRoles.get_all_values()) college = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True) diff --git a/db/donor.py b/db/donor.py index 07847111..be29d494 100644 --- a/db/donor.py +++ b/db/donor.py @@ -3,20 +3,20 @@ from django.conf import settings -# class Donor(models.Model): -# id = models.CharField(primary_key=True, max_length=36) -# payment_id = models.CharField(max_length=100) -# payment_method = models.CharField(max_length=100) -# amount = models.FloatField() -# currency = models.CharField(max_length=30) -# name = models.CharField(max_length=100) -# email = models.EmailField(max_length=200) -# company = models.CharField(max_length=100, null=True) -# phone_number = models.CharField(max_length=20, null=True) -# pan_number = models.CharField(max_length=10, null=True) -# created_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='created_by', related_name='donor_created_by') -# created_at = models.DateTimeField(auto_now_add=True) +class Donor(models.Model): + id = models.CharField(primary_key=True, max_length=36) + payment_id = models.CharField(max_length=100) + payment_method = models.CharField(max_length=100) + amount = models.FloatField() + currency = models.CharField(max_length=30) + name = models.CharField(max_length=100) + email = models.EmailField(max_length=200) + company = models.CharField(max_length=100, null=True) + phone_number = models.CharField(max_length=20, null=True) + pan_number = models.CharField(max_length=10, null=True) + created_by = models.ForeignKey(User, on_delete=models.SET(settings.SYSTEM_ADMIN_ID), db_column='created_by', related_name='donor_created_by') + created_at = models.DateTimeField(auto_now_add=True) -# class Meta: -# managed = False -# db_table = 'donor' + class Meta: + managed = False + db_table = 'donor' From 0be9f58232ca56c1c8e18e8b1b20826e6c5091a5 Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Mon, 17 Jun 2024 00:49:25 +0530 Subject: [PATCH 09/14] alter script for creating first launchpad admin user --- .env.sample | 4 +++- alter-scripts/alter-1.50.py | 35 ++++++++++++++++++++++++++++++++ api/launchpad/launchpad_views.py | 12 ++--------- db/launchpad.py | 8 ++++++++ 4 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 alter-scripts/alter-1.50.py diff --git a/.env.sample b/.env.sample index 8c82fb25..158f7350 100644 --- a/.env.sample +++ b/.env.sample @@ -36,4 +36,6 @@ RAZORPAY_SECRET= PROTECTED_API_KEY = -SYSTEM_ADMIN_ID = \ No newline at end of file +SYSTEM_ADMIN_ID = + +LAUNCHPAD_ADMIN_EMAIL = \ No newline at end of file diff --git a/alter-scripts/alter-1.50.py b/alter-scripts/alter-1.50.py new file mode 100644 index 00000000..34abb27a --- /dev/null +++ b/alter-scripts/alter-1.50.py @@ -0,0 +1,35 @@ +import os +import sys +import uuid +from decouple import config +import django + +from connection import execute + +os.chdir('..') +sys.path.append(os.getcwd()) +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mulearnbackend.settings') +django.setup() + +from db.launchpad import LaunchPadUsers +from utils.types import LaunchPadRoles + +def create_launchpad_admin(): + query = f"SELECT id FROM launchpad_user WHERE email = '{config('LAUNCHPAD_ADMIN_EMAIL')}'" + if execute(query): + return + query = f""" + INSERT INTO launchpad_user (id, email, role, created_at, updated_at) + VALUES ( + '{uuid.uuid4()}', + '{config('LAUNCHPAD_ADMIN_EMAIL')}', + '{LaunchPadRoles.ADMIN.value}', + UTC_TIMESTAMP, + UTC_TIMESTAMP + ) + """ + execute(query) + +if __name__ == '__main__': + create_launchpad_admin() + execute("UPDATE system_setting SET value = '1.50', updated_at = now() WHERE `key` = 'db.version';") diff --git a/api/launchpad/launchpad_views.py b/api/launchpad/launchpad_views.py index 9616dbf6..bbb95fa9 100644 --- a/api/launchpad/launchpad_views.py +++ b/api/launchpad/launchpad_views.py @@ -233,11 +233,7 @@ def post(self, request): errors = {} error = False not_found_colleges = [] - user = LaunchPadUsers.objects.create( - email=data.get('email'), - full_name=data.get('full_name'), - role=data.get('role') - ) + user = LaunchPadUsers.objects.create(**serializer.validated_data) if data.get('college') is None: return CustomResponse(general_message="Successfully added user").get_error_response() for college in data.get('college'): @@ -361,11 +357,7 @@ def create(self, request): serializer = LaunchpadUserSerializer(data=data) if not serializer.is_valid(): return CustomResponse(message=serializer.errors).get_error_response() - user = LaunchPadUsers.objects.create( - email=data.get('email'), - full_name=data.get('full_name'), - role=data.get('role') - ) + user = LaunchPadUsers.objects.create(**serializer.validated_data) if data.get('college') is None: continue for college in data.get('college'): diff --git a/db/launchpad.py b/db/launchpad.py index fff8d149..1542ce61 100644 --- a/db/launchpad.py +++ b/db/launchpad.py @@ -13,6 +13,11 @@ class LaunchPadUsers(models.Model): role = models.CharField(max_length=20) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) + + class Meta: + managed = False + db_table = 'launchpad_user' + class LaunchPadUserCollegeLink(models.Model): id = models.CharField(primary_key=True, max_length=36) @@ -23,3 +28,6 @@ class LaunchPadUserCollegeLink(models.Model): created_by = models.ForeignKey(LaunchPadUsers, on_delete=models.CASCADE, related_name='launchpad_user_college_link_created_by') updated_by = models.ForeignKey(LaunchPadUsers, on_delete=models.CASCADE, related_name='launchpad_user_college_link_updated_by') + class Meta: + managed = False + db_table = 'launchpad_user_college_link' \ No newline at end of file From 42b834ea85bb5470c21d9e5ad08e90b7bfc27fe4 Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Mon, 17 Jun 2024 11:54:08 +0530 Subject: [PATCH 10/14] feat: Add API endpoint for user-college-link with user_id parameter --- api/donate/views.py | 4 +- api/launchpad/launchpad_views.py | 141 ++++++++++++++++++++++--------- api/launchpad/serializers.py | 80 +++++++++++++++++- api/launchpad/urls.py | 1 + db/launchpad.py | 4 +- 5 files changed, 182 insertions(+), 48 deletions(-) diff --git a/api/donate/views.py b/api/donate/views.py index a1edda0a..e5705727 100644 --- a/api/donate/views.py +++ b/api/donate/views.py @@ -86,7 +86,7 @@ def post(self, request): order = razorpay_client.order.create(data) return CustomResponse(response=order).get_success_response() except razorpay.errors.BadRequestError as e: - return CustomResponse(message=str(e)).get_error_response() + return CustomResponse(message=str(e)).get_failure_response() class RazorPayVerification(APIView): @@ -123,4 +123,4 @@ def post(self, request): return create_receipt(transaction_details) except razorpay.errors.SignatureVerificationError as e: - return CustomResponse(message=str(e)).get_error_response() + return CustomResponse(message=str(e)).get_failure_response() diff --git a/api/launchpad/launchpad_views.py b/api/launchpad/launchpad_views.py index bbb95fa9..d70fc375 100644 --- a/api/launchpad/launchpad_views.py +++ b/api/launchpad/launchpad_views.py @@ -1,9 +1,11 @@ +import uuid + from django.db.models import Sum, Max, Prefetch, F, OuterRef, Subquery, IntegerField, Count, Q from rest_framework.views import APIView from .serializers import LaunchpadLeaderBoardSerializer, LaunchpadParticipantsSerializer, \ - CollegeDataSerializer, LaunchpadUserSerializer + CollegeDataSerializer, LaunchpadUserSerializer, UserProfileUpdateSerializer, LaunchpadUpdateUserSerializer from utils.response import CustomResponse from utils.utils import CommonUtils, ImportCSV from utils.types import LaunchPadLevels, LaunchPadRoles @@ -223,63 +225,111 @@ class LaunchPadUser(APIView): def post(self, request): data = request.data - auth_mail = data.pop('current_user') - if not (auth_user := LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN).first()): - return CustomResponse(general_message="Unauthorized").get_error_response() + auth_mail = data.pop('current_user', None) + if not (auth_user := LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN.value).first()): + return CustomResponse(general_message="Unauthorized").get_failure_response() serializer = LaunchpadUserSerializer(data=data) if not serializer.is_valid(): - return CustomResponse(message=serializer.errors).get_error_response() + return CustomResponse(message=serializer.errors).get_failure_response() + colleges = data.get('college') errors = {} error = False not_found_colleges = [] - user = LaunchPadUsers.objects.create(**serializer.validated_data) - if data.get('college') is None: - return CustomResponse(general_message="Successfully added user").get_error_response() - for college in data.get('college'): + already_linked = [] + user = serializer.save() + if colleges is None: + return CustomResponse(general_message="Successfully added user").get_failure_response() + for college in colleges: if not Organization.objects.filter(id=college, org_type="College").exists(): error = True - not_found_colleges.append[college] - LaunchPadUserCollegeLink.objects.create( - user=user, - college_id=college, - created_by=auth_user, - updated_by=auth_user - ) - errors[data.get('email')] = not_found_colleges + not_found_colleges.append(college) + elif link := LaunchPadUserCollegeLink.objects.filter(college_id=college).first(): + link.delete() + elif LaunchPadUserCollegeLink.objects.filter(user=user, college_id=college).exists(): + error = True + already_linked.append(college) + else: + LaunchPadUserCollegeLink.objects.create( + id=uuid.uuid4(), + user=user, + college_id=college, + created_by=auth_user, + updated_by=auth_user + ) + errors[data.get('email')] = {} + errors[data.get('email')]["not_found_colleges"] = not_found_colleges + errors[data.get('email')]["already_linked"] = already_linked if error: - return CustomResponse(message=errors).get_error_response() + return CustomResponse(message=errors).get_failure_response() return CustomResponse(general_message="Successfully added user").get_success_response() def get(self, request): data = request.data - auth_mail = data.pop('current_user') - if not LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN).exists(): - return CustomResponse(general_message="Unauthorized").get_error_response() + auth_mail = data.pop('current_user', None) + if not LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN.value).exists(): + return CustomResponse(general_message="Unauthorized").get_failure_response() users = LaunchPadUsers.objects.all() - serializer = LaunchpadUserSerializer(users, many=True) - return CustomResponse(data=serializer.data).get_success_response() + paginated_queryset = CommonUtils.get_paginated_queryset( + users, + request, + ["full_name", "phone_number", "email", "role", "district", "zone"] + ) + + serializer = LaunchpadUserSerializer( + paginated_queryset.get("queryset"), many=True + ) + return CustomResponse().paginated_response( + data=serializer.data, pagination=paginated_queryset.get("pagination") + ) + + def put(self, request, user_id): + data = request.data + auth_mail = data.pop('current_user', None) + if not (auth_user := LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN.value).first()): + return CustomResponse(general_message="Unauthorized").get_failure_response() + try: + user = LaunchPadUsers.objects.get(id=user_id) + except LaunchPadUsers.DoesNotExist: + return CustomResponse(general_message="User not found").get_failure_response() + serializer = LaunchpadUpdateUserSerializer(user, data=data, context={"auth_user": auth_user}) + if serializer.is_valid(): + serializer.save() + return CustomResponse(general_message="Successfully updated user").get_success_response() + return CustomResponse(message=serializer.errors).get_failure_response() class UserProfile(APIView): def get(self, request): data = request.data - auth_mail = data.pop('current_user') + auth_mail = data.pop('current_user', None) if not LaunchPadUsers.objects.filter(email=auth_mail).exists(): - return CustomResponse(general_message="Unauthorized").get_error_response() + return CustomResponse(general_message="Unauthorized").get_failure_response() user = LaunchPadUsers.objects.get(email=auth_mail) serializer = LaunchpadUserSerializer(user) return CustomResponse(data=serializer.data).get_success_response() + def put(self, request): + data = request.data + auth_mail = data.pop('current_user', None) + if not (user := LaunchPadUsers.objects.filter(email=auth_mail).first()): + return CustomResponse(general_message="Unauthorized").get_failure_response() + + serializer = UserProfileUpdateSerializer(user, data=data) + if serializer.is_valid(): + serializer.save() + return CustomResponse(general_message="Successfully updated user").get_success_response() + return CustomResponse(message=serializer.errors).get_failure_response() + class UserBasedCollegeData(APIView): def get(self, request): data = request.data - auth_mail = data.pop('current_user') + auth_mail = data.pop('current_user', None) if not LaunchPadUsers.objects.filter(email=auth_mail).exists(): - return CustomResponse(general_message="Unauthorized").get_error_response() + return CustomResponse(general_message="Unauthorized").get_failure_response() user = LaunchPadUsers.objects.get(email=auth_mail) colleges = LaunchPadUserCollegeLink.objects.filter(user=user) college_ids = [college.college_id for college in colleges] @@ -338,9 +388,9 @@ class BulkLaunchpadUser(APIView): def create(self, request): data = request.data - auth_mail = data.pop('current_user') - if not (auth_user := LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN).first()): - return CustomResponse(general_message="Unauthorized").get_error_response() + auth_mail = data.pop('current_user', None) + if not (auth_user := LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN.value).first()): + return CustomResponse(general_message="Unauthorized").get_failure_response() try: file_obj = request.FILES['user_data'] except KeyError: @@ -354,23 +404,32 @@ def create(self, request): for data in excel_data: not_found_colleges = [] + already_linked = [] serializer = LaunchpadUserSerializer(data=data) if not serializer.is_valid(): - return CustomResponse(message=serializer.errors).get_error_response() - user = LaunchPadUsers.objects.create(**serializer.validated_data) + return CustomResponse(message=serializer.errors).get_failure_response() + user = serializer.save() if data.get('college') is None: continue for college in data.get('college'): if not Organization.objects.filter(title=college, org_type="College").exists(): error = True - not_found_colleges.append[college] - LaunchPadUserCollegeLink.objects.create( - user=user, - college_id=college, - created_by=auth_user, - updated_by=auth_user - ) - errors[data.get('email')] = not_found_colleges + not_found_colleges.append(college) + elif link := LaunchPadUserCollegeLink.objects.filter(college_id=college).first(): + link.delete() + elif LaunchPadUserCollegeLink.objects.filter(user=user, college_id=college).exists(): + error = True + already_linked.append(college) + else: + LaunchPadUserCollegeLink.objects.create( + id=uuid.uuid4(), + user=user, + college_id=college, + created_by=auth_user, + updated_by=auth_user + ) + errors[data.get('email')]["not_found_colleges"] = not_found_colleges + errors[data.get('email')]["already_linked"] if error: - return CustomResponse(message=errors).get_error_response() + return CustomResponse(message=errors).get_failure_response() return CustomResponse(general_message="Successfully added users").get_success_response() \ No newline at end of file diff --git a/api/launchpad/serializers.py b/api/launchpad/serializers.py index 980c3df6..e40f9404 100644 --- a/api/launchpad/serializers.py +++ b/api/launchpad/serializers.py @@ -1,3 +1,5 @@ +import uuid + from django.db.models import Sum, Max, Prefetch, F, OuterRef, Subquery, IntegerField from rest_framework import serializers @@ -5,7 +7,9 @@ from db.user import User from db.organization import UserOrganizationLink, Organization from db.task import KarmaActivityLog +from db.launchpad import LaunchPadUsers, LaunchPadUserCollegeLink from utils.types import LaunchPadRoles +from utils.utils import DateTimeUtils class LaunchpadLeaderBoardSerializer(serializers.ModelSerializer): @@ -99,9 +103,79 @@ class Meta: class LaunchpadUserSerializer(serializers.ModelSerializer): role = serializers.ChoiceField(choices=LaunchPadRoles.get_all_values()) - college = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True) + college = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True, write_only=True) class Meta: - model = User + model = LaunchPadUsers fields = ("full_name", "email", "phone_number", "role", "college", "district", "zone") - \ No newline at end of file + + def create(self, validated_data): + validated_data.pop("college") + + validated_data["id"] = uuid.uuid4() + validated_data["created_at"] = DateTimeUtils.get_current_utc_time() + validated_data["updated_at"] = DateTimeUtils.get_current_utc_time() + user = LaunchPadUsers.objects.create(**validated_data) + + return user + + +class LaunchpadUpdateUserSerializer(serializers.ModelSerializer): + role = serializers.ChoiceField(choices=LaunchPadRoles.get_all_values()) + remove_colleges = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True) + add_colleges = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True) + + class Meta: + model = LaunchPadUsers + fields = ("full_name", "email", "phone_number", "role", "college", "district", "zone", "remove_colleges", "add_colleges") + + def update(self, instance, validated_data): + auth_user = self.context.get("auth_user") + user_id = instance.id + remove_colleges = validated_data.pop("remove_colleges") + add_colleges = validated_data.pop("add_colleges") + + instance.full_name = validated_data.get("full_name", instance.full_name) + instance.email = validated_data.get("email", instance.email) + instance.phone_number = validated_data.get("phone_number", instance.phone_number) + instance.role = validated_data.get("role", instance.role) + instance.district = validated_data.get("district", instance.district) + instance.zone = validated_data.get("zone", instance.zone) + instance.updated_at = DateTimeUtils.get_current_utc_time() + instance.save() + + if remove_colleges: + LaunchPadUserCollegeLink.objects.filter(college_id__in=remove_colleges, user_id=user_id).delete() + + if add_colleges: + LaunchPadUserCollegeLink.objects.filter(college_id__in=add_colleges).delete() + LaunchPadUserCollegeLink.objects.bulk_create([ + LaunchPadUserCollegeLink( + id=uuid.uuid4(), + user=instance, + college_id=college_id, + created_at=DateTimeUtils.get_current_utc_time(), + updated_at=DateTimeUtils.get_current_utc_time(), + created_by=auth_user, + updated_by=auth_user + ) for college_id in add_colleges if Organization.objects.filter(id=college_id).exists() + ]) + + return instance + + +class UserProfileUpdateSerializer(serializers.ModelSerializer): + + class Meta: + model = LaunchPadUsers + fields = ("full_name", "phone_number", "district", "zone", "email") + + def update(self, instance, validated_data): + instance.full_name = validated_data.get("full_name", instance.full_name) + instance.phone_number = validated_data.get("phone_number", instance.phone_number) + instance.district = validated_data.get("district", instance.district) + instance.zone = validated_data.get("zone", instance.zone) + instance.email = validated_data.get("email", instance.email) + instance.updated_at = DateTimeUtils.get_current_utc_time() + instance.save() + return instance \ No newline at end of file diff --git a/api/launchpad/urls.py b/api/launchpad/urls.py index aea0f90f..cb465343 100644 --- a/api/launchpad/urls.py +++ b/api/launchpad/urls.py @@ -8,6 +8,7 @@ path('launchpad-details/', launchpad_views.LaunchpadDetailsCount.as_view()), path('college-data/', launchpad_views.CollegeData.as_view()), path('user-college-link/', launchpad_views.LaunchPadUser.as_view()), + path('user-college-link//', launchpad_views.LaunchPadUser.as_view()), path('user-profile/', launchpad_views.UserProfile.as_view()), path('user-college-data/', launchpad_views.UserBasedCollegeData.as_view()), path('bulk-user-college-link/', launchpad_views.BulkLaunchpadUser.as_view()), diff --git a/db/launchpad.py b/db/launchpad.py index 1542ce61..f958db3e 100644 --- a/db/launchpad.py +++ b/db/launchpad.py @@ -21,8 +21,8 @@ class Meta: class LaunchPadUserCollegeLink(models.Model): id = models.CharField(primary_key=True, max_length=36) - user = models.ForeignKey(LaunchPadUsers, on_delete=models.CASCADE) - college = models.ForeignKey(Organization, on_delete=models.CASCADE) + user = models.ForeignKey(LaunchPadUsers, on_delete=models.CASCADE, related_name="launchpaduserlink_user") + college = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name="launchpaduserlink_college") created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.ForeignKey(LaunchPadUsers, on_delete=models.CASCADE, related_name='launchpad_user_college_link_created_by') From e9ce8ce8f647aac6bd1163e79c5bc5b6676b8db9 Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Mon, 17 Jun 2024 13:35:59 +0530 Subject: [PATCH 11/14] Refactor LaunchPadUser and BulkLaunchpadUser views and serializers --- api/launchpad/launchpad_views.py | 6 ------ api/launchpad/serializers.py | 20 +++++++++++++++----- api/launchpad/urls.py | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/api/launchpad/launchpad_views.py b/api/launchpad/launchpad_views.py index d70fc375..f0e83ac6 100644 --- a/api/launchpad/launchpad_views.py +++ b/api/launchpad/launchpad_views.py @@ -246,9 +246,6 @@ def post(self, request): not_found_colleges.append(college) elif link := LaunchPadUserCollegeLink.objects.filter(college_id=college).first(): link.delete() - elif LaunchPadUserCollegeLink.objects.filter(user=user, college_id=college).exists(): - error = True - already_linked.append(college) else: LaunchPadUserCollegeLink.objects.create( id=uuid.uuid4(), @@ -417,9 +414,6 @@ def create(self, request): not_found_colleges.append(college) elif link := LaunchPadUserCollegeLink.objects.filter(college_id=college).first(): link.delete() - elif LaunchPadUserCollegeLink.objects.filter(user=user, college_id=college).exists(): - error = True - already_linked.append(college) else: LaunchPadUserCollegeLink.objects.create( id=uuid.uuid4(), diff --git a/api/launchpad/serializers.py b/api/launchpad/serializers.py index e40f9404..1abac298 100644 --- a/api/launchpad/serializers.py +++ b/api/launchpad/serializers.py @@ -102,12 +102,14 @@ class Meta: ) class LaunchpadUserSerializer(serializers.ModelSerializer): + id = serializers.CharField(max_length=36, read_only=True) role = serializers.ChoiceField(choices=LaunchPadRoles.get_all_values()) college = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True, write_only=True) + colleges = serializers.SerializerMethodField() class Meta: model = LaunchPadUsers - fields = ("full_name", "email", "phone_number", "role", "college", "district", "zone") + fields = ("id", "full_name", "email", "phone_number", "role", "college", "district", "zone", "colleges") def create(self, validated_data): validated_data.pop("college") @@ -119,6 +121,9 @@ def create(self, validated_data): return user + def get_colleges(self, obj): + return LaunchPadUserCollegeLink.objects.filter(user=obj).values_list("college_id", "college__title") + class LaunchpadUpdateUserSerializer(serializers.ModelSerializer): role = serializers.ChoiceField(choices=LaunchPadRoles.get_all_values()) @@ -127,7 +132,7 @@ class LaunchpadUpdateUserSerializer(serializers.ModelSerializer): class Meta: model = LaunchPadUsers - fields = ("full_name", "email", "phone_number", "role", "college", "district", "zone", "remove_colleges", "add_colleges") + fields = ("full_name", "email", "phone_number", "role", "district", "zone", "remove_colleges", "add_colleges") def update(self, instance, validated_data): auth_user = self.context.get("auth_user") @@ -165,10 +170,12 @@ def update(self, instance, validated_data): class UserProfileUpdateSerializer(serializers.ModelSerializer): - + id = serializers.CharField(max_length=36, read_only=True) + colleges = serializers.SerializerMethodField() + class Meta: model = LaunchPadUsers - fields = ("full_name", "phone_number", "district", "zone", "email") + fields = ("id", "full_name", "phone_number", "district", "zone", "email") def update(self, instance, validated_data): instance.full_name = validated_data.get("full_name", instance.full_name) @@ -178,4 +185,7 @@ def update(self, instance, validated_data): instance.email = validated_data.get("email", instance.email) instance.updated_at = DateTimeUtils.get_current_utc_time() instance.save() - return instance \ No newline at end of file + return instance + + def get_colleges(self, obj): + return LaunchPadUserCollegeLink.objects.filter(user=obj).values_list("college_id", "college__title") \ No newline at end of file diff --git a/api/launchpad/urls.py b/api/launchpad/urls.py index cb465343..9e163bfd 100644 --- a/api/launchpad/urls.py +++ b/api/launchpad/urls.py @@ -8,7 +8,7 @@ path('launchpad-details/', launchpad_views.LaunchpadDetailsCount.as_view()), path('college-data/', launchpad_views.CollegeData.as_view()), path('user-college-link/', launchpad_views.LaunchPadUser.as_view()), - path('user-college-link//', launchpad_views.LaunchPadUser.as_view()), + path('user-college-link/', launchpad_views.LaunchPadUser.as_view()), path('user-profile/', launchpad_views.UserProfile.as_view()), path('user-college-data/', launchpad_views.UserBasedCollegeData.as_view()), path('bulk-user-college-link/', launchpad_views.BulkLaunchpadUser.as_view()), From e9d104eaecc828fb909912133b3222c9bae1aa2c Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Mon, 17 Jun 2024 13:37:32 +0530 Subject: [PATCH 12/14] chore: Remove unused variables in LaunchPadUser and BulkLaunchpadUser views --- api/launchpad/launchpad_views.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/api/launchpad/launchpad_views.py b/api/launchpad/launchpad_views.py index f0e83ac6..3da3ae87 100644 --- a/api/launchpad/launchpad_views.py +++ b/api/launchpad/launchpad_views.py @@ -236,7 +236,6 @@ def post(self, request): errors = {} error = False not_found_colleges = [] - already_linked = [] user = serializer.save() if colleges is None: return CustomResponse(general_message="Successfully added user").get_failure_response() @@ -256,7 +255,6 @@ def post(self, request): ) errors[data.get('email')] = {} errors[data.get('email')]["not_found_colleges"] = not_found_colleges - errors[data.get('email')]["already_linked"] = already_linked if error: return CustomResponse(message=errors).get_failure_response() return CustomResponse(general_message="Successfully added user").get_success_response() @@ -401,7 +399,6 @@ def create(self, request): for data in excel_data: not_found_colleges = [] - already_linked = [] serializer = LaunchpadUserSerializer(data=data) if not serializer.is_valid(): return CustomResponse(message=serializer.errors).get_failure_response() @@ -422,8 +419,8 @@ def create(self, request): created_by=auth_user, updated_by=auth_user ) + errors[data.get('email')] = {} errors[data.get('email')]["not_found_colleges"] = not_found_colleges - errors[data.get('email')]["already_linked"] if error: return CustomResponse(message=errors).get_failure_response() return CustomResponse(general_message="Successfully added users").get_success_response() \ No newline at end of file From 5f229a8724b7be7db52ad83cd0cba51c9d89494c Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Mon, 17 Jun 2024 15:15:08 +0530 Subject: [PATCH 13/14] feat: Update BulkLaunchpadUser view to use POST method for creating users --- api/launchpad/launchpad_views.py | 13 ++++++++----- api/launchpad/serializers.py | 6 +++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/api/launchpad/launchpad_views.py b/api/launchpad/launchpad_views.py index 3da3ae87..2b66af5d 100644 --- a/api/launchpad/launchpad_views.py +++ b/api/launchpad/launchpad_views.py @@ -381,9 +381,10 @@ def get(self, request): class BulkLaunchpadUser(APIView): - def create(self, request): + def post(self, request): data = request.data auth_mail = data.pop('current_user', None) + auth_mail = auth_mail[0] if isinstance(auth_mail, list) else auth_mail if not (auth_user := LaunchPadUsers.objects.filter(email=auth_mail, role=LaunchPadRoles.ADMIN.value).first()): return CustomResponse(general_message="Unauthorized").get_failure_response() try: @@ -397,16 +398,18 @@ def create(self, request): errors = {} error = False - for data in excel_data: + for data in excel_data[1:]: + print(data) not_found_colleges = [] + data['college'] = data['college'].split(",") if data.get('college') else [] serializer = LaunchpadUserSerializer(data=data) if not serializer.is_valid(): - return CustomResponse(message=serializer.errors).get_failure_response() + continue user = serializer.save() if data.get('college') is None: continue for college in data.get('college'): - if not Organization.objects.filter(title=college, org_type="College").exists(): + if not (org := Organization.objects.filter(title=college, org_type="College").first()): error = True not_found_colleges.append(college) elif link := LaunchPadUserCollegeLink.objects.filter(college_id=college).first(): @@ -415,7 +418,7 @@ def create(self, request): LaunchPadUserCollegeLink.objects.create( id=uuid.uuid4(), user=user, - college_id=college, + college=org, created_by=auth_user, updated_by=auth_user ) diff --git a/api/launchpad/serializers.py b/api/launchpad/serializers.py index 1abac298..40545223 100644 --- a/api/launchpad/serializers.py +++ b/api/launchpad/serializers.py @@ -104,7 +104,7 @@ class Meta: class LaunchpadUserSerializer(serializers.ModelSerializer): id = serializers.CharField(max_length=36, read_only=True) role = serializers.ChoiceField(choices=LaunchPadRoles.get_all_values()) - college = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True, write_only=True) + college = serializers.ListField(child=serializers.CharField(), allow_empty=True, write_only=True) colleges = serializers.SerializerMethodField() class Meta: @@ -127,8 +127,8 @@ def get_colleges(self, obj): class LaunchpadUpdateUserSerializer(serializers.ModelSerializer): role = serializers.ChoiceField(choices=LaunchPadRoles.get_all_values()) - remove_colleges = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True) - add_colleges = serializers.ListField(child=serializers.CharField(max_length=36), allow_empty=True) + remove_colleges = serializers.ListField(child=serializers.CharField(), allow_empty=True) + add_colleges = serializers.ListField(child=serializers.CharField(), allow_empty=True) class Meta: model = LaunchPadUsers From 0a2516b3a07774ae3b21d053a3f2438e5a74cec8 Mon Sep 17 00:00:00 2001 From: jelanmathewjames Date: Mon, 17 Jun 2024 15:17:16 +0530 Subject: [PATCH 14/14] refactor: Remove print statement in BulkLaunchpadUser view --- api/launchpad/launchpad_views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/launchpad/launchpad_views.py b/api/launchpad/launchpad_views.py index 2b66af5d..8782fdf3 100644 --- a/api/launchpad/launchpad_views.py +++ b/api/launchpad/launchpad_views.py @@ -399,7 +399,6 @@ def post(self, request): error = False for data in excel_data[1:]: - print(data) not_found_colleges = [] data['college'] = data['college'].split(",") if data.get('college') else [] serializer = LaunchpadUserSerializer(data=data)