From b1306ac77e6c78a0677c883c1d6832bffc24cf7a Mon Sep 17 00:00:00 2001 From: Muhammed Zafar Date: Tue, 17 Oct 2023 21:59:06 +0530 Subject: [PATCH 1/4] [OPTIM] Leaderboard patches --- api/leaderboard/leaderboard_view.py | 34 ++++++++++++++++------------- api/leaderboard/serializers.py | 26 +++++++++++++++++----- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/api/leaderboard/leaderboard_view.py b/api/leaderboard/leaderboard_view.py index 3f206840..65811990 100644 --- a/api/leaderboard/leaderboard_view.py +++ b/api/leaderboard/leaderboard_view.py @@ -1,7 +1,8 @@ -from django.db.models import Sum, F, Value, OuterRef, Subquery, Count, Q +from django.db.models import Sum, F, Value, OuterRef, Subquery, Count, Q, Prefetch from django.db.models.functions import Concat, Coalesce from rest_framework.views import APIView import datetime +from . import serializers from db.organization import Organization, UserOrganizationLink from db.user import User @@ -12,14 +13,6 @@ class StudentsLeaderboard(APIView): def get(self, request): - user_college = ( - UserOrganizationLink.objects.filter( - user_id=OuterRef("id"), org__org_type=OrganizationType.COLLEGE.value - ) - .order_by("id") - .values("org__title")[:1] - ) - students_leaderboard = ( User.objects.filter( user_organization_link_user__org__org_type=OrganizationType.COLLEGE.value, @@ -28,14 +21,25 @@ def get(self, request): active=True, ) .distinct() - .values( - total_karma=F("wallet_user__karma"), - full_name=Concat(F("first_name"), Value(" "), F("last_name")), - institution=Subquery(user_college), + .select_related("wallet_user") + .prefetch_related( + Prefetch( + "user_organization_link_user", + queryset=UserOrganizationLink.objects.filter( + org__org_type=OrganizationType.COLLEGE.value + ).select_related("org"), + to_attr="colleges" + ) ) - .order_by("-total_karma")[:20] + .order_by("-wallet_user__karma")[:20] + ) + serialized_students_leaderboard = serializers.StudentLeaderboardSerializer( + students_leaderboard, many=True ) - return CustomResponse(response=students_leaderboard).get_success_response() + + return CustomResponse( + response=serialized_students_leaderboard.data + ).get_success_response() class StudentsMonthlyLeaderboard(APIView): diff --git a/api/leaderboard/serializers.py b/api/leaderboard/serializers.py index fb591aba..e35a4dc3 100644 --- a/api/leaderboard/serializers.py +++ b/api/leaderboard/serializers.py @@ -5,6 +5,7 @@ from db.organization import UserOrganizationLink from db.task import KarmaActivityLog +from db.user import User from utils.types import OrganizationType from utils.utils import DateTimeUtils @@ -46,12 +47,12 @@ def get_active_members(self, obj): def get_total_karma(self, obj): return ( - obj.org.user_organization_link_org.filter( - org__org_type=OrganizationType.COLLEGE.value, - verified=True, - user__wallet_user__isnull=False, - ).aggregate(total_karma=Sum("user__wallet_user__karma"))["total_karma"] - or 0 + obj.org.user_organization_link_org.filter( + org__org_type=OrganizationType.COLLEGE.value, + verified=True, + user__wallet_user__isnull=False, + ).aggregate(total_karma=Sum("user__wallet_user__karma"))["total_karma"] + or 0 ) def get_rank(self, obj): @@ -74,3 +75,16 @@ def get_rank(self, obj): keys_list = list(sorted_rank_dict.keys()) position = keys_list.index(obj.org.id) return position + 1 + + +class StudentLeaderboardSerializer(serializers.ModelSerializer): + institution = serializers.SerializerMethodField() + total_karma = serializers.IntegerField(source="wallet_user.karma", default=0) + full_name = serializers.CharField(source="fullname") + + def get_institution(self, user): + return user.colleges[0].org.title if user.colleges else None + + class Meta: + model = User + fields = ["full_name", "total_karma", "institution"] From fe647c5bf8d45e6d5d0dbce48bfe406ed3b86983 Mon Sep 17 00:00:00 2001 From: Muhammed Zafar Date: Tue, 17 Oct 2023 22:22:31 +0530 Subject: [PATCH 2/4] [PATCH] User EDIT dashboard --- api/dashboard/user/dash_user_serializer.py | 50 +++++++--------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/api/dashboard/user/dash_user_serializer.py b/api/dashboard/user/dash_user_serializer.py index b9ad1bee..30d1633b 100644 --- a/api/dashboard/user/dash_user_serializer.py +++ b/api/dashboard/user/dash_user_serializer.py @@ -60,17 +60,16 @@ def get_roles(self, obj): class CollegeSerializer(serializers.ModelSerializer): - title = serializers.CharField(source="org.title", allow_null=True) org_type = serializers.CharField(source="org.org_type") - department = serializers.CharField(source="department.title") - country = serializers.CharField(source="country.name") - state = serializers.CharField(source="state.name") - district = serializers.CharField(source="district.name") + department = serializers.CharField(source="department.pk") + country = serializers.CharField(source="country.pk") + state = serializers.CharField(source="state.pk") + district = serializers.CharField(source="district.pk") class Meta: model = UserOrganizationLink fields = [ - "title", + "org", "org_type", "department", "graduation_year", @@ -80,33 +79,25 @@ class Meta: ] -class CommunitySerializer(serializers.ModelSerializer): - title = serializers.CharField(source="org.title", read_only=True) +class OrgSerializer(serializers.ModelSerializer): org_type = serializers.CharField(source="org.org_type", read_only=True) class Meta: model = UserOrganizationLink - fields = ["title", "org_type"] - - -class CompanySerializer(serializers.ModelSerializer): - title = serializers.CharField(source="org.title", read_only=True) - org_type = serializers.CharField(source="org.org_type", read_only=True) - - class Meta: - model = UserOrganizationLink - fields = ["title", "org_type"] + fields = ["org", "org_type"] class UserDetailsSerializer(serializers.ModelSerializer): user_id = serializers.CharField(source="id") - organizations = serializers.SerializerMethodField(read_only=True) - interest_groups = serializers.SerializerMethodField(read_only=True) + igs = serializers.ListField(write_only=True) - role = serializers.SerializerMethodField(read_only=True) department = serializers.CharField(write_only=True) graduation_year = serializers.CharField(write_only=True) + organizations = serializers.SerializerMethodField(read_only=True) + interest_groups = serializers.SerializerMethodField(read_only=True) + role = serializers.SerializerMethodField(read_only=True) + class Meta: model = User fields = [ @@ -125,7 +116,7 @@ class Meta: "interest_groups", "igs", ] - + def validate(self, data): if "id" not in data: raise serializers.ValidationError("User id is a required field") @@ -193,26 +184,17 @@ def get_organizations(self, user): for link in organization_links: if link.org.org_type == OrganizationType.COLLEGE.value: serializer = CollegeSerializer(link) - elif link.org.org_type == OrganizationType.COMPANY.value: - serializer = CompanySerializer(link) else: - serializer = CommunitySerializer(link) + serializer = OrgSerializer(link) organizations_data.append(serializer.data) return organizations_data def get_interest_groups(self, user): - igs = user.user_ig_link_user.all() - if igs: - igs = [ig.ig.name for ig in igs] - return igs + return user.user_ig_link_user.all().values_list("ig", flat=True) def get_role(self, user): - if roles := UserRoleLink.objects.filter(user=user).values_list( - "role__title", flat=True - ): - return list(roles) - return None + return user.user_role_link_user.all().values_list("role", flat=True) class UserVerificationSerializer(serializers.ModelSerializer): From 584ab30d87ea563d9f4de630fb296b96a5840fd4 Mon Sep 17 00:00:00 2001 From: Muhammed Zafar Date: Thu, 19 Oct 2023 19:27:17 +0530 Subject: [PATCH 3/4] [FEAT] Set up error log and customresponse --- api/dashboard/campus/campus_views.py | 23 ++++----- api/dashboard/error_log/error_view.py | 42 ++++++++++++----- api/dashboard/location/location_views.py | 60 +++++++++++------------- api/dashboard/profile/profile_view.py | 56 ++++++++-------------- api/integrations/kkem/kkem_views.py | 31 ++---------- mulearnbackend/middlewares.py | 39 ++++++++++++--- mulearnbackend/settings.py | 7 +-- utils/exception.py | 3 +- 8 files changed, 125 insertions(+), 136 deletions(-) diff --git a/api/dashboard/campus/campus_views.py b/api/dashboard/campus/campus_views.py index 58a0ff1a..5909e8e1 100644 --- a/api/dashboard/campus/campus_views.py +++ b/api/dashboard/campus/campus_views.py @@ -212,21 +212,14 @@ class WeeklyKarmaAPI(APIView): @role_required([RoleType.CAMPUS_LEAD.value, RoleType.ENABLER.value]) def get(self, request): - try: - user_id = JWTUtils.fetch_user_id(request) - - user_org_link = get_user_college_link(user_id) + user_id = JWTUtils.fetch_user_id(request) - if user_org_link.org is None: - return CustomResponse( - general_message="Campus lead has no college" - ).get_failure_response() + user_org_link = get_user_college_link(user_id) - serializer = serializers.WeeklyKarmaSerializer(user_org_link) - return CustomResponse(response=serializer.data).get_success_response() + if user_org_link.org is None: + return CustomResponse( + general_message="Campus lead has no college" + ).get_failure_response() - except Exception as e: - raise CustomException( - detail='somthing went wrong', - status_code=403 - ) + serializer = serializers.WeeklyKarmaSerializer(user_org_link) + return CustomResponse(response=serializer.data).get_success_response() \ No newline at end of file diff --git a/api/dashboard/error_log/error_view.py b/api/dashboard/error_log/error_view.py index cecc2508..9441be3d 100644 --- a/api/dashboard/error_log/error_view.py +++ b/api/dashboard/error_log/error_view.py @@ -14,31 +14,43 @@ class DownloadErrorLogAPI(APIView): authentication_classes = [CustomizePermission] - @role_required([RoleType.ADMIN.value, RoleType.FELLOW.value, RoleType.TECH_TEAM.value]) + @role_required( + [RoleType.ADMIN.value, RoleType.FELLOW.value, RoleType.TECH_TEAM.value] + ) def get(self, request, log_name): error_log = f"{log_path}/{log_name}.log" if os.path.exists(error_log): - response = FileResponse(open(error_log, 'rb'), content_type='application/octet-stream') - response['Content-Disposition'] = f'attachment; filename="{log_name}"' + response = FileResponse( + open(error_log, "rb"), content_type="application/octet-stream" + ) + response["Content-Disposition"] = f'attachment; filename="{log_name}"' return response - return CustomResponse(general_message=f"{log_name} Not Found").get_failure_response() + return CustomResponse( + general_message=f"{log_name} Not Found" + ).get_failure_response() class ViewErrorLogAPI(APIView): authentication_classes = [CustomizePermission] - @role_required([RoleType.ADMIN.value, RoleType.FELLOW.value, RoleType.TECH_TEAM.value]) + @role_required( + [RoleType.ADMIN.value, RoleType.FELLOW.value, RoleType.TECH_TEAM.value] + ) def get(self, request, log_name): error_log = f"{log_path}/{log_name}.log" if os.path.exists(error_log): try: - with open(error_log, 'r') as log_file: + with open(error_log, "r") as log_file: log_content = log_file.read() return CustomResponse(response=log_content).get_success_response() except Exception as e: - return CustomResponse(general_message=f'Error reading log file').get_failure_response() + return CustomResponse( + general_message="Error reading log file" + ).get_failure_response() - return CustomResponse(general_message=f"{log_name} Not Found").get_failure_response() + return CustomResponse( + general_message=f"{log_name} Not Found" + ).get_failure_response() class ClearErrorLogAPI(APIView): @@ -46,11 +58,17 @@ def get(self, request, log_name): error_log = f"{log_path}/{log_name}.log" if os.path.exists(error_log): try: - with open(error_log, 'w') as log_file: + with open(error_log, "w") as log_file: log_file.truncate(0) - return CustomResponse(general_message=f'{log_name} log cleared successfully').get_success_response() + return CustomResponse( + general_message=f"{log_name} log cleared successfully" + ).get_success_response() except Exception as e: print(e) - return CustomResponse(general_message=f'Error reading log file').get_failure_response() + return CustomResponse( + general_message="Error reading log file" + ).get_failure_response() - return CustomResponse(general_message=f"{log_name} Not Found").get_failure_response() + return CustomResponse( + general_message=f"{log_name} Not Found" + ).get_failure_response() diff --git a/api/dashboard/location/location_views.py b/api/dashboard/location/location_views.py index 77cbbde9..2650d862 100644 --- a/api/dashboard/location/location_views.py +++ b/api/dashboard/location/location_views.py @@ -15,44 +15,38 @@ class CountryDataAPI(APIView): @role_required([RoleType.ADMIN.value]) def get(self, request, country_id=None): - try: - if country_id: - countries = Country.objects.filter(id=country_id) - else: - countries = Country.objects.all() - - paginated_queryset = CommonUtils.get_paginated_queryset( - countries, request, ["name"], {"name": "name"} - ) - - serializer = location_serializer.CountryRetrievalSerializer( - paginated_queryset.get("queryset"), many=True - ) - return CustomResponse().paginated_response( - data=serializer.data, pagination=paginated_queryset.get("pagination") - ) - except Exception as e: - return CustomResponse(general_message=str(e)).get_failure_response() + if country_id: + countries = Country.objects.filter(id=country_id) + else: + countries = Country.objects.all() + + paginated_queryset = CommonUtils.get_paginated_queryset( + countries, request, ["name"], {"name": "name"} + ) + + serializer = location_serializer.CountryRetrievalSerializer( + paginated_queryset.get("queryset"), many=True + ) + return CustomResponse().paginated_response( + data=serializer.data, pagination=paginated_queryset.get("pagination") + ) @role_required([RoleType.ADMIN.value]) def post(self, request): - try: - user_id = JWTUtils.fetch_user_id(request) - serializer = location_serializer.CountryCreateEditSerializer( - data=request.data, context={"user_id": user_id} - ) - - if serializer.is_valid(): - serializer.save() - return CustomResponse( - general_message=serializer.data - ).get_success_response() + user_id = JWTUtils.fetch_user_id(request) + serializer = location_serializer.CountryCreateEditSerializer( + data=request.data, context={"user_id": user_id} + ) + if serializer.is_valid(): + serializer.save() return CustomResponse( - general_message=serializer.errors - ).get_failure_response() - except Exception as e: - return CustomResponse(general_message=str(e)).get_failure_response() + general_message=serializer.data + ).get_success_response() + + return CustomResponse( + general_message=serializer.errors + ).get_failure_response() @role_required([RoleType.ADMIN.value]) def patch(self, request, country_id): diff --git a/api/dashboard/profile/profile_view.py b/api/dashboard/profile/profile_view.py index cb97e0b9..bd7d99f6 100644 --- a/api/dashboard/profile/profile_view.py +++ b/api/dashboard/profile/profile_view.py @@ -14,8 +14,6 @@ import logging - - class UserProfileEditView(APIView): authentication_classes = [CustomizePermission] @@ -60,37 +58,23 @@ class UserIgEditView(APIView): def get(self, request): user_id = JWTUtils.fetch_user_id(request) - user_ig = InterestGroup.objects.filter( - user_ig_link_ig__user_id=user_id - ).all() + user_ig = InterestGroup.objects.filter(user_ig_link_ig__user_id=user_id).all() - serializer = profile_serializer.UserIgListSerializer( - user_ig, - many=True - ) + serializer = profile_serializer.UserIgListSerializer(user_ig, many=True) - return CustomResponse( - response=serializer.data - ).get_success_response() + return CustomResponse(response=serializer.data).get_success_response() def patch(self, request): - - logger = logging.getLogger(__name__) - try: user_id = JWTUtils.fetch_user_id(request) user = User.objects.get(id=user_id) serializer = profile_serializer.UserIgEditSerializer( - user, - data=request.data, - partial=True + user, data=request.data, partial=True ) if not serializer.is_valid(): - return CustomResponse( - response=serializer.errors - ).get_failure_response() + return CustomResponse(response=serializer.errors).get_failure_response() serializer.save() DiscordWebhooks.general_updates( @@ -103,15 +87,7 @@ def patch(self, request): ).get_success_response() except ValueError as e: - return CustomResponse( - general_message=str(e) - ).get_failure_response() - - except Exception as e: - logger.exception("An error occurred: %s", str(e)) - return CustomResponse( - general_message="Somthing went wrong" - ).get_failure_response() + return CustomResponse(general_message=str(e)).get_failure_response() class UserProfileAPI(APIView): @@ -140,9 +116,7 @@ def get(self, request, muid=None): else: JWTUtils.is_jwt_authenticated(request) - serializer = profile_serializer.UserProfileSerializer( - user, many=False - ) + serializer = profile_serializer.UserProfileSerializer(user, many=False) return CustomResponse(response=serializer.data).get_success_response() @@ -259,10 +233,14 @@ def get(self, request, muid=None): if muid is not None: user = User.objects.filter(muid=muid).first() if user is None: - return CustomResponse(general_message="Invalid muid").get_failure_response() + return CustomResponse( + general_message="Invalid muid" + ).get_failure_response() user_settings = UserSettings.objects.filter(user_id=user).first() if not user_settings.is_public: - return CustomResponse(general_message="Private Profile").get_failure_response() + return CustomResponse( + general_message="Private Profile" + ).get_failure_response() user_id = user.id else: JWTUtils.is_jwt_authenticated(request) @@ -279,8 +257,12 @@ class SocialsAPI(APIView): def put(self, request): user_id = JWTUtils.fetch_user_id(request) social_instance = Socials.objects.filter(user_id=user_id).first() - serializer = LinkSocials(instance=social_instance, data=request.data, context={"request": request}) + serializer = LinkSocials( + instance=social_instance, data=request.data, context={"request": request} + ) if serializer.is_valid(): serializer.save() - return CustomResponse(general_message="Socials Updated").get_success_response() + return CustomResponse( + general_message="Socials Updated" + ).get_success_response() return CustomResponse(response=serializer.errors).get_failure_response() diff --git a/api/integrations/kkem/kkem_views.py b/api/integrations/kkem/kkem_views.py index aa1a8902..e57cc6ec 100644 --- a/api/integrations/kkem/kkem_views.py +++ b/api/integrations/kkem/kkem_views.py @@ -127,12 +127,6 @@ def post(self, request): general_message=str(e) ).get_failure_response() - except Exception as e: - logger.exception("An error occurred: %s", str(e)) - return CustomResponse( - general_message="Somthing went wrong" - ).get_failure_response() - def patch(self, request, token): try: link_id = integrations_helper.get_authorization_id(token) @@ -161,13 +155,6 @@ def patch(self, request, token): general_message=str(e) ).get_failure_response() - except Exception as e: - logger.exception("An error occurred: %s", str(e)) - return CustomResponse( - general_message="Somthing went wrong" - ).get_failure_response() - - class KKEMIntegrationLogin(APIView): def post(self, request): try: @@ -206,11 +193,7 @@ def post(self, request): general_message=str(e) ).get_failure_response() - except Exception as e: - logger.exception("An error occurred: %s", str(e)) - return CustomResponse( - general_message="Somthing went wrong" - ).get_failure_response() + class KKEMdetailsFetchAPI(APIView): @@ -248,11 +231,7 @@ def get(self, request, encrypted_data): general_message=str(e) ).get_failure_response() - except Exception as e: - logger.exception("An error occurred: %s", str(e)) - return CustomResponse( - general_message="Somthing went wrong" - ).get_failure_response() + class KKEMUserStatusAPI(APIView): @@ -268,8 +247,4 @@ def get(self, request, encrypted_data): general_message=str(e) ).get_failure_response() - except Exception as e: - logger.exception("An error occurred: %s", str(e)) - return CustomResponse( - general_message="Somthing went wrong" - ).get_failure_response() + diff --git a/mulearnbackend/middlewares.py b/mulearnbackend/middlewares.py index 430fce02..8892ae06 100644 --- a/mulearnbackend/middlewares.py +++ b/mulearnbackend/middlewares.py @@ -4,10 +4,12 @@ import decouple from django.http import JsonResponse from rest_framework import status +from rest_framework.renderers import JSONRenderer +from utils.response import CustomResponse from utils.utils import _CustomHTTPHandler -logger = logging.getLogger(__name__) +logger = logging.getLogger('django') class IpBindingMiddleware(object): @@ -31,8 +33,7 @@ def __call__(self, request): status=status.HTTP_401_UNAUTHORIZED, ) - response = self.get_response(request) - return response + return self.get_response(request) class ApiSignatureMiddleware(object): @@ -49,7 +50,9 @@ def __call__(self, request): request_method = request.META.get("REQUEST_METHOD") key = f"{request_path}::{request_method}::{timestamp}" new_signature = hmac.new( - key=decouple.config("SECRET_KEY").encode(), msg=key.encode(), digestmod="SHA256" + key=decouple.config("SECRET_KEY").encode(), + msg=key.encode(), + digestmod="SHA256", ).hexdigest() print(new_signature) if new_signature != signature: @@ -62,5 +65,29 @@ def __call__(self, request): }, status=status.HTTP_401_UNAUTHORIZED, ) - response = self.get_response(request) - return response + return self.get_response(request) + + +class UniversalErrorHandlerMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + return self.get_response(request) + + def process_exception(self, request, exception): + logger.error(str(exception)) + response = CustomResponse( + general_message="Something went wrong" + ).get_failure_response() + + # Set the renderer and renderer context + renderer = JSONRenderer() + response.accepted_renderer = renderer + response.accepted_media_type = renderer.media_type + response.renderer_context = { + "request": request, + "view": None, + } + + return response.render() diff --git a/mulearnbackend/settings.py b/mulearnbackend/settings.py index ad495eb3..daa1faca 100644 --- a/mulearnbackend/settings.py +++ b/mulearnbackend/settings.py @@ -51,12 +51,13 @@ ] MIDDLEWARE = [ - "debug_toolbar.middleware.DebugToolbarMiddleware", "django.middleware.security.SecurityMiddleware", - "corsheaders.middleware.CorsMiddleware", - "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", + "debug_toolbar.middleware.DebugToolbarMiddleware", + "django.middleware.common.CommonMiddleware", + "corsheaders.middleware.CorsMiddleware", + "mulearnbackend.middlewares.UniversalErrorHandlerMiddleware", ] ROOT_URLCONF = "mulearnbackend.urls" diff --git a/utils/exception.py b/utils/exception.py index f926dea7..79935dc4 100644 --- a/utils/exception.py +++ b/utils/exception.py @@ -2,7 +2,6 @@ class CustomException(ValidationError): - def __init__(self, detail='Something went wrong', status_code=403): + def __init__(self, detail="Something went wrong", status_code=403): self.detail = detail self.status_code = status_code - From 77aeb5c8146f7f63e7a60dca4dbe963a165dfaea Mon Sep 17 00:00:00 2001 From: Muhammed Zafar Date: Thu, 19 Oct 2023 19:46:35 +0530 Subject: [PATCH 4/4] [MAJOR] Created a system to handle all unhandled exceptions --- api/dashboard/profile/profile_serializer.py | 7 ++-- api/dashboard/profile/profile_view.py | 3 +- api/integrations/integrations_helper.py | 36 +++++++++-------- api/integrations/kkem/kkem_helper.py | 9 +++-- api/integrations/kkem/kkem_serializer.py | 26 ++++++------ api/integrations/kkem/kkem_views.py | 45 +++++++-------------- api/register/serializers.py | 30 ++++++++------ 7 files changed, 77 insertions(+), 79 deletions(-) diff --git a/api/dashboard/profile/profile_serializer.py b/api/dashboard/profile/profile_serializer.py index e129f648..abe8db9d 100644 --- a/api/dashboard/profile/profile_serializer.py +++ b/api/dashboard/profile/profile_serializer.py @@ -8,6 +8,7 @@ from db.organization import UserOrganizationLink from db.task import InterestGroup, KarmaActivityLog, Level, TaskList, Wallet, UserIgLink, UserLvlLink from db.user import User, UserSettings, Socials +from utils.exception import CustomException from utils.permission import JWTUtils from utils.types import OrganizationType, RoleType, MainRoles from utils.utils import DateTimeUtils @@ -323,7 +324,7 @@ def update(self, instance, validated_data): for ig_data in ig_details ] if len(user_ig_links) > 3: - raise ValueError("Cannot add more than 3 interest groups") + raise CustomException("Cannot add more than 3 interest groups") UserIgLink.objects.bulk_create(user_ig_links) return super().update(instance, validated_data) @@ -383,9 +384,9 @@ def create_karma_activity_log(task_title, karma_value): old_account_url = old_accounts[account] if account_url != old_account_url: if old_account_url is None: - create_karma_activity_log("social_" + account, 50) + create_karma_activity_log(f"social_{account}", 50) elif account_url is None: - create_karma_activity_log("social_" + account, -50) + create_karma_activity_log(f"social_{account}", -50) instance.save() return instance diff --git a/api/dashboard/profile/profile_view.py b/api/dashboard/profile/profile_view.py index bd7d99f6..f8cb3474 100644 --- a/api/dashboard/profile/profile_view.py +++ b/api/dashboard/profile/profile_view.py @@ -5,6 +5,7 @@ from db.task import InterestGroup, KarmaActivityLog, Level from db.user import Role from db.user import User, UserSettings, UserRoleLink, Socials +from utils.exception import CustomException from utils.permission import CustomizePermission, JWTUtils from utils.response import CustomResponse from utils.types import WebHookActions, WebHookCategory @@ -86,7 +87,7 @@ def patch(self, request): general_message="Interest Group edited successfully" ).get_success_response() - except ValueError as e: + except CustomException as e: return CustomResponse(general_message=str(e)).get_failure_response() diff --git a/api/integrations/integrations_helper.py b/api/integrations/integrations_helper.py index 03599f0c..707868b8 100644 --- a/api/integrations/integrations_helper.py +++ b/api/integrations/integrations_helper.py @@ -7,6 +7,7 @@ from db.integrations import Integration from mulearnbackend.settings import SECRET_KEY +from utils.exception import CustomException from utils.response import CustomResponse @@ -21,16 +22,19 @@ def get_authorization_id(token: str) -> str | None: :return: the authorization ID if the token is valid and has not expired. If the token is invalid or has expired, it returns None. """ - payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"]) - authorization_id = payload.get("authorization_id") - exp_timestamp = payload.get("exp", 0) - - if exp_timestamp and datetime.now(pytz.utc) < datetime.fromtimestamp( - exp_timestamp, tz=pytz.utc - ): - return authorization_id - else: - raise ValueError("Token invalid or expired") + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"]) + authorization_id = payload.get("authorization_id") + exp_timestamp = payload.get("exp", 0) + + if exp_timestamp and datetime.now(pytz.utc) < datetime.fromtimestamp( + exp_timestamp, tz=pytz.utc + ): + return authorization_id + else: + raise CustomException("Token invalid or expired") + except jwt.ExpiredSignatureError: + raise CustomException("This token has expired, maybe again with a new one!") def generate_confirmation_token(authorization_id: str) -> str: @@ -68,14 +72,14 @@ def wrapper(self, request, *args, **kwargs): try: auth_header = request.META.get("HTTP_AUTHORIZATION") if not auth_header or not auth_header.startswith("Bearer "): - raise ValueError("Invalid Authorization header") + raise CustomException("Invalid Authorization header") token = auth_header.split(" ")[1] if not Integration.objects.filter( token=token, name=integration_name ).first(): - raise ValueError("Invalid Authorization header") + raise CustomException("Invalid Authorization header") else: result = func(self, request, *args, **kwargs) return result @@ -111,7 +115,7 @@ def get_access_token( ).json() if response.get("statusCode") != 200: - raise ValueError( + raise CustomException( "Oops! The username or password didn't match our records. Please double-check and try again." ) else: @@ -120,7 +124,7 @@ def get_access_token( ).json() if response.get("statusCode") != 200: - raise ValueError( + raise CustomException( "Oops! We couldn't find that account. Please double-check your details and try again." ) @@ -137,10 +141,10 @@ def get_access_token( def handle_response(response: dict) -> None: if response.get("statusCode") != 200: if "emailOrMuid" in response: - raise ValueError( + raise CustomException( "Oops! The username or password didn't match our records. Please double-check and try again." ) else: - raise ValueError( + raise CustomException( "Oops! We couldn't find that account. Please double-check your details and try again." ) diff --git a/api/integrations/kkem/kkem_helper.py b/api/integrations/kkem/kkem_helper.py index 7cc6e8cb..237182c1 100644 --- a/api/integrations/kkem/kkem_helper.py +++ b/api/integrations/kkem/kkem_helper.py @@ -9,6 +9,7 @@ from Crypto.Util.Padding import unpad from db.integrations import Integration +from utils.exception import CustomException from utils.types import IntegrationType from utils.utils import send_template_mail @@ -31,7 +32,7 @@ def send_data_to_kkem(kkem_link): response_data = response.json() if not response_data["request_status"]: - raise ValueError("Invalid jsid") + raise CustomException("Invalid jsid") send_connection_successful_email(kkem_link.user) return response_data @@ -65,11 +66,13 @@ def ensure_padding(encoded_str): try: decrypted = unpad(decrypted_data, AES.block_size) except: - raise ValueError("Invalid padding or incorrect key") + raise CustomException("Invalid padding or incorrect key") return parse_qs(decrypted.decode("utf-8")) except Exception as e: - raise ValueError("The given token seems to be invalid do re-check and try again!") + raise CustomException( + "The given token seems to be invalid do re-check and try again!" + ) def send_connection_successful_email(user): diff --git a/api/integrations/kkem/kkem_serializer.py b/api/integrations/kkem/kkem_serializer.py index 004083e8..236d7c53 100644 --- a/api/integrations/kkem/kkem_serializer.py +++ b/api/integrations/kkem/kkem_serializer.py @@ -3,6 +3,7 @@ from db.integrations import Integration, IntegrationAuthorization from db.user import User +from utils.exception import CustomException from utils.types import IntegrationType from utils.utils import DateTimeUtils @@ -28,8 +29,7 @@ class Meta: def get_total_karma(self, obj): karma = ( obj.wallet_user.karma - if hasattr(obj, "wallet_user") - and hasattr(obj.wallet_user, "karma") + if hasattr(obj, "wallet_user") and hasattr(obj.wallet_user, "karma") else 0 ) return karma @@ -117,21 +117,21 @@ def create(self, validated_data): if kkem_link: if ( - self.context["type"] == "login" - and kkem_link.integration_value == jsid - and kkem_link.verified + self.context["type"] == "login" + and kkem_link.integration_value == jsid + and kkem_link.verified ): return kkem_link elif kkem_link.verified: - raise ValueError( + raise CustomException( "Your μLearn account is already connected to a KKEM account" ) elif kkem_link.user == user: self.update_integration(validated_data, kkem_link) else: - raise ValueError("Something went wrong") + raise CustomException("Something went wrong") else: kkem_link = self.create_kkem_link( user, integration, dwms_id, jsid, validated_data["verified"] @@ -142,24 +142,22 @@ def create(self, validated_data): return kkem_link def verify_user(self, user_muid): - if user := User.objects.filter( - Q(muid=user_muid) | Q(email=user_muid) - ).first(): + if user := User.objects.filter(Q(muid=user_muid) | Q(email=user_muid)).first(): return user else: - raise ValueError( + raise CustomException( "Oops! We couldn't find that account. Please double-check your details and try again." ) def get_kkem_link(self, user, integration, jsid): if kkem_link := IntegrationAuthorization.objects.filter( - user=user, integration=integration + user=user, integration=integration ).first(): return kkem_link if IntegrationAuthorization.objects.filter( - integration_value=jsid, integration=integration + integration_value=jsid, integration=integration ).exists(): - raise ValueError("This KKEM account is already connected to another user") + raise CustomException("This KKEM account is already connected to another user") return None diff --git a/api/integrations/kkem/kkem_views.py b/api/integrations/kkem/kkem_views.py index e57cc6ec..561675bc 100644 --- a/api/integrations/kkem/kkem_views.py +++ b/api/integrations/kkem/kkem_views.py @@ -1,6 +1,6 @@ +import logging from datetime import datetime -import logging import requests from django.db.models import F, Prefetch from rest_framework.views import APIView @@ -8,6 +8,7 @@ from db.integrations import Integration, IntegrationAuthorization from db.task import InterestGroup, KarmaActivityLog, TaskList, UserIgLink from db.user import User +from utils.exception import CustomException from utils.response import CustomResponse from utils.types import IntegrationType from utils.utils import DateTimeUtils, send_template_mail @@ -60,7 +61,7 @@ def get(self, request): base_queryset = base_queryset.filter( karma_activity_log_user__updated_at__gte=from_datetime ) - except ValueError: + except CustomException: return CustomResponse( general_message="Invalid datetime format", ).get_failure_response() @@ -122,10 +123,8 @@ def post(self, request): general_message="We've set up your authorization! Please check your email for further instructions." ).get_success_response() - except ValueError as e: - return CustomResponse( - general_message=str(e) - ).get_failure_response() + except CustomException as e: + return CustomResponse(general_message=str(e)).get_failure_response() def patch(self, request, token): try: @@ -150,10 +149,9 @@ def patch(self, request, token): general_message="Invalid or missing Token" ).get_failure_response() - except ValueError as e: - return CustomResponse( - general_message=str(e) - ).get_failure_response() + except CustomException as e: + return CustomResponse(general_message=str(e)).get_failure_response() + class KKEMIntegrationLogin(APIView): def post(self, request): @@ -184,16 +182,11 @@ def post(self, request): response["data"] = serialized_set.data return CustomResponse( - general_message=general_message, - response=response + general_message=general_message, response=response ).get_success_response() - except ValueError as e: - return CustomResponse( - general_message=str(e) - ).get_failure_response() - - + except CustomException as e: + return CustomResponse(general_message=str(e)).get_failure_response() class KKEMdetailsFetchAPI(APIView): @@ -226,12 +219,8 @@ def get(self, request, encrypted_data): return CustomResponse(response=result_data).get_success_response() - except ValueError as e: - return CustomResponse( - general_message=str(e) - ).get_failure_response() - - + except CustomException as e: + return CustomResponse(general_message=str(e)).get_failure_response() class KKEMUserStatusAPI(APIView): @@ -242,9 +231,5 @@ def get(self, request, encrypted_data): response={"muid": details["mu_id"][0] if "mu_id" in details else None} ).get_success_response() - except ValueError as e: - return CustomResponse( - general_message=str(e) - ).get_failure_response() - - + except CustomException as e: + return CustomResponse(general_message=str(e)).get_failure_response() diff --git a/api/register/serializers.py b/api/register/serializers.py index d1a851e4..3b31feba 100644 --- a/api/register/serializers.py +++ b/api/register/serializers.py @@ -2,19 +2,23 @@ from django.db import transaction from rest_framework import serializers -from api.integrations.kkem.kkem_helper import send_data_to_kkem, decrypt_kkem_data +from api.integrations.kkem.kkem_helper import decrypt_kkem_data, send_data_to_kkem from db.integrations import Integration, IntegrationAuthorization -from db.organization import Country, State, Zone -from db.organization import District, Department, Organization, UserOrganizationLink -from db.task import ( - InterestGroup, - Wallet, - MucoinInviteLog, +from db.organization import ( + Country, + Department, + District, + Organization, + State, + UserOrganizationLink, + Zone, ) -from db.task import UserLvlLink, Level -from db.user import Role, User, UserRoleLink, UserSettings, UserReferralLink, Socials +from db.task import InterestGroup, Level, MucoinInviteLog, UserLvlLink, Wallet +from db.user import Role, Socials, User, UserReferralLink, UserRoleLink, UserSettings +from utils.exception import CustomException from utils.types import OrganizationType, RoleType from utils.utils import DateTimeUtils + from . import register_helper @@ -169,7 +173,9 @@ def validate_invite_code(self, invite_code): ) from e def create(self, validated_data): - referral = validated_data.get("invite_code", None) or validated_data.get("muid", None) + referral = validated_data.get("invite_code", None) or validated_data.get( + "muid", None + ) validated_data.update( { @@ -194,7 +200,7 @@ def validate_param(self, param): dwms_id = details["dwms_id"][0] if IntegrationAuthorization.objects.filter(integration_value=jsid).exists(): - raise ValueError( + raise CustomException( "This KKEM account is already connected to another user" ) @@ -270,7 +276,7 @@ class Meta: "password", "role", "integration", - "referral" + "referral", ]