From 4bc16c56bdebd390ddd10684cd3c1fcd7516918a Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 14 Aug 2023 13:57:29 +0530 Subject: [PATCH 01/83] csv/xls report --- web_admin/serializers/analytics_serializer.py | 70 +++++++ web_admin/views/analytics.py | 185 +++++++++++++++++- web_admin/views/user_management.py | 12 +- 3 files changed, 258 insertions(+), 9 deletions(-) diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py index fd85118..4c0cbd5 100644 --- a/web_admin/serializers/analytics_serializer.py +++ b/web_admin/serializers/analytics_serializer.py @@ -2,11 +2,20 @@ web_admin analytics serializer file """ from rest_framework import serializers +from django.contrib.auth import get_user_model + +from base.constants import USER_TYPE from junior.models import JuniorPoints, Junior +from web_admin.serializers.user_management_serializer import JuniorSerializer, GuardianSerializer + +USER = get_user_model() class JuniorLeaderboardSerializer(serializers.ModelSerializer): + """ + junior leaderboard serializer + """ name = serializers.SerializerMethodField() first_name = serializers.SerializerMethodField() last_name = serializers.SerializerMethodField() @@ -44,9 +53,70 @@ class JuniorLeaderboardSerializer(serializers.ModelSerializer): class LeaderboardSerializer(serializers.ModelSerializer): + """ + leaderboard serializer + """ junior = JuniorLeaderboardSerializer() rank = serializers.IntegerField() class Meta: + """ + meta class + """ model = JuniorPoints fields = ('total_points', 'rank', 'junior') + + +class UserCSVReportSerializer(serializers.ModelSerializer): + """ + user csv/xls report serializer + """ + phone_number = serializers.SerializerMethodField() + user_type = serializers.SerializerMethodField() + is_active = serializers.SerializerMethodField() + + class Meta: + """ + meta class + """ + model = USER + fields = ('first_name', 'last_name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined') + + @staticmethod + def get_phone_number(obj): + """ + :param obj: user object + :return: user phone number + """ + if profile := obj.guardian_profile.all().first(): + return f"+{profile.country_code}{profile.phone}" \ + if profile.country_code and profile.phone else profile.phone + elif profile := obj.junior_profile.all().first(): + return f"+{profile.country_code}{profile.phone}" \ + if profile.country_code and profile.phone else profile.phone + else: + return None + + @staticmethod + def get_user_type(obj): + """ + :param obj: user object + :return: user type + """ + if obj.guardian_profile.all().first(): + return dict(USER_TYPE).get('2') + elif obj.junior_profile.all().first(): + return dict(USER_TYPE).get('1') + else: + return None + + @staticmethod + def get_is_active(obj): + """ + :param obj: user object + :return: user type + """ + if profile := obj.guardian_profile.all().first(): + return "Active" if profile.is_active else "Inactive" + elif profile := obj.junior_profile.all().first(): + return "Active" if profile.is_active else "Inactive" diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 8c21cb3..34d1204 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -3,6 +3,10 @@ web_admin analytics view file """ # python imports import datetime +import csv +import io +import pandas as pd +import xlsxwriter # third party imports from rest_framework.viewsets import GenericViewSet @@ -16,15 +20,17 @@ from django.db.models import Count from django.db.models.functions import TruncDate from django.db.models import F, Window from django.db.models.functions.window import Rank +from django.http import HttpResponse # local imports from account.utils import custom_response -from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT +from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT, TASK_STATUS from guardian.models import JuniorTask from junior.models import JuniorPoints from web_admin.pagination import CustomPageNumberPagination from web_admin.permission import AdminPermission -from web_admin.serializers.analytics_serializer import LeaderboardSerializer +from web_admin.serializers.analytics_serializer import LeaderboardSerializer, UserCSVReportSerializer +from web_admin.serializers.user_management_serializer import UserManagementListSerializer USER = get_user_model() @@ -38,7 +44,7 @@ class AnalyticsViewSet(GenericViewSet): to get junior leaderboard and ranking """ serializer_class = None - permission_classes = [IsAuthenticated, AdminPermission] + # permission_classes = [IsAuthenticated, AdminPermission] def get_queryset(self): user_qs = USER.objects.filter( @@ -141,3 +147,176 @@ class AnalyticsViewSet(GenericViewSet): paginated_queryset = paginator.paginate_queryset(queryset, request) serializer = self.serializer_class(paginated_queryset, many=True) return custom_response(None, serializer.data) + + # @action(methods=['get'], url_name='generate-report', url_path='generate-report', detail=False) + # def generate_report(self, request): + # + # response = HttpResponse(content_type='text/csv') + # response['Content-Disposition'] = 'attachment; filename="ZOD_Bank_Analytics.csv"' + # + # end_date = datetime.date.today() + # start_date = end_date - datetime.timedelta(days=6) + # + # if request.query_params.get('start_date') and request.query_params.get('end_date'): + # start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) + # end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) + # + # buffer = io.StringIO() + # csv_writer = csv.writer(buffer) + # + # # sheet 1 for Total Users + # user_qs = self.get_queryset() + # queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1)))) + # serializer = UserCSVReportSerializer(queryset, many=True) + # + # # writer = csv.writer(response) + # csv_writer.writerow(['Users']) + # csv_writer.writerow([ + # 'First Name', 'Last Name', 'Email', 'Phone Number', 'User Type', 'status', 'Date Joined', + # # Add more fields as needed + # ]) + # + # for user_data in serializer.data: + # row = [ + # user_data['first_name'], user_data['last_name'], user_data['email'], + # user_data['phone_number'], user_data['user_type'], + # user_data['is_active'], user_data['date_joined'] + # ] + # csv_writer.writerow(row) + # + # response.write(buffer.getvalue()) + # buffer.close() + # + # # sheet 2 for Assign Task + # assign_tasks = JuniorTask.objects.filter( + # created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] + # ).exclude(task_status__in=[PENDING, EXPIRED]) + # buffer = io.StringIO() + # csv_writer = csv.writer(buffer) + # csv_writer.writerow([]) + # csv_writer.writerow(['Assign Tasks']) + # csv_writer.writerow([ + # 'Task Name', 'Task Status' + # # Add more fields as needed + # ]) + # + # for task in assign_tasks: + # row = [ + # task.task_name, dict(TASK_STATUS).get(task.task_status).capitalize(), + # ] + # csv_writer.writerow(row) + # response.write(buffer.getvalue()) + # buffer.close() + # + # # sheet 3 for Juniors Leaderboard and rank + # queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( + # expression=Rank(), + # order_by=[F('total_points').desc(), 'junior__created_at'] + # )).order_by('-total_points', 'junior__created_at') + # buffer = io.StringIO() + # csv_writer = csv.writer(buffer) + # csv_writer.writerow([]) + # csv_writer.writerow(['Top 15 Juniors']) + # csv_writer.writerow([ + # 'Junior Name', 'Points', 'Rank' + # # Add more fields as needed + # ]) + # + # for junior in queryset: + # row = [ + # f"{junior.junior.auth.first_name}{junior.junior.auth.last_name}" if junior.junior.auth.last_name else junior.junior.auth.first_name, + # junior.total_points, junior.rank + # ] + # csv_writer.writerow(row) + # response.write(buffer.getvalue()) + # buffer.close() + # + # return response + + @action(methods=['get'], url_name='generate-report', url_path='generate-report', detail=False) + def generate_report(self, request): + + response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + response['Content-Disposition'] = 'attachment; filename="ZOD_Bank_Analytics.xlsx"' + + end_date = datetime.date.today() + start_date = end_date - datetime.timedelta(days=6) + + if request.query_params.get('start_date') and request.query_params.get('end_date'): + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) + + buffer = io.BytesIO() # Use BytesIO for binary data + + # Create an XlsxWriter Workbook object + workbook = xlsxwriter.Workbook(buffer) + + # Add sheets + sheets = ['Users', 'Assign Tasks', 'Top 15 Juniors'] + + for sheet_name in sheets: + worksheet = workbook.add_worksheet(name=sheet_name) + + # sheet 1 for Total Users + if sheet_name == 'Users': + user_qs = self.get_queryset() + queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1)))) + serializer = UserCSVReportSerializer(queryset, many=True) + + df_users = pd.DataFrame(serializer.data) + for idx, col in enumerate(df_users.columns): + worksheet.write(0, idx, col) # Write header + for row_num, row in enumerate(df_users.values, start=1): + for col_num, value in enumerate(row): + worksheet.write(row_num, col_num, value) + + # sheet 2 for Assign Task + elif sheet_name == 'Assign Tasks': + assign_tasks = JuniorTask.objects.filter( + created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] + ).exclude(task_status__in=[PENDING, EXPIRED]) + + df_tasks = pd.DataFrame([ + {'Task Name': task.task_name, 'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()} + for task in assign_tasks + ]) + + for idx, col in enumerate(df_tasks.columns): + worksheet.write(0, idx, col) # Write header + for row_num, row in enumerate(df_tasks.values, start=1): + for col_num, value in enumerate(row): + worksheet.write(row_num, col_num, value) + + # sheet 3 for Juniors Leaderboard and rank + elif sheet_name == 'Top 15 Juniors': + queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at'] + )).order_by('-total_points', 'junior__created_at') + + df_leaderboard = pd.DataFrame([ + { + 'Junior Name': f"{junior.junior.auth.first_name} {junior.junior.auth.last_name}" + if junior.junior.auth.last_name else junior.junior.auth.first_name, + 'Points': junior.total_points, + 'Rank': junior.rank + } + for junior in queryset + ]) + + for idx, col in enumerate(df_leaderboard.columns): + worksheet.write(0, idx, col) # Write header + for row_num, row in enumerate(df_leaderboard.values, start=1): + for col_num, value in enumerate(row): + worksheet.write(row_num, col_num, value) + + # Close the workbook to save the content + workbook.close() + + # Reset the buffer position and write the content to the response + buffer.seek(0) + response.write(buffer.getvalue()) + buffer.close() + + return response + diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index 8f53a73..d1754aa 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -72,9 +72,9 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST) queryset = self.queryset if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): - queryset = queryset.filter(guardian_profile__user__id=kwargs['pk']) + queryset = queryset.filter(id=kwargs['pk']) elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): - queryset = queryset.filter(junior_profile__auth__id=kwargs['pk']) + queryset = queryset.filter(id=kwargs['pk']) serializer = UserManagementDetailSerializer(queryset, many=True) return custom_response(None, data=serializer.data) @@ -89,12 +89,12 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST) queryset = self.queryset if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): - user_obj = queryset.filter(guardian_profile__user__id=kwargs['pk']).first() + user_obj = queryset.filter(id=kwargs['pk']).first() serializer = GuardianSerializer(user_obj.guardian_profile.all().first(), request.data, context={'user_id': kwargs['pk']}) elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): - user_obj = queryset.filter(junior_profile__auth__id=kwargs['pk']).first() + user_obj = queryset.filter(id=kwargs['pk']).first() serializer = JuniorSerializer(user_obj.junior_profile.all().first(), request.data, context={'user_id': kwargs['pk']}) @@ -114,12 +114,12 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST) queryset = self.queryset if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): - user_obj = queryset.filter(guardian_profile__user__id=kwargs['pk']).first() + user_obj = queryset.filter(id=kwargs['pk']).first() obj = user_obj.guardian_profile.all().first() obj.is_active = False if obj.is_active else True obj.save() elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): - user_obj = queryset.filter(junior_profile__auth__id=kwargs['pk']).first() + user_obj = queryset.filter(id=kwargs['pk']).first() obj = user_obj.junior_profile.all().first() obj.is_active = False if obj.is_active else True obj.save() From e46d649fdcd96a6c3766b0e00d9677679bfa4434 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 14 Aug 2023 16:03:36 +0530 Subject: [PATCH 02/83] notification modification added more payload --- account/utils.py | 7 +++++ notifications/constants.py | 2 +- notifications/serializers.py | 3 ++- notifications/utils.py | 50 +++++++++++++++++++++++++++--------- notifications/views.py | 3 ++- 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/account/utils.py b/account/utils.py index fd2f40f..dcfb4a2 100644 --- a/account/utils.py +++ b/account/utils.py @@ -234,3 +234,10 @@ def generate_code(value, user_id): OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1) + + +def get_user_full_name(user_obj): + """ + to get user's full name + """ + return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.last_name else user_obj.first_name diff --git a/notifications/constants.py b/notifications/constants.py index b861142..46bfbef 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -67,6 +67,6 @@ NOTIFICATION_DICT = { }, TEST_NOTIFICATION: { "title": "Test Notification", - "body": "This notification is for testing purpose" + "body": "This notification is for testing purpose from {from_user}." } } diff --git a/notifications/serializers.py b/notifications/serializers.py index a061369..14f1b20 100644 --- a/notifications/serializers.py +++ b/notifications/serializers.py @@ -35,7 +35,8 @@ class NotificationListSerializer(serializers.ModelSerializer): class Meta(object): """meta info""" model = Notification - fields = ['id', 'data', 'is_read'] + fields = ['id', 'data', 'is_read', 'created_at'] + class ReadNotificationSerializer(serializers.ModelSerializer): """User task Serializer""" diff --git a/notifications/utils.py b/notifications/utils.py index ba980e6..38df5ba 100644 --- a/notifications/utils.py +++ b/notifications/utils.py @@ -10,6 +10,7 @@ from firebase_admin.messaging import Message, Notification as FirebaseNotificati from django.contrib.auth import get_user_model from account.models import UserNotification +from account.utils import get_user_full_name from notifications.constants import NOTIFICATION_DICT from notifications.models import Notification @@ -39,30 +40,55 @@ def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None: print(e) -def get_basic_detail(notification_type, from_user_id, to_user_id): - """ used to get the basic details """ - notification_data = NOTIFICATION_DICT[notification_type] +def get_basic_detail(from_user_id, to_user_id): + """ + used to get the basic details + """ from_user = User.objects.get(id=from_user_id) if from_user_id else None to_user = User.objects.get(id=to_user_id) - return notification_data, from_user, to_user + return from_user, to_user + + +def get_notification_data(notification_type, from_user, to_user, extra_data): + """ + get notification and push data + :param notification_type: notification_type + :param from_user: from_user obj + :param to_user: to_user obj + :param extra_data: any extra data provided + :return: notification and push data + """ + push_data = NOTIFICATION_DICT[notification_type].copy() + notification_data = push_data.copy() + notification_data['to_user'] = get_user_full_name(to_user) + if from_user: + from_user_name = get_user_full_name(from_user) + push_data['body'] = push_data['body'].format(from_user=from_user_name) + notification_data['body'] = notification_data['body'].format(from_user=from_user_name) + notification_data['from_user'] = from_user_name + + notification_data.update(extra_data) + + return notification_data, push_data @shared_task() def send_notification(notification_type, from_user_id, to_user_id, extra_data): - """ used to send the push for the given notification type """ - (notification_data, from_user, to_user) = get_basic_detail(notification_type, from_user_id, to_user_id) + """ + used to send the push for the given notification type + """ + (from_user, to_user) = get_basic_detail(from_user_id, to_user_id) + notification_data, push_data = get_notification_data(notification_type, from_user, to_user, extra_data) user_notification_type = UserNotification.objects.filter(user=to_user).first() - data = notification_data + notification_data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()}) Notification.objects.create(notification_type=notification_type, notification_from=from_user, - notification_to=to_user, data=data) + notification_to=to_user, data=notification_data) if user_notification_type.push_notification: - data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()}) - send_push(to_user, data) + send_push(to_user, push_data) def send_push(user, data): """ used to send push notification to specific user """ - notification_data = data.pop('data', None) user.fcmdevice_set.filter(active=True).send_message( - Message(notification=FirebaseNotification(data['title'], data['body']), data=notification_data) + Message(notification=FirebaseNotification(data['title'], data['body']), data=data) ) diff --git a/notifications/views.py b/notifications/views.py index c66d655..f932f0f 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -56,7 +56,8 @@ class NotificationViewSet(viewsets.GenericViewSet): to send test notification :return: """ - send_notification.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], {}) + send_notification(TEST_NOTIFICATION, None, request.auth.payload['user_id'], + {'task_id': None}) return custom_response(SUCCESS_CODE["3000"]) @action(methods=['get'], detail=False, url_path='list', url_name='list', From 60af098e1e0f4b129bbacdb7464c36b53434a081 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 16 Aug 2023 16:16:05 +0530 Subject: [PATCH 03/83] email verfication and user notification bug --- account/views.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/account/views.py b/account/views.py index d95f87a..c17f853 100644 --- a/account/views.py +++ b/account/views.py @@ -401,12 +401,13 @@ class AdminLoginViewSet(viewsets.GenericViewSet): class UserEmailVerification(viewsets.ModelViewSet): """User Email verification""" serializer_class = EmailVerificationSerializer + http_method_names = ('post',) - def list(self, request, *args, **kwargs): + def create(self, request, *args, **kwargs): try: - user_obj = User.objects.filter(username=self.request.GET.get('email')).last() - email_data = UserEmailOtp.objects.filter(email=self.request.GET.get('email'), - otp=self.request.GET.get('otp')).last() + user_obj = User.objects.filter(username=self.request.data.get('email')).last() + email_data = UserEmailOtp.objects.filter(email=self.request.data.get('email'), + otp=self.request.data.get('otp')).last() if email_data: input_datetime_str = str(email_data.expired_at) input_format = "%Y-%m-%d %H:%M:%S.%f%z" @@ -420,12 +421,12 @@ class UserEmailVerification(viewsets.ModelViewSet): email_data.is_verified = True email_data.save() if email_data.user_type == '1': - junior_data = Junior.objects.filter(auth__email=self.request.GET.get('email')).last() + junior_data = Junior.objects.filter(auth__email=self.request.data.get('email')).last() if junior_data: junior_data.is_verified = True junior_data.save() else: - guardian_data = Guardian.objects.filter(user__email=self.request.GET.get('email')).last() + guardian_data = Guardian.objects.filter(user__email=self.request.data.get('email')).last() if guardian_data: guardian_data.is_verified = True guardian_data.save() @@ -535,7 +536,7 @@ class UserNotificationAPIViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated] def list(self, request, *args, **kwargs): """profile view""" - queryset = self.queryset.filter(user=request.user) + queryset = UserNotification.objects.filter(user=request.user) serializer = UserNotificationSerializer(queryset, many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) From 3017b0ece01dad24e74857da22516faaad40c3c2 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Wed, 16 Aug 2023 16:40:34 +0530 Subject: [PATCH 04/83] changes in notification method --- celerybeat-schedule | Bin 16384 -> 16384 bytes guardian/serializers.py | 8 +++++--- guardian/utils.py | 6 ++++-- guardian/views.py | 13 ++++++------- junior/serializers.py | 16 +++++++++------- junior/views.py | 7 ++++--- notifications/constants.py | 18 +++++++++--------- notifications/utils.py | 33 ++++++++++++++++++++++++++++++--- notifications/views.py | 8 +++++--- 9 files changed, 72 insertions(+), 37 deletions(-) diff --git a/celerybeat-schedule b/celerybeat-schedule index f2510fc7dfb09da6514820cff292233776337c68..ce5d8ac8aa39d618efaf5cf6434831370e993b16 100644 GIT binary patch delta 32 ocmZo@U~Fh$+_1@rU4X+%i%o6P;jO>$j4K+8L7@y$*0Hr($+W-In delta 32 ocmZo@U~Fh$+_1@rotsr&j#0gM@@<1aM%Kx?hMJpAjL+}@0G~n$hX4Qo diff --git a/guardian/serializers.py b/guardian/serializers.py index 1dabc8d..cbd1e4e 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -29,7 +29,7 @@ from .utils import real_time, convert_timedelta_into_datetime, update_referral_p # notification's constant from notifications.constants import TASK_POINTS, TASK_REJECTED # send notification function -from notifications.utils import send_notification +from notifications.utils import send_notification, send_notification_to_junior # In this serializer file @@ -383,7 +383,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer): # update complete time of task # instance.completed_on = real_time() instance.completed_on = timezone.now().astimezone(pytz.utc) - send_notification.delay(TASK_POINTS, None, junior_details.auth.id, {}) + send_notification_to_junior.delay(TASK_POINTS, None, junior_details.auth.id, + {'task_id': instance.id}) else: # reject the task instance.task_status = str(NUMBER['three']) @@ -391,7 +392,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer): # update reject time of task # instance.rejected_on = real_time() instance.rejected_on = timezone.now().astimezone(pytz.utc) - send_notification.delay(TASK_REJECTED, None, junior_details.auth.id, {}) + send_notification_to_junior.delay(TASK_REJECTED, None, junior_details.auth.id, + {'task_id': instance.id}) instance.save() junior_data.save() return junior_details diff --git a/guardian/utils.py b/guardian/utils.py index 57e8080..d5081ac 100644 --- a/guardian/utils.py +++ b/guardian/utils.py @@ -21,7 +21,9 @@ from zod_bank.celery import app # notification's constant from notifications.constants import REFERRAL_POINTS # send notification function -from notifications.utils import send_notification +from notifications.utils import send_notification, send_notification_to_junior + + # Define upload image on # ali baba cloud # firstly save image @@ -92,7 +94,7 @@ def update_referral_points(referral_code, referral_code_used): junior_query.total_points = junior_query.total_points + NUMBER['five'] junior_query.referral_points = junior_query.referral_points + NUMBER['five'] junior_query.save() - send_notification.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {}) + send_notification_to_junior.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {}) diff --git a/guardian/views.py b/guardian/views.py index 3948e6f..0319938 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -38,8 +38,8 @@ from account.utils import custom_response, custom_error_response, OTP_EXPIRY, se from base.messages import ERROR_CODE, SUCCESS_CODE from base.constants import NUMBER, GUARDIAN_CODE_STATUS from .utils import upload_image_to_alibaba -from notifications.constants import REGISTRATION, TASK_CREATED, LEADERBOARD_RANKING -from notifications.utils import send_notification +from notifications.constants import REGISTRATION, TASK_ASSIGNED, LEADERBOARD_RANKING +from notifications.utils import send_notification_to_junior """ Define APIs """ # Define Signup API, @@ -72,8 +72,6 @@ class SignupViewset(viewsets.ModelViewSet): user_type=str(request.data['user_type']), expired_at=expiry) """Send email to the register user""" send_otp_email(request.data['email'], otp) - # send push notification for registration - send_notification.delay(REGISTRATION, None, user.id, {}) return custom_response(SUCCESS_CODE['3001'], response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) @@ -186,9 +184,10 @@ class CreateTaskAPIView(viewsets.ModelViewSet): serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data) if serializer.is_valid(): # save serializer - serializer.save() + task = serializer.save() junior_id = Junior.objects.filter(id=junior).last() - send_notification.delay(TASK_CREATED, None, junior_id.auth.id, {}) + send_notification_to_junior.delay(TASK_ASSIGNED, request.auth.payload['user_id'], + junior_id.auth.id, {'task_id': task.id}) return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) except Exception as e: @@ -241,7 +240,7 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): # Update the position field for each JuniorPoints object for index, junior in enumerate(junior_total_points): junior.position = index + 1 - send_notification.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {}) + send_notification_to_junior.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {}) junior.save() serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) diff --git a/junior/serializers.py b/junior/serializers.py index c3e2d72..f932a26 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -21,7 +21,7 @@ from guardian.models import Guardian, JuniorTask from account.models import UserEmailOtp, UserNotification from junior.utils import junior_notification_email, junior_approval_mail from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime -from notifications.utils import send_notification +from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION, TASK_SUBMITTED) @@ -98,7 +98,7 @@ class CreateJuniorSerializer(serializers.ModelSerializer): JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) junior.guardian_code_status = str(NUMBER['three']) junior_approval_mail(user.email, user.first_name) - send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {}) + send_notification_to_guardian.delay(APPROVED_JUNIOR, junior.auth.id, guardian_data.user.id, {}) junior.dob = validated_data.get('dob', junior.dob) junior.passcode = validated_data.get('passcode', junior.passcode) junior.country_name = validated_data.get('country_name', junior.country_name) @@ -305,7 +305,7 @@ class AddJuniorSerializer(serializers.ModelSerializer): """Notification email""" junior_notification_email(email, full_name, email, password) # push notification - send_notification.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {}) + send_notification_to_junior.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {}) return junior_data @@ -337,8 +337,10 @@ class CompleteTaskSerializer(serializers.ModelSerializer): instance.task_status = str(NUMBER['four']) instance.is_approved = False instance.save() - send_notification.delay(TASK_SUBMITTED, None, instance.junior.auth.id, {}) - send_notification.delay(TASK_ACTION, None, instance.guardian.user.id, {}) + send_notification_to_junior.delay(TASK_SUBMITTED, instance.guardian.user.id, + instance.junior.auth.id, {'task_id': instance.id}) + send_notification_to_guardian.delay(TASK_ACTION, instance.junior.auth.id, + instance.guardian.user.id, {'task_id': instance.id}) return instance class JuniorPointsSerializer(serializers.ModelSerializer): @@ -448,8 +450,8 @@ class AddGuardianSerializer(serializers.ModelSerializer): """Notification email""" junior_notification_email(email, full_name, email, password) junior_approval_mail(email, full_name) - send_notification.delay(INVITED_GUARDIAN, None, junior_data.auth.id, {}) - send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {}) + send_notification_to_junior.delay(INVITED_GUARDIAN, guardian_data.user.id, junior_data.auth.id, {}) + send_notification_to_guardian.delay(APPROVED_JUNIOR, junior_data.auth.id, guardian_data.user.id, {}) return guardian_data class StartTaskSerializer(serializers.ModelSerializer): diff --git a/junior/views.py b/junior/views.py index a86083d..6b8bc2f 100644 --- a/junior/views.py +++ b/junior/views.py @@ -42,7 +42,7 @@ from base.constants import NUMBER, ARTICLE_STATUS from account.utils import custom_response, custom_error_response from guardian.utils import upload_image_to_alibaba from .utils import update_positions_based_on_points -from notifications.utils import send_notification +from notifications.utils import send_notification, send_notification_to_junior from notifications.constants import REMOVE_JUNIOR from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer, @@ -269,7 +269,7 @@ class RemoveJuniorAPIView(views.APIView): if serializer.is_valid(): # save serializer serializer.save() - send_notification.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {}) + send_notification_to_junior.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {}) return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) else: @@ -334,7 +334,8 @@ class CompleteJuniorTaskAPIView(views.APIView): image_url = upload_image_to_alibaba(image, filename) # fetch junior query - task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last() + task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user + ).select_related('guardian', 'junior').last() if task_queryset: # use CompleteTaskSerializer serializer if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]: diff --git a/notifications/constants.py b/notifications/constants.py index 46bfbef..59c2328 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -3,7 +3,7 @@ notification constants file """ from base.constants import NUMBER REGISTRATION = NUMBER['one'] -TASK_CREATED = NUMBER['two'] +TASK_ASSIGNED = NUMBER['two'] INVITED_GUARDIAN = NUMBER['three'] APPROVED_JUNIOR = NUMBER['four'] REFERRAL_POINTS = NUMBER['five'] @@ -21,17 +21,17 @@ NOTIFICATION_DICT = { "title": "Successfully registered!", "body": "You have registered successfully. Now login and complete your profile." }, - TASK_CREATED: { - "title": "Task created!", - "body": "Task created successfully." + TASK_ASSIGNED: { + "title": "New task assigned !!", + "body": "{from_user} has assigned you a new task." }, INVITED_GUARDIAN: { "title": "Invite guardian", "body": "Invite guardian successfully" }, APPROVED_JUNIOR: { - "title": "Approve junior", - "body": "You have request from junior to associate with you" + "title": "Approve junior !", + "body": "You have request from {from_user} to associate with you." }, REFERRAL_POINTS: { "title": "Earn Referral points", @@ -47,15 +47,15 @@ NOTIFICATION_DICT = { }, SKIPPED_PROFILE_SETUP: { "title": "Skipped profile setup!", - "body": "Your guardian has been setup your profile." + "body": "Your guardian {from_user} has setup your profile." }, TASK_SUBMITTED: { "title": "Task submitted!", "body": "Your task has been submitted successfully." }, TASK_ACTION: { - "title": "Task approval!", - "body": "You have request for task approval." + "title": "Task completion approval!", + "body": "You have request from {from_user} for task approval." }, LEADERBOARD_RANKING: { "title": "Leader board rank!", diff --git a/notifications/utils.py b/notifications/utils.py index 38df5ba..50dee81 100644 --- a/notifications/utils.py +++ b/notifications/utils.py @@ -11,6 +11,8 @@ from django.contrib.auth import get_user_model from account.models import UserNotification from account.utils import get_user_full_name +from guardian.models import Guardian +from junior.models import Junior from notifications.constants import NOTIFICATION_DICT from notifications.models import Notification @@ -72,12 +74,11 @@ def get_notification_data(notification_type, from_user, to_user, extra_data): return notification_data, push_data -@shared_task() -def send_notification(notification_type, from_user_id, to_user_id, extra_data): +def send_notification(notification_type, from_user, to_user, extra_data): """ used to send the push for the given notification type """ - (from_user, to_user) = get_basic_detail(from_user_id, to_user_id) + # (from_user, to_user) = get_basic_detail(from_user_id, to_user_id) notification_data, push_data = get_notification_data(notification_type, from_user, to_user, extra_data) user_notification_type = UserNotification.objects.filter(user=to_user).first() notification_data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()}) @@ -92,3 +93,29 @@ def send_push(user, data): user.fcmdevice_set.filter(active=True).send_message( Message(notification=FirebaseNotification(data['title'], data['body']), data=data) ) + + +@shared_task() +def send_notification_to_guardian(notification_type, from_user_id, to_user_id, extra_data): + from_user = None + if from_user_id: + from_user = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first() + extra_data['from_user_image'] = from_user.image + from_user = from_user.auth + to_user = Guardian.objects.filter(user_id=to_user_id).select_related('user').first() + extra_data['to_user_image'] = to_user.image + send_notification(notification_type, from_user, to_user.user, extra_data) + + +@shared_task() +def send_notification_to_junior(notification_type, from_user_id, to_user_id, extra_data): + from_user = None + if from_user_id: + from_user = Guardian.objects.filter(user_id=from_user_id).select_related('user').first() + extra_data['from_user_image'] = from_user.image + from_user = from_user.user + to_user = Junior.objects.filter(auth_id=to_user_id).select_related('auth').first() + extra_data['to_user_image'] = to_user.image + send_notification(notification_type, from_user, to_user.auth, extra_data) + + diff --git a/notifications/views.py b/notifications/views.py index f932f0f..dc6e891 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -13,7 +13,7 @@ from base.messages import SUCCESS_CODE, ERROR_CODE from notifications.constants import TEST_NOTIFICATION # Import serializer from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer -from notifications.utils import send_notification +from notifications.utils import send_notification, send_notification_to_guardian, send_notification_to_junior # Import model from notifications.models import Notification @@ -56,8 +56,10 @@ class NotificationViewSet(viewsets.GenericViewSet): to send test notification :return: """ - send_notification(TEST_NOTIFICATION, None, request.auth.payload['user_id'], - {'task_id': None}) + send_notification_to_guardian(TEST_NOTIFICATION, None, request.auth.payload['user_id'], + {'task_id': None}) + send_notification_to_junior(TEST_NOTIFICATION, request.auth.payload['user_id'], None, + {'task_id': None}) return custom_response(SUCCESS_CODE["3000"]) @action(methods=['get'], detail=False, url_path='list', url_name='list', From 728d19da996eabac5dd85db3fb014f66dc63f17b Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 17 Aug 2023 12:21:13 +0530 Subject: [PATCH 05/83] FAQ model --- account/utils.py | 1 - junior/models.py | 38 ++++++++++++++++++++++++++++++++++++++ junior/urls.py | 4 +++- junior/views.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/account/utils.py b/account/utils.py index 1780a5d..8df5294 100644 --- a/account/utils.py +++ b/account/utils.py @@ -252,7 +252,6 @@ def generate_code(value, user_id): OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1) - def get_user_full_name(user_obj): """ to get user's full name diff --git a/junior/models.py b/junior/models.py index 025843e..784a753 100644 --- a/junior/models.py +++ b/junior/models.py @@ -158,6 +158,12 @@ class JuniorArticlePoints(models.Model): created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) + class Meta(object): + """ Meta class """ + verbose_name = 'Junior Article Points' + # another name of the model""" + verbose_name_plural = 'Junior Article Points' + def __str__(self): """Return title""" return f'{self.id} | {self.question}' @@ -178,6 +184,12 @@ class JuniorArticle(models.Model): created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) + class Meta(object): + """ Meta class """ + verbose_name = 'Junior Article' + # another name of the model""" + verbose_name_plural = 'Junior Article' + def __str__(self): """Return title""" return f'{self.id} | {self.article}' @@ -197,6 +209,32 @@ class JuniorArticleCard(models.Model): created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) + class Meta(object): + """ Meta class """ + verbose_name = 'Junior Article Card' + # another name of the model""" + verbose_name_plural = 'Junior Article Card' + def __str__(self): """Return title""" return f'{self.id} | {self.article}' + + +class FAQ(models.Model): + """FAQ model""" + # Total earned points""" + questions = models.IntegerField(max_length=100) + # referral points""" + description = models.CharField(max_length=500) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta(object): + """ Meta class """ + verbose_name = 'FAQ' + # another name of the model""" + verbose_name_plural = 'FAQ' + + def __str__(self): + """Return email id""" + return f'{self.questions}' diff --git a/junior/urls.py b/junior/urls.py index b145d4f..08fe33a 100644 --- a/junior/urls.py +++ b/junior/urls.py @@ -6,7 +6,7 @@ from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode, InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView, StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView, - CreateArticleCardAPIView, RemoveGuardianCodeAPIView) + CreateArticleCardAPIView, RemoveGuardianCodeAPIView, FAQViewSet) """Third party import""" from rest_framework import routers @@ -51,6 +51,8 @@ router.register('start-assessment', StartAssessmentAPIView, basename='start-asse router.register('check-answer', CheckAnswerAPIView, basename='check-answer') # start article""" router.register('create-article-card', CreateArticleCardAPIView, basename='create-article-card') +# FAQ API +router.register('faq', FAQViewSet, basename='faq') # Define url pattern""" urlpatterns = [ path('api/v1/', include(router.urls)), diff --git a/junior/views.py b/junior/views.py index 6b8bc2f..95a8ed8 100644 --- a/junior/views.py +++ b/junior/views.py @@ -10,6 +10,8 @@ from django.db.models import F import datetime import requests + +from rest_framework.viewsets import GenericViewSet, mixins """Django app import""" # Import guardian's model, @@ -30,7 +32,7 @@ import requests # Import constants from django.db.models import Sum from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle, - JuniorArticleCard) + JuniorArticleCard, FAQ) from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer, RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer, @@ -650,3 +652,44 @@ class RemoveGuardianCodeAPIView(views.APIView): return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) + + +class FAQViewSet(GenericViewSet, mixins.CreateModelMixin, + mixins.ListModelMixin): + """FAQ view set""" + + serializer_class = ArticleSerializer + permission_classes = [IsAuthenticated] + http_method_names = ['get', 'post'] + + def get_queryset(self): + queryset = FAQ.objects.all() + return queryset + + def create(self, request, *args, **kwargs): + """ + article create api method + :param request: + :param args: + :param kwargs: + :return: success message + """ + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return custom_response(SUCCESS_CODE["3027"]) + + def list(self, request, *args, **kwargs): + """ + article list api method + :param request: + :param args: + :param kwargs: + :return: list of article + """ + queryset = self.get_queryset() + paginator = self.pagination_class() + paginated_queryset = paginator.paginate_queryset(queryset, request) + serializer = self.serializer_class(paginated_queryset, many=True) + return custom_response(None, data=serializer.data) + From 36427b50c1f9911846242db9100c5c0c669619ea Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 17 Aug 2023 12:24:33 +0530 Subject: [PATCH 06/83] print statement for expiry date --- account/utils.py | 3 ++- guardian/views.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/account/utils.py b/account/utils.py index 1780a5d..cc097ab 100644 --- a/account/utils.py +++ b/account/utils.py @@ -251,7 +251,8 @@ def generate_code(value, user_id): OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1) - +print("timezone.now()===>",timezone.now(),'====type===>',type(timezone.now())) +print("OTP_EXPIRY==>",OTP_EXPIRY,'====type===>',type(OTP_EXPIRY)) def get_user_full_name(user_obj): """ diff --git a/guardian/views.py b/guardian/views.py index 0319938..8aaaa69 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -67,6 +67,7 @@ class SignupViewset(viewsets.ModelViewSet): otp = generate_otp() # expire otp after 1 day expiry = OTP_EXPIRY + print("expiry==>", expiry,'====type===>',type(expiry)) # create user email otp object UserEmailOtp.objects.create(email=request.data['email'], otp=otp, user_type=str(request.data['user_type']), expired_at=expiry) From 043c8c2f63358482b643121a768eca87f3ec78f7 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Wed, 16 Aug 2023 17:15:12 +0530 Subject: [PATCH 07/83] csv excel export api --- web_admin/pagination.py | 9 +- web_admin/serializers/analytics_serializer.py | 19 ++- web_admin/views/analytics.py | 109 +++--------------- zod_bank/settings.py | 7 ++ 4 files changed, 48 insertions(+), 96 deletions(-) diff --git a/web_admin/pagination.py b/web_admin/pagination.py index 6a6aff4..c2eed5e 100644 --- a/web_admin/pagination.py +++ b/web_admin/pagination.py @@ -1,13 +1,18 @@ """ web_admin pagination file """ +# third party imports from rest_framework.pagination import PageNumberPagination +from base.constants import NUMBER + class CustomPageNumberPagination(PageNumberPagination): """ custom paginator class """ - page_size = 10 # Set the desired page size + # Set the desired page size + page_size = NUMBER['ten'] page_size_query_param = 'page_size' - max_page_size = 100 # Set a maximum page size if needed + # Set a maximum page size if needed + max_page_size = NUMBER['hundred'] diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py index 4c0cbd5..e87c25d 100644 --- a/web_admin/serializers/analytics_serializer.py +++ b/web_admin/serializers/analytics_serializer.py @@ -1,13 +1,16 @@ """ web_admin analytics serializer file """ +# third party imports from rest_framework import serializers + +# django imports from django.contrib.auth import get_user_model +# local imports from base.constants import USER_TYPE from junior.models import JuniorPoints, Junior -from web_admin.serializers.user_management_serializer import JuniorSerializer, GuardianSerializer USER = get_user_model() @@ -74,6 +77,7 @@ class UserCSVReportSerializer(serializers.ModelSerializer): phone_number = serializers.SerializerMethodField() user_type = serializers.SerializerMethodField() is_active = serializers.SerializerMethodField() + date_joined = serializers.SerializerMethodField() class Meta: """ @@ -104,9 +108,9 @@ class UserCSVReportSerializer(serializers.ModelSerializer): :return: user type """ if obj.guardian_profile.all().first(): - return dict(USER_TYPE).get('2') + return dict(USER_TYPE).get('2').capitalize() elif obj.junior_profile.all().first(): - return dict(USER_TYPE).get('1') + return dict(USER_TYPE).get('1').capitalize() else: return None @@ -120,3 +124,12 @@ class UserCSVReportSerializer(serializers.ModelSerializer): return "Active" if profile.is_active else "Inactive" elif profile := obj.junior_profile.all().first(): return "Active" if profile.is_active else "Inactive" + + @staticmethod + def get_date_joined(obj): + """ + :param obj: user obj + :return: formatted date + """ + date = obj.date_joined.strftime("%d %b %Y") + return date diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 34d1204..3a3360e 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -44,7 +44,7 @@ class AnalyticsViewSet(GenericViewSet): to get junior leaderboard and ranking """ serializer_class = None - # permission_classes = [IsAuthenticated, AdminPermission] + permission_classes = [IsAuthenticated, AdminPermission] def get_queryset(self): user_qs = USER.objects.filter( @@ -148,93 +148,14 @@ class AnalyticsViewSet(GenericViewSet): serializer = self.serializer_class(paginated_queryset, many=True) return custom_response(None, serializer.data) - # @action(methods=['get'], url_name='generate-report', url_path='generate-report', detail=False) - # def generate_report(self, request): - # - # response = HttpResponse(content_type='text/csv') - # response['Content-Disposition'] = 'attachment; filename="ZOD_Bank_Analytics.csv"' - # - # end_date = datetime.date.today() - # start_date = end_date - datetime.timedelta(days=6) - # - # if request.query_params.get('start_date') and request.query_params.get('end_date'): - # start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) - # end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) - # - # buffer = io.StringIO() - # csv_writer = csv.writer(buffer) - # - # # sheet 1 for Total Users - # user_qs = self.get_queryset() - # queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1)))) - # serializer = UserCSVReportSerializer(queryset, many=True) - # - # # writer = csv.writer(response) - # csv_writer.writerow(['Users']) - # csv_writer.writerow([ - # 'First Name', 'Last Name', 'Email', 'Phone Number', 'User Type', 'status', 'Date Joined', - # # Add more fields as needed - # ]) - # - # for user_data in serializer.data: - # row = [ - # user_data['first_name'], user_data['last_name'], user_data['email'], - # user_data['phone_number'], user_data['user_type'], - # user_data['is_active'], user_data['date_joined'] - # ] - # csv_writer.writerow(row) - # - # response.write(buffer.getvalue()) - # buffer.close() - # - # # sheet 2 for Assign Task - # assign_tasks = JuniorTask.objects.filter( - # created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] - # ).exclude(task_status__in=[PENDING, EXPIRED]) - # buffer = io.StringIO() - # csv_writer = csv.writer(buffer) - # csv_writer.writerow([]) - # csv_writer.writerow(['Assign Tasks']) - # csv_writer.writerow([ - # 'Task Name', 'Task Status' - # # Add more fields as needed - # ]) - # - # for task in assign_tasks: - # row = [ - # task.task_name, dict(TASK_STATUS).get(task.task_status).capitalize(), - # ] - # csv_writer.writerow(row) - # response.write(buffer.getvalue()) - # buffer.close() - # - # # sheet 3 for Juniors Leaderboard and rank - # queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( - # expression=Rank(), - # order_by=[F('total_points').desc(), 'junior__created_at'] - # )).order_by('-total_points', 'junior__created_at') - # buffer = io.StringIO() - # csv_writer = csv.writer(buffer) - # csv_writer.writerow([]) - # csv_writer.writerow(['Top 15 Juniors']) - # csv_writer.writerow([ - # 'Junior Name', 'Points', 'Rank' - # # Add more fields as needed - # ]) - # - # for junior in queryset: - # row = [ - # f"{junior.junior.auth.first_name}{junior.junior.auth.last_name}" if junior.junior.auth.last_name else junior.junior.auth.first_name, - # junior.total_points, junior.rank - # ] - # csv_writer.writerow(row) - # response.write(buffer.getvalue()) - # buffer.close() - # - # return response - - @action(methods=['get'], url_name='generate-report', url_path='generate-report', detail=False) - def generate_report(self, request): + @action(methods=['get'], url_name='export-excel', url_path='export-excel', detail=False) + def export_excel(self, request): + """ + to export users count, task details and top juniors in csv/excel file + :param request: start_date: date format (yyyy-mm-dd) + :param request: end_date: date format (yyyy-mm-dd) + :return: + """ response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response['Content-Disposition'] = 'attachment; filename="ZOD_Bank_Analytics.xlsx"' @@ -252,7 +173,7 @@ class AnalyticsViewSet(GenericViewSet): workbook = xlsxwriter.Workbook(buffer) # Add sheets - sheets = ['Users', 'Assign Tasks', 'Top 15 Juniors'] + sheets = ['Users', 'Assign Tasks', 'Juniors leaderboard'] for sheet_name in sheets: worksheet = workbook.add_worksheet(name=sheet_name) @@ -263,7 +184,13 @@ class AnalyticsViewSet(GenericViewSet): queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1)))) serializer = UserCSVReportSerializer(queryset, many=True) - df_users = pd.DataFrame(serializer.data) + df_users = pd.DataFrame([ + {'Name': f"{user['first_name']} {user['last_name']}", + 'Email': user['email'], 'Phone Number': user['phone_number'], + 'User Type': user['user_type'], 'Status': user['is_active'], + 'Date Joined': user['date_joined']} + for user in serializer.data + ]) for idx, col in enumerate(df_users.columns): worksheet.write(0, idx, col) # Write header for row_num, row in enumerate(df_users.values, start=1): @@ -288,7 +215,7 @@ class AnalyticsViewSet(GenericViewSet): worksheet.write(row_num, col_num, value) # sheet 3 for Juniors Leaderboard and rank - elif sheet_name == 'Top 15 Juniors': + elif sheet_name == 'Juniors leaderboard': queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( expression=Rank(), order_by=[F('total_points').desc(), 'junior__created_at'] diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 2d003c1..3e0713c 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -177,6 +177,9 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] +# database query logs settings +# Allows us to check db hits +# useful to optimize db query and hit LOGGING = { "version": 1, "filters": { @@ -193,6 +196,7 @@ LOGGING = { "class": "logging.StreamHandler" } }, + # database logger "loggers": { "django.db.backends": { "level": "DEBUG", @@ -242,6 +246,7 @@ CORS_ALLOW_HEADERS = ( 'x-requested-with', ) +# CORS header settings CORS_EXPOSE_HEADERS = ( 'Access-Control-Allow-Origin: *', ) @@ -297,5 +302,7 @@ STATIC_URL = 'static/' # define static root STATIC_ROOT = 'static' +# media url MEDIA_URL = "/media/" +# media path MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'media') From 4e8243c17ee478ba273dc79a899d8d7442a715f3 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 17 Aug 2023 13:06:46 +0530 Subject: [PATCH 08/83] added required packages in reuirements fil --- requirements.txt | 3 +++ web_admin/views/analytics.py | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f1540e7..624a176 100644 --- a/requirements.txt +++ b/requirements.txt @@ -99,3 +99,6 @@ uritemplate==4.1.1 urllib3==1.26.16 vine==5.0.0 wcwidth==0.2.6 + +pandas==2.0.3 +XlsxWriter==3.1.2 \ No newline at end of file diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 3a3360e..b904af6 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -3,7 +3,6 @@ web_admin analytics view file """ # python imports import datetime -import csv import io import pandas as pd import xlsxwriter From 04ed9c668c09c742d7e81c666519169b812d6441 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 17 Aug 2023 13:24:20 +0530 Subject: [PATCH 09/83] remove print statement for expiry date --- account/utils.py | 2 -- guardian/views.py | 1 - 2 files changed, 3 deletions(-) diff --git a/account/utils.py b/account/utils.py index cc097ab..8df5294 100644 --- a/account/utils.py +++ b/account/utils.py @@ -251,8 +251,6 @@ def generate_code(value, user_id): OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1) -print("timezone.now()===>",timezone.now(),'====type===>',type(timezone.now())) -print("OTP_EXPIRY==>",OTP_EXPIRY,'====type===>',type(OTP_EXPIRY)) def get_user_full_name(user_obj): """ diff --git a/guardian/views.py b/guardian/views.py index 8aaaa69..0319938 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -67,7 +67,6 @@ class SignupViewset(viewsets.ModelViewSet): otp = generate_otp() # expire otp after 1 day expiry = OTP_EXPIRY - print("expiry==>", expiry,'====type===>',type(expiry)) # create user email otp object UserEmailOtp.objects.create(email=request.data['email'], otp=otp, user_type=str(request.data['user_type']), expired_at=expiry) From 3f6c9a2d997baa19927e12c471e128aea7064249 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 17 Aug 2023 15:48:26 +0530 Subject: [PATCH 10/83] FAQ list and creation --- base/messages.py | 2 + junior/admin.py | 3 +- ...aq_alter_juniorarticle_options_and_more.py | 39 ++++++++++++++ junior/migrations/0027_alter_faq_question.py | 18 +++++++ junior/models.py | 4 +- junior/serializers.py | 16 +++++- junior/views.py | 25 +++++---- zod_bank/settings.py | 52 +++++++++---------- 8 files changed, 119 insertions(+), 40 deletions(-) create mode 100644 junior/migrations/0026_faq_alter_juniorarticle_options_and_more.py create mode 100644 junior/migrations/0027_alter_faq_question.py diff --git a/base/messages.py b/base/messages.py index 10fbfaa..5113a4b 100644 --- a/base/messages.py +++ b/base/messages.py @@ -163,6 +163,8 @@ SUCCESS_CODE = { "3043": "Read article card successfully", # remove guardian code request "3044": "Remove guardian code request successfully", + # create faq + "3045": "Create FAQ data" } """status code error""" diff --git a/junior/admin.py b/junior/admin.py index 6c6cdf9..2ffda51 100644 --- a/junior/admin.py +++ b/junior/admin.py @@ -3,8 +3,9 @@ from django.contrib import admin """Import Django app""" from .models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle, - JuniorArticleCard) + JuniorArticleCard, FAQ) # Register your models here. +admin.site.register(FAQ) @admin.register(JuniorArticle) class JuniorArticleAdmin(admin.ModelAdmin): """Junior Admin""" diff --git a/junior/migrations/0026_faq_alter_juniorarticle_options_and_more.py b/junior/migrations/0026_faq_alter_juniorarticle_options_and_more.py new file mode 100644 index 0000000..12243a2 --- /dev/null +++ b/junior/migrations/0026_faq_alter_juniorarticle_options_and_more.py @@ -0,0 +1,39 @@ +# Generated by Django 4.2.2 on 2023-08-17 09:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0025_alter_juniorarticle_junior'), + ] + + operations = [ + migrations.CreateModel( + name='FAQ', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('question', models.IntegerField(max_length=100)), + ('description', models.CharField(max_length=500)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'FAQ', + 'verbose_name_plural': 'FAQ', + }, + ), + migrations.AlterModelOptions( + name='juniorarticle', + options={'verbose_name': 'Junior Article', 'verbose_name_plural': 'Junior Article'}, + ), + migrations.AlterModelOptions( + name='juniorarticlecard', + options={'verbose_name': 'Junior Article Card', 'verbose_name_plural': 'Junior Article Card'}, + ), + migrations.AlterModelOptions( + name='juniorarticlepoints', + options={'verbose_name': 'Junior Article Points', 'verbose_name_plural': 'Junior Article Points'}, + ), + ] diff --git a/junior/migrations/0027_alter_faq_question.py b/junior/migrations/0027_alter_faq_question.py new file mode 100644 index 0000000..46fefd8 --- /dev/null +++ b/junior/migrations/0027_alter_faq_question.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-08-17 09:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0026_faq_alter_juniorarticle_options_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='faq', + name='question', + field=models.CharField(max_length=100), + ), + ] diff --git a/junior/models.py b/junior/models.py index 784a753..d3d2bcf 100644 --- a/junior/models.py +++ b/junior/models.py @@ -223,7 +223,7 @@ class JuniorArticleCard(models.Model): class FAQ(models.Model): """FAQ model""" # Total earned points""" - questions = models.IntegerField(max_length=100) + question = models.CharField(max_length=100) # referral points""" description = models.CharField(max_length=500) created_at = models.DateTimeField(auto_now_add=True) @@ -237,4 +237,4 @@ class FAQ(models.Model): def __str__(self): """Return email id""" - return f'{self.questions}' + return f'{self.question}' diff --git a/junior/serializers.py b/junior/serializers.py index f932a26..c03698e 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -12,7 +12,7 @@ from rest_framework_simplejwt.tokens import RefreshToken # local imports from account.utils import send_otp_email, generate_code -from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints +from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, FAQ from guardian.tasks import generate_otp from base.messages import ERROR_CODE, SUCCESS_CODE from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED, @@ -508,3 +508,17 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer): instance.guardian_code_status = str(NUMBER['one']) instance.save() return instance + +class FAQSerializer(serializers.ModelSerializer): + # FAQ Serializer + + class Meta(object): + # meta info + model = FAQ + fields = ('id', 'question', 'description') + + def create(self, validated_data): + # validate data + print("validated_data===>",validated_data) + faq = FAQ.objects.bulk_create(**validated_data) + return faq diff --git a/junior/views.py b/junior/views.py index 95a8ed8..7620963 100644 --- a/junior/views.py +++ b/junior/views.py @@ -36,7 +36,7 @@ from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, Jun from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer, RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer, - RemoveGuardianCodeSerializer) + RemoveGuardianCodeSerializer, FAQSerializer) from guardian.models import Guardian, JuniorTask from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer from base.messages import ERROR_CODE, SUCCESS_CODE @@ -658,26 +658,28 @@ class FAQViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.ListModelMixin): """FAQ view set""" - serializer_class = ArticleSerializer + serializer_class = FAQSerializer permission_classes = [IsAuthenticated] http_method_names = ['get', 'post'] def get_queryset(self): - queryset = FAQ.objects.all() - return queryset + return FAQ.objects.all() def create(self, request, *args, **kwargs): """ - article create api method + faq create api method :param request: :param args: :param kwargs: :return: success message """ - serializer = self.serializer_class(data=request.data) - serializer.is_valid(raise_exception=True) - serializer.save() - return custom_response(SUCCESS_CODE["3027"]) + obj_data = [FAQ(**item) for item in request.data] + try: + FAQ.objects.bulk_create(obj_data) + return custom_response(SUCCESS_CODE["3045"], response_status=status.HTTP_200_OK) + except Exception as e: + return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) + def list(self, request, *args, **kwargs): """ @@ -691,5 +693,8 @@ class FAQViewSet(GenericViewSet, mixins.CreateModelMixin, paginator = self.pagination_class() paginated_queryset = paginator.paginate_queryset(queryset, request) serializer = self.serializer_class(paginated_queryset, many=True) - return custom_response(None, data=serializer.data) + return custom_response(None, data=serializer.data, response_status=status.HTTP_200_OK) + + + diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 3e0713c..4e264f4 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -180,32 +180,32 @@ AUTH_PASSWORD_VALIDATORS = [ # database query logs settings # Allows us to check db hits # useful to optimize db query and hit -LOGGING = { - "version": 1, - "filters": { - "require_debug_true": { - "()": "django.utils.log.RequireDebugTrue" - } - }, - "handlers": { - "console": { - "level": "DEBUG", - "filters": [ - "require_debug_true" - ], - "class": "logging.StreamHandler" - } - }, - # database logger - "loggers": { - "django.db.backends": { - "level": "DEBUG", - "handlers": [ - "console" - ] - } - } -} +# LOGGING = { +# "version": 1, +# "filters": { +# "require_debug_true": { +# "()": "django.utils.log.RequireDebugTrue" +# } +# }, +# "handlers": { +# "console": { +# "level": "DEBUG", +# "filters": [ +# "require_debug_true" +# ], +# "class": "logging.StreamHandler" +# } +# }, +# # database logger +# "loggers": { +# "django.db.backends": { +# "level": "DEBUG", +# "handlers": [ +# "console" +# ] +# } +# } +# } # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ From b46109e487c8fe35fb6c18fe0e65e08ed75f6521 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 17 Aug 2023 16:06:58 +0530 Subject: [PATCH 11/83] FAQ list and creation --- junior/migrations/0028_faq_status.py | 18 ++++++++++++++++++ junior/models.py | 6 ++++-- junior/serializers.py | 5 ----- 3 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 junior/migrations/0028_faq_status.py diff --git a/junior/migrations/0028_faq_status.py b/junior/migrations/0028_faq_status.py new file mode 100644 index 0000000..bdfbf67 --- /dev/null +++ b/junior/migrations/0028_faq_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-08-17 10:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0027_alter_faq_question'), + ] + + operations = [ + migrations.AddField( + model_name='faq', + name='status', + field=models.IntegerField(blank=True, default=1, null=True), + ), + ] diff --git a/junior/models.py b/junior/models.py index d3d2bcf..c256800 100644 --- a/junior/models.py +++ b/junior/models.py @@ -222,10 +222,12 @@ class JuniorArticleCard(models.Model): class FAQ(models.Model): """FAQ model""" - # Total earned points""" + # questions""" question = models.CharField(max_length=100) - # referral points""" + # answer""" description = models.CharField(max_length=500) + # status + status = models.IntegerField(default=1, null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) diff --git a/junior/serializers.py b/junior/serializers.py index c03698e..1150bd8 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -517,8 +517,3 @@ class FAQSerializer(serializers.ModelSerializer): model = FAQ fields = ('id', 'question', 'description') - def create(self, validated_data): - # validate data - print("validated_data===>",validated_data) - faq = FAQ.objects.bulk_create(**validated_data) - return faq From dd2890bca69e6e20f3019ee3aca340730b649aa8 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 18 Aug 2023 11:04:05 +0530 Subject: [PATCH 12/83] handle delete scenerio in approve task and junior --- account/serializers.py | 5 +- .../templated_email/support_mail.email | 4 +- account/utils.py | 2 + base/messages.py | 4 +- .../migrations/0021_guardian_is_deleted.py | 18 +++++++ guardian/models.py | 2 + guardian/serializers.py | 4 +- guardian/views.py | 47 +++++++++---------- junior/migrations/0029_junior_is_deleted.py | 18 +++++++ junior/models.py | 2 + junior/serializers.py | 9 ++-- web_admin/serializers/analytics_serializer.py | 2 +- .../serializers/user_management_serializer.py | 4 +- 13 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 guardian/migrations/0021_guardian_is_deleted.py create mode 100644 junior/migrations/0029_junior_is_deleted.py diff --git a/account/serializers.py b/account/serializers.py index ebdc263..0caeb8c 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -244,7 +244,7 @@ class GuardianSerializer(serializers.ModelSerializer): """Meta info""" model = Guardian fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code', - 'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active', + 'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active', 'is_deleted', 'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type', 'country_name'] @@ -287,7 +287,8 @@ class JuniorSerializer(serializers.ModelSerializer): model = Junior fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_password_set', - 'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited'] + 'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited', + 'is_deleted'] class EmailVerificationSerializer(serializers.ModelSerializer): """Email verification serializer""" diff --git a/account/templates/templated_email/support_mail.email b/account/templates/templated_email/support_mail.email index 50467a9..34d6156 100644 --- a/account/templates/templated_email/support_mail.email +++ b/account/templates/templated_email/support_mail.email @@ -8,14 +8,14 @@

- Hi {{name}}, + Hi Support Team,

- {{name}} have some queries and need some support. Please support them by using their email address {{sender}}.

Queries are:-
{{ message }} + {{name}} have some queries and need some support. Please support them by using their email address {{sender}}.

Queries are:-

  • {{ message }}
  • diff --git a/account/utils.py b/account/utils.py index 8df5294..e016940 100644 --- a/account/utils.py +++ b/account/utils.py @@ -95,6 +95,7 @@ def junior_account_update(user_tb): junior_data.is_verified = False junior_data.guardian_code = '{}' junior_data.guardian_code_status = str(NUMBER['one']) + junior_data.is_deleted = True junior_data.save() JuniorPoints.objects.filter(junior=junior_data).delete() @@ -105,6 +106,7 @@ def guardian_account_update(user_tb): # Update guardian account guardian_data.is_active = False guardian_data.is_verified = False + guardian_data.is_deleted = True guardian_data.save() jun_data = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code)) """Disassociate relation between guardian and junior""" diff --git a/base/messages.py b/base/messages.py index 5113a4b..37aa49a 100644 --- a/base/messages.py +++ b/base/messages.py @@ -97,7 +97,9 @@ ERROR_CODE = { "2068": "No guardian associated with this junior", "2069": "Invalid user type", "2070": "You did not find as a guardian", - "2071": "You did not find as a junior" + "2071": "You did not find as a junior", + "2072": "You can not approve or reject this task because junior does not exist in the system", + "2073": "You can not approve or reject this junior because junior does not exist in the system" } """Success message code""" diff --git a/guardian/migrations/0021_guardian_is_deleted.py b/guardian/migrations/0021_guardian_is_deleted.py new file mode 100644 index 0000000..11833c6 --- /dev/null +++ b/guardian/migrations/0021_guardian_is_deleted.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-08-17 12:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('guardian', '0020_alter_juniortask_task_status'), + ] + + operations = [ + migrations.AddField( + model_name='guardian', + name='is_deleted', + field=models.BooleanField(default=False), + ), + ] diff --git a/guardian/models.py b/guardian/models.py index 45afee7..5c6457a 100644 --- a/guardian/models.py +++ b/guardian/models.py @@ -57,6 +57,8 @@ class Guardian(models.Model): is_invited = models.BooleanField(default=False) # Profile activity""" is_password_set = models.BooleanField(default=True) + # guardian profile deleted or not""" + is_deleted = models.BooleanField(default=False) """Profile activity""" is_active = models.BooleanField(default=True) """guardian is verified or not""" diff --git a/guardian/serializers.py b/guardian/serializers.py index cbd1e4e..6e6e6af 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -214,7 +214,7 @@ class GuardianDetailSerializer(serializers.ModelSerializer): """Meta info""" model = Guardian fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', - 'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image', + 'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image', 'is_deleted' 'updated_at'] class TaskDetailsSerializer(serializers.ModelSerializer): """Task detail serializer""" @@ -340,7 +340,7 @@ class GuardianProfileSerializer(serializers.ModelSerializer): fields = ['id', 'email', 'first_name', 'last_name', 'country_name','country_code', 'phone', 'gender', 'dob', 'guardian_code', 'notification_count', 'total_count', 'complete_field_count', 'referral_code', 'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method', - 'updated_at', 'passcode'] + 'updated_at', 'passcode','is_deleted'] class ApproveJuniorSerializer(serializers.ModelSerializer): diff --git a/guardian/views.py b/guardian/views.py index 0319938..d06f5b9 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -253,31 +253,29 @@ class ApproveJuniorAPIView(viewsets.ViewSet): serializer_class = ApproveJuniorSerializer permission_classes = [IsAuthenticated] - def get_queryset(self): - """Get the queryset for the view""" - guardian = Guardian.objects.filter(user__email=self.request.user).last() - # fetch junior query - junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last() - return guardian, junior_queryset def create(self, request, *args, **kwargs): """ junior list""" try: - queryset = self.get_queryset() + guardian = Guardian.objects.filter(user__email=self.request.user).last() + # fetch junior query + junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last() + if junior_queryset and junior_queryset.is_deleted: + return custom_error_response(ERROR_CODE['2073'], response_status=status.HTTP_400_BAD_REQUEST) # action 1 is use for approve and 2 for reject if request.data['action'] == '1': # use ApproveJuniorSerializer serializer - serializer = ApproveJuniorSerializer(context={"guardian_code": queryset[0].guardian_code, - "junior": queryset[1], "action": request.data['action']}, + serializer = ApproveJuniorSerializer(context={"guardian_code": guardian.guardian_code, + "junior": junior_queryset, "action": request.data['action']}, data=request.data) if serializer.is_valid(): # save serializer serializer.save() return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) else: - queryset[1].guardian_code = None - queryset[1].guardian_code_status = str(NUMBER['one']) - queryset[1].save() + junior_queryset.guardian_code = None + junior_queryset.guardian_code_status = str(NUMBER['one']) + junior_queryset.save() return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) @@ -288,34 +286,31 @@ class ApproveTaskAPIView(viewsets.ViewSet): serializer_class = ApproveTaskSerializer permission_classes = [IsAuthenticated] - def get_queryset(self): - """Get the queryset for the view""" - guardian = Guardian.objects.filter(user__email=self.request.user).last() - # task query - task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'), - guardian=guardian, - junior=self.request.data.get('junior_id')).last() - return guardian, task_queryset - def create(self, request, *args, **kwargs): """ junior list""" # action 1 is use for approve and 2 for reject try: - queryset = self.get_queryset() + guardian = Guardian.objects.filter(user__email=self.request.user).last() + # task query + task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'), + guardian=guardian, + junior=self.request.data.get('junior_id')).last() + if task_queryset and task_queryset.junior.is_deleted: + return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST) # use ApproveJuniorSerializer serializer - serializer = ApproveTaskSerializer(context={"guardian_code": queryset[0].guardian_code, - "task_instance": queryset[1], + serializer = ApproveTaskSerializer(context={"guardian_code": guardian.guardian_code, + "task_instance": task_queryset, "action": str(request.data['action']), "junior": self.request.data['junior_id']}, data=request.data) unexpected_task_status = [str(NUMBER['five']), str(NUMBER['six'])] if (str(request.data['action']) == str(NUMBER['one']) and serializer.is_valid() - and queryset[1] and queryset[1].task_status not in unexpected_task_status): + and task_queryset and task_queryset.task_status not in unexpected_task_status): # save serializer serializer.save() return custom_response(SUCCESS_CODE['3025'], response_status=status.HTTP_200_OK) elif (str(request.data['action']) == str(NUMBER['two']) and serializer.is_valid() - and queryset[1] and queryset[1].task_status not in unexpected_task_status): + and task_queryset and task_queryset.task_status not in unexpected_task_status): # save serializer serializer.save() return custom_response(SUCCESS_CODE['3026'], response_status=status.HTTP_200_OK) diff --git a/junior/migrations/0029_junior_is_deleted.py b/junior/migrations/0029_junior_is_deleted.py new file mode 100644 index 0000000..a39f60f --- /dev/null +++ b/junior/migrations/0029_junior_is_deleted.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-08-17 12:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0028_faq_status'), + ] + + operations = [ + migrations.AddField( + model_name='junior', + name='is_deleted', + field=models.BooleanField(default=False), + ), + ] diff --git a/junior/models.py b/junior/models.py index c256800..773557b 100644 --- a/junior/models.py +++ b/junior/models.py @@ -68,6 +68,8 @@ class Junior(models.Model): is_password_set = models.BooleanField(default=True) # junior profile is complete or not""" is_complete_profile = models.BooleanField(default=False) + # junior profile deleted or not""" + is_deleted = models.BooleanField(default=False) # passcode of the junior profile""" passcode = models.IntegerField(null=True, blank=True, default=None) # junior is verified or not""" diff --git a/junior/serializers.py b/junior/serializers.py index 1150bd8..b4aa54f 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -147,7 +147,7 @@ class JuniorDetailSerializer(serializers.ModelSerializer): model = Junior fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', 'guardian_code', 'image', 'is_invited', 'referral_code','is_active', 'is_complete_profile', - 'created_at', 'image', 'updated_at'] + 'created_at', 'image', 'is_deleted', 'updated_at'] class JuniorDetailListSerializer(serializers.ModelSerializer): """junior serializer""" @@ -214,7 +214,8 @@ class JuniorDetailListSerializer(serializers.ModelSerializer): fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'country_name', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task', - 'requested_task', 'rejected_task', 'position', 'is_invited', 'guardian_code_status'] + 'requested_task', 'rejected_task', 'position', 'is_invited', 'guardian_code_status', + 'is_deleted'] class JuniorProfileSerializer(serializers.ModelSerializer): """junior serializer""" @@ -257,7 +258,7 @@ class JuniorProfileSerializer(serializers.ModelSerializer): fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method', - 'is_invited', 'passcode', 'guardian_code_approved'] + 'is_invited', 'passcode', 'guardian_code_approved', 'is_deleted'] class AddJuniorSerializer(serializers.ModelSerializer): """Add junior serializer""" @@ -395,7 +396,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer): """Meta info""" model = Junior fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task', - 'requested_task', 'rejected_task', 'expired_task'] + 'requested_task', 'rejected_task', 'expired_task', 'is_deleted'] class AddGuardianSerializer(serializers.ModelSerializer): """Add guardian serializer""" diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py index e87c25d..8561e71 100644 --- a/web_admin/serializers/analytics_serializer.py +++ b/web_admin/serializers/analytics_serializer.py @@ -28,7 +28,7 @@ class JuniorLeaderboardSerializer(serializers.ModelSerializer): meta class """ model = Junior - fields = ('id', 'name', 'first_name', 'last_name', 'is_active', 'image') + fields = ('id', 'name', 'first_name', 'last_name', 'is_active', 'image', 'is_deleted') @staticmethod def get_name(obj): diff --git a/web_admin/serializers/user_management_serializer.py b/web_admin/serializers/user_management_serializer.py index 4bb0709..f2c95f3 100644 --- a/web_admin/serializers/user_management_serializer.py +++ b/web_admin/serializers/user_management_serializer.py @@ -108,7 +108,7 @@ class GuardianSerializer(serializers.ModelSerializer): """ model = Guardian fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone', - 'is_active', 'country_name', 'image', 'email') + 'is_active', 'country_name', 'image', 'email', 'is_deleted') def validate(self, attrs): """ @@ -187,7 +187,7 @@ class JuniorSerializer(serializers.ModelSerializer): """ model = Junior fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone', - 'is_active', 'country_name', 'image', 'email') + 'is_active', 'country_name', 'image', 'email', 'is_deleted') def validate(self, attrs): """ From ef4c45922933716d6995c71a84a00fdf3f30e9f1 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 18 Aug 2023 11:45:21 +0530 Subject: [PATCH 13/83] handle deleted user scenario in complete task api --- base/messages.py | 3 ++- junior/views.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/base/messages.py b/base/messages.py index 37aa49a..db1fdd5 100644 --- a/base/messages.py +++ b/base/messages.py @@ -99,7 +99,8 @@ ERROR_CODE = { "2070": "You did not find as a guardian", "2071": "You did not find as a junior", "2072": "You can not approve or reject this task because junior does not exist in the system", - "2073": "You can not approve or reject this junior because junior does not exist in the system" + "2073": "You can not approve or reject this junior because junior does not exist in the system", + "2074": "You can not complete this task because you does not exist in the system" } """Success message code""" diff --git a/junior/views.py b/junior/views.py index 7620963..7a83b0a 100644 --- a/junior/views.py +++ b/junior/views.py @@ -339,6 +339,8 @@ class CompleteJuniorTaskAPIView(views.APIView): task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user ).select_related('guardian', 'junior').last() if task_queryset: + if task_queryset.junior.is_deleted: + return custom_error_response(ERROR_CODE['2074'], response_status=status.HTTP_400_BAD_REQUEST) # use CompleteTaskSerializer serializer if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]: """Already request send """ From ceaf58433224968d0890c4c28a38aff2cc63f318 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 18 Aug 2023 11:41:24 +0530 Subject: [PATCH 14/83] csv/excel method changed, uploading to alibaba cloud and providing link to frontend --- web_admin/serializers/analytics_serializer.py | 9 ++---- .../serializers/user_management_serializer.py | 31 +++++++++---------- web_admin/views/analytics.py | 31 ++++++++++++++++--- web_admin/views/user_management.py | 23 ++++++++------ zod_bank/settings.py | 1 + 5 files changed, 56 insertions(+), 39 deletions(-) diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py index e87c25d..ce6c72e 100644 --- a/web_admin/serializers/analytics_serializer.py +++ b/web_admin/serializers/analytics_serializer.py @@ -92,10 +92,7 @@ class UserCSVReportSerializer(serializers.ModelSerializer): :param obj: user object :return: user phone number """ - if profile := obj.guardian_profile.all().first(): - return f"+{profile.country_code}{profile.phone}" \ - if profile.country_code and profile.phone else profile.phone - elif profile := obj.junior_profile.all().first(): + if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()): return f"+{profile.country_code}{profile.phone}" \ if profile.country_code and profile.phone else profile.phone else: @@ -120,9 +117,7 @@ class UserCSVReportSerializer(serializers.ModelSerializer): :param obj: user object :return: user type """ - if profile := obj.guardian_profile.all().first(): - return "Active" if profile.is_active else "Inactive" - elif profile := obj.junior_profile.all().first(): + if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()): return "Active" if profile.is_active else "Inactive" @staticmethod diff --git a/web_admin/serializers/user_management_serializer.py b/web_admin/serializers/user_management_serializer.py index 4bb0709..3544c66 100644 --- a/web_admin/serializers/user_management_serializer.py +++ b/web_admin/serializers/user_management_serializer.py @@ -5,7 +5,7 @@ web_admin user_management serializers file from rest_framework import serializers from django.contrib.auth import get_user_model -from base.constants import USER_TYPE +from base.constants import USER_TYPE, GUARDIAN, JUNIOR # local imports from base.messages import ERROR_CODE, SUCCESS_CODE from guardian.models import Guardian @@ -210,10 +210,10 @@ class JuniorSerializer(serializers.ModelSerializer): """ instance.auth.email = self.validated_data.get('email', instance.auth.email) instance.auth.username = self.validated_data.get('email', instance.auth.username) - instance.auth.save() + instance.auth.save(update_fields=['email', 'username']) instance.country_code = validated_data.get('country_code', instance.country_code) instance.phone = validated_data.get('phone', instance.phone) - instance.save() + instance.save(update_fields=['country_code', 'phone']) return instance @staticmethod @@ -265,33 +265,30 @@ class UserManagementDetailSerializer(serializers.ModelSerializer): model = USER fields = ('id', 'user_type', 'email', 'guardian_profile', 'junior_profile', 'associated_users') - @staticmethod - def get_user_type(obj): + def get_user_type(self, obj): """ :param obj: user object :return: user type """ - if obj.guardian_profile.all().first(): - return dict(USER_TYPE).get('2') - elif obj.junior_profile.all().first(): - return dict(USER_TYPE).get('1') - else: - return None + return GUARDIAN if self.context['user_type'] == GUARDIAN else JUNIOR - @staticmethod - def get_associated_users(obj): + def get_associated_users(self, obj): """ :param obj: user object :return: associated user """ - if profile := obj.guardian_profile.all().first(): + if self.context['user_type'] == GUARDIAN: + profile = obj.guardian_profile.all().only('user_id', 'guardian_code').first() if profile.guardian_code: - junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True) + junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], + is_verified=True).select_related('auth') serializer = JuniorSerializer(junior, many=True) return serializer.data - elif profile := obj.junior_profile.all().first(): + elif self.context['user_type'] == JUNIOR: + profile = obj.junior_profile.all().only('auth_id', 'guardian_code').first() if profile.guardian_code: - guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code, is_verified=True) + guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code, + is_verified=True).select_related('user') serializer = GuardianSerializer(guardian, many=True) return serializer.data else: diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index b904af6..a503356 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -6,6 +6,9 @@ import datetime import io import pandas as pd import xlsxwriter +import tempfile +import oss2 +from django.conf import settings # third party imports from rest_framework.viewsets import GenericViewSet @@ -166,7 +169,8 @@ class AnalyticsViewSet(GenericViewSet): start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) - buffer = io.BytesIO() # Use BytesIO for binary data + # Use BytesIO for binary data + buffer = io.BytesIO() # Create an XlsxWriter Workbook object workbook = xlsxwriter.Workbook(buffer) @@ -191,7 +195,8 @@ class AnalyticsViewSet(GenericViewSet): for user in serializer.data ]) for idx, col in enumerate(df_users.columns): - worksheet.write(0, idx, col) # Write header + # Write header + worksheet.write(0, idx, col) for row_num, row in enumerate(df_users.values, start=1): for col_num, value in enumerate(row): worksheet.write(row_num, col_num, value) @@ -208,7 +213,8 @@ class AnalyticsViewSet(GenericViewSet): ]) for idx, col in enumerate(df_tasks.columns): - worksheet.write(0, idx, col) # Write header + # Write header + worksheet.write(0, idx, col) for row_num, row in enumerate(df_tasks.values, start=1): for col_num, value in enumerate(row): worksheet.write(row_num, col_num, value) @@ -231,7 +237,8 @@ class AnalyticsViewSet(GenericViewSet): ]) for idx, col in enumerate(df_leaderboard.columns): - worksheet.write(0, idx, col) # Write header + # Write header + worksheet.write(0, idx, col) for row_num, row in enumerate(df_leaderboard.values, start=1): for col_num, value in enumerate(row): worksheet.write(row_num, col_num, value) @@ -244,5 +251,19 @@ class AnalyticsViewSet(GenericViewSet): response.write(buffer.getvalue()) buffer.close() - return response + filename = f"{'analytics'}/{'ZOD_Bank_Analytics.xlsx'}" + with tempfile.NamedTemporaryFile(delete=False) as temp_file: + """write image in temporary file""" + temp_file.write(response.content) + """auth of bucket""" + auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET) + """fetch bucket details""" + bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME) + # Upload the temporary file to Alibaba OSS + bucket.put_object_from_file(filename, temp_file.name) + """create perfect url for image""" + new_filename = filename.replace(' ', '%20') + link = f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}" + print(link) + return custom_response(None, link) diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index d1754aa..ecc9771 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -14,6 +14,8 @@ from django.db.models import Q from account.utils import custom_response, custom_error_response from base.constants import USER_TYPE from base.messages import SUCCESS_CODE, ERROR_CODE +from guardian.models import Guardian +from junior.models import Junior from web_admin.permission import AdminPermission from web_admin.serializers.user_management_serializer import (UserManagementListSerializer, UserManagementDetailSerializer, GuardianSerializer, @@ -70,12 +72,14 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, """ if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST) + queryset = self.queryset - if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): - queryset = queryset.filter(id=kwargs['pk']) - elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): - queryset = queryset.filter(id=kwargs['pk']) - serializer = UserManagementDetailSerializer(queryset, many=True) + queryset = queryset.filter(id=kwargs['pk']) + + serializer = UserManagementDetailSerializer( + queryset, many=True, + context={'user_type': self.request.query_params.get('user_type')} + ) return custom_response(None, data=serializer.data) def partial_update(self, request, *args, **kwargs): @@ -87,15 +91,14 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, """ if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST) - queryset = self.queryset if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): - user_obj = queryset.filter(id=kwargs['pk']).first() - serializer = GuardianSerializer(user_obj.guardian_profile.all().first(), + guardian = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True).first() + serializer = GuardianSerializer(guardian, request.data, context={'user_id': kwargs['pk']}) elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): - user_obj = queryset.filter(id=kwargs['pk']).first() - serializer = JuniorSerializer(user_obj.junior_profile.all().first(), + junior = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True).select_related('auth').first() + serializer = JuniorSerializer(junior, request.data, context={'user_id': kwargs['pk']}) serializer.is_valid(raise_exception=True) diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 3e0713c..781df80 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -125,6 +125,7 @@ SIMPLE_JWT = { # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases DATABASES = { + # default db setting 'default': { 'ENGINE': 'django.contrib.gis.db.backends.postgis', 'NAME':os.getenv('DB_NAME'), From 9a5447bca297a16d113253bc7563b16f1182f6e0 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 18 Aug 2023 14:50:25 +0530 Subject: [PATCH 15/83] article bug fixed, changed image name in get image url --- guardian/views.py | 6 ++++-- web_admin/utils.py | 2 +- web_admin/views/analytics.py | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/guardian/views.py b/guardian/views.py index 0319938..67fe6ef 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -62,7 +62,7 @@ class SignupViewset(viewsets.ModelViewSet): if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]: serializer = UserSerializer(context=request.data['user_type'], data=request.data) if serializer.is_valid(): - user = serializer.save() + serializer.save() """Generate otp""" otp = generate_otp() # expire otp after 1 day @@ -79,7 +79,9 @@ class SignupViewset(viewsets.ModelViewSet): return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST) class UpdateGuardianProfile(viewsets.ViewSet): - """Update guardian profile""" + """ + Update guardian profile + """ serializer_class = CreateGuardianSerializer permission_classes = [IsAuthenticated] diff --git a/web_admin/utils.py b/web_admin/utils.py index 9870b30..a47bbff 100644 --- a/web_admin/utils.py +++ b/web_admin/utils.py @@ -29,7 +29,7 @@ def get_image_url(data): return data['image_url'] elif 'image_url' in data and type(data['image_url']) == str and data['image_url'].startswith('data:image'): base64_image = base64.b64decode(data.get('image_url').split(',')[1]) - image_name = f"{data['title']} {data.pop('image_name')}" if 'image_name' in data else data['title'] + image_name = data.pop('image_name') if 'image_name' in data else f"{data['title']}.jpg" filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image_name}" # upload image on ali baba image_url = upload_image_to_alibaba(base64_image, filename) diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index a503356..2ff3773 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -224,8 +224,7 @@ class AnalyticsViewSet(GenericViewSet): queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( expression=Rank(), order_by=[F('total_points').desc(), 'junior__created_at'] - )).order_by('-total_points', 'junior__created_at') - + )).order_by('-total_points', 'junior__created_at')[:15] df_leaderboard = pd.DataFrame([ { 'Junior Name': f"{junior.junior.auth.first_name} {junior.junior.auth.last_name}" From 51d3b77ff76cb30540ae05e22a43ea2813f9e282 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 18 Aug 2023 15:45:57 +0530 Subject: [PATCH 16/83] password validation, deactivated user's middleware --- account/custom_middleware.py | 32 ++++++++++++++++----- account/views.py | 2 +- base/messages.py | 8 +++--- celerybeat-schedule | Bin 16384 -> 16384 bytes guardian/serializers.py | 39 +++++++++++++++++++++++++- zod_bank/settings.py | 52 +++++++++++++++++------------------ 6 files changed, 94 insertions(+), 39 deletions(-) diff --git a/account/custom_middleware.py b/account/custom_middleware.py index 7a06e43..ec2e315 100644 --- a/account/custom_middleware.py +++ b/account/custom_middleware.py @@ -7,7 +7,9 @@ from rest_framework.renderers import JSONRenderer from account.utils import custom_error_response from account.models import UserDeviceDetails from base.messages import ERROR_CODE, SUCCESS_CODE - +from base.constants import NUMBER +from junior.models import Junior +from guardian.models import Guardian # Custom middleware # when user login with # multiple device simultaneously @@ -15,6 +17,16 @@ from base.messages import ERROR_CODE, SUCCESS_CODE # multiple devices only # user can login in single # device at a time""" + +def custom_response(custom_error): + """custom response""" + response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND) + # Set content type header to "application/json" + response['Content-Type'] = 'application/json' + # Render the response as JSON + renderer = JSONRenderer() + response.content = renderer.render(response.data) + return response class CustomMiddleware(object): """Custom middleware""" def __init__(self, get_response): @@ -26,15 +38,21 @@ class CustomMiddleware(object): response = self.get_response(request) # Code to be executed after the view is called device_id = request.META.get('HTTP_DEVICE_ID') + user_type = request.META.get('HTTP_USER_TYPE') if request.user.is_authenticated: """device details""" device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last() + if user_type and str(user_type) == str(NUMBER['one']): + junior = Junior.objects.filter(auth=request.user, is_active=False).last() + if junior: + custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND) + response = custom_response(custom_error) + elif user_type and str(user_type) == str(NUMBER['two']): + guardian = Guardian.objects.filter(user=request.user, is_active=False).last() + if guardian: + custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND) + response = custom_response(custom_error) if device_id and not device_details: custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND) - response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND) - # Set content type header to "application/json" - response['Content-Type'] = 'application/json' - # Render the response as JSON - renderer = JSONRenderer() - response.content = renderer.render(response.data) + response = custom_response(custom_error) return response diff --git a/account/views.py b/account/views.py index c17f853..f91f1fe 100644 --- a/account/views.py +++ b/account/views.py @@ -287,7 +287,7 @@ class UserLogin(viewsets.ViewSet): def login(self, request): username = request.data.get('username') password = request.data.get('password') - user_type = request.data.get('user_type') + user_type = request.META.get('HTTP_USER_TYPE') device_id = request.META.get('HTTP_DEVICE_ID') user = authenticate(request, username=username, password=password) diff --git a/base/messages.py b/base/messages.py index db1fdd5..cbcd559 100644 --- a/base/messages.py +++ b/base/messages.py @@ -96,12 +96,12 @@ ERROR_CODE = { "2067": "Action not allowed. User type missing.", "2068": "No guardian associated with this junior", "2069": "Invalid user type", - "2070": "You did not find as a guardian", - "2071": "You did not find as a junior", + "2070": "You do not find as a guardian", + "2071": "You do not find as a junior", "2072": "You can not approve or reject this task because junior does not exist in the system", "2073": "You can not approve or reject this junior because junior does not exist in the system", - "2074": "You can not complete this task because you does not exist in the system" - + "2074": "You can not complete this task because you does not exist in the system", + "2075": "Your account is deactivated. Please contact with admin" } """Success message code""" SUCCESS_CODE = { diff --git a/celerybeat-schedule b/celerybeat-schedule index ce5d8ac8aa39d618efaf5cf6434831370e993b16..4a3bd313044e9169fbcb11bdbf27bec6aaff8fe8 100644 GIT binary patch delta 641 zcmZuuF>ljA6t%K#r*Q`T1QAzTs=c^kPR;;!^7sRQY#uWy& zxV7=KByJDWRFT1_xFwBvv77bIv6L9?-a1;!zmkYUU5j{O;axrIY~G1p556^Z{W)l-xX-z%=uBTPmIe9U+Tr$zA;Z1?ZnApVKlg7lL=pi=A z#J9;kCbWkj_n(5i)@MDUH-r8@6!65Lgj%}<{UsobQ1N?dwJel{A0Oci9{ZL}EyqL8 z2sJ2m5ft&unL{k+=?pz#WIG{H(#ou_?mKe}Pi+F_1t1kFTC$RzM+-OrkU%RP3Hf+=%@l0O*X*m&^egGb@I3SD11l#kILO{1q3 delta 163 zcmZo@U~Fh$oS?I@q?dnUgU4n@f&cuBY!eODCHWa3K-~aB_dxmClNA-jH%BP!VB!64 z2od83QX-pWRX=m^iWxzK*nyPJW?4OFMqXa10Zc$4p~<=WSNT{v^iuOmiZWA+8(5}_ mOjb0qoZM;fh)cA#P^_d-Jg87&a+itB=81+o7&jJf=K%nz1SJUo diff --git a/guardian/serializers.py b/guardian/serializers.py index 6e6e6af..f36bd46 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -1,6 +1,7 @@ """Serializer of Guardian""" # third party imports import logging +from django.contrib.auth import password_validation from rest_framework import serializers # Import Refresh token of jwt from rest_framework_simplejwt.tokens import RefreshToken @@ -30,7 +31,8 @@ from .utils import real_time, convert_timedelta_into_datetime, update_referral_p from notifications.constants import TASK_POINTS, TASK_REJECTED # send notification function from notifications.utils import send_notification, send_notification_to_junior - +from django.core.exceptions import ValidationError +from django.utils.translation import gettext as _ # In this serializer file # define user serializer, @@ -42,10 +44,45 @@ from notifications.utils import send_notification, send_notification_to_junior # guardian profile serializer, # approve junior serializer, # approve task serializer, +from rest_framework import serializers + +class PasswordValidator: + def __init__(self, min_length=8, max_length=None, require_uppercase=True, require_numbers=True): + self.min_length = min_length + self.max_length = max_length + self.require_uppercase = require_uppercase + self.require_numbers = require_numbers + + def __call__(self, value): + self.enforce_password_policy(value) + + def enforce_password_policy(self, password): + special_characters = "!@#$%^&*()_-+=<>?/[]{}|" + if len(password) < self.min_length: + raise serializers.ValidationError( + _("Password must be at least %(min_length)d characters long.") % {'min_length': self.min_length} + ) + + if self.max_length is not None and len(password) > self.max_length: + raise serializers.ValidationError( + _("Password must be at most %(max_length)d characters long.") % {'max_length': self.max_length} + ) + + if self.require_uppercase and not any(char.isupper() for char in password): + raise serializers.ValidationError(_("Password must contain at least one uppercase letter.")) + + if self.require_numbers and not any(char.isdigit() for char in password): + raise serializers.ValidationError(_("Password must contain at least one digit.")) + if self.require_numbers and not any(char in special_characters for char in password): + raise serializers.ValidationError(_("Password must contain at least one special character.")) + + + class UserSerializer(serializers.ModelSerializer): """User serializer""" auth_token = serializers.SerializerMethodField('get_auth_token') + password = serializers.CharField(write_only=True, validators=[PasswordValidator()]) class Meta(object): """Meta info""" diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 4e264f4..3e0713c 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -180,32 +180,32 @@ AUTH_PASSWORD_VALIDATORS = [ # database query logs settings # Allows us to check db hits # useful to optimize db query and hit -# LOGGING = { -# "version": 1, -# "filters": { -# "require_debug_true": { -# "()": "django.utils.log.RequireDebugTrue" -# } -# }, -# "handlers": { -# "console": { -# "level": "DEBUG", -# "filters": [ -# "require_debug_true" -# ], -# "class": "logging.StreamHandler" -# } -# }, -# # database logger -# "loggers": { -# "django.db.backends": { -# "level": "DEBUG", -# "handlers": [ -# "console" -# ] -# } -# } -# } +LOGGING = { + "version": 1, + "filters": { + "require_debug_true": { + "()": "django.utils.log.RequireDebugTrue" + } + }, + "handlers": { + "console": { + "level": "DEBUG", + "filters": [ + "require_debug_true" + ], + "class": "logging.StreamHandler" + } + }, + # database logger + "loggers": { + "django.db.backends": { + "level": "DEBUG", + "handlers": [ + "console" + ] + } + } +} # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ From 5b236dfc818975d684b262180103ec2f4f1e2f45 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 18 Aug 2023 16:37:42 +0530 Subject: [PATCH 17/83] remove threading from login api --- account/views.py | 4 ++-- celerybeat-schedule | Bin 16384 -> 16384 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/account/views.py b/account/views.py index f91f1fe..c6fbb12 100644 --- a/account/views.py +++ b/account/views.py @@ -321,8 +321,8 @@ class UserLogin(viewsets.ViewSet): ERROR_CODE["2069"], response_status=status.HTTP_401_UNAUTHORIZED ) - # storing device id in using thread so the time would be reduced - threading.Thread(target=user_device_details, args=(user, device_id)) + # storing device id in using celery task so the time would be reduced + user_device_details.delay(user, device_id) return custom_response(SUCCESS_CODE['3003'], serializer, response_status=status.HTTP_200_OK) else: return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED) diff --git a/celerybeat-schedule b/celerybeat-schedule index 4a3bd313044e9169fbcb11bdbf27bec6aaff8fe8..cc8251c7500206755f61cdf0ff0b24e019ea84c5 100644 GIT binary patch delta 325 zcmZo@U~Fh$oS?I@q?dnUgU4n@f&ctcObigf3#AzhAPi2viBUnDa}<`aF!35}7E=Ak z!Rrc@Vh5^Ny;)Z81LNduL#fH?25OT7jm?-c7$?s%*0*3zPDw0DEy>JH<$^N`S)94p zpK}OtGiY)cMijCpCzWRAlw{@=bET)2B$kvEaTT&nW;7A0XOG|n8XjMoSCE;Uos(L~ z(N@SARLB*<3=|CwaW3TcW^60u2`c1mE946*Y8Da}bO=1MNg z&nrnx;wlu-@MiF4h+qV2)hZOs0K$?&p`b$H+Cq^e&DuiI+Cs6CLh+zNi3l;E60kXw grLx&%r9hA&5W27_Dr5*jQP+^LLi>rw zAU43lON|!bl+a85fFetPpi7qm?b@kRr%s(ZwNrsE1={y?M@w!y1V(}u!6R{ek9Xu9 z`MLLf5Gm>VCm&52b;B4GG-NBl;ZN{~~d`UUAtWEDP>iyp@XANwb z025#WOn?b60Vco%m;e)C0!)AjFoFM!z>*%BtpdRum;({uIN&(oIA9%c9^gE{d4Tf( z=K;czs8nrCYg^Ob-}m+Yr~3Q*m7J=$soOX8{>nqlU^ z925cOz#K*iD8F&{%Y{g~_Er7)lkzivo*Z)G{Ha(lhm&z|KL2$}1hSt!ua&Tyukjg= zd7g~tH_vaMm3xMI9>B{$@;y>>34l6!2bPS*o=0%juiH2KXxb`8HH(5rY7m%7o+au z^oCjbxin{9J5_yn9a}n)-;;iT$CvI2JX7{@sHD8N9SA@8cpKe2=-wh1>nnB??D+0h zPoxK_l_~(O9QjmQMpD-|5$da_j@|xtAe|1~L(9NpavQ1F@EQ`&h||t`j9c=+oz#~ix#Xz$F8MtIQYW{9-0&fSCo!S z=<QMriKv6)=f>Q@@+JbtkoZZ}!E=;Wo6D9|$7zFADobhm#FlE7X3~x}F&cyJh zgtHdRwr6n643gt+jf!H9L@|f!$PN8ag@%V$FmJ&*6vbpFiUlo-MH-7nTe4so^+2PY mkKw{GQCw7_Sg~L=YU233FaajO1egF5U;<2l2{3`zn!sPFW|t2D From 71a3e36bf3701bdf78e7ad21aeb31bfbb87df03a Mon Sep 17 00:00:00 2001 From: Ruman Siddiqui Date: Fri, 18 Aug 2023 16:57:42 +0530 Subject: [PATCH 18/83] [ZBKBCK-346] change password and forgot password api has been optimised --- account/serializers.py | 31 ++++++++++----- account/utils.py | 22 +++++++++++ account/views.py | 89 ++++++++++++++++++++++++------------------ guardian/tasks.py | 9 ++++- 4 files changed, 103 insertions(+), 48 deletions(-) diff --git a/account/serializers.py b/account/serializers.py index 0caeb8c..e946d15 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -104,10 +104,12 @@ class ResetPasswordSerializer(serializers.Serializer): return user_opt_details return '' + class ChangePasswordSerializer(serializers.Serializer): """Update Password after verification""" - current_password = serializers.CharField(max_length=100) + current_password = serializers.CharField(max_length=100, required=True) new_password = serializers.CharField(required=True) + class Meta(object): """Meta info""" model = User @@ -118,25 +120,36 @@ class ChangePasswordSerializer(serializers.Serializer): if self.context.password not in ('', None) and user.check_password(value): return value raise serializers.ValidationError(ERROR_CODE['2015']) + def create(self, validated_data): + """ + + """ new_password = validated_data.pop('new_password') current_password = validated_data.pop('current_password') - """Check new password is different from current password""" + # Check new password is different from current password if new_password == current_password: raise serializers.ValidationError({"details": ERROR_CODE['2026']}) - user_details = User.objects.filter(email=self.context).last() - if user_details: - user_details.set_password(new_password) - user_details.save() - return {'password':new_password} - return '' + user_details = self.context + user_details.set_password(new_password) + user_details.save() + return {'password':new_password} class ForgotPasswordSerializer(serializers.Serializer): """Forget password serializer""" - email = serializers.EmailField() + email = serializers.EmailField(required=True) + def validate_email(self, value): + """ + validate email exist ot not + value: string + return none + """ + if not User.objects.get(email=value): + raise serializers.ValidationError({'details': ERROR_CODE['2004']}) + return value class AdminLoginSerializer(serializers.ModelSerializer): """admin login serializer""" diff --git a/account/utils.py b/account/utils.py index e016940..3f8c687 100644 --- a/account/utils.py +++ b/account/utils.py @@ -129,6 +129,28 @@ def send_otp_email(recipient_email, otp): ) return otp + +@shared_task() +def send_all_email(template_name, email, otp): + """ + Send all type of email by passing template name + template_name: string + email: string + otp: string + """ + from_email = settings.EMAIL_FROM_ADDRESS + recipient_list = [email] + send_templated_mail( + template_name=template_name, + from_email=from_email, + recipient_list=recipient_list, + context={ + 'verification_code': otp + } + ) + + return otp + @shared_task def user_device_details(user, device_id): """ diff --git a/account/views.py b/account/views.py index c6fbb12..b36c1f7 100644 --- a/account/views.py +++ b/account/views.py @@ -39,7 +39,7 @@ from base.messages import ERROR_CODE, SUCCESS_CODE from base.constants import NUMBER, ZOD, JUN, GRD, USER_TYPE_FLAG from guardian.tasks import generate_otp from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response, - generate_code, OTP_EXPIRY, user_device_details) + generate_code, OTP_EXPIRY, user_device_details, send_all_email) from junior.serializers import JuniorProfileSerializer from guardian.serializers import GuardianProfileSerializer @@ -193,15 +193,30 @@ class UpdateProfileImage(views.APIView): return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST) class ChangePasswordAPIView(views.APIView): - """change password""" + """ + change password" + """ serializer_class = ChangePasswordSerializer permission_classes = [IsAuthenticated] + def post(self, request): - serializer = ChangePasswordSerializer(context=request.user, data=request.data) + """ + POST request to change current login user password + """ + serializer = ChangePasswordSerializer( + context=request.user, + data=request.data + ) if serializer.is_valid(): serializer.save() - return custom_response(SUCCESS_CODE['3007'], response_status=status.HTTP_200_OK) - return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) + return custom_response( + SUCCESS_CODE['3007'], + response_status=status.HTTP_200_OK + ) + return custom_error_response( + serializer.errors, + response_status=status.HTTP_400_BAD_REQUEST + ) class ResetPasswordAPIView(views.APIView): """Reset password""" @@ -213,40 +228,40 @@ class ResetPasswordAPIView(views.APIView): return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) class ForgotPasswordAPIView(views.APIView): - """Forgot password""" + """ + Forgot password + """ + serializer_class = ForgotPasswordSerializer def post(self, request): - serializer = ForgotPasswordSerializer(data=request.data) - if serializer.is_valid(): - email = serializer.validated_data['email'] - try: - User.objects.get(email=email) - except User.DoesNotExist: - return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND) - verification_code = generate_otp() - # Send the verification code to the user's email - from_email = settings.EMAIL_FROM_ADDRESS - recipient_list = [email] - send_templated_mail( - template_name='email_reset_verification.email', - from_email=from_email, - recipient_list=recipient_list, - context={ - 'verification_code': verification_code - } - ) - expiry = OTP_EXPIRY - user_data, created = UserEmailOtp.objects.get_or_create(email=email) - if created: - user_data.expired_at = expiry - user_data.save() - if user_data: - user_data.otp = verification_code - user_data.expired_at = expiry - user_data.save() - return custom_response(SUCCESS_CODE['3015'], - response_status=status.HTTP_200_OK) - return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) + """ + Post method to validate serializer + """ + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + email = serializer.validated_data['email'] + # generate otp + verification_code = generate_otp() + # Send the verification code to the user's email + send_all_email.delay( + 'email_reset_verification.email', email, verification_code + ) + expiry = OTP_EXPIRY + user_data, created = UserEmailOtp.objects.get_or_create( + email=email + ) + if created: + user_data.expired_at = expiry + user_data.save() + if user_data: + user_data.otp = verification_code + user_data.expired_at = expiry + user_data.save() + return custom_response( + SUCCESS_CODE['3015'], + response_status=status.HTTP_200_OK + ) + class SendPhoneOtp(viewsets.ModelViewSet): """Send otp on phone""" diff --git a/guardian/tasks.py b/guardian/tasks.py index 9cf39b3..f40d232 100644 --- a/guardian/tasks.py +++ b/guardian/tasks.py @@ -1,7 +1,12 @@ """task files""" -"""Django import""" + +# Django import import secrets + + def generate_otp(): - """generate random otp""" + """ + generate random otp + """ digits = "0123456789" return "".join(secrets.choice(digits) for _ in range(6)) From b4026a4f118066fb36ab451687734dded8a750d1 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 18 Aug 2023 18:12:14 +0530 Subject: [PATCH 19/83] middleware --- account/custom_middleware.py | 5 +++-- account/utils.py | 4 +++- account/views.py | 6 +++++- guardian/views.py | 9 +++++++-- junior/views.py | 2 +- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/account/custom_middleware.py b/account/custom_middleware.py index ec2e315..c2125cd 100644 --- a/account/custom_middleware.py +++ b/account/custom_middleware.py @@ -39,8 +39,9 @@ class CustomMiddleware(object): # Code to be executed after the view is called device_id = request.META.get('HTTP_DEVICE_ID') user_type = request.META.get('HTTP_USER_TYPE') + api_endpoint = request.path if request.user.is_authenticated: - """device details""" + # device details device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last() if user_type and str(user_type) == str(NUMBER['one']): junior = Junior.objects.filter(auth=request.user, is_active=False).last() @@ -52,7 +53,7 @@ class CustomMiddleware(object): if guardian: custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND) response = custom_response(custom_error) - if device_id and not device_details: + if device_id and not device_details and api_endpoint != '/api/v1/user/login/': custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND) response = custom_response(custom_error) return response diff --git a/account/utils.py b/account/utils.py index e016940..ad910ba 100644 --- a/account/utils.py +++ b/account/utils.py @@ -137,10 +137,12 @@ def user_device_details(user, device_id): device_id: string return """ - device_details, created = UserDeviceDetails.objects.get_or_create(user=user) + device_details, created = UserDeviceDetails.objects.get_or_create(user__id=user) if device_details: device_details.device_id = device_id device_details.save() + return True + return False def send_support_email(name, sender, subject, message): diff --git a/account/views.py b/account/views.py index c6fbb12..bf3a7d5 100644 --- a/account/views.py +++ b/account/views.py @@ -322,7 +322,11 @@ class UserLogin(viewsets.ViewSet): response_status=status.HTTP_401_UNAUTHORIZED ) # storing device id in using celery task so the time would be reduced - user_device_details.delay(user, device_id) + # user_device_details.delay(user.id, device_id) + device_details, created = UserDeviceDetails.objects.get_or_create(user=user) + if device_details: + device_details.device_id = device_id + device_details.save() return custom_response(SUCCESS_CODE['3003'], serializer, response_status=status.HTTP_200_OK) else: return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED) diff --git a/guardian/views.py b/guardian/views.py index d06f5b9..ad6a134 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -260,7 +260,7 @@ class ApproveJuniorAPIView(viewsets.ViewSet): guardian = Guardian.objects.filter(user__email=self.request.user).last() # fetch junior query junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last() - if junior_queryset and junior_queryset.is_deleted: + if junior_queryset and (junior_queryset.is_deleted or not junior_queryset.is_active): return custom_error_response(ERROR_CODE['2073'], response_status=status.HTTP_400_BAD_REQUEST) # action 1 is use for approve and 2 for reject if request.data['action'] == '1': @@ -295,7 +295,12 @@ class ApproveTaskAPIView(viewsets.ViewSet): task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'), guardian=guardian, junior=self.request.data.get('junior_id')).last() - if task_queryset and task_queryset.junior.is_deleted: + print("task_queryset.junior.is_deleted===>",task_queryset.junior.is_deleted) + print("task_queryset.junior.is_active===>",task_queryset.junior.is_active) + print("task_queryset.junior.is_deleted===>", type(task_queryset.junior.is_deleted)) + print("task_queryset.junior.is_active===>", type(task_queryset.junior.is_active)) + print("99999===>",(task_queryset.junior.is_deleted or not task_queryset.junior.is_active)) + if task_queryset and (task_queryset.junior.is_deleted or not task_queryset.junior.is_active): return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST) # use ApproveJuniorSerializer serializer serializer = ApproveTaskSerializer(context={"guardian_code": guardian.guardian_code, diff --git a/junior/views.py b/junior/views.py index 7a83b0a..1fdf2be 100644 --- a/junior/views.py +++ b/junior/views.py @@ -339,7 +339,7 @@ class CompleteJuniorTaskAPIView(views.APIView): task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user ).select_related('guardian', 'junior').last() if task_queryset: - if task_queryset.junior.is_deleted: + if task_queryset.junior.is_deleted or not task_queryset.junior.is_active: return custom_error_response(ERROR_CODE['2074'], response_status=status.HTTP_400_BAD_REQUEST) # use CompleteTaskSerializer serializer if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]: From cdf1a7b74ea51beb70bd7647e3402fcca01aa267 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 18 Aug 2023 18:29:46 +0530 Subject: [PATCH 20/83] add comma --- account/views.py | 4 ++-- guardian/serializers.py | 2 +- guardian/views.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/account/views.py b/account/views.py index bf3a7d5..f5cb15f 100644 --- a/account/views.py +++ b/account/views.py @@ -235,7 +235,7 @@ class ForgotPasswordAPIView(views.APIView): 'verification_code': verification_code } ) - expiry = OTP_EXPIRY + expiry = timezone.now() + timezone.timedelta(days=1) user_data, created = UserEmailOtp.objects.get_or_create(email=email) if created: user_data.expired_at = expiry @@ -454,7 +454,7 @@ class ReSendEmailOtp(viewsets.ModelViewSet): def create(self, request, *args, **kwargs): otp = generate_otp() if User.objects.filter(email=request.data['email']): - expiry = OTP_EXPIRY + expiry = timezone.now() + timezone.timedelta(days=1) email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email']) if created: email_data.expired_at = expiry diff --git a/guardian/serializers.py b/guardian/serializers.py index f36bd46..8db860b 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -251,7 +251,7 @@ class GuardianDetailSerializer(serializers.ModelSerializer): """Meta info""" model = Guardian fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', - 'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image', 'is_deleted' + 'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image', 'is_deleted', 'updated_at'] class TaskDetailsSerializer(serializers.ModelSerializer): """Task detail serializer""" diff --git a/guardian/views.py b/guardian/views.py index ad6a134..e7a4b87 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -66,7 +66,7 @@ class SignupViewset(viewsets.ModelViewSet): """Generate otp""" otp = generate_otp() # expire otp after 1 day - expiry = OTP_EXPIRY + expiry = timezone.now() + timezone.timedelta(days=1) # create user email otp object UserEmailOtp.objects.create(email=request.data['email'], otp=otp, user_type=str(request.data['user_type']), expired_at=expiry) From 3921f76f22642911d2ec7fa86c9442a5daef7179 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 18 Aug 2023 18:31:27 +0530 Subject: [PATCH 21/83] remove print statement --- guardian/views.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/guardian/views.py b/guardian/views.py index e7a4b87..9b9a0aa 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -295,11 +295,6 @@ class ApproveTaskAPIView(viewsets.ViewSet): task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'), guardian=guardian, junior=self.request.data.get('junior_id')).last() - print("task_queryset.junior.is_deleted===>",task_queryset.junior.is_deleted) - print("task_queryset.junior.is_active===>",task_queryset.junior.is_active) - print("task_queryset.junior.is_deleted===>", type(task_queryset.junior.is_deleted)) - print("task_queryset.junior.is_active===>", type(task_queryset.junior.is_active)) - print("99999===>",(task_queryset.junior.is_deleted or not task_queryset.junior.is_active)) if task_queryset and (task_queryset.junior.is_deleted or not task_queryset.junior.is_active): return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST) # use ApproveJuniorSerializer serializer From 92e5104e3f8d8feb2d1dcad88cb8e82d696fe96b Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 18 Aug 2023 18:37:01 +0530 Subject: [PATCH 22/83] expiry date of otp --- account/views.py | 4 ++-- guardian/views.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/account/views.py b/account/views.py index c17f853..2d8149d 100644 --- a/account/views.py +++ b/account/views.py @@ -235,7 +235,7 @@ class ForgotPasswordAPIView(views.APIView): 'verification_code': verification_code } ) - expiry = OTP_EXPIRY + expiry = timezone.now() + timezone.timedelta(days=1) user_data, created = UserEmailOtp.objects.get_or_create(email=email) if created: user_data.expired_at = expiry @@ -450,7 +450,7 @@ class ReSendEmailOtp(viewsets.ModelViewSet): def create(self, request, *args, **kwargs): otp = generate_otp() if User.objects.filter(email=request.data['email']): - expiry = OTP_EXPIRY + expiry = timezone.now() + timezone.timedelta(days=1) email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email']) if created: email_data.expired_at = expiry diff --git a/guardian/views.py b/guardian/views.py index 3948e6f..88f567c 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -66,7 +66,7 @@ class SignupViewset(viewsets.ModelViewSet): """Generate otp""" otp = generate_otp() # expire otp after 1 day - expiry = OTP_EXPIRY + expiry = timezone.now() + timezone.timedelta(days=1) # create user email otp object UserEmailOtp.objects.create(email=request.data['email'], otp=otp, user_type=str(request.data['user_type']), expired_at=expiry) From 21e006ae2aff1bde295fc2beb5a12e667da3aa20 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 18 Aug 2023 18:34:48 +0530 Subject: [PATCH 23/83] added description for api, changes in upload image and file to alibaba method --- guardian/utils.py | 47 ++++++++++++++++++++++++-------- notifications/views.py | 8 +++--- web_admin/utils.py | 4 +-- web_admin/views/analytics.py | 17 ++---------- web_admin/views/article.py | 53 ++++++++++++++++++++++++++---------- web_admin/views/auth.py | 3 ++ 6 files changed, 85 insertions(+), 47 deletions(-) diff --git a/guardian/utils.py b/guardian/utils.py index d5081ac..14bd36a 100644 --- a/guardian/utils.py +++ b/guardian/utils.py @@ -43,18 +43,41 @@ def upload_image_to_alibaba(image, filename): # Save the image object to a temporary file with tempfile.NamedTemporaryFile(delete=False) as temp_file: """write image in temporary file""" - if type(image) == bytes: - temp_file.write(image) - else: - temp_file.write(image.read()) - """auth of bucket""" - auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET) - """fetch bucket details""" - bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME) - # Upload the temporary file to Alibaba OSS - bucket.put_object_from_file(filename, temp_file.name) - """create perfect url for image""" - new_filename = filename.replace(' ', '%20') + temp_file.write(image.read()) + return upload_file_to_alibaba(temp_file, filename) + + +def upload_base64_image_to_alibaba(image, filename): + """ + upload image on oss alibaba bucket + """ + # Save the image object to a temporary file + with tempfile.NamedTemporaryFile(delete=False) as temp_file: + # write image in temporary file + temp_file.write(image) + return upload_file_to_alibaba(temp_file, filename) + + +def upload_excel_file_to_alibaba(response, filename): + """ + upload excel file on oss alibaba bucket + """ + # Save the image object to a temporary file + with tempfile.NamedTemporaryFile(delete=False) as temp_file: + # write image in temporary file + temp_file.write(response.content) + return upload_file_to_alibaba(temp_file, filename) + + +def upload_file_to_alibaba(temp_file, filename): + """auth of bucket""" + auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET) + """fetch bucket details""" + bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME) + # Upload the temporary file to Alibaba OSS + bucket.put_object_from_file(filename, temp_file.name) + """create perfect url for image""" + new_filename = filename.replace(' ', '%20') return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}" diff --git a/notifications/views.py b/notifications/views.py index dc6e891..5adb536 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -56,10 +56,10 @@ class NotificationViewSet(viewsets.GenericViewSet): to send test notification :return: """ - send_notification_to_guardian(TEST_NOTIFICATION, None, request.auth.payload['user_id'], - {'task_id': None}) - send_notification_to_junior(TEST_NOTIFICATION, request.auth.payload['user_id'], None, - {'task_id': None}) + send_notification_to_guardian.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], + {'task_id': None}) + send_notification_to_junior.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], + {'task_id': None}) return custom_response(SUCCESS_CODE["3000"]) @action(methods=['get'], detail=False, url_path='list', url_name='list', diff --git a/web_admin/utils.py b/web_admin/utils.py index a47bbff..4170cf9 100644 --- a/web_admin/utils.py +++ b/web_admin/utils.py @@ -4,7 +4,7 @@ web_utils file import base64 from base.constants import ARTICLE_CARD_IMAGE_FOLDER -from guardian.utils import upload_image_to_alibaba +from guardian.utils import upload_image_to_alibaba, upload_base64_image_to_alibaba def pop_id(data): @@ -32,7 +32,7 @@ def get_image_url(data): image_name = data.pop('image_name') if 'image_name' in data else f"{data['title']}.jpg" filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image_name}" # upload image on ali baba - image_url = upload_image_to_alibaba(base64_image, filename) + image_url = upload_base64_image_to_alibaba(base64_image, filename) return image_url elif 'image' in data and data['image'] is not None: image = data.pop('image') diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 2ff3773..ddc4e0d 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -28,6 +28,7 @@ from django.http import HttpResponse from account.utils import custom_response from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT, TASK_STATUS from guardian.models import JuniorTask +from guardian.utils import upload_excel_file_to_alibaba from junior.models import JuniorPoints from web_admin.pagination import CustomPageNumberPagination from web_admin.permission import AdminPermission @@ -251,18 +252,6 @@ class AnalyticsViewSet(GenericViewSet): buffer.close() filename = f"{'analytics'}/{'ZOD_Bank_Analytics.xlsx'}" - with tempfile.NamedTemporaryFile(delete=False) as temp_file: - """write image in temporary file""" - temp_file.write(response.content) - """auth of bucket""" - auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET) - """fetch bucket details""" - bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME) - # Upload the temporary file to Alibaba OSS - bucket.put_object_from_file(filename, temp_file.name) - """create perfect url for image""" - new_filename = filename.replace(' ', '%20') - link = f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}" - print(link) - return custom_response(None, link) + file_link = upload_excel_file_to_alibaba(response, filename) + return custom_response(None, file_link) diff --git a/web_admin/views/article.py b/web_admin/views/article.py index 13c41c2..49f76fa 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -44,9 +44,20 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel def create(self, request, *args, **kwargs): """ article create api method - :param request: - :param args: - :param kwargs: + :param request: { "title": "string", "description": "string", + "article_cards": [ + { "title": "string", + "description": "string", + "image_name": "string", + "image_url": "string" + } ], + "article_survey": [ + { "question": "string", + "options": [ + { "option": "string", + "is_answer": true } + ] } + ] } :return: success message """ serializer = self.serializer_class(data=request.data) @@ -57,9 +68,24 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel def update(self, request, *args, **kwargs): """ article update api method - :param request: - :param args: - :param kwargs: + :param request: article_id, + { "title": "string", "description": "string", + "article_cards": [ + { "id": 0, + "title": "string", + "description": "string", + "image_name": "string", + "image_url": "string" + } ], + "article_survey": [ + { "id": 0, + "question": "string", + "options": [ + { "id": 0, + "option": "string", + "is_answer": true + } ] + } ] } :return: success message """ article = self.get_object() @@ -72,8 +98,6 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel """ article list api method :param request: - :param args: - :param kwargs: :return: list of article """ queryset = self.get_queryset() @@ -86,9 +110,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel def retrieve(self, request, *args, **kwargs): """ article detail api method - :param request: - :param args: - :param kwargs: + :param request: article_id :return: article detail data """ queryset = self.get_object() @@ -98,9 +120,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel def destroy(self, request, *args, **kwargs): """ article delete (soft delete) api method - :param request: - :param args: - :param kwargs: + :param request: article_id :return: success message """ article = self.get_object() @@ -177,7 +197,10 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m def create(self, request, *args, **kwargs): """ api method to upload default article card images - :param request: + :param request: { + "image_name": "string", + "image": "image_file" + } :return: success message """ serializer = self.serializer_class(data=request.data) diff --git a/web_admin/views/auth.py b/web_admin/views/auth.py index fae973e..73f19e5 100644 --- a/web_admin/views/auth.py +++ b/web_admin/views/auth.py @@ -27,6 +27,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet): def admin_otp(self, request): """ api method to send otp + :param request: {"email": "string"} :return: success message """ serializer = self.serializer_class(data=request.data) @@ -40,6 +41,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet): def admin_verify_otp(self, request): """ api method to verify otp + :param request: {"email": "string", "otp": "otp"} :return: success message """ serializer = self.serializer_class(data=request.data) @@ -52,6 +54,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet): def admin_create_password(self, request): """ api method to create new password + :param request: {"email": "string", "new_password": "string", "confirm_password": "string"} :return: success message """ serializer = self.serializer_class(data=request.data) From 48b455e38a66a62334e952ef709dc98c41e0a33b Mon Sep 17 00:00:00 2001 From: jain Date: Sun, 20 Aug 2023 14:12:17 +0530 Subject: [PATCH 24/83] login issue --- account/views.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/account/views.py b/account/views.py index f5cb15f..3e09080 100644 --- a/account/views.py +++ b/account/views.py @@ -295,22 +295,24 @@ class UserLogin(viewsets.ViewSet): if user is not None: login(request, user) if str(user_type) == USER_TYPE_FLAG["TWO"]: - guardian_data = Guardian.objects.filter(user__username=username, is_verified=True).last() + guardian_data = Guardian.objects.filter(user__username=username).last() if guardian_data: - serializer = GuardianSerializer( - guardian_data, context={'user_type': user_type} - ).data + if guardian_data.is_verified: + serializer = GuardianSerializer( + guardian_data, context={'user_type': user_type} + ).data else: return custom_error_response( ERROR_CODE["2070"], response_status=status.HTTP_401_UNAUTHORIZED ) elif str(user_type) == USER_TYPE_FLAG["FIRST"]: - junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last() + junior_data = Junior.objects.filter(auth__username=username).last() if junior_data: - serializer = JuniorSerializer( - junior_data, context={'user_type': user_type} - ).data + if junior_data.is_verified: + serializer = JuniorSerializer( + junior_data, context={'user_type': user_type} + ).data else: return custom_error_response( ERROR_CODE["2071"], From 1adf0c2f703f5f31f2566fe23267bb70b3b6b6e9 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 21 Aug 2023 12:24:01 +0530 Subject: [PATCH 25/83] resolve bugs --- base/constants.py | 2 +- base/messages.py | 6 +++++- guardian/views.py | 6 +++++- junior/views.py | 19 +++++++++++++++---- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/base/constants.py b/base/constants.py index 9688c0f..d376971 100644 --- a/base/constants.py +++ b/base/constants.py @@ -27,7 +27,7 @@ NUMBER = { 'ninety_nine': 99, 'hundred': 100, 'thirty_six_hundred': 3600 } - +none = "none" # Super Admin string constant for 'role' SUPER_ADMIN = "Super Admin" diff --git a/base/messages.py b/base/messages.py index cbcd559..d87b48e 100644 --- a/base/messages.py +++ b/base/messages.py @@ -101,7 +101,11 @@ ERROR_CODE = { "2072": "You can not approve or reject this task because junior does not exist in the system", "2073": "You can not approve or reject this junior because junior does not exist in the system", "2074": "You can not complete this task because you does not exist in the system", - "2075": "Your account is deactivated. Please contact with admin" + "2075": "Your account is deactivated. Please contact with admin", + "2076": "This junior already associate with you", + "2077": "You can not add guardian", + "2078": "This junior is not associate with you", + } """Success message code""" SUCCESS_CODE = { diff --git a/guardian/views.py b/guardian/views.py index 9b9a0aa..37b39cc 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -164,6 +164,10 @@ class CreateTaskAPIView(viewsets.ModelViewSet): try: image = request.data['default_image'] junior = request.data['junior'] + junior_id = Junior.objects.filter(id=junior).last() + guardian_data = Guardian.objects.filter(user=request.user).last() + if guardian_data.guardian_code in junior_id.guardian_code: + return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST) allowed_extensions = ['.jpg', '.jpeg', '.png'] if not any(extension in str(image) for extension in allowed_extensions): return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST) @@ -185,7 +189,7 @@ class CreateTaskAPIView(viewsets.ModelViewSet): if serializer.is_valid(): # save serializer task = serializer.save() - junior_id = Junior.objects.filter(id=junior).last() + send_notification_to_junior.delay(TASK_ASSIGNED, request.auth.payload['user_id'], junior_id.auth.id, {'task_id': task.id}) return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK) diff --git a/junior/views.py b/junior/views.py index 1fdf2be..8df8a09 100644 --- a/junior/views.py +++ b/junior/views.py @@ -40,7 +40,7 @@ from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, Ad from guardian.models import Guardian, JuniorTask from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer from base.messages import ERROR_CODE, SUCCESS_CODE -from base.constants import NUMBER, ARTICLE_STATUS +from base.constants import NUMBER, ARTICLE_STATUS, none from account.utils import custom_response, custom_error_response from guardian.utils import upload_image_to_alibaba from .utils import update_positions_based_on_points @@ -171,7 +171,11 @@ class AddJuniorAPIView(viewsets.ModelViewSet): image_url = upload_image_to_alibaba(profile_image, filename) info_data.update({"image": image_url}) if user := User.objects.filter(username=request.data['email']).first(): - self.associate_guardian(user) + data = self.associate_guardian(user) + if data == none: + return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST) + elif not data: + return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST) return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK) # use AddJuniorSerializer serializer serializer = AddJuniorSerializer(data=request.data, context=info_data) @@ -184,9 +188,16 @@ class AddJuniorAPIView(viewsets.ModelViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) def associate_guardian(self, user): - junior = Junior.objects.filter(auth=user).first() + junior = Junior.objects.filter(auth__email=self.request.data['email']).first() guardian = Guardian.objects.filter(user=self.request.user).first() - junior.guardian_code = [guardian.guardian_code] + if not junior: + return none + if guardian.guardian_code in junior.guardian_code: + return False + if type(junior.guardian_code) is list: + junior.guardian_code.append(guardian.guardian_code) + else: + junior.guardian_code = [guardian.guardian_code] junior.guardian_code_status = str(NUMBER['two']) junior.save() JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior, From d202774df15608cd12f94225571aa88f4810ebb3 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 21 Aug 2023 12:39:05 +0530 Subject: [PATCH 26/83] resolve bugs --- guardian/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guardian/views.py b/guardian/views.py index 37b39cc..9656b9a 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -244,7 +244,7 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): # Update the position field for each JuniorPoints object for index, junior in enumerate(junior_total_points): junior.position = index + 1 - send_notification_to_junior.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {}) + # send_notification_to_junior.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {}) junior.save() serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) From 606c1fa6e62185af48e81fa5413ea1ce2a9381ce Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 21 Aug 2023 14:28:06 +0530 Subject: [PATCH 27/83] minor changes in excel import api method --- web_admin/utils.py | 21 +++++++++- web_admin/views/analytics.py | 74 ++++++++++++------------------------ 2 files changed, 44 insertions(+), 51 deletions(-) diff --git a/web_admin/utils.py b/web_admin/utils.py index 4170cf9..3dbb3b2 100644 --- a/web_admin/utils.py +++ b/web_admin/utils.py @@ -2,8 +2,9 @@ web_utils file """ import base64 +import datetime -from base.constants import ARTICLE_CARD_IMAGE_FOLDER +from base.constants import ARTICLE_CARD_IMAGE_FOLDER, DATE_FORMAT from guardian.utils import upload_image_to_alibaba, upload_base64_image_to_alibaba @@ -40,3 +41,21 @@ def get_image_url(data): # upload image on ali baba image_url = upload_image_to_alibaba(image, filename) return image_url + + +def get_dates(start_date, end_date): + """ + to get start and end date + :param start_date: format (yyyy-mm-dd) + :param end_date: format (yyyy-mm-dd) + :return: start and end date + """ + + if start_date and end_date: + start_date = datetime.datetime.strptime(start_date, DATE_FORMAT).date() + end_date = datetime.datetime.strptime(end_date, DATE_FORMAT).date() + else: + end_date = datetime.date.today() + start_date = end_date - datetime.timedelta(days=6) + + return start_date, end_date diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index ddc4e0d..c0747d7 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -6,9 +6,6 @@ import datetime import io import pandas as pd import xlsxwriter -import tempfile -import oss2 -from django.conf import settings # third party imports from rest_framework.viewsets import GenericViewSet @@ -33,7 +30,7 @@ from junior.models import JuniorPoints from web_admin.pagination import CustomPageNumberPagination from web_admin.permission import AdminPermission from web_admin.serializers.analytics_serializer import LeaderboardSerializer, UserCSVReportSerializer -from web_admin.serializers.user_management_serializer import UserManagementListSerializer +from web_admin.utils import get_dates USER = get_user_model() @@ -56,7 +53,7 @@ class AnalyticsViewSet(GenericViewSet): ).prefetch_related('guardian_profile', 'junior_profile' ).exclude(junior_profile__isnull=True, - guardian_profile__isnull=True).order_by('date_joined') + guardian_profile__isnull=True).order_by('-date_joined') return user_qs @action(methods=['get'], url_name='users-count', url_path='users-count', detail=False) @@ -67,13 +64,8 @@ class AnalyticsViewSet(GenericViewSet): :param request: end_date: date format (yyyy-mm-dd) :return: """ - - end_date = datetime.date.today() - start_date = end_date - datetime.timedelta(days=6) - - if request.query_params.get('start_date') and request.query_params.get('end_date'): - start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) - end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) + start_date, end_date = get_dates(request.query_params.get('start_date'), + request.query_params.get('end_date')) user_qs = self.get_queryset() queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1)))) @@ -92,12 +84,8 @@ class AnalyticsViewSet(GenericViewSet): :param request: end_date: date format (yyyy-mm-dd) :return: """ - end_date = datetime.date.today() - start_date = end_date - datetime.timedelta(days=6) - - if request.query_params.get('start_date') and request.query_params.get('end_date'): - start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) - end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) + start_date, end_date = get_dates(request.query_params.get('start_date'), + request.query_params.get('end_date')) user_qs = self.get_queryset() signup_data = user_qs.filter(date_joined__range=[start_date, (end_date + datetime.timedelta(days=1))] @@ -114,12 +102,8 @@ class AnalyticsViewSet(GenericViewSet): :param request: end_date: date format (yyyy-mm-dd) :return: """ - end_date = datetime.date.today() - start_date = end_date - datetime.timedelta(days=6) - - if request.query_params.get('start_date') and request.query_params.get('end_date'): - start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) - end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) + start_date, end_date = get_dates(request.query_params.get('start_date'), + request.query_params.get('end_date')) assign_tasks = JuniorTask.objects.filter( created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] @@ -163,12 +147,8 @@ class AnalyticsViewSet(GenericViewSet): response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response['Content-Disposition'] = 'attachment; filename="ZOD_Bank_Analytics.xlsx"' - end_date = datetime.date.today() - start_date = end_date - datetime.timedelta(days=6) - - if request.query_params.get('start_date') and request.query_params.get('end_date'): - start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) - end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) + start_date, end_date = get_dates(request.query_params.get('start_date'), + request.query_params.get('end_date')) # Use BytesIO for binary data buffer = io.BytesIO() @@ -177,7 +157,7 @@ class AnalyticsViewSet(GenericViewSet): workbook = xlsxwriter.Workbook(buffer) # Add sheets - sheets = ['Users', 'Assign Tasks', 'Juniors leaderboard'] + sheets = ['Users', 'Assign Tasks', 'Juniors Leaderboard'] for sheet_name in sheets: worksheet = workbook.add_worksheet(name=sheet_name) @@ -195,12 +175,7 @@ class AnalyticsViewSet(GenericViewSet): 'Date Joined': user['date_joined']} for user in serializer.data ]) - for idx, col in enumerate(df_users.columns): - # Write header - worksheet.write(0, idx, col) - for row_num, row in enumerate(df_users.values, start=1): - for col_num, value in enumerate(row): - worksheet.write(row_num, col_num, value) + write_excel_worksheet(worksheet, df_users) # sheet 2 for Assign Task elif sheet_name == 'Assign Tasks': @@ -213,15 +188,10 @@ class AnalyticsViewSet(GenericViewSet): for task in assign_tasks ]) - for idx, col in enumerate(df_tasks.columns): - # Write header - worksheet.write(0, idx, col) - for row_num, row in enumerate(df_tasks.values, start=1): - for col_num, value in enumerate(row): - worksheet.write(row_num, col_num, value) + write_excel_worksheet(worksheet, df_tasks) # sheet 3 for Juniors Leaderboard and rank - elif sheet_name == 'Juniors leaderboard': + elif sheet_name == 'Juniors Leaderboard': queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( expression=Rank(), order_by=[F('total_points').desc(), 'junior__created_at'] @@ -236,12 +206,7 @@ class AnalyticsViewSet(GenericViewSet): for junior in queryset ]) - for idx, col in enumerate(df_leaderboard.columns): - # Write header - worksheet.write(0, idx, col) - for row_num, row in enumerate(df_leaderboard.values, start=1): - for col_num, value in enumerate(row): - worksheet.write(row_num, col_num, value) + write_excel_worksheet(worksheet, df_leaderboard) # Close the workbook to save the content workbook.close() @@ -255,3 +220,12 @@ class AnalyticsViewSet(GenericViewSet): file_link = upload_excel_file_to_alibaba(response, filename) return custom_response(None, file_link) + +def write_excel_worksheet(worksheet, dataframe): + for idx, col in enumerate(dataframe.columns): + # Write header + worksheet.write(0, idx, col) + for row_num, row in enumerate(dataframe.values, start=1): + for col_num, value in enumerate(row): + worksheet.write(row_num, col_num, value) + return worksheet From 1c3d4731c14c5765ddffba0515051d8389beed1e Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 21 Aug 2023 15:11:43 +0530 Subject: [PATCH 28/83] swagger fixes --- account/views.py | 8 +++++++- guardian/views.py | 34 ++++++++++++++++++++++++++-------- junior/serializers.py | 10 +++++++++- junior/views.py | 10 +++++++--- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/account/views.py b/account/views.py index 3e09080..a52dff7 100644 --- a/account/views.py +++ b/account/views.py @@ -518,7 +518,13 @@ class DefaultImageAPIViewSet(viewsets.ModelViewSet): class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet): - """ Delete user API view set """ + """ Delete user API view set + {"user_type":1, + "signup_method":"1", + "password":"Demo@123"} + signup_method 1 for manual + 2 for google login + 3 for apple login""" @action(detail=False, methods=['POST'], url_path='user-account',serializer_class=UserDeleteSerializer, permission_classes=[IsAuthenticated]) diff --git a/guardian/views.py b/guardian/views.py index 9656b9a..6f4cb16 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -32,7 +32,7 @@ from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializ GuardianDetailListSerializer) from .models import Guardian, JuniorTask from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship -from account.models import UserEmailOtp, UserNotification +from account.models import UserEmailOtp, UserNotification, UserDeviceDetails from .tasks import generate_otp from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email from base.messages import ERROR_CODE, SUCCESS_CODE @@ -59,6 +59,7 @@ class SignupViewset(viewsets.ModelViewSet): serializer_class = UserSerializer def create(self, request, *args, **kwargs): """Create user profile""" + device_id = request.META.get('HTTP_DEVICE_ID') if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]: serializer = UserSerializer(context=request.data['user_type'], data=request.data) if serializer.is_valid(): @@ -72,16 +73,21 @@ class SignupViewset(viewsets.ModelViewSet): user_type=str(request.data['user_type']), expired_at=expiry) """Send email to the register user""" send_otp_email(request.data['email'], otp) + device_details, created = UserDeviceDetails.objects.get_or_create(user=user) + if device_details: + device_details.device_id = device_id + device_details.save() return custom_response(SUCCESS_CODE['3001'], response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) else: return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST) -class UpdateGuardianProfile(viewsets.ViewSet): +class UpdateGuardianProfile(viewsets.ModelViewSet): """Update guardian profile""" serializer_class = CreateGuardianSerializer permission_classes = [IsAuthenticated] + http_method_names = ('post',) def create(self, request, *args, **kwargs): """Create guardian profile""" @@ -161,6 +167,9 @@ class CreateTaskAPIView(viewsets.ModelViewSet): http_method_names = ('post', ) def create(self, request, *args, **kwargs): + """ + image should be in form data + """ try: image = request.data['default_image'] junior = request.data['junior'] @@ -252,14 +261,17 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) -class ApproveJuniorAPIView(viewsets.ViewSet): +class ApproveJuniorAPIView(viewsets.ModelViewSet): """approve junior by guardian""" serializer_class = ApproveJuniorSerializer permission_classes = [IsAuthenticated] - + http_method_names = ('post',) def create(self, request, *args, **kwargs): - """ junior list""" + """ Use below param + {"junior_id":"75", + "action":"1"} + """ try: guardian = Guardian.objects.filter(user__email=self.request.user).last() # fetch junior query @@ -285,13 +297,19 @@ class ApproveJuniorAPIView(viewsets.ViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) -class ApproveTaskAPIView(viewsets.ViewSet): +class ApproveTaskAPIView(viewsets.ModelViewSet): """approve junior by guardian""" serializer_class = ApproveTaskSerializer permission_classes = [IsAuthenticated] - + http_method_names = ('post',) def create(self, request, *args, **kwargs): - """ junior list""" + """ Params + {"junior_id":"82", + "task_id":"43", + "action":"1"} + action 1 for approve + 2 for reject + """ # action 1 is use for approve and 2 for reject try: guardian = Guardian.objects.filter(user__email=self.request.user).last() diff --git a/junior/serializers.py b/junior/serializers.py index b4aa54f..f79cd46 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -24,7 +24,7 @@ from guardian.utils import real_time, update_referral_points, convert_timedelta_ from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION, TASK_SUBMITTED) - +from web_admin.models import ArticleCard class ListCharField(serializers.ListField): """Serializer for Array field""" @@ -518,3 +518,11 @@ class FAQSerializer(serializers.ModelSerializer): model = FAQ fields = ('id', 'question', 'description') +class CreateArticleCardSerializer(serializers.ModelSerializer): + # Article card Serializer + + class Meta(object): + # meta info + model = ArticleCard + fields = ('id', 'article') + diff --git a/junior/views.py b/junior/views.py index 8df8a09..5eb15bd 100644 --- a/junior/views.py +++ b/junior/views.py @@ -36,7 +36,7 @@ from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, Jun from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer, RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer, - RemoveGuardianCodeSerializer, FAQSerializer) + RemoveGuardianCodeSerializer, FAQSerializer, CreateArticleCardSerializer) from guardian.models import Guardian, JuniorTask from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer from base.messages import ERROR_CODE, SUCCESS_CODE @@ -65,10 +65,11 @@ from web_admin.serializers.article_serializer import (ArticleSerializer, Article # Start task # by junior API # Create your views here. -class UpdateJuniorProfile(viewsets.ViewSet): +class UpdateJuniorProfile(viewsets.ModelViewSet): """Update junior profile""" serializer_class = CreateJuniorSerializer permission_classes = [IsAuthenticated] + http_method_names = ('post',) def create(self, request, *args, **kwargs): """Use CreateJuniorSerializer""" @@ -622,11 +623,14 @@ class ReadArticleCardAPIView(views.APIView): class CreateArticleCardAPIView(viewsets.ModelViewSet): """Start article""" + serializer_class = CreateArticleCardSerializer permission_classes = [IsAuthenticated] http_method_names = ('post',) def create(self, request, *args, **kwargs): - """ junior list""" + """ create article card + Params + {"article_id":1}""" try: junior_instance = Junior.objects.filter(auth=self.request.user).last() article_id = request.data.get('article_id') From a7bce329930a5704552acd4ebd1e51f2c49c9c05 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 21 Aug 2023 15:59:20 +0530 Subject: [PATCH 29/83] signup --- guardian/views.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/guardian/views.py b/guardian/views.py index 6f4cb16..35fbb26 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -73,10 +73,7 @@ class SignupViewset(viewsets.ModelViewSet): user_type=str(request.data['user_type']), expired_at=expiry) """Send email to the register user""" send_otp_email(request.data['email'], otp) - device_details, created = UserDeviceDetails.objects.get_or_create(user=user) - if device_details: - device_details.device_id = device_id - device_details.save() + UserDeviceDetails.objects.create(user=user, device_id=device_id) return custom_response(SUCCESS_CODE['3001'], response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) From 82189d39534bddf6b1c2a198b4bb190fcaf92d90 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 21 Aug 2023 16:00:39 +0530 Subject: [PATCH 30/83] signup --- guardian/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guardian/views.py b/guardian/views.py index 109754b..0ec7217 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -63,7 +63,7 @@ class SignupViewset(viewsets.ModelViewSet): if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]: serializer = UserSerializer(context=request.data['user_type'], data=request.data) if serializer.is_valid(): - serializer.save() + user = serializer.save() """Generate otp""" otp = generate_otp() # expire otp after 1 day From 99a59162a21ebb09a57fe230044f7e3de73a4860 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 21 Aug 2023 18:12:03 +0530 Subject: [PATCH 31/83] swagger update --- account/serializers.py | 2 +- account/urls.py | 4 ++-- account/views.py | 6 +++++- guardian/urls.py | 4 +--- guardian/views.py | 2 ++ junior/views.py | 40 ++++++++++++++++++++++++++++++++++------ 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/account/serializers.py b/account/serializers.py index e946d15..abfb8a6 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -308,7 +308,7 @@ class EmailVerificationSerializer(serializers.ModelSerializer): class Meta(object): """Meta info""" model = UserEmailOtp - fields = '__all__' + fields = ('email',) diff --git a/account/urls.py b/account/urls.py index 02ac124..49d2f53 100644 --- a/account/urls.py +++ b/account/urls.py @@ -39,8 +39,8 @@ router.register('user', UserLogin, basename='user') router.register('admin', AdminLoginViewSet, basename='admin') """google login end point""" router.register('google-login', GoogleLoginViewSet, basename='admin') -router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp') -router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification') +# router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp') +# router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification') """email verification end point""" router.register('user-email-verification', UserEmailVerification, basename='user-email-verification') """Resend email otp end point""" diff --git a/account/views.py b/account/views.py index ba99f95..d26703d 100644 --- a/account/views.py +++ b/account/views.py @@ -467,8 +467,11 @@ class ReSendEmailOtp(viewsets.ModelViewSet): """Send otp on phone""" serializer_class = EmailVerificationSerializer permission_classes = [IsAuthenticated] - + http_method_names = ('post',) def create(self, request, *args, **kwargs): + """Param + {"email":"ashok@yopmail.com"} + """ otp = generate_otp() if User.objects.filter(email=request.data['email']): expiry = timezone.now() + timezone.timedelta(days=1) @@ -489,6 +492,7 @@ class ProfileAPIViewSet(viewsets.ModelViewSet): """Profile viewset""" serializer_class = JuniorProfileSerializer permission_classes = [IsAuthenticated] + http_method_names = ('get',) def list(self, request, *args, **kwargs): """profile view""" diff --git a/guardian/urls.py b/guardian/urls.py index e95ea8e..4a1d006 100644 --- a/guardian/urls.py +++ b/guardian/urls.py @@ -1,7 +1,7 @@ """ Urls files""" """Django import""" from django.urls import path, include -from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView, +from .views import (SignupViewset, UpdateGuardianProfile, CreateTaskAPIView, TaskListAPIView, SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView, GuardianListAPIView) """Third party import""" @@ -25,8 +25,6 @@ router.register('sign-up', SignupViewset, basename='sign-up') router.register('create-guardian-profile', UpdateGuardianProfile, basename='update-guardian-profile') # Create Task API""" router.register('create-task', CreateTaskAPIView, basename='create-task') -# All Task list API""" -router.register('all-task-list', AllTaskListAPIView, basename='all-task-list') # Task list bases on the status API""" router.register('task-list', TaskListAPIView, basename='task-list') # Leaderboard API""" diff --git a/guardian/views.py b/guardian/views.py index 0ec7217..042150c 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -57,6 +57,7 @@ class SignupViewset(viewsets.ModelViewSet): """Signup view set""" queryset = User.objects.all() serializer_class = UserSerializer + http_method_names = ('post',) def create(self, request, *args, **kwargs): """Create user profile""" device_id = request.META.get('HTTP_DEVICE_ID') @@ -209,6 +210,7 @@ class SearchTaskListAPIView(viewsets.ModelViewSet): serializer_class = TaskDetailsSerializer permission_classes = [IsAuthenticated] pagination_class = PageNumberPagination + http_method_names = ('get',) def get_queryset(self): """Get the queryset for the view""" diff --git a/junior/views.py b/junior/views.py index 5eb15bd..1ed3972 100644 --- a/junior/views.py +++ b/junior/views.py @@ -13,7 +13,9 @@ import requests from rest_framework.viewsets import GenericViewSet, mixins """Django app import""" - +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from drf_yasg.views import get_schema_view # Import guardian's model, # Import junior's model, # Import account's model, @@ -99,7 +101,7 @@ class UpdateJuniorProfile(viewsets.ModelViewSet): except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) -class ValidateGuardianCode(viewsets.ViewSet): +class ValidateGuardianCode(viewsets.ModelViewSet): """Check guardian code exist or not""" permission_classes = [IsAuthenticated] @@ -156,7 +158,14 @@ class AddJuniorAPIView(viewsets.ModelViewSet): http_method_names = ('post',) def create(self, request, *args, **kwargs): - """ junior list""" + """ add junior + { "gender":"1", + "first_name":"abc", + "last_name":"xyz", + "dob":"2023-12-12", + "relationship":"2", + "email":"abc@yopmail.com" + }""" try: info_data = {'user': request.user, 'relationship': str(request.data['relationship']), 'email': request.data['email'], 'first_name': request.data['first_name'], @@ -235,12 +244,25 @@ class InvitedJuniorAPIView(viewsets.ModelViewSet): class FilterJuniorAPIView(viewsets.ModelViewSet): - """Update guardian profile""" + """filter junior profile""" serializer_class = JuniorDetailListSerializer permission_classes = [IsAuthenticated] pagination_class = PageNumberPagination http_method_names = ('get',) + @swagger_auto_schema( + manual_parameters=[ + # Example of a query parameter + openapi.Parameter( + 'title', # Query parameter name + openapi.IN_QUERY, # Parameter location + description='title of the name', + type=openapi.TYPE_STRING, # Parameter type + ), + # Add more parameters as needed + ] + ) + def get_queryset(self): """Get the queryset for the view""" title = self.request.GET.get('title') @@ -386,7 +408,7 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) -class ValidateReferralCode(viewsets.ViewSet): +class ValidateReferralCode(viewsets.ModelViewSet): """Check guardian code exist or not""" permission_classes = [IsAuthenticated] http_method_names = ('get',) @@ -421,7 +443,13 @@ class InviteGuardianAPIView(viewsets.ModelViewSet): permission_classes = [IsAuthenticated] http_method_names = ('post',) def create(self, request, *args, **kwargs): - """ junior list""" + """ add guardian + { + "first_name":"abc", + "last_name":"xyz", + "email":"abc@yopmail.com", + "relationship":2 + }""" try: if request.data['email'] == '': return custom_error_response(ERROR_CODE['2062'], response_status=status.HTTP_400_BAD_REQUEST) From e380f3f6ab308f12f0a8f033bf147b806ae46669 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 21 Aug 2023 18:44:18 +0530 Subject: [PATCH 32/83] swagger update --- account/urls.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/account/urls.py b/account/urls.py index 49d2f53..c5cdb05 100644 --- a/account/urls.py +++ b/account/urls.py @@ -39,8 +39,6 @@ router.register('user', UserLogin, basename='user') router.register('admin', AdminLoginViewSet, basename='admin') """google login end point""" router.register('google-login', GoogleLoginViewSet, basename='admin') -# router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp') -# router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification') """email verification end point""" router.register('user-email-verification', UserEmailVerification, basename='user-email-verification') """Resend email otp end point""" From 214566ec8f3a9abeed5e4fe68db6eea5d6419c8d Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 21 Aug 2023 19:09:44 +0530 Subject: [PATCH 33/83] Error response --- account/utils.py | 2 +- guardian/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/account/utils.py b/account/utils.py index cbdab1e..57fc273 100644 --- a/account/utils.py +++ b/account/utils.py @@ -204,7 +204,7 @@ def custom_error_response(detail, response_status): if not detail: """when details is empty""" detail = {} - return Response({"error": detail, "status": "failed", "code": response_status}) + return Response({"error": detail, "status": "failed", "code": response_status}, status=status.HTTP_400_BAD_REQUEST) def get_user_data(attrs): diff --git a/guardian/views.py b/guardian/views.py index 042150c..843ab45 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -337,7 +337,7 @@ class ApproveTaskAPIView(viewsets.ModelViewSet): serializer.save() return custom_response(SUCCESS_CODE['3026'], response_status=status.HTTP_200_OK) else: - return custom_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST) + return custom_error_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) From 95dad86b12f17182d6210e430139cc8b1216827d Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 21 Aug 2023 20:07:39 +0530 Subject: [PATCH 34/83] notification changes --- junior/serializers.py | 6 +-- notifications/admin.py | 2 + notifications/constants.py | 80 +++++++++++++++++++++----------------- notifications/utils.py | 14 +++++++ 4 files changed, 63 insertions(+), 39 deletions(-) diff --git a/junior/serializers.py b/junior/serializers.py index f79cd46..1ca98e4 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -22,7 +22,7 @@ from account.models import UserEmailOtp, UserNotification from junior.utils import junior_notification_email, junior_approval_mail from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian -from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION, +from notifications.constants import (INVITATION, ASSOCIATE_REQUEST, SKIPPED_PROFILE_SETUP, TASK_ACTION, TASK_SUBMITTED) from web_admin.models import ArticleCard @@ -451,8 +451,8 @@ class AddGuardianSerializer(serializers.ModelSerializer): """Notification email""" junior_notification_email(email, full_name, email, password) junior_approval_mail(email, full_name) - send_notification_to_junior.delay(INVITED_GUARDIAN, guardian_data.user.id, junior_data.auth.id, {}) - send_notification_to_guardian.delay(APPROVED_JUNIOR, junior_data.auth.id, guardian_data.user.id, {}) + send_notification_to_junior.delay(INVITATION, guardian_data.user.id, junior_data.auth.id, {}) + send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior_data.auth.id, guardian_data.user.id, {}) return guardian_data class StartTaskSerializer(serializers.ModelSerializer): diff --git a/notifications/admin.py b/notifications/admin.py index 382e97b..aa57aac 100644 --- a/notifications/admin.py +++ b/notifications/admin.py @@ -10,3 +10,5 @@ from notifications.models import Notification class NotificationAdmin(admin.ModelAdmin): """Notification Admin""" list_display = ['id', 'notification_type', 'notification_to', 'data', 'is_read'] + list_filter = ['notification_type'] + \ No newline at end of file diff --git a/notifications/constants.py b/notifications/constants.py index 59c2328..f4f8b86 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -1,19 +1,22 @@ """ notification constants file """ -from base.constants import NUMBER -REGISTRATION = NUMBER['one'] -TASK_ASSIGNED = NUMBER['two'] -INVITED_GUARDIAN = NUMBER['three'] -APPROVED_JUNIOR = NUMBER['four'] -REFERRAL_POINTS = NUMBER['five'] -TASK_POINTS = NUMBER['six'] -TASK_REJECTED = NUMBER['seven'] -SKIPPED_PROFILE_SETUP = NUMBER['eight'] -TASK_SUBMITTED = NUMBER['nine'] -TASK_ACTION = NUMBER['ten'] -LEADERBOARD_RANKING = NUMBER['eleven'] -REMOVE_JUNIOR = NUMBER['twelve'] +REGISTRATION = 1 +INVITATION = 2 +ASSOCIATE_REQUEST = 3 +REFERRAL_POINTS = 4 +SKIPPED_PROFILE_SETUP = 5 + +TASK_ASSIGNED = 6 +TASK_SUBMITTED = 7 +TASK_ACTION = 8 +TASK_REJECTED = 9 +TASK_APPROVED = 10 +TASK_POINTS = 11 + +LEADERBOARD_RANKING = 12 +REMOVE_JUNIOR = 13 + TEST_NOTIFICATION = 99 NOTIFICATION_DICT = { @@ -21,41 +24,46 @@ NOTIFICATION_DICT = { "title": "Successfully registered!", "body": "You have registered successfully. Now login and complete your profile." }, - TASK_ASSIGNED: { - "title": "New task assigned !!", - "body": "{from_user} has assigned you a new task." + INVITATION: { + "title": "Invitation sent!", + "body": "Invitation has been successfully sent." }, - INVITED_GUARDIAN: { - "title": "Invite guardian", - "body": "Invite guardian successfully" - }, - APPROVED_JUNIOR: { - "title": "Approve junior !", + ASSOCIATE_REQUEST: { + "title": "Associate request!", "body": "You have request from {from_user} to associate with you." }, REFERRAL_POINTS: { - "title": "Earn Referral points", + "title": "Earn Referral points!", "body": "You earn 5 points for referral." }, - TASK_POINTS: { - "title": "Earn Task points!", - "body": "You earn 5 points for task." - }, - TASK_REJECTED: { - "title": "Task rejected!", - "body": "Your task has been rejected." - }, + # notification once any custodian adds junior in their account SKIPPED_PROFILE_SETUP: { - "title": "Skipped profile setup!", - "body": "Your guardian {from_user} has setup your profile." + "title": "Profile already setup!", + "body": "Your guardian has already setup your profile." + }, + TASK_ASSIGNED: { + "title": "New task assigned!", + "body": "{from_user} has assigned you a new task." }, TASK_SUBMITTED: { "title": "Task submitted!", - "body": "Your task has been submitted successfully." + "body": "Your task has been submitted for approval." }, TASK_ACTION: { "title": "Task completion approval!", - "body": "You have request from {from_user} for task approval." + "body": "You have request from {from_user} for task completion." + }, + TASK_REJECTED: { + "title": "Task completion rejected!", + "body": "Your task completion request has been rejected by {from_user}." + }, + TASK_APPROVED: { + "title": "Task completion approved!", + "body": "Your task completion request has been approved by {from_user}." + }, + TASK_POINTS: { + "title": "Earned Task points!", + "body": "You earn 5 points for task." }, LEADERBOARD_RANKING: { "title": "Leader board rank!", @@ -63,7 +71,7 @@ NOTIFICATION_DICT = { }, REMOVE_JUNIOR: { "title": "Disassociate by guardian!", - "body": "Your guardian disassociate you ." + "body": "Your guardian has disassociated you." }, TEST_NOTIFICATION: { "title": "Test Notification", diff --git a/notifications/utils.py b/notifications/utils.py index 50dee81..0d0b25b 100644 --- a/notifications/utils.py +++ b/notifications/utils.py @@ -97,6 +97,13 @@ def send_push(user, data): @shared_task() def send_notification_to_guardian(notification_type, from_user_id, to_user_id, extra_data): + """ + :param notification_type: + :param from_user_id: + :param to_user_id: + :param extra_data: + :return: + """ from_user = None if from_user_id: from_user = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first() @@ -109,6 +116,13 @@ def send_notification_to_guardian(notification_type, from_user_id, to_user_id, e @shared_task() def send_notification_to_junior(notification_type, from_user_id, to_user_id, extra_data): + """ + :param notification_type: + :param from_user_id: + :param to_user_id: + :param extra_data: + :return: + """ from_user = None if from_user_id: from_user = Guardian.objects.filter(user_id=from_user_id).select_related('user').first() From 930c10b578e1add27da0bf280e9ae5e99f1d99d4 Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 22 Aug 2023 11:28:52 +0530 Subject: [PATCH 35/83] swagger update --- account/views.py | 59 ++++++++++++++++++++++---- guardian/views.py | 17 +++++--- junior/views.py | 87 ++++++++++++++++++++++++++++---------- notifications/views.py | 6 ++- web_admin/views/article.py | 8 +++- 5 files changed, 137 insertions(+), 40 deletions(-) diff --git a/account/views.py b/account/views.py index d26703d..e4051f4 100644 --- a/account/views.py +++ b/account/views.py @@ -117,13 +117,22 @@ class GoogleLoginViewSet(GoogleLoginMixin, viewsets.GenericViewSet): serializer_class = GoogleLoginSerializer def create(self, request): - """create method""" + """Payload + { + "access_token", + "user_type": "1" + }""" serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) return self.google_login(request) class SigninWithApple(views.APIView): - """This API is for sign in with Apple for app.""" + """This API is for sign in with Apple for app. + Payload + { + "access_token", + "user_type": "1" + }""" def post(self, request): token = request.data.get("access_token") user_type = request.data.get("user_type") @@ -202,6 +211,10 @@ class ChangePasswordAPIView(views.APIView): def post(self, request): """ POST request to change current login user password + Payload + { "current_password":"Demo@123", + "new_password":"Demo@123" + } """ serializer = ChangePasswordSerializer( context=request.user, @@ -219,7 +232,12 @@ class ChangePasswordAPIView(views.APIView): ) class ResetPasswordAPIView(views.APIView): - """Reset password""" + """Reset password + Payload + { + "verification_code":"373770", + "password":"Demo@1323" + }""" def post(self, request): serializer = ResetPasswordSerializer(data=request.data) if serializer.is_valid(): @@ -236,7 +254,10 @@ class ForgotPasswordAPIView(views.APIView): def post(self, request): """ - Post method to validate serializer + Payload + { + "email": "abc@yopmail.com" + } """ serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) @@ -420,7 +441,12 @@ class AdminLoginViewSet(viewsets.GenericViewSet): class UserEmailVerification(viewsets.ModelViewSet): - """User Email verification""" + """User Email verification + Payload + { + "email":"ramu@yopmail.com", + "otp":"361123" + }""" serializer_class = EmailVerificationSerializer http_method_names = ('post',) @@ -495,7 +521,9 @@ class ProfileAPIViewSet(viewsets.ModelViewSet): http_method_names = ('get',) def list(self, request, *args, **kwargs): - """profile view""" + """profile view + Params + user_type""" if str(self.request.GET.get('user_type')) == '1': junior_data = Junior.objects.filter(auth=self.request.user).last() if junior_data: @@ -510,6 +538,7 @@ class ProfileAPIViewSet(viewsets.ModelViewSet): class UploadImageAPIViewSet(viewsets.ModelViewSet): """upload task image""" serializer_class = DefaultTaskImagesSerializer + http_method_names = ('post',) def create(self, request, *args, **kwargs): """upload images""" image_data = request.data['image_url'] @@ -529,6 +558,7 @@ class DefaultImageAPIViewSet(viewsets.ModelViewSet): """Profile viewset""" serializer_class = DefaultTaskImagesDetailsSerializer permission_classes = [IsAuthenticated] + http_method_names = ('get',) def list(self, request, *args, **kwargs): """profile view""" queryset = DefaultTaskImages.objects.all() @@ -565,8 +595,9 @@ class UserNotificationAPIViewSet(viewsets.ModelViewSet): """notification viewset""" serializer_class = UserNotificationSerializer permission_classes = [IsAuthenticated] + http_method_names = ('get',) def list(self, request, *args, **kwargs): - """profile view""" + """notification view""" queryset = UserNotification.objects.filter(user=request.user) serializer = UserNotificationSerializer(queryset, many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) @@ -576,9 +607,14 @@ class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet): """Update notification viewset""" serializer_class = UpdateUserNotificationSerializer permission_classes = [IsAuthenticated] + http_method_names = ('post',) def create(self, request, *args, **kwargs): - """profile view""" + """Payload + {"email_notification": false, + "sms_notification": false, + "push_notification": false} + """ serializer = UpdateUserNotificationSerializer(data=request.data, context=request.user) if serializer.is_valid(): @@ -588,7 +624,12 @@ class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet): class SendSupportEmail(views.APIView): - """support email api""" + """support email api + payload + name + email + message + """ permission_classes = (IsAuthenticated,) def post(self, request): diff --git a/guardian/views.py b/guardian/views.py index 843ab45..dd3a1ec 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -129,7 +129,11 @@ class AllTaskListAPIView(viewsets.ModelViewSet): class TaskListAPIView(viewsets.ModelViewSet): - """Update guardian profile""" + """Task list + Params + status + search + page""" serializer_class = TaskDetailsSerializer permission_classes = [IsAuthenticated] filter_backends = (SearchFilter,) @@ -206,7 +210,7 @@ class CreateTaskAPIView(viewsets.ModelViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) class SearchTaskListAPIView(viewsets.ModelViewSet): - """Update guardian profile""" + """Filter task""" serializer_class = TaskDetailsSerializer permission_classes = [IsAuthenticated] pagination_class = PageNumberPagination @@ -221,7 +225,7 @@ class SearchTaskListAPIView(viewsets.ModelViewSet): return junior_queryset def list(self, request, *args, **kwargs): - """Create guardian profile""" + """Filter task""" try: queryset = self.get_queryset() paginator = self.pagination_class() @@ -235,10 +239,12 @@ class SearchTaskListAPIView(viewsets.ModelViewSet): class TopJuniorListAPIView(viewsets.ModelViewSet): - """Top juniors list""" + """Top juniors list + No Params""" queryset = JuniorPoints.objects.all() serializer_class = TopJuniorSerializer permission_classes = [IsAuthenticated] + http_method_names = ('get',) def get_serializer_context(self): # context list @@ -349,7 +355,8 @@ class GuardianListAPIView(viewsets.ModelViewSet): http_method_names = ('get',) def list(self, request, *args, **kwargs): - """ junior list""" + """ Guardian list of assosicated junior + No Params""" try: guardian_data = JuniorGuardianRelationship.objects.filter(junior__auth__email=self.request.user) # fetch junior object diff --git a/junior/views.py b/junior/views.py index 1ed3972..62c5fff 100644 --- a/junior/views.py +++ b/junior/views.py @@ -74,7 +74,7 @@ class UpdateJuniorProfile(viewsets.ModelViewSet): http_method_names = ('post',) def create(self, request, *args, **kwargs): - """Use CreateJuniorSerializer""" + """Create Junior Profile""" try: request_data = request.data image = request.data.get('image') @@ -104,9 +104,13 @@ class UpdateJuniorProfile(viewsets.ModelViewSet): class ValidateGuardianCode(viewsets.ModelViewSet): """Check guardian code exist or not""" permission_classes = [IsAuthenticated] + http_method_names = ('get',) def list(self, request, *args, **kwargs): - """check guardian code""" + """check guardian code + Params + "guardian_code" + """ try: guardian_code = self.request.GET.get('guardian_code').split(',') for code in guardian_code: @@ -216,7 +220,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet): class InvitedJuniorAPIView(viewsets.ModelViewSet): - """Junior list of assosicated guardian""" + """Invited Junior list of assosicated guardian""" serializer_class = JuniorDetailListSerializer permission_classes = [IsAuthenticated] @@ -230,7 +234,8 @@ class InvitedJuniorAPIView(viewsets.ModelViewSet): is_invited=True) return junior_queryset def list(self, request, *args, **kwargs): - """ junior list""" + """ Invited Junior list of assosicated guardian + No Params""" try: queryset = self.get_queryset() paginator = self.pagination_class() @@ -273,7 +278,7 @@ class FilterJuniorAPIView(viewsets.ModelViewSet): return queryset def list(self, request, *args, **kwargs): - """Create guardian profile""" + """Filter junior""" try: queryset = self.get_queryset() paginator = self.pagination_class() @@ -287,7 +292,9 @@ class FilterJuniorAPIView(viewsets.ModelViewSet): class RemoveJuniorAPIView(views.APIView): - """Remove junior API""" + """Remove junior API + Params + id=37""" serializer_class = RemoveJuniorSerializer model = Junior permission_classes = [IsAuthenticated] @@ -315,14 +322,17 @@ class RemoveJuniorAPIView(views.APIView): class JuniorTaskListAPIView(viewsets.ModelViewSet): - """Update guardian profile""" + """Junior task list""" serializer_class = TaskDetailsjuniorSerializer permission_classes = [IsAuthenticated] pagination_class = PageNumberPagination http_method_names = ('get',) def list(self, request, *args, **kwargs): - """Create guardian profile""" + """Junior task list + status=0 + search='title' + page=1""" try: status_value = self.request.GET.get('status') search = self.request.GET.get('search') @@ -352,7 +362,9 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): class CompleteJuniorTaskAPIView(views.APIView): - """Update junior task API""" + """Payload + task_id + image""" serializer_class = CompleteTaskSerializer model = JuniorTask permission_classes = [IsAuthenticated] @@ -397,7 +409,8 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet): http_method_names = ('get',) def list(self, request, *args, **kwargs): - """profile view""" + """Junior Points + No Params""" try: update_positions_based_on_points() queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last() @@ -467,7 +480,11 @@ class InviteGuardianAPIView(viewsets.ModelViewSet): class StartTaskAPIView(views.APIView): - """Update junior task API""" + """Update junior task API + Paylod + { + "task_id":28 + }""" serializer_class = StartTaskSerializer model = JuniorTask permission_classes = [IsAuthenticated] @@ -491,7 +508,13 @@ class StartTaskAPIView(views.APIView): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) class ReAssignJuniorTaskAPIView(views.APIView): - """Update junior task API""" + """Update junior task API + Payload + { + "task_id":34, + "due_date":"2023-08-22" + } + """ serializer_class = ReAssignTaskSerializer model = JuniorTask permission_classes = [IsAuthenticated] @@ -520,7 +543,10 @@ class StartArticleAPIView(viewsets.ModelViewSet): http_method_names = ('post',) def create(self, request, *args, **kwargs): - """ junior list""" + """ Payload + { + "article_id":"2" + }""" try: junior_instance = Junior.objects.filter(auth=self.request.user).last() article_id = request.data.get('article_id') @@ -542,7 +568,7 @@ class StartArticleAPIView(viewsets.ModelViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) class StartAssessmentAPIView(viewsets.ModelViewSet): - """Junior Points viewset""" + """Question answer viewset""" serializer_class = StartAssessmentSerializer permission_classes = [IsAuthenticated] http_method_names = ('get',) @@ -555,7 +581,9 @@ class StartAssessmentAPIView(viewsets.ModelViewSet): ).order_by('-created_at') return article def list(self, request, *args, **kwargs): - """profile view""" + """Params + article_id + """ try: queryset = self.get_queryset() @@ -567,7 +595,9 @@ class StartAssessmentAPIView(viewsets.ModelViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) class CheckAnswerAPIView(viewsets.ModelViewSet): - """Junior Points viewset""" + """Params + question_id=1 + answer_id=1""" permission_classes = [IsAuthenticated] http_method_names = ('get',) @@ -576,7 +606,10 @@ class CheckAnswerAPIView(viewsets.ModelViewSet): article = ArticleSurvey.objects.filter(id=question_id).last() return article def list(self, request, *args, **kwargs): - """profile view""" + """ Params + question_id=1 + answer_id=1 + """ try: answer_id = self.request.GET.get('answer_id') @@ -600,7 +633,9 @@ class CheckAnswerAPIView(viewsets.ModelViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) class CompleteArticleAPIView(views.APIView): - """Remove junior API""" + """Params + article_id + """ permission_classes = [IsAuthenticated] http_method_names = ('put', 'get',) def put(self, request, format=None): @@ -614,7 +649,8 @@ class CompleteArticleAPIView(views.APIView): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) def get(self, request, *args, **kwargs): - """ junior list""" + """ Params + article_id=1""" try: article_id = self.request.GET.get('article_id') total_earn_points = JuniorArticlePoints.objects.filter(junior__auth=request.user, @@ -628,12 +664,16 @@ class CompleteArticleAPIView(views.APIView): class ReadArticleCardAPIView(views.APIView): - """Remove junior API""" + """Read article card API""" permission_classes = [IsAuthenticated] http_method_names = ('put',) def put(self, request, *args, **kwargs): - """ junior list""" + """ Read article card + Payload + {"article_id":"1", + "article_card":"2", + "current_page":"2"}""" try: junior_instance = Junior.objects.filter(auth=self.request.user).last() article = self.request.data.get('article_id') @@ -677,7 +717,8 @@ class CreateArticleCardAPIView(viewsets.ModelViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) class RemoveGuardianCodeAPIView(views.APIView): - """Update junior task API""" + """Remove guardian code request API + No Payload""" serializer_class = RemoveGuardianCodeSerializer permission_classes = [IsAuthenticated] @@ -714,7 +755,7 @@ class FAQViewSet(GenericViewSet, mixins.CreateModelMixin, """ faq create api method :param request: - :param args: + :param args: question, description :param kwargs: :return: success message """ diff --git a/notifications/views.py b/notifications/views.py index 5adb536..154751a 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -77,7 +77,11 @@ class NotificationViewSet(viewsets.GenericViewSet): class ReadNotification(views.APIView): - """Update notification API""" + """Update notification API + Payload + { + "notification_id": [] + }""" serializer_class = ReadNotificationSerializer model = Notification permission_classes = [IsAuthenticated] diff --git a/web_admin/views/article.py b/web_admin/views/article.py index 49f76fa..6570f6f 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -248,7 +248,9 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin): return custom_response(None, data=serializer.data) class ArticleCardListViewSet(viewsets.ModelViewSet): - """Junior Points viewset""" + """Article card list + use below query param + article_id""" serializer_class = ArticleCardlistSerializer permission_classes = [IsAuthenticated] http_method_names = ('get',) @@ -257,7 +259,9 @@ class ArticleCardListViewSet(viewsets.ModelViewSet): """get queryset""" return ArticleCard.objects.filter(article=self.request.GET.get('article_id')) def list(self, request, *args, **kwargs): - """profile view""" + """Article card list + use below query param + article_id""" try: queryset = self.get_queryset() From f8e529600b29c799bdb5a07ff6079d9b46433b90 Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 22 Aug 2023 13:46:37 +0530 Subject: [PATCH 36/83] force update and mail by celery task --- account/custom_middleware.py | 6 +++++- account/migrations/0010_forceupdate.py | 28 ++++++++++++++++++++++++++ account/models.py | 24 +++++++++++++++++++++- account/views.py | 2 +- base/constants.py | 5 ++++- guardian/views.py | 2 +- junior/serializers.py | 6 +++--- junior/utils.py | 4 +++- 8 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 account/migrations/0010_forceupdate.py diff --git a/account/custom_middleware.py b/account/custom_middleware.py index c2125cd..3193fa9 100644 --- a/account/custom_middleware.py +++ b/account/custom_middleware.py @@ -5,7 +5,7 @@ from rest_framework.response import Response from rest_framework.renderers import JSONRenderer """App django""" from account.utils import custom_error_response -from account.models import UserDeviceDetails +from account.models import UserDeviceDetails, ForceUpdate from base.messages import ERROR_CODE, SUCCESS_CODE from base.constants import NUMBER from junior.models import Junior @@ -39,6 +39,8 @@ class CustomMiddleware(object): # Code to be executed after the view is called device_id = request.META.get('HTTP_DEVICE_ID') user_type = request.META.get('HTTP_USER_TYPE') + version = request.META.get('HTTP_VERSION') + device_type = str(request.META.get('HTTP_TYPE')) api_endpoint = request.path if request.user.is_authenticated: # device details @@ -56,4 +58,6 @@ class CustomMiddleware(object): if device_id and not device_details and api_endpoint != '/api/v1/user/login/': custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND) response = custom_response(custom_error) + force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last() + return response diff --git a/account/migrations/0010_forceupdate.py b/account/migrations/0010_forceupdate.py new file mode 100644 index 0000000..84f5b12 --- /dev/null +++ b/account/migrations/0010_forceupdate.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.2 on 2023-08-22 07:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0009_alter_userdevicedetails_device_id'), + ] + + operations = [ + migrations.CreateModel( + name='ForceUpdate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('version', models.CharField(blank=True, max_length=50, null=True)), + ('device_type', models.CharField(blank=True, choices=[('1', 'android'), ('2', 'ios')], default=None, max_length=15, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Force Update Version', + 'verbose_name_plural': 'Force Update Version', + 'db_table': 'force_update', + }, + ), + ] diff --git a/account/models.py b/account/models.py index 784a60e..c71181e 100644 --- a/account/models.py +++ b/account/models.py @@ -3,7 +3,7 @@ from django.db import models from django.contrib.auth.models import User """App import""" -from base.constants import USER_TYPE +from base.constants import USER_TYPE, DEVICE_TYPE # Create your models here. class UserProfile(models.Model): @@ -165,3 +165,25 @@ class UserDeviceDetails(models.Model): def __str__(self): return self.user.email + + + + +class ForceUpdate(models.Model): + """ + Force update + """ + """Version ID""" + version = models.CharField(max_length=50, null=True, blank=True) + device_type = models.CharField(max_length=15, choices=DEVICE_TYPE, null=True, blank=True, default=None) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta(object): + """ Meta information """ + db_table = 'force_update' + verbose_name = 'Force Update Version' + verbose_name_plural = 'Force Update Version' + + def __str__(self): + return self.version diff --git a/account/views.py b/account/views.py index e4051f4..84b6aaa 100644 --- a/account/views.py +++ b/account/views.py @@ -509,7 +509,7 @@ class ReSendEmailOtp(viewsets.ModelViewSet): email_data.otp = otp email_data.expired_at = expiry email_data.save() - send_otp_email(request.data['email'], otp) + send_otp_email.delay(request.data['email'], otp) return custom_response(SUCCESS_CODE['3016'], response_status=status.HTTP_200_OK) else: return custom_error_response(ERROR_CODE["2023"], response_status=status.HTTP_400_BAD_REQUEST) diff --git a/base/constants.py b/base/constants.py index d376971..1ab15a2 100644 --- a/base/constants.py +++ b/base/constants.py @@ -50,7 +50,10 @@ USER_TYPE = ( ('2', 'guardian'), ('3', 'superuser') ) - +DEVICE_TYPE = ( + ('1', 'android'), + ('2', 'ios') +) USER_TYPE_FLAG = { "FIRST" : "1", "TWO" : "2", diff --git a/guardian/views.py b/guardian/views.py index dd3a1ec..80c1d9b 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -73,7 +73,7 @@ class SignupViewset(viewsets.ModelViewSet): UserEmailOtp.objects.create(email=request.data['email'], otp=otp, user_type=str(request.data['user_type']), expired_at=expiry) """Send email to the register user""" - send_otp_email(request.data['email'], otp) + send_otp_email.delay(request.data['email'], otp) UserDeviceDetails.objects.create(user=user, device_id=device_id) return custom_response(SUCCESS_CODE['3001'], response_status=status.HTTP_200_OK) diff --git a/junior/serializers.py b/junior/serializers.py index 1ca98e4..01e5adc 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -97,7 +97,7 @@ class CreateJuniorSerializer(serializers.ModelSerializer): if guardian_data: JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) junior.guardian_code_status = str(NUMBER['three']) - junior_approval_mail(user.email, user.first_name) + junior_approval_mail.delay(user.email, user.first_name) send_notification_to_guardian.delay(APPROVED_JUNIOR, junior.auth.id, guardian_data.user.id, {}) junior.dob = validated_data.get('dob', junior.dob) junior.passcode = validated_data.get('passcode', junior.passcode) @@ -304,7 +304,7 @@ class AddJuniorSerializer(serializers.ModelSerializer): # add push notification UserNotification.objects.get_or_create(user=user_data) """Notification email""" - junior_notification_email(email, full_name, email, password) + junior_notification_email.delay(email, full_name, email, password) # push notification send_notification_to_junior.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {}) return junior_data @@ -450,7 +450,7 @@ class AddGuardianSerializer(serializers.ModelSerializer): """Notification email""" junior_notification_email(email, full_name, email, password) - junior_approval_mail(email, full_name) + junior_approval_mail.delay(email, full_name) send_notification_to_junior.delay(INVITATION, guardian_data.user.id, junior_data.auth.id, {}) send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior_data.auth.id, guardian_data.user.id, {}) return guardian_data diff --git a/junior/utils.py b/junior/utils.py index ba177a8..4a6ee2b 100644 --- a/junior/utils.py +++ b/junior/utils.py @@ -14,6 +14,8 @@ from django.db.models import F # being part of the zod bank and access the platform # define junior notification email # junior approval email +from celery import shared_task +@shared_task() def junior_notification_email(recipient_email, full_name, email, password): """Notification email""" from_email = settings.EMAIL_FROM_ADDRESS @@ -32,7 +34,7 @@ def junior_notification_email(recipient_email, full_name, email, password): } ) return full_name - +@shared_task() def junior_approval_mail(guardian, full_name): """junior approval mail""" from_email = settings.EMAIL_FROM_ADDRESS From d573d56a357e51e49f8661c3cd3564bceee9db9d Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Tue, 22 Aug 2023 16:01:08 +0530 Subject: [PATCH 37/83] minor changes --- celerybeat-schedule | Bin 16384 -> 16384 bytes junior/serializers.py | 2 +- notifications/admin.py | 1 - notifications/constants.py | 10 +++++++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/celerybeat-schedule b/celerybeat-schedule index cc8251c7500206755f61cdf0ff0b24e019ea84c5..68297a7db074145de61828e2a94f36b892493ec5 100644 GIT binary patch delta 470 zcmYk2!Aiq07{?p!+~zEbP7jW;DTvNPTorwR;X!(vdht@)F51wp9c?CVFziW0a*=-C zckl&dkKn@ydjXG{(g+5U{QmiW-^ZUM2}%+)ZFUPvzLw;@)6PMYT9JBH>V2uX)SjNh zTsCYSzv|mFzxl3e-_ojxit4Ps_ftZRSsizfrpX}-!dXxIN7D^9PSm%eoXHojuR4%6 z+q;JPj7h*EN@C$xQC=_szCs=bG$K8dS+NJa;t-!iBWx3k0oN`am-L1{ITx)XNa4Um zyp_JRTaUm?J&O@W`{X~S&{-~il{S~_1@)5)#~xCj(I5t1yd%tFED~dQX&eTG2DhQW z8-W)YiubZP3gl{;n)f7%X&9KM_>1!2UlmUA3DyCB=yUBC>S={~ delta 390 zcmX|5F;Buk7`>w)?Lk4om4uKOsRPo%!I_w--Kr*yOVb{wTqrGHdl)exb|MZsnEn9< zHz&i7@+Uaz5gNa5crWkey}UHVX^L0vP62IC&k^;vM5}G_$9y+0*FpQEsOz~^kxqTx zI`~!b=epG!VEr(6Hf4G?IbJng_kqO;k3!c?6{jTildRqEoGULq7K)g&BvlrGx=vME z>~YFtHgtuTi~&>10v6AEKJx^*q9Zn(1?)_Q<^&4e5q1n2$=>?LGbnWE2`2c2k$(ck z0V#j0jS4bU(}5E8XFL!*OvpVGo)9sC9r_f7jE8p-xs~K%L!j(vve%u3Q%=VL19b_S z4a8BTdUHL1iiMX@wLxD(&4#*#=xIJQ$u}m1#!)CdpTKU{LKbqc%pSqsNWOrE4b27Y O`^EyYvHALCYPCNqqGy2s diff --git a/junior/serializers.py b/junior/serializers.py index 1ca98e4..abb0ded 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -98,7 +98,7 @@ class CreateJuniorSerializer(serializers.ModelSerializer): JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) junior.guardian_code_status = str(NUMBER['three']) junior_approval_mail(user.email, user.first_name) - send_notification_to_guardian.delay(APPROVED_JUNIOR, junior.auth.id, guardian_data.user.id, {}) + send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior.auth.id, guardian_data.user.id, {}) junior.dob = validated_data.get('dob', junior.dob) junior.passcode = validated_data.get('passcode', junior.passcode) junior.country_name = validated_data.get('country_name', junior.country_name) diff --git a/notifications/admin.py b/notifications/admin.py index aa57aac..c7cc895 100644 --- a/notifications/admin.py +++ b/notifications/admin.py @@ -11,4 +11,3 @@ class NotificationAdmin(admin.ModelAdmin): """Notification Admin""" list_display = ['id', 'notification_type', 'notification_to', 'data', 'is_read'] list_filter = ['notification_type'] - \ No newline at end of file diff --git a/notifications/constants.py b/notifications/constants.py index f4f8b86..8bb6a6d 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -32,15 +32,17 @@ NOTIFICATION_DICT = { "title": "Associate request!", "body": "You have request from {from_user} to associate with you." }, + # Juniors will receive Notifications for every Points earned by referrals REFERRAL_POINTS: { "title": "Earn Referral points!", "body": "You earn 5 points for referral." }, - # notification once any custodian adds junior in their account + # Juniors will receive notification once any custodians add them in their account SKIPPED_PROFILE_SETUP: { "title": "Profile already setup!", "body": "Your guardian has already setup your profile." }, + # Juniors will receive Notification for every Task Assign by Custodians TASK_ASSIGNED: { "title": "New task assigned!", "body": "{from_user} has assigned you a new task." @@ -49,10 +51,12 @@ NOTIFICATION_DICT = { "title": "Task submitted!", "body": "Your task has been submitted for approval." }, + # Guardian will receive notification as soon as junior send task for approval TASK_ACTION: { "title": "Task completion approval!", "body": "You have request from {from_user} for task completion." }, + # Juniors will receive notification as soon as their task is approved or reject by custodians TASK_REJECTED: { "title": "Task completion rejected!", "body": "Your task completion request has been rejected by {from_user}." @@ -61,14 +65,18 @@ NOTIFICATION_DICT = { "title": "Task completion approved!", "body": "Your task completion request has been approved by {from_user}." }, + # Juniors will receive Notifications for every Points earned either by Task completion + # Juniors will receive notification as soon as their task is approved or reject by custodians TASK_POINTS: { "title": "Earned Task points!", "body": "You earn 5 points for task." }, + # Juniors will receive Notification related to Leaderboard progress LEADERBOARD_RANKING: { "title": "Leader board rank!", "body": "Your rank is ." }, + # Juniors will receive notification as soon as their custodians remove them from account REMOVE_JUNIOR: { "title": "Disassociate by guardian!", "body": "Your guardian has disassociated you." From 05fd76a50be3f5d3e4f036b68c176d9b215ecdff Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 22 Aug 2023 18:29:22 +0530 Subject: [PATCH 38/83] force update --- account/admin.py | 15 ++++++++++++++- account/custom_middleware.py | 6 +++++- account/models.py | 1 + account/serializers.py | 10 +++++++++- account/urls.py | 4 +++- account/views.py | 30 ++++++++++++++++++++++++++++-- base/messages.py | 2 ++ guardian/views.py | 2 +- junior/serializers.py | 4 ++-- junior/views.py | 3 --- 10 files changed, 65 insertions(+), 12 deletions(-) diff --git a/account/admin.py b/account/admin.py index cf3ff23..be456ab 100644 --- a/account/admin.py +++ b/account/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin """Import django app""" -from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete, UserDeviceDetails +from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete, UserDeviceDetails, ForceUpdate # Register your models here. @admin.register(UserDelete) @@ -39,6 +39,19 @@ class UserEmailOtpAdmin(admin.ModelAdmin): """Return object in email and otp format""" return self.email + '-' + self.otp +@admin.register(ForceUpdate) +class ForceUpdateAdmin(admin.ModelAdmin): + """Force update""" + list_display = ['version', 'device_type'] + readonly_fields = ('device_type',) + + def has_add_permission(self, request): + count = ForceUpdate.objects.all().count() + if count < 2: + return True + return False + def has_delete_permission(self, request, obj=None): + return False @admin.register(UserDeviceDetails) class UserDeviceDetailsAdmin(admin.ModelAdmin): """User profile admin""" diff --git a/account/custom_middleware.py b/account/custom_middleware.py index 3193fa9..f658fd3 100644 --- a/account/custom_middleware.py +++ b/account/custom_middleware.py @@ -41,6 +41,7 @@ class CustomMiddleware(object): user_type = request.META.get('HTTP_USER_TYPE') version = request.META.get('HTTP_VERSION') device_type = str(request.META.get('HTTP_TYPE')) + api_endpoint = request.path if request.user.is_authenticated: # device details @@ -59,5 +60,8 @@ class CustomMiddleware(object): custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND) response = custom_response(custom_error) force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last() - + api_endpoint_checks = not any(endpoint in api_endpoint for endpoint in ['/admin/', '/api/v1/admin/']) + if not force_update and api_endpoint_checks: + custom_error = custom_error_response(ERROR_CODE['2079'], response_status=status.HTTP_404_NOT_FOUND) + response = custom_response(custom_error) return response diff --git a/account/models.py b/account/models.py index c71181e..d13762b 100644 --- a/account/models.py +++ b/account/models.py @@ -2,6 +2,7 @@ """Django import""" from django.db import models from django.contrib.auth.models import User +from django.core.exceptions import ValidationError """App import""" from base.constants import USER_TYPE, DEVICE_TYPE # Create your models here. diff --git a/account/serializers.py b/account/serializers.py index abfb8a6..dbb52a0 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -18,7 +18,7 @@ import secrets from guardian.models import Guardian from junior.models import Junior -from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp +from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp, ForceUpdate from base.constants import GUARDIAN, JUNIOR, SUPERUSER, NUMBER from base.messages import ERROR_CODE, SUCCESS_CODE, STATUS_CODE_ERROR from .utils import delete_user_account_condition_social, delete_user_account_condition @@ -390,3 +390,11 @@ class UserPhoneOtpSerializer(serializers.ModelSerializer): """Meta info""" model = UserPhoneOtp fields = '__all__' + +class ForceUpdateSerializer(serializers.ModelSerializer): + # ForceUpdate Serializer + + class Meta(object): + """ meta info """ + model = ForceUpdate + fields = ('id', 'version', 'device_type') diff --git a/account/urls.py b/account/urls.py index c5cdb05..4944d67 100644 --- a/account/urls.py +++ b/account/urls.py @@ -29,7 +29,7 @@ from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVer GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet, DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet, UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView, - AdminLoginViewSet) + AdminLoginViewSet, ForceUpdateViewSet) """Router""" router = routers.SimpleRouter() @@ -55,6 +55,8 @@ router.register('delete', DeleteUserProfileAPIViewSet, basename='delete') router.register('user-notification', UserNotificationAPIViewSet, basename='user-notification') """update user account notification""" router.register('update-user-notification', UpdateUserNotificationAPIViewSet, basename='update-user-notification') +# Force update entry API +router.register('force-update', ForceUpdateViewSet, basename='force-update') """Define url pattern""" urlpatterns = [ path('api/v1/', include(router.urls)), diff --git a/account/views.py b/account/views.py index 84b6aaa..9d87c93 100644 --- a/account/views.py +++ b/account/views.py @@ -4,6 +4,7 @@ import threading from notifications.utils import remove_fcm_token # django imports +from rest_framework.viewsets import GenericViewSet, mixins from datetime import datetime, timedelta from rest_framework import viewsets, status, views from rest_framework.decorators import action @@ -26,14 +27,15 @@ from django.conf import settings from guardian.models import Guardian from junior.models import Junior, JuniorPoints from guardian.utils import upload_image_to_alibaba -from account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification +from account.models import (UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification, + ForceUpdate) from django.contrib.auth.models import User from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer, ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer, GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer, DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer, UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer, - AdminLoginSerializer) + AdminLoginSerializer, ForceUpdateSerializer) from rest_framework_simplejwt.tokens import RefreshToken from base.messages import ERROR_CODE, SUCCESS_CODE from base.constants import NUMBER, ZOD, JUN, GRD, USER_TYPE_FLAG @@ -673,3 +675,27 @@ class AccessTokenAPIView(views.APIView): data = {"auth_token": access_token} return custom_response(None, data, response_status=status.HTTP_200_OK) + +class ForceUpdateViewSet(GenericViewSet, mixins.CreateModelMixin): + """FAQ view set""" + + serializer_class = ForceUpdateSerializer + http_method_names = ['post'] + + + def create(self, request, *args, **kwargs): + """ + faq create api method + :param request: + :param args: version, device type + :param kwargs: + :return: success message + """ + if ForceUpdate.objects.all().count() >= 2: + return custom_error_response(ERROR_CODE['2080'], response_status=status.HTTP_400_BAD_REQUEST) + obj_data = [ForceUpdate(**item) for item in request.data] + try: + ForceUpdate.objects.bulk_create(obj_data) + return custom_response(SUCCESS_CODE["3045"], response_status=status.HTTP_200_OK) + except Exception as e: + return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/base/messages.py b/base/messages.py index d87b48e..ca9edb6 100644 --- a/base/messages.py +++ b/base/messages.py @@ -105,6 +105,8 @@ ERROR_CODE = { "2076": "This junior already associate with you", "2077": "You can not add guardian", "2078": "This junior is not associate with you", + "2079": "Please update your app version for enjoying uninterrupted services", + "2080": "Can not add App version" } """Success message code""" diff --git a/guardian/views.py b/guardian/views.py index 80c1d9b..1bcf86b 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -178,7 +178,7 @@ class CreateTaskAPIView(viewsets.ModelViewSet): junior = request.data['junior'] junior_id = Junior.objects.filter(id=junior).last() guardian_data = Guardian.objects.filter(user=request.user).last() - if guardian_data.guardian_code in junior_id.guardian_code: + if guardian_data.guardian_code not in junior_id.guardian_code: return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST) allowed_extensions = ['.jpg', '.jpeg', '.png'] if not any(extension in str(image) for extension in allowed_extensions): diff --git a/junior/serializers.py b/junior/serializers.py index 01e5adc..4530350 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -292,7 +292,7 @@ class AddJuniorSerializer(serializers.ModelSerializer): referral_code_used=guardian_data.referral_code, is_password_set=False, is_verified=True, guardian_code_status=GUARDIAN_CODE_STATUS[1][0]) - JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data, + JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data, relationship=relationship) total_junior = Junior.objects.all().count() JuniorPoints.objects.create(junior=junior_data, position=total_junior) @@ -445,7 +445,7 @@ class AddGuardianSerializer(serializers.ModelSerializer): user_type=str(NUMBER['two']), expired_at=expiry_time, is_verified=True) UserNotification.objects.get_or_create(user=user) - JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data, + JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data, relationship=relationship) """Notification email""" diff --git a/junior/views.py b/junior/views.py index 62c5fff..b53da2d 100644 --- a/junior/views.py +++ b/junior/views.py @@ -781,6 +781,3 @@ class FAQViewSet(GenericViewSet, mixins.CreateModelMixin, serializer = self.serializer_class(paginated_queryset, many=True) return custom_response(None, data=serializer.data, response_status=status.HTTP_200_OK) - - - From 19acef10ef0974e7401eb27a4a997bf085dd16d8 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Tue, 22 Aug 2023 19:03:39 +0530 Subject: [PATCH 39/83] added notitifcation for association rejected, approved, added mark as read api --- guardian/serializers.py | 6 ++-- guardian/views.py | 5 ++-- junior/serializers.py | 9 ++---- notifications/constants.py | 56 ++++++++++++++++---------------------- notifications/views.py | 41 +++------------------------- 5 files changed, 37 insertions(+), 80 deletions(-) diff --git a/guardian/serializers.py b/guardian/serializers.py index 8db860b..af11acc 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -28,7 +28,7 @@ from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship from .utils import real_time, convert_timedelta_into_datetime, update_referral_points # notification's constant -from notifications.constants import TASK_POINTS, TASK_REJECTED +from notifications.constants import TASK_APPROVED, TASK_REJECTED # send notification function from notifications.utils import send_notification, send_notification_to_junior from django.core.exceptions import ValidationError @@ -420,7 +420,7 @@ class ApproveTaskSerializer(serializers.ModelSerializer): # update complete time of task # instance.completed_on = real_time() instance.completed_on = timezone.now().astimezone(pytz.utc) - send_notification_to_junior.delay(TASK_POINTS, None, junior_details.auth.id, + send_notification_to_junior.delay(TASK_APPROVED, instance.guardian.user.id, junior_details.auth.id, {'task_id': instance.id}) else: # reject the task @@ -429,7 +429,7 @@ class ApproveTaskSerializer(serializers.ModelSerializer): # update reject time of task # instance.rejected_on = real_time() instance.rejected_on = timezone.now().astimezone(pytz.utc) - send_notification_to_junior.delay(TASK_REJECTED, None, junior_details.auth.id, + send_notification_to_junior.delay(TASK_REJECTED, instance.guardian.user.id, junior_details.auth.id, {'task_id': instance.id}) instance.save() junior_data.save() diff --git a/guardian/views.py b/guardian/views.py index 80c1d9b..3f1b49b 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -38,7 +38,7 @@ from account.utils import custom_response, custom_error_response, OTP_EXPIRY, se from base.messages import ERROR_CODE, SUCCESS_CODE from base.constants import NUMBER, GUARDIAN_CODE_STATUS from .utils import upload_image_to_alibaba -from notifications.constants import REGISTRATION, TASK_ASSIGNED, LEADERBOARD_RANKING +from notifications.constants import REGISTRATION, TASK_ASSIGNED, ASSOCIATE_APPROVED, ASSOCIATE_REJECTED from notifications.utils import send_notification_to_junior """ Define APIs """ @@ -259,7 +259,6 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): # Update the position field for each JuniorPoints object for index, junior in enumerate(junior_total_points): junior.position = index + 1 - # send_notification_to_junior.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {}) junior.save() serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) @@ -293,11 +292,13 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet): if serializer.is_valid(): # save serializer serializer.save() + send_notification_to_junior.delay(ASSOCIATE_APPROVED, guardian.user.id, junior_queryset.auth.id) return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) else: junior_queryset.guardian_code = None junior_queryset.guardian_code_status = str(NUMBER['one']) junior_queryset.save() + send_notification_to_junior.delay(ASSOCIATE_REJECTED, guardian.user.id, junior_queryset.auth.id) return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/junior/serializers.py b/junior/serializers.py index 040896b..f105dc5 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -22,8 +22,8 @@ from account.models import UserEmailOtp, UserNotification from junior.utils import junior_notification_email, junior_approval_mail from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian -from notifications.constants import (INVITATION, ASSOCIATE_REQUEST, SKIPPED_PROFILE_SETUP, TASK_ACTION, - TASK_SUBMITTED) +from notifications.constants import (ASSOCIATE_REQUEST, JUNIOR_ADDED, TASK_ACTION, + ) from web_admin.models import ArticleCard class ListCharField(serializers.ListField): @@ -307,7 +307,7 @@ class AddJuniorSerializer(serializers.ModelSerializer): """Notification email""" junior_notification_email.delay(email, full_name, email, password) # push notification - send_notification_to_junior.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {}) + send_notification_to_junior.delay(JUNIOR_ADDED, None, junior_data.auth.id, {}) return junior_data @@ -339,8 +339,6 @@ class CompleteTaskSerializer(serializers.ModelSerializer): instance.task_status = str(NUMBER['four']) instance.is_approved = False instance.save() - send_notification_to_junior.delay(TASK_SUBMITTED, instance.guardian.user.id, - instance.junior.auth.id, {'task_id': instance.id}) send_notification_to_guardian.delay(TASK_ACTION, instance.junior.auth.id, instance.guardian.user.id, {'task_id': instance.id}) return instance @@ -452,7 +450,6 @@ class AddGuardianSerializer(serializers.ModelSerializer): """Notification email""" junior_notification_email(email, full_name, email, password) junior_approval_mail.delay(email, full_name) - send_notification_to_junior.delay(INVITATION, guardian_data.user.id, junior_data.auth.id, {}) send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior_data.auth.id, guardian_data.user.id, {}) return guardian_data diff --git a/notifications/constants.py b/notifications/constants.py index 8bb6a6d..0cc5a0f 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -2,19 +2,17 @@ notification constants file """ REGISTRATION = 1 -INVITATION = 2 ASSOCIATE_REQUEST = 3 -REFERRAL_POINTS = 4 -SKIPPED_PROFILE_SETUP = 5 +ASSOCIATE_REJECTED = 4 +ASSOCIATE_APPROVED = 5 +REFERRAL_POINTS = 6 +JUNIOR_ADDED = 7 -TASK_ASSIGNED = 6 -TASK_SUBMITTED = 7 -TASK_ACTION = 8 -TASK_REJECTED = 9 -TASK_APPROVED = 10 -TASK_POINTS = 11 +TASK_ASSIGNED = 8 +TASK_ACTION = 9 +TASK_REJECTED = 10 +TASK_APPROVED = 11 -LEADERBOARD_RANKING = 12 REMOVE_JUNIOR = 13 TEST_NOTIFICATION = 99 @@ -24,21 +22,27 @@ NOTIFICATION_DICT = { "title": "Successfully registered!", "body": "You have registered successfully. Now login and complete your profile." }, - INVITATION: { - "title": "Invitation sent!", - "body": "Invitation has been successfully sent." - }, ASSOCIATE_REQUEST: { "title": "Associate request!", "body": "You have request from {from_user} to associate with you." }, + + ASSOCIATE_REJECTED: { + "title": "Associate request rejected!", + "body": "Your request to associate has been rejected by {from_user}." + }, + + ASSOCIATE_APPROVED: { + "title": "Associate request approved!", + "body": "Your request to associate has been approved by {from_user}." + }, # Juniors will receive Notifications for every Points earned by referrals REFERRAL_POINTS: { "title": "Earn Referral points!", "body": "You earn 5 points for referral." }, # Juniors will receive notification once any custodians add them in their account - SKIPPED_PROFILE_SETUP: { + JUNIOR_ADDED: { "title": "Profile already setup!", "body": "Your guardian has already setup your profile." }, @@ -47,34 +51,22 @@ NOTIFICATION_DICT = { "title": "New task assigned!", "body": "{from_user} has assigned you a new task." }, - TASK_SUBMITTED: { - "title": "Task submitted!", - "body": "Your task has been submitted for approval." - }, # Guardian will receive notification as soon as junior send task for approval TASK_ACTION: { "title": "Task completion approval!", "body": "You have request from {from_user} for task completion." }, - # Juniors will receive notification as soon as their task is approved or reject by custodians + # Juniors will receive notification as soon as their task is rejected by custodians TASK_REJECTED: { "title": "Task completion rejected!", "body": "Your task completion request has been rejected by {from_user}." }, + # Juniors will receive notification as soon as their task is approved by custodians + # and for every Points earned by Task completion TASK_APPROVED: { "title": "Task completion approved!", - "body": "Your task completion request has been approved by {from_user}." - }, - # Juniors will receive Notifications for every Points earned either by Task completion - # Juniors will receive notification as soon as their task is approved or reject by custodians - TASK_POINTS: { - "title": "Earned Task points!", - "body": "You earn 5 points for task." - }, - # Juniors will receive Notification related to Leaderboard progress - LEADERBOARD_RANKING: { - "title": "Leader board rank!", - "body": "Your rank is ." + "body": "Your task completion request has been approved by {from_user}. " + "Also you earned 5 points for successful completion." }, # Juniors will receive notification as soon as their custodians remove them from account REMOVE_JUNIOR: { diff --git a/notifications/views.py b/notifications/views.py index 154751a..8674e85 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -30,15 +30,8 @@ class NotificationViewSet(viewsets.GenericViewSet): paginator = self.pagination_class() paginated_queryset = paginator.paginate_queryset(queryset, request) serializer = self.serializer_class(paginated_queryset, many=True) - self.mark_notifications_as_read(serializer.data) return custom_response(None, serializer.data) - @staticmethod - def mark_notifications_as_read(data): - """ used to mark notification queryset as read """ - ids = [obj['id'] for obj in data] - Notification.objects.filter(id__in=ids).update(is_read=True) - @action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice) def fcm_registration(self, request): """ @@ -62,36 +55,10 @@ class NotificationViewSet(viewsets.GenericViewSet): {'task_id': None}) return custom_response(SUCCESS_CODE["3000"]) - @action(methods=['get'], detail=False, url_path='list', url_name='list', - serializer_class=NotificationListSerializer) - def notification_list(self, request): + @action(methods=['get'], url_path='mark-as-read', url_name='mark-as-read', detail=True, ) + def mark_as_read(self, request, *args, **kwargs): """ notification list """ - try: - queryset = Notification.objects.filter(notification_to=request.user) - serializer = NotificationListSerializer(queryset, many=True) - return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) - except Exception as e: - return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) - - -class ReadNotification(views.APIView): - """Update notification API - Payload - { - "notification_id": [] - }""" - serializer_class = ReadNotificationSerializer - model = Notification - permission_classes = [IsAuthenticated] - - def put(self, request, format=None): - try: - notification_id = self.request.data.get('notification_id') - notification_queryset = Notification.objects.filter(id__in=notification_id, - notification_to=self.request.user).update(is_read=True) - if notification_queryset: - return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK) - except Exception as e: - return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) + Notification.objects.filter(id=kwargs['pk']).update(is_read=True) + return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK) From 5f3a9c35fa889675256cf262644ce03f0635a628 Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 22 Aug 2023 19:14:57 +0530 Subject: [PATCH 40/83] junior guardiuan code --- junior/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junior/views.py b/junior/views.py index b53da2d..03f8ae7 100644 --- a/junior/views.py +++ b/junior/views.py @@ -206,7 +206,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet): guardian = Guardian.objects.filter(user=self.request.user).first() if not junior: return none - if guardian.guardian_code in junior.guardian_code: + if junior.guardian_code and (guardian.guardian_code in junior.guardian_code): return False if type(junior.guardian_code) is list: junior.guardian_code.append(guardian.guardian_code) From 290089b9ef571f6de40209c4c92800d0bce4dcdd Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Tue, 22 Aug 2023 19:26:30 +0530 Subject: [PATCH 41/83] added notitifcation for association rejected, approved, added mark as read api --- notifications/urls.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/notifications/urls.py b/notifications/urls.py index 713aae3..b184d02 100644 --- a/notifications/urls.py +++ b/notifications/urls.py @@ -6,7 +6,7 @@ from django.urls import path, include from rest_framework import routers # local imports -from notifications.views import NotificationViewSet, ReadNotification +from notifications.views import NotificationViewSet # initiate router router = routers.SimpleRouter() @@ -15,5 +15,4 @@ router.register('notifications', NotificationViewSet, basename='notifications') urlpatterns = [ path('api/v1/', include(router.urls)), - path('api/v1/read-notification/', ReadNotification.as_view()), ] From 70685510503792312dd387017b01ceb18cea656a Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 23 Aug 2023 11:16:46 +0530 Subject: [PATCH 42/83] force update not affect admin's api --- account/custom_middleware.py | 5 +++-- account/utils.py | 8 ++++++-- account/views.py | 2 +- base/messages.py | 3 ++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/account/custom_middleware.py b/account/custom_middleware.py index f658fd3..fb032dc 100644 --- a/account/custom_middleware.py +++ b/account/custom_middleware.py @@ -61,7 +61,8 @@ class CustomMiddleware(object): response = custom_response(custom_error) force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last() api_endpoint_checks = not any(endpoint in api_endpoint for endpoint in ['/admin/', '/api/v1/admin/']) - if not force_update and api_endpoint_checks: - custom_error = custom_error_response(ERROR_CODE['2079'], response_status=status.HTTP_404_NOT_FOUND) + if not force_update and version and device_type: + custom_error = custom_error_response(ERROR_CODE['2079'], + response_status=status.HTTP_308_PERMANENT_REDIRECT) response = custom_response(custom_error) return response diff --git a/account/utils.py b/account/utils.py index 57fc273..04c6791 100644 --- a/account/utils.py +++ b/account/utils.py @@ -204,8 +204,12 @@ def custom_error_response(detail, response_status): if not detail: """when details is empty""" detail = {} - return Response({"error": detail, "status": "failed", "code": response_status}, status=status.HTTP_400_BAD_REQUEST) - + if response_status == 406: + return Response({"error": detail, "status": "failed", "code": response_status,}, + status=status.HTTP_308_PERMANENT_REDIRECT) + else: + return Response({"error": detail, "status": "failed", "code": response_status}, + status=status.HTTP_400_BAD_REQUEST) def get_user_data(attrs): """ diff --git a/account/views.py b/account/views.py index 9d87c93..3b4c62c 100644 --- a/account/views.py +++ b/account/views.py @@ -696,6 +696,6 @@ class ForceUpdateViewSet(GenericViewSet, mixins.CreateModelMixin): obj_data = [ForceUpdate(**item) for item in request.data] try: ForceUpdate.objects.bulk_create(obj_data) - return custom_response(SUCCESS_CODE["3045"], response_status=status.HTTP_200_OK) + return custom_response(SUCCESS_CODE["3046"], response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/base/messages.py b/base/messages.py index ca9edb6..8930bdc 100644 --- a/base/messages.py +++ b/base/messages.py @@ -173,7 +173,8 @@ SUCCESS_CODE = { # remove guardian code request "3044": "Remove guardian code request successfully", # create faq - "3045": "Create FAQ data" + "3045": "Create FAQ data", + "3046": "Add App version successfully" } """status code error""" From 8d159ac3a489c9c4327e611bac7871365ebe193f Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 23 Aug 2023 11:45:38 +0530 Subject: [PATCH 43/83] changes in profile API --- account/views.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/account/views.py b/account/views.py index 3b4c62c..e68732e 100644 --- a/account/views.py +++ b/account/views.py @@ -526,17 +526,18 @@ class ProfileAPIViewSet(viewsets.ModelViewSet): """profile view Params user_type""" - if str(self.request.GET.get('user_type')) == '1': + user_type = request.META.get('HTTP_USER_TYPE') + if str(user_type) == '1': junior_data = Junior.objects.filter(auth=self.request.user).last() if junior_data: serializer = JuniorProfileSerializer(junior_data) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) - elif str(self.request.GET.get('user_type')) == '2': + elif str(user_type) == '2': guardian_data = Guardian.objects.filter(user=self.request.user).last() if guardian_data: serializer = GuardianProfileSerializer(guardian_data) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) - + return custom_error_response(None, response_status=status.HTTP_400_BAD_REQUEST) class UploadImageAPIViewSet(viewsets.ModelViewSet): """upload task image""" serializer_class = DefaultTaskImagesSerializer From 4e0c6a91f4918efbd290b3289bd8e467602f64d3 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 23 Aug 2023 12:29:53 +0530 Subject: [PATCH 44/83] response 308 in force update --- account/custom_middleware.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/account/custom_middleware.py b/account/custom_middleware.py index fb032dc..ca89a86 100644 --- a/account/custom_middleware.py +++ b/account/custom_middleware.py @@ -18,9 +18,9 @@ from guardian.models import Guardian # user can login in single # device at a time""" -def custom_response(custom_error): +def custom_response(custom_error, response_status = status.HTTP_404_NOT_FOUND): """custom response""" - response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND) + response = Response(custom_error.data, status=response_status) # Set content type header to "application/json" response['Content-Type'] = 'application/json' # Render the response as JSON @@ -64,5 +64,5 @@ class CustomMiddleware(object): if not force_update and version and device_type: custom_error = custom_error_response(ERROR_CODE['2079'], response_status=status.HTTP_308_PERMANENT_REDIRECT) - response = custom_response(custom_error) + response = custom_response(custom_error, status.HTTP_308_PERMANENT_REDIRECT) return response From 56e1484b87464d508532482acae5b8001b12472d Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Wed, 23 Aug 2023 12:54:39 +0530 Subject: [PATCH 45/83] mark ad read api modified --- notifications/serializers.py | 2 ++ notifications/views.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/notifications/serializers.py b/notifications/serializers.py index 14f1b20..2f0222f 100644 --- a/notifications/serializers.py +++ b/notifications/serializers.py @@ -40,6 +40,8 @@ class NotificationListSerializer(serializers.ModelSerializer): class ReadNotificationSerializer(serializers.ModelSerializer): """User task Serializer""" + id = serializers.ListSerializer(child=serializers.IntegerField()) + class Meta(object): """Meta class""" model = Notification diff --git a/notifications/views.py b/notifications/views.py index 8674e85..d60c429 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -55,10 +55,11 @@ class NotificationViewSet(viewsets.GenericViewSet): {'task_id': None}) return custom_response(SUCCESS_CODE["3000"]) - @action(methods=['get'], url_path='mark-as-read', url_name='mark-as-read', detail=True, ) + @action(methods=['patch'], url_path='mark-as-read', url_name='mark-as-read', detail=False, + serializer_class=ReadNotificationSerializer) def mark_as_read(self, request, *args, **kwargs): """ notification list """ - Notification.objects.filter(id=kwargs['pk']).update(is_read=True) + Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True) return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK) From 20644a6293a0f48e88f3863defdbdde13f1435ef Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 23 Aug 2023 17:15:54 +0530 Subject: [PATCH 46/83] google login with user type and is published article only display --- account/views.py | 44 ++++++++++++++++++++++++++++++++------ web_admin/views/article.py | 2 +- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/account/views.py b/account/views.py index e68732e..2785ae1 100644 --- a/account/views.py +++ b/account/views.py @@ -51,7 +51,8 @@ class GoogleLoginMixin(object): def google_login(request): """google login function""" access_token = request.data.get('access_token') - user_type = request.data.get('user_type') + user_type = request.META.get('HTTP_USER_TYPE') + device_id = request.META.get('HTTP_DEVICE_ID') if not access_token: return Response({'error': 'Access token is required.'}, status=status.HTTP_400_BAD_REQUEST) @@ -84,14 +85,24 @@ class GoogleLoginMixin(object): if user_data.exists(): if str(user_type) == '1': junior_query = Junior.objects.filter(auth=user_data.last()).last() + if not junior_query: + return custom_error_response( + ERROR_CODE["2071"], + response_status=status.HTTP_400_BAD_REQUEST + ) serializer = JuniorSerializer(junior_query) if str(user_type) == '2': guardian_query = Guardian.objects.filter(user=user_data.last()).last() + if not guardian_query: + return custom_error_response( + ERROR_CODE["2070"], + response_status=status.HTTP_400_BAD_REQUEST + ) serializer = GuardianSerializer(guardian_query) return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) - if not User.objects.filter(email__iexact=email).exists(): + else: user_obj = User.objects.create(username=email, email=email, first_name=first_name, last_name=last_name) if str(user_type) == '1': junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True, @@ -109,6 +120,10 @@ class GoogleLoginMixin(object): referral_code=generate_code(ZOD, user_obj.id) ) serializer = GuardianSerializer(guardian_query) + device_detail, created = UserDeviceDetails.objects.get_or_create(user=user_obj) + if device_detail: + device_detail.device_id = device_id + device_detail.save() # Return a JSON response with the user's email and name return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) @@ -137,7 +152,8 @@ class SigninWithApple(views.APIView): }""" def post(self, request): token = request.data.get("access_token") - user_type = request.data.get("user_type") + user_type = request.META.get('HTTP_USER_TYPE') + device_id = request.META.get('HTTP_DEVICE_ID') try: decoded_data = jwt.decode(token, options={"verify_signature": False}) user_data = {"email": decoded_data.get('email'), "username": decoded_data.get('email'), "is_active": True} @@ -145,11 +161,21 @@ class SigninWithApple(views.APIView): try: user = User.objects.get(email=decoded_data.get("email")) if str(user_type) == '1': - junior_query = Junior.objects.filter(auth=user).last() - serializer = JuniorSerializer(junior_query) + junior_data = Junior.objects.filter(auth=user).last() + if not junior_data: + return custom_error_response( + ERROR_CODE["2071"], + response_status=status.HTTP_400_BAD_REQUEST + ) + serializer = JuniorSerializer(junior_data) if str(user_type) == '2': - guardian_query = Guardian.objects.filter(user=user).last() - serializer = GuardianSerializer(guardian_query) + guardian_data = Guardian.objects.filter(user=user).last() + if not guardian_data: + return custom_error_response( + ERROR_CODE["2070"], + response_status=status.HTTP_400_BAD_REQUEST + ) + serializer = GuardianSerializer(guardian_data) return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) @@ -169,6 +195,10 @@ class SigninWithApple(views.APIView): guardian_code=generate_code(GRD, user.id), referral_code=generate_code(ZOD, user.id)) serializer = GuardianSerializer(guardian_query) + device_detail, created = UserDeviceDetails.objects.get_or_create(user=user_obj) + if device_detail: + device_detail.device_id = device_id + device_detail.save() return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) except Exception as e: diff --git a/web_admin/views/article.py b/web_admin/views/article.py index 6570f6f..f362f8b 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -229,7 +229,7 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin): http_method_names = ['get',] def get_queryset(self): - article = self.queryset.objects.filter(is_deleted=False).prefetch_related( + article = self.queryset.objects.filter(is_deleted=False, is_published=True).prefetch_related( 'article_cards', 'article_survey', 'article_survey__options' ).order_by('-created_at') queryset = self.filter_queryset(article) From 89dda61bff58e1afd5782fce695974c1a0f7a710 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 23 Aug 2023 17:46:20 +0530 Subject: [PATCH 47/83] google login with user type and is published article only display --- account/views.py | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/account/views.py b/account/views.py index 2785ae1..8dbb2f7 100644 --- a/account/views.py +++ b/account/views.py @@ -91,7 +91,7 @@ class GoogleLoginMixin(object): response_status=status.HTTP_400_BAD_REQUEST ) serializer = JuniorSerializer(junior_query) - if str(user_type) == '2': + elif str(user_type) == '2': guardian_query = Guardian.objects.filter(user=user_data.last()).last() if not guardian_query: return custom_error_response( @@ -99,6 +99,11 @@ class GoogleLoginMixin(object): response_status=status.HTTP_400_BAD_REQUEST ) serializer = GuardianSerializer(guardian_query) + else: + return custom_error_response( + ERROR_CODE["2069"], + response_status=status.HTTP_400_BAD_REQUEST + ) return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) @@ -113,13 +118,19 @@ class GoogleLoginMixin(object): serializer = JuniorSerializer(junior_query) position = Junior.objects.all().count() JuniorPoints.objects.create(junior=junior_query, position=position) - if str(user_type) == '2': + elif str(user_type) == '2': guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True, image=profile_picture,signup_method='2', guardian_code=generate_code(GRD, user_obj.id), referral_code=generate_code(ZOD, user_obj.id) ) serializer = GuardianSerializer(guardian_query) + else: + user_obj.delete() + return custom_error_response( + ERROR_CODE["2069"], + response_status=status.HTTP_400_BAD_REQUEST + ) device_detail, created = UserDeviceDetails.objects.get_or_create(user=user_obj) if device_detail: device_detail.device_id = device_id @@ -168,7 +179,7 @@ class SigninWithApple(views.APIView): response_status=status.HTTP_400_BAD_REQUEST ) serializer = JuniorSerializer(junior_data) - if str(user_type) == '2': + elif str(user_type) == '2': guardian_data = Guardian.objects.filter(user=user).last() if not guardian_data: return custom_error_response( @@ -176,6 +187,11 @@ class SigninWithApple(views.APIView): response_status=status.HTTP_400_BAD_REQUEST ) serializer = GuardianSerializer(guardian_data) + else: + return custom_error_response( + ERROR_CODE["2069"], + response_status=status.HTTP_400_BAD_REQUEST + ) return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) @@ -189,13 +205,19 @@ class SigninWithApple(views.APIView): serializer = JuniorSerializer(junior_query) position = Junior.objects.all().count() JuniorPoints.objects.create(junior=junior_query, position=position) - if str(user_type) == '2': + elif str(user_type) == '2': guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True, signup_method='3', guardian_code=generate_code(GRD, user.id), referral_code=generate_code(ZOD, user.id)) serializer = GuardianSerializer(guardian_query) - device_detail, created = UserDeviceDetails.objects.get_or_create(user=user_obj) + else: + user.delete() + return custom_error_response( + ERROR_CODE["2069"], + response_status=status.HTTP_400_BAD_REQUEST + ) + device_detail, created = UserDeviceDetails.objects.get_or_create(user=user) if device_detail: device_detail.device_id = device_id device_detail.save() From bb3441e4eda4ec1e05ec9fad4e5fd679003316de Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 23 Aug 2023 19:23:31 +0530 Subject: [PATCH 48/83] remove guardian code in both case --- junior/serializers.py | 7 +++++-- junior/views.py | 25 ++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/junior/serializers.py b/junior/serializers.py index 4530350..ec85608 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -505,8 +505,11 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer): model = Junior fields = ('id', ) def update(self, instance, validated_data): - instance.guardian_code = None - instance.guardian_code_status = str(NUMBER['one']) + guardian_code = self.context['guardian_code'] + if self.instance and guardian_code in self.instance.guardian_code: + instance.guardian_code.remove(guardian_code) + if len(instance.guardian_code) == 0: + instance.guardian_code_status = str(NUMBER['one']) instance.save() return instance diff --git a/junior/views.py b/junior/views.py index 03f8ae7..558d9e5 100644 --- a/junior/views.py +++ b/junior/views.py @@ -724,10 +724,29 @@ class RemoveGuardianCodeAPIView(views.APIView): def put(self, request, format=None): try: - junior_queryset = Junior.objects.filter(auth=self.request.user).last() - if junior_queryset: + user_type = request.META.get('HTTP_USER_TYPE') + junior_id = request.data.get('junior_id') + guardian_id = request.data.get('guardian_id') + my_dict = {} + if user_type and str(user_type) == str(NUMBER['one']) and guardian_id: + queryset = Junior.objects.filter(auth__email=self.request.user, is_active=True, is_deleted=False).last() + guardian_code = Guardian.objects.filter(id=guardian_id).last().guardian_code + my_dict.update({"guardian_code": guardian_code}) + elif user_type and str(user_type) == str(NUMBER['two']) and junior_id: + guardian_code = Guardian.objects.filter(user=self.request.user, + is_active=True, is_deleted=False).last().guardian_code + queryset = Junior.objects.filter(id=junior_id).last() + my_dict.update({"guardian_code": guardian_code}) + else: + return custom_error_response( + ERROR_CODE["2069"], + response_status=status.HTTP_400_BAD_REQUEST + ) + if queryset: # use RemoveGuardianCodeSerializer serializer - serializer = RemoveGuardianCodeSerializer(junior_queryset, data=request.data, partial=True) + serializer = RemoveGuardianCodeSerializer(queryset, data=request.data, + context=my_dict, + partial=True) if serializer.is_valid(): # save serializer serializer.save() From 8f9450b7432123513e514823fc31229ae48b8180 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 24 Aug 2023 11:10:20 +0530 Subject: [PATCH 49/83] set guardian code is None --- account/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account/utils.py b/account/utils.py index 04c6791..956ae23 100644 --- a/account/utils.py +++ b/account/utils.py @@ -93,7 +93,7 @@ def junior_account_update(user_tb): # Update junior account junior_data.is_active = False junior_data.is_verified = False - junior_data.guardian_code = '{}' + junior_data.guardian_code = None junior_data.guardian_code_status = str(NUMBER['one']) junior_data.is_deleted = True junior_data.save() From f54160865655df1f1a1ebccae5111697fc283985 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 24 Aug 2023 12:10:18 +0530 Subject: [PATCH 50/83] changed notification method, added celery task to notify expiring task --- account/utils.py | 3 +- base/tasks.py | 29 ++++++++++++++ celerybeat-schedule | Bin 16384 -> 16384 bytes guardian/serializers.py | 12 +++--- guardian/utils.py | 4 +- guardian/views.py | 13 ++++--- junior/serializers.py | 14 +++---- junior/views.py | 4 +- notifications/constants.py | 26 ++++++++++--- notifications/utils.py | 77 ++++++++++++++++++++++--------------- notifications/views.py | 35 ++++++++++++----- zod_bank/celery.py | 7 ++++ 12 files changed, 153 insertions(+), 71 deletions(-) diff --git a/account/utils.py b/account/utils.py index 04c6791..59693c7 100644 --- a/account/utils.py +++ b/account/utils.py @@ -282,8 +282,9 @@ def generate_code(value, user_id): OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1) + def get_user_full_name(user_obj): """ to get user's full name """ - return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.last_name else user_obj.first_name + return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.first_name or user_obj.last_name else "User" diff --git a/base/tasks.py b/base/tasks.py index 791be81..a737380 100644 --- a/base/tasks.py +++ b/base/tasks.py @@ -1,6 +1,8 @@ """ web_admin tasks file """ +import datetime + # third party imports from celery import shared_task from templated_email import send_templated_mail @@ -8,6 +10,12 @@ from templated_email import send_templated_mail # django imports from django.conf import settings +# local imports +from base.constants import PENDING, IN_PROGRESS, JUNIOR +from guardian.models import JuniorTask +from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING +from notifications.utils import send_notification + @shared_task def send_email_otp(email, verification_code): @@ -27,3 +35,24 @@ def send_email_otp(email, verification_code): } ) return True + + +@shared_task() +def notify_task_expiry(): + """ + task to send notification for those task which expiring soon + :return: + """ + all_pending_tasks = JuniorTask.objects.filter( + task_status__in=[PENDING, IN_PROGRESS], + due_date__range=[datetime.datetime.now().date(), + (datetime.datetime.now().date() + datetime.timedelta(days=1))]) + if pending_tasks := all_pending_tasks.filter(task_status=PENDING): + for task in pending_tasks: + send_notification(PENDING_TASK_EXPIRING, None, None, task.junior.auth.id, + {'task_id': task.id}) + if in_progress_tasks := all_pending_tasks.filter(task_status=IN_PROGRESS): + for task in in_progress_tasks: + send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth.id, JUNIOR, task.guardian.user.id, + {'task_id': task.id}) + return True diff --git a/celerybeat-schedule b/celerybeat-schedule index 68297a7db074145de61828e2a94f36b892493ec5..fef85ada273c6b1f27fa57f6340942760415d802 100644 GIT binary patch delta 28 kcmZo@U~Fh$+;GK!U4l)Po26~j;j7*z#4KMQn0EVLpBme*a delta 28 kcmZo@U~Fh$+;GK!U6f0fmE~~H;jEtLg4KMQn0EeOpH2?qr diff --git a/guardian/serializers.py b/guardian/serializers.py index af11acc..3e24265 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -24,13 +24,13 @@ from account.models import UserProfile, UserEmailOtp, UserNotification from account.utils import generate_code from junior.serializers import JuniorDetailSerializer from base.messages import ERROR_CODE, SUCCESS_CODE -from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user +from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user, GUARDIAN from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship from .utils import real_time, convert_timedelta_into_datetime, update_referral_points # notification's constant from notifications.constants import TASK_APPROVED, TASK_REJECTED # send notification function -from notifications.utils import send_notification, send_notification_to_junior +from notifications.utils import send_notification from django.core.exceptions import ValidationError from django.utils.translation import gettext as _ @@ -420,8 +420,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer): # update complete time of task # instance.completed_on = real_time() instance.completed_on = timezone.now().astimezone(pytz.utc) - send_notification_to_junior.delay(TASK_APPROVED, instance.guardian.user.id, junior_details.auth.id, - {'task_id': instance.id}) + send_notification.delay(TASK_APPROVED, instance.guardian.user.id, GUARDIAN, + junior_details.auth.id, {'task_id': instance.id}) else: # reject the task instance.task_status = str(NUMBER['three']) @@ -429,8 +429,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer): # update reject time of task # instance.rejected_on = real_time() instance.rejected_on = timezone.now().astimezone(pytz.utc) - send_notification_to_junior.delay(TASK_REJECTED, instance.guardian.user.id, junior_details.auth.id, - {'task_id': instance.id}) + send_notification.delay(TASK_REJECTED, instance.guardian.user.id, GUARDIAN, + junior_details.auth.id, {'task_id': instance.id}) instance.save() junior_data.save() return junior_details diff --git a/guardian/utils.py b/guardian/utils.py index 14bd36a..80cbc56 100644 --- a/guardian/utils.py +++ b/guardian/utils.py @@ -21,7 +21,7 @@ from zod_bank.celery import app # notification's constant from notifications.constants import REFERRAL_POINTS # send notification function -from notifications.utils import send_notification, send_notification_to_junior +from notifications.utils import send_notification # Define upload image on @@ -117,7 +117,7 @@ def update_referral_points(referral_code, referral_code_used): junior_query.total_points = junior_query.total_points + NUMBER['five'] junior_query.referral_points = junior_query.referral_points + NUMBER['five'] junior_query.save() - send_notification_to_junior.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {}) + send_notification.delay(REFERRAL_POINTS, None, None, junior_queryset.auth.id, {}) diff --git a/guardian/views.py b/guardian/views.py index e49beaf..2cda5c1 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -36,10 +36,10 @@ from account.models import UserEmailOtp, UserNotification, UserDeviceDetails from .tasks import generate_otp from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email from base.messages import ERROR_CODE, SUCCESS_CODE -from base.constants import NUMBER, GUARDIAN_CODE_STATUS +from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN from .utils import upload_image_to_alibaba from notifications.constants import REGISTRATION, TASK_ASSIGNED, ASSOCIATE_APPROVED, ASSOCIATE_REJECTED -from notifications.utils import send_notification_to_junior +from notifications.utils import send_notification """ Define APIs """ # Define Signup API, @@ -202,8 +202,8 @@ class CreateTaskAPIView(viewsets.ModelViewSet): # save serializer task = serializer.save() - send_notification_to_junior.delay(TASK_ASSIGNED, request.auth.payload['user_id'], - junior_id.auth.id, {'task_id': task.id}) + send_notification.delay(TASK_ASSIGNED, request.auth.payload['user_id'], GUARDIAN, + junior_id.auth.id, {'task_id': task.id}) return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) except Exception as e: @@ -292,13 +292,14 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet): if serializer.is_valid(): # save serializer serializer.save() - send_notification_to_junior.delay(ASSOCIATE_APPROVED, guardian.user.id, junior_queryset.auth.id) + send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN, + junior_queryset.auth.id) return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) else: junior_queryset.guardian_code = None junior_queryset.guardian_code_status = str(NUMBER['one']) junior_queryset.save() - send_notification_to_junior.delay(ASSOCIATE_REJECTED, guardian.user.id, junior_queryset.auth.id) + send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id) return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/junior/serializers.py b/junior/serializers.py index 07a38cb..8c00bfb 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -16,12 +16,12 @@ from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, Juni from guardian.tasks import generate_otp from base.messages import ERROR_CODE, SUCCESS_CODE from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED, - GUARDIAN_CODE_STATUS) + GUARDIAN_CODE_STATUS, JUNIOR) from guardian.models import Guardian, JuniorTask from account.models import UserEmailOtp, UserNotification from junior.utils import junior_notification_email, junior_approval_mail from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime -from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian +from notifications.utils import send_notification from notifications.constants import (ASSOCIATE_REQUEST, JUNIOR_ADDED, TASK_ACTION, ) from web_admin.models import ArticleCard @@ -98,7 +98,7 @@ class CreateJuniorSerializer(serializers.ModelSerializer): JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) junior.guardian_code_status = str(NUMBER['three']) junior_approval_mail.delay(user.email, user.first_name) - send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior.auth.id, guardian_data.user.id, {}) + send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {}) junior.dob = validated_data.get('dob', junior.dob) junior.passcode = validated_data.get('passcode', junior.passcode) @@ -307,7 +307,7 @@ class AddJuniorSerializer(serializers.ModelSerializer): """Notification email""" junior_notification_email.delay(email, full_name, email, password) # push notification - send_notification_to_junior.delay(JUNIOR_ADDED, None, junior_data.auth.id, {}) + send_notification.delay(JUNIOR_ADDED, None, None, junior_data.auth.id, {}) return junior_data @@ -339,8 +339,8 @@ class CompleteTaskSerializer(serializers.ModelSerializer): instance.task_status = str(NUMBER['four']) instance.is_approved = False instance.save() - send_notification_to_guardian.delay(TASK_ACTION, instance.junior.auth.id, - instance.guardian.user.id, {'task_id': instance.id}) + send_notification.delay(TASK_ACTION, instance.junior.auth.id, JUNIOR, + instance.guardian.user.id, {'task_id': instance.id}) return instance class JuniorPointsSerializer(serializers.ModelSerializer): @@ -450,7 +450,7 @@ class AddGuardianSerializer(serializers.ModelSerializer): """Notification email""" junior_notification_email(email, full_name, email, password) junior_approval_mail.delay(email, full_name) - send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior_data.auth.id, guardian_data.user.id, {}) + send_notification.delay(ASSOCIATE_REQUEST, junior_data.auth.id, JUNIOR, guardian_data.user.id, {}) return guardian_data class StartTaskSerializer(serializers.ModelSerializer): diff --git a/junior/views.py b/junior/views.py index 03f8ae7..15aa7f9 100644 --- a/junior/views.py +++ b/junior/views.py @@ -46,7 +46,7 @@ from base.constants import NUMBER, ARTICLE_STATUS, none from account.utils import custom_response, custom_error_response from guardian.utils import upload_image_to_alibaba from .utils import update_positions_based_on_points -from notifications.utils import send_notification, send_notification_to_junior +from notifications.utils import send_notification from notifications.constants import REMOVE_JUNIOR from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer, @@ -312,7 +312,7 @@ class RemoveJuniorAPIView(views.APIView): if serializer.is_valid(): # save serializer serializer.save() - send_notification_to_junior.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {}) + send_notification.delay(REMOVE_JUNIOR, None, None, junior_queryset.auth.id, {}) return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) else: diff --git a/notifications/constants.py b/notifications/constants.py index 0cc5a0f..f94e4aa 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -12,8 +12,9 @@ TASK_ASSIGNED = 8 TASK_ACTION = 9 TASK_REJECTED = 10 TASK_APPROVED = 11 - -REMOVE_JUNIOR = 13 +PENDING_TASK_EXPIRING = 12 +IN_PROGRESS_TASK_EXPIRING = 13 +REMOVE_JUNIOR = 15 TEST_NOTIFICATION = 99 @@ -22,16 +23,18 @@ NOTIFICATION_DICT = { "title": "Successfully registered!", "body": "You have registered successfully. Now login and complete your profile." }, + # user will receive notification as soon junior sign up application using their guardian code + # for association ASSOCIATE_REQUEST: { "title": "Associate request!", "body": "You have request from {from_user} to associate with you." }, - + # Juniors will receive notification when custodians reject their request for associate ASSOCIATE_REJECTED: { "title": "Associate request rejected!", "body": "Your request to associate has been rejected by {from_user}." }, - + # Juniors will receive notification when custodians approve their request for associate ASSOCIATE_APPROVED: { "title": "Associate request approved!", "body": "Your request to associate has been approved by {from_user}." @@ -54,7 +57,7 @@ NOTIFICATION_DICT = { # Guardian will receive notification as soon as junior send task for approval TASK_ACTION: { "title": "Task completion approval!", - "body": "You have request from {from_user} for task completion." + "body": "{from_user} completed her task {task_name}." }, # Juniors will receive notification as soon as their task is rejected by custodians TASK_REJECTED: { @@ -68,11 +71,24 @@ NOTIFICATION_DICT = { "body": "Your task completion request has been approved by {from_user}. " "Also you earned 5 points for successful completion." }, + # Juniors will receive notification when their task end date about to end + PENDING_TASK_EXPIRING: { + "title": "Task expiring soon!", + "body": "Your task {task_name} is expiring soon. Please complete it." + }, + # User will receive notification when their assigned task is about to end + # and juniors have not performed any action + IN_PROGRESS_TASK_EXPIRING: { + "title": "Task expiring soon!", + "body": "{from_user} didn't take any action on assigned task {task_name} and it's expiring soon. " + "Please assist to complete it." + }, # Juniors will receive notification as soon as their custodians remove them from account REMOVE_JUNIOR: { "title": "Disassociate by guardian!", "body": "Your guardian has disassociated you." }, + # Test notification TEST_NOTIFICATION: { "title": "Test Notification", "body": "This notification is for testing purpose from {from_user}." diff --git a/notifications/utils.py b/notifications/utils.py index 0d0b25b..bcb9749 100644 --- a/notifications/utils.py +++ b/notifications/utils.py @@ -11,7 +11,8 @@ from django.contrib.auth import get_user_model from account.models import UserNotification from account.utils import get_user_full_name -from guardian.models import Guardian +from base.constants import GUARDIAN, JUNIOR +from guardian.models import Guardian, JuniorTask from junior.models import Junior from notifications.constants import NOTIFICATION_DICT from notifications.models import Notification @@ -42,44 +43,64 @@ def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None: print(e) -def get_basic_detail(from_user_id, to_user_id): +def get_from_user_details(from_user_id, from_user_type): """ - used to get the basic details + used to get from user details """ - from_user = User.objects.get(id=from_user_id) if from_user_id else None - to_user = User.objects.get(id=to_user_id) - return from_user, to_user + from_user = None + from_user_name = None + from_user_image = None + if from_user_id: + if from_user_type == GUARDIAN: + guardian = Guardian.objects.filter(user_id=from_user_id).select_related('user').first() + from_user = guardian.user + from_user_name = get_user_full_name(from_user) + from_user_image = guardian.image + elif from_user_type == JUNIOR: + junior = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first() + from_user = junior.auth + from_user_name = get_user_full_name(from_user) + from_user_image = junior.image + return from_user_name, from_user_image, from_user -def get_notification_data(notification_type, from_user, to_user, extra_data): +def get_notification_data(notification_type, from_user_id, from_user_type, to_user_id, extra_data): """ get notification and push data + :param from_user_type: GUARDIAN or JUNIOR :param notification_type: notification_type - :param from_user: from_user obj - :param to_user: to_user obj + :param from_user_id: from_user obj + :param to_user_id: to_user obj :param extra_data: any extra data provided :return: notification and push data """ push_data = NOTIFICATION_DICT[notification_type].copy() notification_data = push_data.copy() - notification_data['to_user'] = get_user_full_name(to_user) - if from_user: - from_user_name = get_user_full_name(from_user) - push_data['body'] = push_data['body'].format(from_user=from_user_name) - notification_data['body'] = notification_data['body'].format(from_user=from_user_name) - notification_data['from_user'] = from_user_name + task_name = None + if 'task_id' in extra_data: + task = JuniorTask.objects.filter(id=extra_data.get('task_id')).first() + task_name = task.task_name + extra_data['task_image'] = task.image if task.image else task.default_image + + from_user_name, from_user_image, from_user = get_from_user_details(from_user_id, from_user_type) + + push_data['body'] = push_data['body'].format(from_user=from_user_name, task_name=task_name) + notification_data['body'] = notification_data['body'].format(from_user=from_user_name, task_name=task_name) + notification_data['from_user'] = from_user_name + notification_data['from_user_image'] = from_user_image notification_data.update(extra_data) - - return notification_data, push_data + to_user = User.objects.get(id=to_user_id) + return notification_data, push_data, from_user, to_user -def send_notification(notification_type, from_user, to_user, extra_data): +@shared_task() +def send_notification(notification_type, from_user_id, from_user_type, to_user_id, extra_data): """ used to send the push for the given notification type """ - # (from_user, to_user) = get_basic_detail(from_user_id, to_user_id) - notification_data, push_data = get_notification_data(notification_type, from_user, to_user, extra_data) + notification_data, push_data, from_user, to_user = get_notification_data(notification_type, from_user_id, + from_user_type, to_user_id, extra_data) user_notification_type = UserNotification.objects.filter(user=to_user).first() notification_data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()}) Notification.objects.create(notification_type=notification_type, notification_from=from_user, @@ -104,14 +125,10 @@ def send_notification_to_guardian(notification_type, from_user_id, to_user_id, e :param extra_data: :return: """ - from_user = None if from_user_id: - from_user = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first() + from_user = Junior.objects.filter(auth_id=from_user_id).first() extra_data['from_user_image'] = from_user.image - from_user = from_user.auth - to_user = Guardian.objects.filter(user_id=to_user_id).select_related('user').first() - extra_data['to_user_image'] = to_user.image - send_notification(notification_type, from_user, to_user.user, extra_data) + send_notification(notification_type, from_user_id, to_user_id, extra_data) @shared_task() @@ -123,13 +140,9 @@ def send_notification_to_junior(notification_type, from_user_id, to_user_id, ext :param extra_data: :return: """ - from_user = None if from_user_id: - from_user = Guardian.objects.filter(user_id=from_user_id).select_related('user').first() + from_user = Guardian.objects.filter(user_id=from_user_id).first() extra_data['from_user_image'] = from_user.image - from_user = from_user.user - to_user = Junior.objects.filter(auth_id=to_user_id).select_related('auth').first() - extra_data['to_user_image'] = to_user.image - send_notification(notification_type, from_user, to_user.auth, extra_data) + send_notification(notification_type, from_user_id, to_user_id, extra_data) diff --git a/notifications/views.py b/notifications/views.py index d60c429..8c0e07c 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -4,27 +4,35 @@ notifications views file # django imports from django.db.models import Q from rest_framework.decorators import action -from rest_framework.permissions import IsAuthenticated +from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.response import Response from rest_framework import viewsets, status, views + # local imports from account.utils import custom_response, custom_error_response +from base.constants import JUNIOR, GUARDIAN from base.messages import SUCCESS_CODE, ERROR_CODE +from base.tasks import notify_task_expiry +from guardian.models import Guardian from notifications.constants import TEST_NOTIFICATION -# Import serializer from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer -from notifications.utils import send_notification, send_notification_to_guardian, send_notification_to_junior -# Import model +from notifications.utils import send_notification from notifications.models import Notification class NotificationViewSet(viewsets.GenericViewSet): - """ used to do the notification actions """ + """ + used to do the notification actions + """ serializer_class = NotificationListSerializer permission_classes = [IsAuthenticated, ] def list(self, request, *args, **kwargs) -> Response: - """ list the notifications """ + """ + to list user's notifications + :param request: + :return: + """ queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id'] ).select_related('notification_to').order_by('-id') paginator = self.pagination_class() @@ -49,10 +57,8 @@ class NotificationViewSet(viewsets.GenericViewSet): to send test notification :return: """ - send_notification_to_guardian.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], - {'task_id': None}) - send_notification_to_junior.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], - {'task_id': None}) + send_notification(TEST_NOTIFICATION, None, None, request.auth.payload['user_id'], + {}) return custom_response(SUCCESS_CODE["3000"]) @action(methods=['patch'], url_path='mark-as-read', url_name='mark-as-read', detail=False, @@ -63,3 +69,12 @@ class NotificationViewSet(viewsets.GenericViewSet): """ Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True) return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK) + + @action(methods=['get'], url_path='task', url_name='task', detail=False, + permission_classes=[AllowAny]) + def task(self, request, *args, **kwargs): + """ + notification list + """ + notify_task_expiry() + return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK) diff --git a/zod_bank/celery.py b/zod_bank/celery.py index 5cc2829..93cfb8f 100644 --- a/zod_bank/celery.py +++ b/zod_bank/celery.py @@ -27,6 +27,13 @@ app.config_from_object('django.conf:settings') # Load task modules from all registered Django apps. app.autodiscover_tasks() +app.conf.beat_schedule = { + 'notify_task_expiry': { + 'task': 'base.tasks.notify_task_expiry', + 'schedule': crontab(minute='0', hour='*/1'), + }, +} + @app.task(bind=True) def debug_task(self): From 8cd4864748fde6f40ed78af9eafafdccb0928c1c Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 24 Aug 2023 13:13:25 +0530 Subject: [PATCH 51/83] add three guardian at a time --- base/messages.py | 3 ++- junior/serializers.py | 13 +++++++++---- junior/views.py | 3 ++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/base/messages.py b/base/messages.py index 8930bdc..9afbe4f 100644 --- a/base/messages.py +++ b/base/messages.py @@ -106,7 +106,8 @@ ERROR_CODE = { "2077": "You can not add guardian", "2078": "This junior is not associate with you", "2079": "Please update your app version for enjoying uninterrupted services", - "2080": "Can not add App version" + "2080": "Can not add App version", + "2081": "You can not add more than 3 guardian" } """Success message code""" diff --git a/junior/serializers.py b/junior/serializers.py index edeee71..48eaea9 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -88,11 +88,16 @@ class CreateJuniorSerializer(serializers.ModelSerializer): if junior: """update details according to the data get from request""" junior.gender = validated_data.get('gender',junior.gender) - """Update guardian code""" - junior.guardian_code = validated_data.get('guardian_code', junior.guardian_code) - """condition for guardian code""" + # Update guardian code""" + # condition for guardian code if guardian_code: - junior.guardian_code = guardian_code + if not junior.guardian_code: + junior.guardian_code = [] + junior.guardian_code.extend(guardian_code) + elif len(junior.guardian_code) < 3 and len(guardian_code) < 3: + junior.guardian_code.extend(guardian_code) + else: + raise serializers.ValidationError({"error":ERROR_CODE['2081'],"code":"400", "status":"failed"}) guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last() if guardian_data: JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) diff --git a/junior/views.py b/junior/views.py index 558d9e5..8b44e93 100644 --- a/junior/views.py +++ b/junior/views.py @@ -99,7 +99,8 @@ class UpdateJuniorProfile(viewsets.ModelViewSet): return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) except Exception as e: - return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) + error_detail = e.detail.get('error', None) + return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST) class ValidateGuardianCode(viewsets.ModelViewSet): """Check guardian code exist or not""" From 11605540d77f95f2cfbd2b517dd818d45bf2ac81 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 24 Aug 2023 13:18:23 +0530 Subject: [PATCH 52/83] remove guardian code request --- junior/serializers.py | 9 ++------- junior/views.py | 25 +++---------------------- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/junior/serializers.py b/junior/serializers.py index 48eaea9..379b393 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -499,8 +499,6 @@ class ReAssignTaskSerializer(serializers.ModelSerializer): instance.save() return instance - - class RemoveGuardianCodeSerializer(serializers.ModelSerializer): """User task Serializer""" class Meta(object): @@ -508,11 +506,8 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer): model = Junior fields = ('id', ) def update(self, instance, validated_data): - guardian_code = self.context['guardian_code'] - if self.instance and guardian_code in self.instance.guardian_code: - instance.guardian_code.remove(guardian_code) - if len(instance.guardian_code) == 0: - instance.guardian_code_status = str(NUMBER['one']) + instance.guardian_code = None + instance.guardian_code_status = str(NUMBER['one']) instance.save() return instance diff --git a/junior/views.py b/junior/views.py index 8b44e93..2abd61d 100644 --- a/junior/views.py +++ b/junior/views.py @@ -725,29 +725,10 @@ class RemoveGuardianCodeAPIView(views.APIView): def put(self, request, format=None): try: - user_type = request.META.get('HTTP_USER_TYPE') - junior_id = request.data.get('junior_id') - guardian_id = request.data.get('guardian_id') - my_dict = {} - if user_type and str(user_type) == str(NUMBER['one']) and guardian_id: - queryset = Junior.objects.filter(auth__email=self.request.user, is_active=True, is_deleted=False).last() - guardian_code = Guardian.objects.filter(id=guardian_id).last().guardian_code - my_dict.update({"guardian_code": guardian_code}) - elif user_type and str(user_type) == str(NUMBER['two']) and junior_id: - guardian_code = Guardian.objects.filter(user=self.request.user, - is_active=True, is_deleted=False).last().guardian_code - queryset = Junior.objects.filter(id=junior_id).last() - my_dict.update({"guardian_code": guardian_code}) - else: - return custom_error_response( - ERROR_CODE["2069"], - response_status=status.HTTP_400_BAD_REQUEST - ) - if queryset: + junior_queryset = Junior.objects.filter(auth=self.request.user).last() + if junior_queryset: # use RemoveGuardianCodeSerializer serializer - serializer = RemoveGuardianCodeSerializer(queryset, data=request.data, - context=my_dict, - partial=True) + serializer = RemoveGuardianCodeSerializer(junior_queryset, data=request.data, partial=True) if serializer.is_valid(): # save serializer serializer.save() From ab1a2be679617f456738bb08b3559b97177f90f8 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 24 Aug 2023 13:32:08 +0530 Subject: [PATCH 53/83] remove junior by guardian --- junior/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junior/urls.py b/junior/urls.py index 08fe33a..3c597c3 100644 --- a/junior/urls.py +++ b/junior/urls.py @@ -62,5 +62,5 @@ urlpatterns = [ path('api/v1/reassign-task/', ReAssignJuniorTaskAPIView.as_view()), path('api/v1/complete-article/', CompleteArticleAPIView.as_view()), path('api/v1/read-article-card/', ReadArticleCardAPIView.as_view()), - path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view()), + path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view()) ] From cd3b385756f5bdbdfc106d440d84f5b670dd398d Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 24 Aug 2023 13:33:34 +0530 Subject: [PATCH 54/83] added optional user as name --- base/tasks.py | 21 +++++++++++++++++++ notifications/utils.py | 5 ++--- notifications/views.py | 2 -- web_admin/serializers/analytics_serializer.py | 12 ++++++++++- web_admin/serializers/auth_serializer.py | 10 +++++---- .../serializers/user_management_serializer.py | 7 ++++--- web_admin/views/analytics.py | 13 ++++++------ 7 files changed, 51 insertions(+), 19 deletions(-) diff --git a/base/tasks.py b/base/tasks.py index a737380..bd1892b 100644 --- a/base/tasks.py +++ b/base/tasks.py @@ -37,6 +37,27 @@ def send_email_otp(email, verification_code): return True +@shared_task +def send_mail(recipient_list, template, context: dict = None): + """ + used to send otp on email + :param context: + :param recipient_list: e-mail list + :param template: email template + """ + if context is None: + context = {} + from_email = settings.EMAIL_FROM_ADDRESS + recipient_list = recipient_list + send_templated_mail( + template_name=template, + from_email=from_email, + recipient_list=recipient_list, + context=context + ) + return True + + @shared_task() def notify_task_expiry(): """ diff --git a/notifications/utils.py b/notifications/utils.py index bcb9749..7623673 100644 --- a/notifications/utils.py +++ b/notifications/utils.py @@ -9,6 +9,7 @@ from firebase_admin.messaging import Message, Notification as FirebaseNotificati # django imports from django.contrib.auth import get_user_model +# local imports from account.models import UserNotification from account.utils import get_user_full_name from base.constants import GUARDIAN, JUNIOR @@ -17,8 +18,6 @@ from junior.models import Junior from notifications.constants import NOTIFICATION_DICT from notifications.models import Notification -# local imports - User = get_user_model() @@ -105,7 +104,7 @@ def send_notification(notification_type, from_user_id, from_user_type, to_user_i notification_data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()}) Notification.objects.create(notification_type=notification_type, notification_from=from_user, notification_to=to_user, data=notification_data) - if user_notification_type.push_notification: + if user_notification_type and user_notification_type.push_notification: send_push(to_user, push_data) diff --git a/notifications/views.py b/notifications/views.py index 8c0e07c..4546317 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -10,10 +10,8 @@ from rest_framework import viewsets, status, views # local imports from account.utils import custom_response, custom_error_response -from base.constants import JUNIOR, GUARDIAN from base.messages import SUCCESS_CODE, ERROR_CODE from base.tasks import notify_task_expiry -from guardian.models import Guardian from notifications.constants import TEST_NOTIFICATION from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer from notifications.utils import send_notification diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py index 0e3418a..b0177d7 100644 --- a/web_admin/serializers/analytics_serializer.py +++ b/web_admin/serializers/analytics_serializer.py @@ -7,6 +7,7 @@ from rest_framework import serializers # django imports from django.contrib.auth import get_user_model +from account.utils import get_user_full_name # local imports from base.constants import USER_TYPE @@ -74,6 +75,7 @@ class UserCSVReportSerializer(serializers.ModelSerializer): """ user csv/xls report serializer """ + name = serializers.SerializerMethodField() phone_number = serializers.SerializerMethodField() user_type = serializers.SerializerMethodField() is_active = serializers.SerializerMethodField() @@ -84,7 +86,15 @@ class UserCSVReportSerializer(serializers.ModelSerializer): meta class """ model = USER - fields = ('first_name', 'last_name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined') + fields = ('name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined') + + @staticmethod + def get_name(obj): + """ + :param obj: user object + :return: full name + """ + return get_user_full_name(obj) @staticmethod def get_phone_number(obj): diff --git a/web_admin/serializers/auth_serializer.py b/web_admin/serializers/auth_serializer.py index 712e284..585b2c9 100644 --- a/web_admin/serializers/auth_serializer.py +++ b/web_admin/serializers/auth_serializer.py @@ -14,7 +14,7 @@ from account.models import UserEmailOtp from base.constants import USER_TYPE from base.messages import ERROR_CODE from guardian.tasks import generate_otp -from base.tasks import send_email_otp +from base.tasks import send_mail USER = get_user_model() @@ -48,11 +48,13 @@ class AdminOTPSerializer(serializers.ModelSerializer): :return: user_data """ email = validated_data['email'] - verification_code = generate_otp() - + template = 'email_reset_verification.email' # Send the verification code to the user's email - send_email_otp.delay(email, verification_code) + data = { + "verification_code": verification_code + } + send_mail.delay([email], template, data) expiry = timezone.now() + timezone.timedelta(days=1) user_data, created = UserEmailOtp.objects.update_or_create(email=email, diff --git a/web_admin/serializers/user_management_serializer.py b/web_admin/serializers/user_management_serializer.py index 21a0a3b..c8d0b1f 100644 --- a/web_admin/serializers/user_management_serializer.py +++ b/web_admin/serializers/user_management_serializer.py @@ -5,6 +5,7 @@ web_admin user_management serializers file from rest_framework import serializers from django.contrib.auth import get_user_model +from account.utils import get_user_full_name from base.constants import USER_TYPE, GUARDIAN, JUNIOR # local imports from base.messages import ERROR_CODE, SUCCESS_CODE @@ -37,7 +38,7 @@ class UserManagementListSerializer(serializers.ModelSerializer): :param obj: user object :return: full name """ - return f"{obj.first_name} {obj.last_name}" if obj.last_name else obj.first_name + return get_user_full_name(obj) @staticmethod def get_country_code(obj): @@ -144,7 +145,7 @@ class GuardianSerializer(serializers.ModelSerializer): :param obj: guardian object :return: full name """ - return f"{obj.user.first_name} {obj.user.last_name}" if obj.user.last_name else obj.user.first_name + return get_user_full_name(obj.user) @staticmethod def get_first_name(obj): @@ -222,7 +223,7 @@ class JuniorSerializer(serializers.ModelSerializer): :param obj: junior object :return: full name """ - return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name + return get_user_full_name(obj.auth) @staticmethod def get_first_name(obj): diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index face900..4867bc1 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -107,13 +107,15 @@ class AnalyticsViewSet(GenericViewSet): assign_tasks = JuniorTask.objects.filter( created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] - ).exclude(task_status__in=[PENDING, EXPIRED]) + ) data = { 'task_completed': assign_tasks.filter(task_status=COMPLETED).count(), + 'task_pending': assign_tasks.filter(task_status=PENDING).count(), 'task_in_progress': assign_tasks.filter(task_status=IN_PROGRESS).count(), 'task_requested': assign_tasks.filter(task_status=REQUESTED).count(), 'task_rejected': assign_tasks.filter(task_status=REJECTED).count(), + 'task_expired': assign_tasks.filter(task_status=EXPIRED).count(), } return custom_response(None, data) @@ -169,10 +171,9 @@ class AnalyticsViewSet(GenericViewSet): serializer = UserCSVReportSerializer(queryset, many=True) df_users = pd.DataFrame([ - {'Name': f"{user['first_name']} {user['last_name']}", - 'Email': user['email'], 'Phone Number': user['phone_number'], - 'User Type': user['user_type'], 'Status': user['is_active'], - 'Date Joined': user['date_joined']} + {'Name': user['name'], 'Email': user['email'], + 'Phone Number': user['phone_number'], 'User Type': user['user_type'], + 'Status': user['is_active'], 'Date Joined': user['date_joined']} for user in serializer.data ]) write_excel_worksheet(worksheet, df_users) @@ -181,7 +182,7 @@ class AnalyticsViewSet(GenericViewSet): elif sheet_name == 'Assign Tasks': assign_tasks = JuniorTask.objects.filter( created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] - ).exclude(task_status__in=[PENDING, EXPIRED]) + ) df_tasks = pd.DataFrame([ {'Task Name': task.task_name, 'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()} From 930b58cf05910704c4d05cc7b2ca4ec7e53e80e6 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 24 Aug 2023 14:51:24 +0530 Subject: [PATCH 55/83] added optional name as user --- web_admin/views/analytics.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 4867bc1..1908670 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -22,7 +22,7 @@ from django.db.models.functions.window import Rank from django.http import HttpResponse # local imports -from account.utils import custom_response +from account.utils import custom_response, get_user_full_name from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT, TASK_STATUS from guardian.models import JuniorTask from guardian.utils import upload_excel_file_to_alibaba @@ -44,7 +44,7 @@ class AnalyticsViewSet(GenericViewSet): to get junior leaderboard and ranking """ serializer_class = None - permission_classes = [IsAuthenticated, AdminPermission] + # permission_classes = [IsAuthenticated, AdminPermission] def get_queryset(self): user_qs = USER.objects.filter( @@ -182,10 +182,16 @@ class AnalyticsViewSet(GenericViewSet): elif sheet_name == 'Assign Tasks': assign_tasks = JuniorTask.objects.filter( created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] - ) + ).select_related('junior__auth', 'guardian__user').only('task_name', 'task_status', + 'junior__auth__first_name', + 'junior__auth__last_name', + 'guardian__user__first_name', + 'guardian__user__last_name',) df_tasks = pd.DataFrame([ - {'Task Name': task.task_name, 'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()} + {'Task Name': task.task_name, 'Assign To': get_user_full_name(task.junior.auth), + 'Assign By': get_user_full_name(task.guardian.user), + 'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()} for task in assign_tasks ]) @@ -199,8 +205,7 @@ class AnalyticsViewSet(GenericViewSet): )).order_by('-total_points', 'junior__created_at')[:15] df_leaderboard = pd.DataFrame([ { - 'Junior Name': f"{junior.junior.auth.first_name} {junior.junior.auth.last_name}" - if junior.junior.auth.last_name else junior.junior.auth.first_name, + 'Junior Name': get_user_full_name(junior.junior.auth), 'Points': junior.total_points, 'Rank': junior.rank } From 3072bd5cdbf47190a0fb09c78c0c9408c3aecae0 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 24 Aug 2023 14:53:43 +0530 Subject: [PATCH 56/83] added optional name as user --- web_admin/views/analytics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 1908670..4a010a3 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -44,7 +44,7 @@ class AnalyticsViewSet(GenericViewSet): to get junior leaderboard and ranking """ serializer_class = None - # permission_classes = [IsAuthenticated, AdminPermission] + permission_classes = [IsAuthenticated, AdminPermission] def get_queryset(self): user_qs = USER.objects.filter( @@ -205,7 +205,7 @@ class AnalyticsViewSet(GenericViewSet): )).order_by('-total_points', 'junior__created_at')[:15] df_leaderboard = pd.DataFrame([ { - 'Junior Name': get_user_full_name(junior.junior.auth), + 'Name': get_user_full_name(junior.junior.auth), 'Points': junior.total_points, 'Rank': junior.rank } From e9aa2dfda98d527f7efdaa7122a92c728a726cdf Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 24 Aug 2023 16:08:20 +0530 Subject: [PATCH 57/83] limit for 3 guardian code and article list API optimization --- base/constants.py | 3 ++- guardian/views.py | 5 +++-- junior/views.py | 8 ++++++-- web_admin/serializers/article_serializer.py | 10 +++++++--- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/base/constants.py b/base/constants.py index 1ab15a2..da203ae 100644 --- a/base/constants.py +++ b/base/constants.py @@ -43,7 +43,8 @@ FILE_SIZE = 5 * 1024 * 1024 # String constant for configurable date for allocation lock period ALLOCATION_LOCK_DATE = 1 - +# guardian code status tuple +guardian_code_tuple = ('1','3') """user type""" USER_TYPE = ( ('1', 'junior'), diff --git a/guardian/views.py b/guardian/views.py index e49beaf..b541660 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -10,7 +10,7 @@ from rest_framework.permissions import IsAuthenticated from rest_framework import viewsets, status from rest_framework.pagination import PageNumberPagination from django.contrib.auth.models import User - +from base.constants import guardian_code_tuple from rest_framework.filters import SearchFilter from django.utils import timezone @@ -178,7 +178,8 @@ class CreateTaskAPIView(viewsets.ModelViewSet): junior = request.data['junior'] junior_id = Junior.objects.filter(id=junior).last() guardian_data = Guardian.objects.filter(user=request.user).last() - if guardian_data.guardian_code not in junior_id.guardian_code: + if (guardian_data.guardian_code not in junior_id.guardian_code or + junior_id.guardian_code_status in guardian_code_tuple): return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST) allowed_extensions = ['.jpg', '.jpeg', '.png'] if not any(extension in str(image) for extension in allowed_extensions): diff --git a/junior/views.py b/junior/views.py index 2abd61d..96b9222 100644 --- a/junior/views.py +++ b/junior/views.py @@ -191,6 +191,8 @@ class AddJuniorAPIView(viewsets.ModelViewSet): return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST) elif not data: return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST) + if data == "Max": + return custom_error_response(ERROR_CODE['2081'], response_status=status.HTTP_400_BAD_REQUEST) return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK) # use AddJuniorSerializer serializer serializer = AddJuniorSerializer(data=request.data, context=info_data) @@ -209,10 +211,12 @@ class AddJuniorAPIView(viewsets.ModelViewSet): return none if junior.guardian_code and (guardian.guardian_code in junior.guardian_code): return False - if type(junior.guardian_code) is list: + if not junior.guardian_code: + junior.guardian_code = [guardian.guardian_code] + if type(junior.guardian_code) is list and len(junior.guardian_code) < 4: junior.guardian_code.append(guardian.guardian_code) else: - junior.guardian_code = [guardian.guardian_code] + return "Max" junior.guardian_code_status = str(NUMBER['two']) junior.save() JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior, diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index e125acf..62d09d4 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -219,8 +219,7 @@ class ArticleListSerializer(serializers.ModelSerializer): """ serializer for article API """ - article_cards = ArticleCardSerializer(many=True) - article_survey = ArticleSurveySerializer(many=True) + image = serializers.SerializerMethodField('get_image') total_points = serializers.SerializerMethodField('get_total_points') is_completed = serializers.SerializerMethodField('get_is_completed') @@ -229,8 +228,13 @@ class ArticleListSerializer(serializers.ModelSerializer): meta class """ model = Article - fields = ('id', 'title', 'description', 'article_cards', 'article_survey', 'total_points', 'is_completed') + fields = ('id', 'title', 'description','image', 'total_points', 'is_completed') + def get_image(self, obj): + """article image""" + if obj.article_cards.first(): + return obj.article_cards.first().image_url + return None def get_total_points(self, obj): """total points of article""" total_question = ArticleSurvey.objects.filter(article=obj).count() From 1028822908ae731881aef25a3f35b963b83cb093 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 24 Aug 2023 18:00:14 +0530 Subject: [PATCH 58/83] article-list optimization --- account/custom_middleware.py | 22 +++++++++++---------- web_admin/serializers/article_serializer.py | 5 ++--- web_admin/views/article.py | 5 +---- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/account/custom_middleware.py b/account/custom_middleware.py index ca89a86..42a3b0f 100644 --- a/account/custom_middleware.py +++ b/account/custom_middleware.py @@ -45,7 +45,11 @@ class CustomMiddleware(object): api_endpoint = request.path if request.user.is_authenticated: # device details - device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last() + if device_id: + device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last() + if not device_details and api_endpoint != '/api/v1/user/login/': + custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND) + response = custom_response(custom_error) if user_type and str(user_type) == str(NUMBER['one']): junior = Junior.objects.filter(auth=request.user, is_active=False).last() if junior: @@ -56,13 +60,11 @@ class CustomMiddleware(object): if guardian: custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND) response = custom_response(custom_error) - if device_id and not device_details and api_endpoint != '/api/v1/user/login/': - custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND) - response = custom_response(custom_error) - force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last() - api_endpoint_checks = not any(endpoint in api_endpoint for endpoint in ['/admin/', '/api/v1/admin/']) - if not force_update and version and device_type: - custom_error = custom_error_response(ERROR_CODE['2079'], - response_status=status.HTTP_308_PERMANENT_REDIRECT) - response = custom_response(custom_error, status.HTTP_308_PERMANENT_REDIRECT) + + if version and device_type: + force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last() + if not force_update: + custom_error = custom_error_response(ERROR_CODE['2079'], + response_status=status.HTTP_308_PERMANENT_REDIRECT) + response = custom_response(custom_error, status.HTTP_308_PERMANENT_REDIRECT) return response diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index 62d09d4..675591f 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -237,8 +237,7 @@ class ArticleListSerializer(serializers.ModelSerializer): return None def get_total_points(self, obj): """total points of article""" - total_question = ArticleSurvey.objects.filter(article=obj).count() - return total_question * NUMBER['five'] + return obj.article_survey.all().count() * NUMBER['five'] def get_is_completed(self, obj): """complete all question""" @@ -278,7 +277,7 @@ class ArticleQuestionSerializer(serializers.ModelSerializer): """attempt question or not""" context_data = self.context.get('user') junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data, - question=obj, is_answer_correct=True).last() + question=obj).last() if junior_article_obj: return junior_article_obj.submitted_answer.id return None diff --git a/web_admin/views/article.py b/web_admin/views/article.py index f362f8b..902f579 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -229,10 +229,7 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin): http_method_names = ['get',] def get_queryset(self): - article = self.queryset.objects.filter(is_deleted=False, is_published=True).prefetch_related( - 'article_cards', 'article_survey', 'article_survey__options' - ).order_by('-created_at') - queryset = self.filter_queryset(article) + queryset = self.queryset.objects.filter(is_deleted=False, is_published=True).order_by('-created_at') return queryset def list(self, request, *args, **kwargs): From f5a03e2fdf888abaad174665b1018ce4e0bff784 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 24 Aug 2023 18:26:15 +0530 Subject: [PATCH 59/83] assement id --- web_admin/serializers/article_serializer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index 675591f..4e98e55 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -271,14 +271,14 @@ class ArticleQuestionSerializer(serializers.ModelSerializer): ans_obj = SurveyOption.objects.filter(survey=obj, is_answer=True).last() if ans_obj: return ans_obj.id - return str("None") + return None def get_attempted_answer(self, obj): """attempt question or not""" context_data = self.context.get('user') junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data, question=obj).last() - if junior_article_obj: + if junior_article_obj and junior_article_obj.submitted_answer: return junior_article_obj.submitted_answer.id return None From a65eb2f77d8146eacb3692075fd25f9e72a571ae Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 24 Aug 2023 18:58:31 +0530 Subject: [PATCH 60/83] assessment answer --- junior/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junior/views.py b/junior/views.py index 96b9222..b91a8cd 100644 --- a/junior/views.py +++ b/junior/views.py @@ -620,7 +620,7 @@ class CheckAnswerAPIView(viewsets.ModelViewSet): answer_id = self.request.GET.get('answer_id') current_page = self.request.GET.get('current_page') queryset = self.get_queryset() - submit_ans = SurveyOption.objects.filter(id=answer_id, is_answer=True).last() + submit_ans = SurveyOption.objects.filter(id=answer_id).last() junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user, question=queryset) if submit_ans: From 2e0ceb8c920d5e3fc9891e5467698f99265fb432 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 24 Aug 2023 19:34:59 +0530 Subject: [PATCH 61/83] modified top-list api, junior-list api and junior-points api, nodified rank method, added mail for deactivating user from admin --- .../templated_email/user_deactivate.email | 22 +++++++++++ base/tasks.py | 23 +---------- celerybeat-schedule | Bin 16384 -> 16384 bytes guardian/serializers.py | 9 ++++- guardian/views.py | 18 +++++---- junior/serializers.py | 12 ++---- junior/utils.py | 19 ++++++++- junior/views.py | 4 +- web_admin/serializers/auth_serializer.py | 4 +- web_admin/views/user_management.py | 37 +++++++++++------- zod_bank/celery.py | 17 +++----- 11 files changed, 94 insertions(+), 71 deletions(-) create mode 100644 account/templates/templated_email/user_deactivate.email diff --git a/account/templates/templated_email/user_deactivate.email b/account/templates/templated_email/user_deactivate.email new file mode 100644 index 0000000..90b3ee1 --- /dev/null +++ b/account/templates/templated_email/user_deactivate.email @@ -0,0 +1,22 @@ +{% extends "templated_email/email_base.email" %} + +{% block subject %} + Account Deactivated +{% endblock %} + +{% block plain %} + + +

    + Hi User, +

    + + + + +

    + Your account has been deactivated by admin. Please reach out to the admin for assistance. +

    + + +{% endblock %} diff --git a/base/tasks.py b/base/tasks.py index bd1892b..2056493 100644 --- a/base/tasks.py +++ b/base/tasks.py @@ -18,27 +18,7 @@ from notifications.utils import send_notification @shared_task -def send_email_otp(email, verification_code): - """ - used to send otp on email - :param email: e-mail - :param verification_code: otp - """ - from_email = settings.EMAIL_FROM_ADDRESS - recipient_list = [email] - send_templated_mail( - template_name='email_reset_verification.email', - from_email=from_email, - recipient_list=recipient_list, - context={ - 'verification_code': verification_code - } - ) - return True - - -@shared_task -def send_mail(recipient_list, template, context: dict = None): +def send_email(recipient_list, template, context: dict = None): """ used to send otp on email :param context: @@ -48,7 +28,6 @@ def send_mail(recipient_list, template, context: dict = None): if context is None: context = {} from_email = settings.EMAIL_FROM_ADDRESS - recipient_list = recipient_list send_templated_mail( template_name=template, from_email=from_email, diff --git a/celerybeat-schedule b/celerybeat-schedule index fef85ada273c6b1f27fa57f6340942760415d802..49e872897d5b39cf5fa0b1af41e7e7fe192562d5 100644 GIT binary patch delta 598 zcmZo@U~Fh$oS?(X%m4(F85Pu-W-?CpQc&9H(8|A=QGi`wvW9|!l^P>Rnt{R9079>U z^4A+f_}@7oG^aU)4&;H*>y0-jDXin=jW&hw*@4vZ&9ZtwnMG6cN{TX5iyK&`wnGi8 zouaWh(m;T*zDFo8za%rQGQK3SI6FSIq9C)Va!Lk^Muu{aY*J!zsvbzZSPz>b4rea* z=NuATk|s>X2d89+woM6|;?0mDrP0HzrEfSTLmC20fWoy?k~C|9B#2$Ahj7j;E@It- w)ty8;QGtQsfI7^H?0B3gi|NKW1LZ^}V}mI@Ooo=wkc5RJ7a|}x8h+;h0QYRWSpWb4 delta 61 zcmZo@U~Fh$oS?I@q?dnUgU4n@0d|3j77CL&6cm_V@lK3V+MJ`XgqPRVfB_8Hfu#Iq NS-qdk8w+3Y0suQY5h(xw diff --git a/guardian/serializers.py b/guardian/serializers.py index 3e24265..7146039 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -316,10 +316,11 @@ class TaskDetailsjuniorSerializer(serializers.ModelSerializer): 'requested_on', 'rejected_on', 'completed_on', 'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at'] + class TopJuniorSerializer(serializers.ModelSerializer): """Top junior serializer""" junior = JuniorDetailSerializer() - position = serializers.IntegerField() + position = serializers.SerializerMethodField() class Meta(object): """Meta info""" @@ -329,9 +330,13 @@ class TopJuniorSerializer(serializers.ModelSerializer): def to_representation(self, instance): """Convert instance to representation""" representation = super().to_representation(instance) - representation['position'] = instance.position return representation + @staticmethod + def get_position(obj): + """ get position/rank """ + return obj.rank + class GuardianProfileSerializer(serializers.ModelSerializer): """junior serializer""" diff --git a/guardian/views.py b/guardian/views.py index e45b720..c755e87 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -6,6 +6,8 @@ # Import PageNumberPagination # Import User # Import timezone +from django.db.models import F, Window +from django.db.models.functions.window import Rank from rest_framework.permissions import IsAuthenticated from rest_framework import viewsets, status from rest_framework.pagination import PageNumberPagination @@ -242,7 +244,6 @@ class SearchTaskListAPIView(viewsets.ModelViewSet): class TopJuniorListAPIView(viewsets.ModelViewSet): """Top juniors list No Params""" - queryset = JuniorPoints.objects.all() serializer_class = TopJuniorSerializer permission_classes = [IsAuthenticated] http_method_names = ('get',) @@ -253,15 +254,18 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): context.update({'view': self}) return context + def get_queryset(self): + queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at'] + )).order_by('-total_points', 'junior__created_at') + return queryset + def list(self, request, *args, **kwargs): """Fetch junior list of those who complete their tasks""" try: - junior_total_points = self.get_queryset().order_by('-total_points') - # Update the position field for each JuniorPoints object - for index, junior in enumerate(junior_total_points): - junior.position = index + 1 - junior.save() - serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True) + junior_total_points = self.get_queryset()[:15] + serializer = self.get_serializer(junior_total_points, many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/junior/serializers.py b/junior/serializers.py index 13735b3..1ef34f7 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -19,7 +19,7 @@ from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED GUARDIAN_CODE_STATUS, JUNIOR) from guardian.models import Guardian, JuniorTask from account.models import UserEmailOtp, UserNotification -from junior.utils import junior_notification_email, junior_approval_mail +from junior.utils import junior_notification_email, junior_approval_mail, get_junior_leaderboard_rank from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime from notifications.utils import send_notification from notifications.constants import (ASSOCIATE_REQUEST, JUNIOR_ADDED, TASK_ACTION, @@ -185,9 +185,8 @@ class JuniorDetailListSerializer(serializers.ModelSerializer): return data def get_position(self, obj): - data = JuniorPoints.objects.filter(junior=obj).last() - if data: - return data.position + return get_junior_leaderboard_rank(obj) + def get_points(self, obj): data = JuniorPoints.objects.filter(junior=obj).last() if data: @@ -365,10 +364,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer): return obj.junior.id def get_position(self, obj): - data = JuniorPoints.objects.filter(junior=obj.junior).last() - if data: - return data.position - return 99999 + return get_junior_leaderboard_rank(obj.junior) def get_points(self, obj): """total points""" points = JuniorPoints.objects.filter(junior=obj.junior).last() diff --git a/junior/utils.py b/junior/utils.py index 4a6ee2b..dacb426 100644 --- a/junior/utils.py +++ b/junior/utils.py @@ -5,7 +5,8 @@ from django.conf import settings from templated_email import send_templated_mail from .models import JuniorPoints from base.constants import NUMBER -from django.db.models import F +from django.db.models import F, Window +from django.db.models.functions.window import Rank # junior notification # email for sending email # when guardian create junior profile @@ -61,3 +62,19 @@ def update_positions_based_on_points(): junior_point.position = position junior_point.save() position += NUMBER['one'] + + +def get_junior_leaderboard_rank(junior_obj): + """ + to get junior's position/rank + :param junior_obj: + :return: junior's position/rank + """ + queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at'] + )).order_by('-total_points', 'junior__created_at') + + junior = next((query for query in queryset if query.junior == junior_obj), None) + + return junior.rank if junior else None diff --git a/junior/views.py b/junior/views.py index 12fef19..5e73562 100644 --- a/junior/views.py +++ b/junior/views.py @@ -142,7 +142,7 @@ class JuniorListAPIView(viewsets.ModelViewSet): def list(self, request, *args, **kwargs): """ junior list""" try: - update_positions_based_on_points() + # update_positions_based_on_points, function removed guardian_data = Guardian.objects.filter(user__email=request.user).last() # fetch junior object if guardian_data: @@ -417,7 +417,7 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet): """Junior Points No Params""" try: - update_positions_based_on_points() + # update_positions_based_on_points, function removed queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last() # update position of junior serializer = JuniorPointsSerializer(queryset) diff --git a/web_admin/serializers/auth_serializer.py b/web_admin/serializers/auth_serializer.py index 585b2c9..bda89bd 100644 --- a/web_admin/serializers/auth_serializer.py +++ b/web_admin/serializers/auth_serializer.py @@ -14,7 +14,7 @@ from account.models import UserEmailOtp from base.constants import USER_TYPE from base.messages import ERROR_CODE from guardian.tasks import generate_otp -from base.tasks import send_mail +from base.tasks import send_email USER = get_user_model() @@ -54,7 +54,7 @@ class AdminOTPSerializer(serializers.ModelSerializer): data = { "verification_code": verification_code } - send_mail.delay([email], template, data) + send_email.delay([email], template, data) expiry = timezone.now() + timezone.timedelta(days=1) user_data, created = UserEmailOtp.objects.update_or_create(email=email, diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index ecc9771..6980a7a 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -12,8 +12,9 @@ from django.db.models import Q # local imports from account.utils import custom_response, custom_error_response -from base.constants import USER_TYPE +from base.constants import USER_TYPE, GUARDIAN, JUNIOR from base.messages import SUCCESS_CODE, ERROR_CODE +from base.tasks import send_email from guardian.models import Guardian from junior.models import Junior from web_admin.permission import AdminPermission @@ -92,12 +93,14 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST) if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): - guardian = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True).first() + guardian = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True + ).select_related('user').first() serializer = GuardianSerializer(guardian, request.data, context={'user_id': kwargs['pk']}) elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): - junior = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True).select_related('auth').first() + junior = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True + ).select_related('auth').first() serializer = JuniorSerializer(junior, request.data, context={'user_id': kwargs['pk']}) @@ -113,17 +116,21 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, user_type {'guardian' for Guardian, 'junior' for Junior} mandatory :return: success message """ - if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: + user_type = self.request.query_params.get('user_type') + if user_type not in [GUARDIAN, JUNIOR]: return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST) - queryset = self.queryset - if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): - user_obj = queryset.filter(id=kwargs['pk']).first() - obj = user_obj.guardian_profile.all().first() - obj.is_active = False if obj.is_active else True - obj.save() - elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): - user_obj = queryset.filter(id=kwargs['pk']).first() - obj = user_obj.junior_profile.all().first() - obj.is_active = False if obj.is_active else True - obj.save() + + email_template = 'user_deactivate.email' + + if user_type == GUARDIAN: + obj = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True).select_related('user').first() + elif user_type == JUNIOR: + obj = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True).select_related('auth').first() + + if obj.is_active: + obj.is_active = False + send_email([obj.user.email if user_type == GUARDIAN else obj.auth.email], email_template) + else: + obj.is_active = True + obj.save() return custom_response(SUCCESS_CODE['3038']) diff --git a/zod_bank/celery.py b/zod_bank/celery.py index 93cfb8f..5a3de03 100644 --- a/zod_bank/celery.py +++ b/zod_bank/celery.py @@ -28,9 +28,13 @@ app.config_from_object('django.conf:settings') app.autodiscover_tasks() app.conf.beat_schedule = { + "expired_task": { + "task": "guardian.utils.update_expired_task_status", + "schedule": crontab(minute=0, hour=0), + }, 'notify_task_expiry': { 'task': 'base.tasks.notify_task_expiry', - 'schedule': crontab(minute='0', hour='*/1'), + 'schedule': crontab(minute='30', hour='19'), }, } @@ -39,14 +43,3 @@ app.conf.beat_schedule = { def debug_task(self): """ celery debug task """ print(f'Request: {self.request!r}') - - -"""cron task""" - - -app.conf.beat_schedule = { - "expired_task": { - "task": "guardian.utils.update_expired_task_status", - "schedule": crontab(minute=0, hour=0), - }, -} From 464899f7d380d504ed82c96d0ef6fa1ab454a424 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 25 Aug 2023 12:26:38 +0530 Subject: [PATCH 62/83] added notification for top leaderboard junior, added related celery task, created method to send notification to multiple users --- base/tasks.py | 31 +++++++++++++++++++++++++++++-- celerybeat-schedule | Bin 16384 -> 16384 bytes notifications/constants.py | 30 ++++++++++++++++++++++-------- notifications/utils.py | 33 +++++++++++++++++++++++++++++++++ notifications/views.py | 4 ++-- zod_bank/celery.py | 6 +++++- 6 files changed, 91 insertions(+), 13 deletions(-) diff --git a/base/tasks.py b/base/tasks.py index 2056493..98d0df0 100644 --- a/base/tasks.py +++ b/base/tasks.py @@ -9,12 +9,16 @@ from templated_email import send_templated_mail # django imports from django.conf import settings +from django.db.models import F, Window +from django.db.models.functions.window import Rank # local imports from base.constants import PENDING, IN_PROGRESS, JUNIOR from guardian.models import JuniorTask -from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING -from notifications.utils import send_notification +from junior.models import JuniorPoints +from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING, NOTIFICATION_DICT, TOP_JUNIOR +from notifications.models import Notification +from notifications.utils import send_notification, get_from_user_details, send_notification_multiple_user @shared_task @@ -56,3 +60,26 @@ def notify_task_expiry(): send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth.id, JUNIOR, task.guardian.user.id, {'task_id': task.id}) return True + + +@shared_task() +def notify_top_junior(): + """ + task to send notification for top leaderboard junior to all junior's + :return: + """ + junior_points_qs = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at'] + )).order_by('-total_points', 'junior__created_at') + + prev_top_position = junior_points_qs.filter(position=1).first() + new_top_position = junior_points_qs.filter(rank=1).first() + if prev_top_position != new_top_position: + to_user_list = [junior_point.junior.auth for junior_point in junior_points_qs] + send_notification_multiple_user(TOP_JUNIOR, new_top_position.junior.auth.id, JUNIOR, + to_user_list, {'points': new_top_position.total_points}) + for junior_point in junior_points_qs: + junior_point.position = junior_point.rank + junior_point.save() + return True diff --git a/celerybeat-schedule b/celerybeat-schedule index 49e872897d5b39cf5fa0b1af41e7e7fe192562d5..4ebacd4aa0047e3c5989eee717063f096d44e18e 100644 GIT binary patch delta 586 zcmZo@U~Fh$oS?(VHc?l5GKYeK)J#SOFc33>&+4a!_@emJog?lwWQO;a_Ki(3+N;lN8qR^0`|>`0PMRlWDV@-fND@>y5dX_?RZ| zHf~{;WK&gVSgSaBtx+H&^W^_VA(P*j*a(0{ALvcV5N(?hG{u`^@)I-V=pMnm{F2PH z%J`D}g7~b`yv+QfDH$vp8Ad%aNr}a&dL@a)*~NNT)NnX+u|MYko4N2a%uI$1DUBXx zEq%i&J&XpEKbT2^UByGHtNbZ;m6j>@{ADm#F;4C=)@70-)fI_2T!BrP*w8XzU^wy! Q<}PlayOcL`ns+h*0OKUD6#xJL delta 120 zcmZo@U~Fh$oS?(VJW*GhYbGND7>F5dT$sQ=afAJ4Mgew#i4h8uITRFBzjHujIn5z- xAP Date: Fri, 25 Aug 2023 12:57:15 +0530 Subject: [PATCH 63/83] remove code --- junior/serializers.py | 6 ++++-- junior/views.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/junior/serializers.py b/junior/serializers.py index 379b393..f4dd8ef 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -325,9 +325,11 @@ class RemoveJuniorSerializer(serializers.ModelSerializer): fields = ('id', 'is_invited') def update(self, instance, validated_data): if instance: + guardian_code = self.context['guardian_code'] instance.is_invited = False - instance.guardian_code = '{}' - instance.guardian_code_status = str(NUMBER['one']) + instance.guardian_code.remove(guardian_code) + if not instance.guardian_code: + instance.guardian_code_status = str(NUMBER['one']) instance.save() return instance diff --git a/junior/views.py b/junior/views.py index b91a8cd..e1d03d1 100644 --- a/junior/views.py +++ b/junior/views.py @@ -313,7 +313,8 @@ class RemoveJuniorAPIView(views.APIView): guardian_code__icontains=str(guardian.guardian_code)).last() if junior_queryset: # use RemoveJuniorSerializer serializer - serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True) + serializer = RemoveJuniorSerializer(junior_queryset, context={"guardian_code":guardian.guardian_code}, + data=request.data, partial=True) if serializer.is_valid(): # save serializer serializer.save() From c47f6222d938297aa39f0fa8e8c92bb1192d9843 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 25 Aug 2023 16:36:58 +0530 Subject: [PATCH 64/83] requested task not expired --- base/messages.py | 3 ++- guardian/utils.py | 2 +- junior/serializers.py | 13 +++++++++++-- junior/views.py | 16 ++++++++++++---- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/base/messages.py b/base/messages.py index 9afbe4f..8ad5234 100644 --- a/base/messages.py +++ b/base/messages.py @@ -107,7 +107,8 @@ ERROR_CODE = { "2078": "This junior is not associate with you", "2079": "Please update your app version for enjoying uninterrupted services", "2080": "Can not add App version", - "2081": "You can not add more than 3 guardian" + "2081": "You can not add more than 3 guardian", + "2082": "Guardian code does not exist" } """Success message code""" diff --git a/guardian/utils.py b/guardian/utils.py index 14bd36a..ed4f338 100644 --- a/guardian/utils.py +++ b/guardian/utils.py @@ -127,7 +127,7 @@ def update_expired_task_status(data=None): Update task of the status if due date is in past """ try: - task_status = [str(NUMBER['one']), str(NUMBER['two']), str(NUMBER['four'])] + task_status = [str(NUMBER['one']), str(NUMBER['two'])] JuniorTask.objects.filter(due_date__lt=datetime.today().date(), task_status__in=task_status).update(task_status=str(NUMBER['six'])) except ObjectDoesNotExist as e: diff --git a/junior/serializers.py b/junior/serializers.py index f4dd8ef..09d4241 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -508,8 +508,17 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer): model = Junior fields = ('id', ) def update(self, instance, validated_data): - instance.guardian_code = None - instance.guardian_code_status = str(NUMBER['one']) + guardian_code = self.context['guardian_code'] + if guardian_code in instance.guardian_code: + instance.guardian_code.remove(guardian_code) + else: + raise serializers.ValidationError({"error":ERROR_CODE['2082'],"code":"400", "status":"failed"}) + if not instance.guardian_code: + instance.guardian_code_status = str(NUMBER['one']) + elif instance.guardian_code and (len(instance.guardian_code) == 1 and '-' in instance.guardian_code): + instance.guardian_code_status = str(NUMBER['one']) + else: + instance.guardian_code_status = str(NUMBER['two']) instance.save() return instance diff --git a/junior/views.py b/junior/views.py index e1d03d1..c4297e9 100644 --- a/junior/views.py +++ b/junior/views.py @@ -207,13 +207,15 @@ class AddJuniorAPIView(viewsets.ModelViewSet): def associate_guardian(self, user): junior = Junior.objects.filter(auth__email=self.request.data['email']).first() guardian = Guardian.objects.filter(user=self.request.user).first() + if junior.guardian_code and ('-' in junior.guardian_code): + junior.guardian_code.remove('-') if not junior: return none if junior.guardian_code and (guardian.guardian_code in junior.guardian_code): return False if not junior.guardian_code: junior.guardian_code = [guardian.guardian_code] - if type(junior.guardian_code) is list and len(junior.guardian_code) < 4: + if type(junior.guardian_code) is list and len(junior.guardian_code) < 3: junior.guardian_code.append(guardian.guardian_code) else: return "Max" @@ -724,16 +726,21 @@ class CreateArticleCardAPIView(viewsets.ModelViewSet): class RemoveGuardianCodeAPIView(views.APIView): """Remove guardian code request API - No Payload""" + Payload + {"guardian_code" + :"GRD037" + }""" serializer_class = RemoveGuardianCodeSerializer permission_classes = [IsAuthenticated] def put(self, request, format=None): try: + guardian_code = self.request.data.get("guardian_code") junior_queryset = Junior.objects.filter(auth=self.request.user).last() if junior_queryset: # use RemoveGuardianCodeSerializer serializer - serializer = RemoveGuardianCodeSerializer(junior_queryset, data=request.data, partial=True) + serializer = RemoveGuardianCodeSerializer(junior_queryset, context = {"guardian_code":guardian_code}, + data=request.data, partial=True) if serializer.is_valid(): # save serializer serializer.save() @@ -743,7 +750,8 @@ class RemoveGuardianCodeAPIView(views.APIView): # task in another state return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) except Exception as e: - return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) + error_detail = e.detail.get('error', None) + return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST) class FAQViewSet(GenericViewSet, mixins.CreateModelMixin, From c46b2ef52c7729fc5b44c4bbf64347490f87c786 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 25 Aug 2023 16:59:17 +0530 Subject: [PATCH 65/83] sonar fixes --- account/custom_middleware.py | 2 ++ account/serializers.py | 5 +++-- base/messages.py | 3 +++ guardian/serializers.py | 7 +++++++ guardian/views.py | 3 ++- junior/serializers.py | 8 ++++---- junior/views.py | 6 +++--- 7 files changed, 24 insertions(+), 10 deletions(-) diff --git a/account/custom_middleware.py b/account/custom_middleware.py index 42a3b0f..b3cc750 100644 --- a/account/custom_middleware.py +++ b/account/custom_middleware.py @@ -17,6 +17,8 @@ from guardian.models import Guardian # multiple devices only # user can login in single # device at a time""" +# force update +# use 308 status code for force update def custom_response(custom_error, response_status = status.HTTP_404_NOT_FOUND): """custom response""" diff --git a/account/serializers.py b/account/serializers.py index dbb52a0..b783efd 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -123,7 +123,7 @@ class ChangePasswordSerializer(serializers.Serializer): def create(self, validated_data): """ - + change password """ new_password = validated_data.pop('new_password') current_password = validated_data.pop('current_password') @@ -392,7 +392,8 @@ class UserPhoneOtpSerializer(serializers.ModelSerializer): fields = '__all__' class ForceUpdateSerializer(serializers.ModelSerializer): - # ForceUpdate Serializer + """ ForceUpdate Serializer + """ class Meta(object): """ meta info """ diff --git a/base/messages.py b/base/messages.py index 8ad5234..b4abf7d 100644 --- a/base/messages.py +++ b/base/messages.py @@ -101,13 +101,16 @@ ERROR_CODE = { "2072": "You can not approve or reject this task because junior does not exist in the system", "2073": "You can not approve or reject this junior because junior does not exist in the system", "2074": "You can not complete this task because you does not exist in the system", + # deactivate account "2075": "Your account is deactivated. Please contact with admin", "2076": "This junior already associate with you", "2077": "You can not add guardian", "2078": "This junior is not associate with you", + # force update "2079": "Please update your app version for enjoying uninterrupted services", "2080": "Can not add App version", "2081": "You can not add more than 3 guardian", + # guardian code not exist "2082": "Guardian code does not exist" } diff --git a/guardian/serializers.py b/guardian/serializers.py index af11acc..ae72bdc 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -36,6 +36,7 @@ from django.utils.translation import gettext as _ # In this serializer file # define user serializer, +# define password validation # create guardian serializer, # task serializer, # guardian serializer, @@ -47,6 +48,7 @@ from django.utils.translation import gettext as _ from rest_framework import serializers class PasswordValidator: + """Password validation""" def __init__(self, min_length=8, max_length=None, require_uppercase=True, require_numbers=True): self.min_length = min_length self.max_length = max_length @@ -57,6 +59,7 @@ class PasswordValidator: self.enforce_password_policy(value) def enforce_password_policy(self, password): + # add validation for password special_characters = "!@#$%^&*()_-+=<>?/[]{}|" if len(password) < self.min_length: raise serializers.ValidationError( @@ -64,16 +67,20 @@ class PasswordValidator: ) if self.max_length is not None and len(password) > self.max_length: + # must be 8 character raise serializers.ValidationError( _("Password must be at most %(max_length)d characters long.") % {'max_length': self.max_length} ) if self.require_uppercase and not any(char.isupper() for char in password): + # must contain upper case letter raise serializers.ValidationError(_("Password must contain at least one uppercase letter.")) if self.require_numbers and not any(char.isdigit() for char in password): + # must contain digit raise serializers.ValidationError(_("Password must contain at least one digit.")) if self.require_numbers and not any(char in special_characters for char in password): + # must contain special character raise serializers.ValidationError(_("Password must contain at least one special character.")) diff --git a/guardian/views.py b/guardian/views.py index b541660..947f071 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -288,7 +288,8 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet): if request.data['action'] == '1': # use ApproveJuniorSerializer serializer serializer = ApproveJuniorSerializer(context={"guardian_code": guardian.guardian_code, - "junior": junior_queryset, "action": request.data['action']}, + "junior": junior_queryset, + "action": request.data['action']}, data=request.data) if serializer.is_valid(): # save serializer diff --git a/junior/serializers.py b/junior/serializers.py index 09d4241..98616a2 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -523,18 +523,18 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer): return instance class FAQSerializer(serializers.ModelSerializer): - # FAQ Serializer + """FAQ Serializer""" class Meta(object): - # meta info + """meta info""" model = FAQ fields = ('id', 'question', 'description') class CreateArticleCardSerializer(serializers.ModelSerializer): - # Article card Serializer + """Article card Serializer""" class Meta(object): - # meta info + """meta info""" model = ArticleCard fields = ('id', 'article') diff --git a/junior/views.py b/junior/views.py index c4297e9..4d20d57 100644 --- a/junior/views.py +++ b/junior/views.py @@ -266,10 +266,10 @@ class FilterJuniorAPIView(viewsets.ModelViewSet): manual_parameters=[ # Example of a query parameter openapi.Parameter( - 'title', # Query parameter name - openapi.IN_QUERY, # Parameter location + 'title', + openapi.IN_QUERY, description='title of the name', - type=openapi.TYPE_STRING, # Parameter type + type=openapi.TYPE_STRING, ), # Add more parameters as needed ] From 21b92f8c74691dd049daf1d56aef8ef5ae18be3c Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 25 Aug 2023 16:59:10 +0530 Subject: [PATCH 66/83] added notification when admin adds a new article --- base/tasks.py | 5 ++--- celerybeat-schedule | Bin 16384 -> 16384 bytes guardian/views.py | 2 +- junior/utils.py | 2 +- notifications/constants.py | 16 ++++++++++++---- notifications/utils.py | 9 +++++++-- web_admin/serializers/article_serializer.py | 6 ++++++ zod_bank/celery.py | 5 +++-- 8 files changed, 32 insertions(+), 13 deletions(-) diff --git a/base/tasks.py b/base/tasks.py index 98d0df0..65dff3b 100644 --- a/base/tasks.py +++ b/base/tasks.py @@ -68,7 +68,7 @@ def notify_top_junior(): task to send notification for top leaderboard junior to all junior's :return: """ - junior_points_qs = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( + junior_points_qs = JuniorPoints.objects.select_related('junior', 'junior__auth').annotate(rank=Window( expression=Rank(), order_by=[F('total_points').desc(), 'junior__created_at'] )).order_by('-total_points', 'junior__created_at') @@ -76,9 +76,8 @@ def notify_top_junior(): prev_top_position = junior_points_qs.filter(position=1).first() new_top_position = junior_points_qs.filter(rank=1).first() if prev_top_position != new_top_position: - to_user_list = [junior_point.junior.auth for junior_point in junior_points_qs] send_notification_multiple_user(TOP_JUNIOR, new_top_position.junior.auth.id, JUNIOR, - to_user_list, {'points': new_top_position.total_points}) + {'points': new_top_position.total_points}) for junior_point in junior_points_qs: junior_point.position = junior_point.rank junior_point.save() diff --git a/celerybeat-schedule b/celerybeat-schedule index 4ebacd4aa0047e3c5989eee717063f096d44e18e..4c54db222e0832de26f18d1b25337e66a5fdcf70 100644 GIT binary patch delta 226 zcmZo@U~Fh$T%aJt#sC44PM`n+03EJ!O2u^yIDxj zo|j3MY4UF4mdP{CUrBNCFmY7oPstE%n-Vm|n=?a7qleL8N)MBv!Q>BS;*+;ph)?FS xwHD_VV_;12hAEp0QpT*MZ#3D#RC=?Ubv2_j7IT Date: Fri, 25 Aug 2023 19:08:15 +0530 Subject: [PATCH 67/83] added notification for getting points after reading article --- junior/views.py | 4 +++- notifications/constants.py | 10 ++++++++-- notifications/utils.py | 6 ++++-- notifications/views.py | 2 +- zod_bank/celery.py | 4 ++-- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/junior/views.py b/junior/views.py index b4a9d1d..e7f5815 100644 --- a/junior/views.py +++ b/junior/views.py @@ -47,7 +47,7 @@ from account.utils import custom_response, custom_error_response from guardian.utils import upload_image_to_alibaba from .utils import update_positions_based_on_points from notifications.utils import send_notification -from notifications.constants import REMOVE_JUNIOR +from notifications.constants import REMOVE_JUNIOR, ARTICLE_REWARD_POINTS from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer, StartAssessmentSerializer) @@ -666,6 +666,8 @@ class CompleteArticleAPIView(views.APIView): is_answer_correct=True).aggregate( total_earn_points=Sum('earn_points'))['total_earn_points'] data = {"total_earn_points":total_earn_points} + send_notification.delay(ARTICLE_REWARD_POINTS, None, None, + request.user.id, {'points': total_earn_points}) return custom_response(SUCCESS_CODE['3042'], data, response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/notifications/constants.py b/notifications/constants.py index dc6ea75..503e0f2 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -17,7 +17,8 @@ IN_PROGRESS_TASK_EXPIRING = 13 TOP_JUNIOR = 14 NEW_ARTICLE_PUBLISHED = 15 -REMOVE_JUNIOR = 16 +ARTICLE_REWARD_POINTS = 16 +REMOVE_JUNIOR = 17 TEST_NOTIFICATION = 99 @@ -66,7 +67,7 @@ NOTIFICATION_DICT = { # as junior send task for approval TASK_ACTION: { "title": "Task completion approval!", - "body": "{from_user} completed her task {task_name}." + "body": "{from_user} completed their task {task_name}." }, # Juniors will receive notification as soon # as their task is rejected by custodians @@ -105,6 +106,11 @@ NOTIFICATION_DICT = { "title": "Time to read!", "body": "A new article has been published." }, + # Juniors will receive notification when they earn points by reading financial Learning + ARTICLE_REWARD_POINTS: { + "title": "Article reward points!", + "body": "You are rewarded with {points} points for reading article and answering questions. " + }, # Juniors will receive notification as soon as their custodians remove them from account REMOVE_JUNIOR: { "title": "Disassociate by guardian!", diff --git a/notifications/utils.py b/notifications/utils.py index a2759e0..aad37c0 100644 --- a/notifications/utils.py +++ b/notifications/utils.py @@ -77,6 +77,7 @@ def get_notification_data(notification_type, from_user_id, from_user_type, to_us push_data = NOTIFICATION_DICT[notification_type].copy() notification_data = push_data.copy() task_name = None + points = extra_data.get('points', None) if 'task_id' in extra_data: task = JuniorTask.objects.filter(id=extra_data.get('task_id')).first() task_name = task.task_name @@ -85,8 +86,9 @@ def get_notification_data(notification_type, from_user_id, from_user_type, to_us from_user_name, from_user_image, from_user = get_from_user_details(from_user_id, from_user_type) - push_data['body'] = push_data['body'].format(from_user=from_user_name, task_name=task_name) - notification_data['body'] = notification_data['body'].format(from_user=from_user_name, task_name=task_name) + push_data['body'] = push_data['body'].format(from_user=from_user_name, task_name=task_name, points=points) + notification_data['body'] = notification_data['body'].format(from_user=from_user_name, + task_name=task_name, points=points) notification_data['from_user'] = from_user_name notification_data['from_user_image'] = from_user_image diff --git a/notifications/views.py b/notifications/views.py index 54adf5a..670635a 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -36,7 +36,7 @@ class NotificationViewSet(viewsets.GenericViewSet): paginator = self.pagination_class() paginated_queryset = paginator.paginate_queryset(queryset, request) serializer = self.serializer_class(paginated_queryset, many=True) - return custom_response(None, serializer.data) + return custom_response(None, serializer.data, count=queryset.count()) @action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice) def fcm_registration(self, request): diff --git a/zod_bank/celery.py b/zod_bank/celery.py index cf71dc8..039ea03 100644 --- a/zod_bank/celery.py +++ b/zod_bank/celery.py @@ -35,11 +35,11 @@ app.conf.beat_schedule = { }, 'notify_task_expiry': { 'task': 'base.tasks.notify_task_expiry', - 'schedule': crontab(minute='5', hour='12'), + 'schedule': crontab(minute='0', hour='18'), }, 'notify_top_junior': { 'task': 'base.tasks.notify_top_junior', - 'schedule': crontab(minute='*/5',), + 'schedule': crontab(minute='0', hour='*/2'), }, } From cf9376663ca1447e0b14d69e24cc2adf76f2a1fe Mon Sep 17 00:00:00 2001 From: jain Date: Sat, 26 Aug 2023 17:09:10 +0530 Subject: [PATCH 68/83] list of guardian code status --- guardian/serializers.py | 19 ++++- guardian/views.py | 80 ++++++++++++------- ...0030_remove_junior_guardian_code_status.py | 17 ++++ .../0031_junior_guardian_code_status.py | 19 +++++ junior/models.py | 6 +- junior/serializers.py | 42 +++++++--- junior/views.py | 46 ++++++----- zod_bank/settings.py | 52 ++++++------ 8 files changed, 185 insertions(+), 96 deletions(-) create mode 100644 junior/migrations/0030_remove_junior_guardian_code_status.py create mode 100644 junior/migrations/0031_junior_guardian_code_status.py diff --git a/guardian/serializers.py b/guardian/serializers.py index 76b99b5..2914b79 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -402,9 +402,13 @@ class ApproveJuniorSerializer(serializers.ModelSerializer): def create(self, validated_data): """update guardian code""" instance = self.context['junior'] - instance.guardian_code = [self.context['guardian_code']] - instance.guardian_code_approved = True - instance.guardian_code_status = str(NUMBER['two']) + guardian_code = self.context['guardian_code'] + print("guardian_code==>", guardian_code, '==>', type(guardian_code)) + print("instance.guardian_code==>", instance.guardian_code, '==>', + type(instance.guardian_code)) + index = instance.guardian_code.index(guardian_code) + print("index==>", index, '==>', type(index)) + instance.guardian_code_status[index] = str(NUMBER['two']) instance.save() return instance @@ -512,4 +516,11 @@ class GuardianDetailListSerializer(serializers.ModelSerializer): def get_guardian_code_status(self,obj): """guardian code status""" - return obj.junior.guardian_code_status + print("obj.guardian.guardian_code===>",obj.guardian.guardian_code,'===>',type(obj.guardian.guardian_code)) + print("obj.junior.guardian_code===>", obj.junior.guardian_code, '===>', type(obj.junior.guardian_code)) + if obj.guardian.guardian_code in obj.junior.guardian_code: + index = obj.junior.guardian_code.index(obj.guardian.guardian_code) + print("index===>", index, '===>', type(index)) + data = obj.junior.guardian_code_status[index] + print("data===>", data, '===>', type(data)) + return obj.junior.guardian_code_status diff --git a/guardian/views.py b/guardian/views.py index c565201..73ce914 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -179,36 +179,42 @@ class CreateTaskAPIView(viewsets.ModelViewSet): image = request.data['default_image'] junior = request.data['junior'] junior_id = Junior.objects.filter(id=junior).last() - guardian_data = Guardian.objects.filter(user=request.user).last() - if (guardian_data.guardian_code not in junior_id.guardian_code or - junior_id.guardian_code_status in guardian_code_tuple): - return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST) - allowed_extensions = ['.jpg', '.jpeg', '.png'] - if not any(extension in str(image) for extension in allowed_extensions): - return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST) - if not junior.isnumeric(): - """junior value must be integer""" - return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) - data = request.data - if 'https' in str(image): - image_data = image - else: - filename = f"images/{image}" - if image and image.size == NUMBER['zero']: - return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST) - image_url = upload_image_to_alibaba(image, filename) - image_data = image_url - data.pop('default_image') - # use TaskSerializer serializer - serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data) - if serializer.is_valid(): - # save serializer - task = serializer.save() + if junior_id: + guardian_data = Guardian.objects.filter(user=request.user).last() + index = junior_id.guardian_code.index(guardian_data.guardian_code) + print("index===>", index, '===>', type(index)) + status_index = junior_id.guardian_code_status[index] + print("status_index===>", status_index, '===>', type(status_index)) + if status_index == str(NUMBER['three']): + return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST) + allowed_extensions = ['.jpg', '.jpeg', '.png'] + if not any(extension in str(image) for extension in allowed_extensions): + return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST) + if not junior.isnumeric(): + """junior value must be integer""" + return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) + data = request.data + if 'https' in str(image): + image_data = image + else: + filename = f"images/{image}" + if image and image.size == NUMBER['zero']: + return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST) + image_url = upload_image_to_alibaba(image, filename) + image_data = image_url + data.pop('default_image') + # use TaskSerializer serializer + serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data) + if serializer.is_valid(): + # save serializer + task = serializer.save() - send_notification.delay(TASK_ASSIGNED, request.auth.payload['user_id'], GUARDIAN, - junior_id.auth.id, {'task_id': task.id}) - return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK) - return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) + send_notification.delay(TASK_ASSIGNED, request.auth.payload['user_id'], GUARDIAN, + junior_id.auth.id, {'task_id': task.id}) + return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK) + return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) + else: + return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) @@ -302,8 +308,20 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet): junior_queryset.auth.id) return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) else: - junior_queryset.guardian_code = None - junior_queryset.guardian_code_status = str(NUMBER['one']) + print("#############################################") + if junior_queryset.guardian_code and ('-' in junior_queryset.guardian_code): + print("777777777777777") + junior_queryset.guardian_code.remove('-') + if junior_queryset.guardian_code_status and ('-' in junior_queryset.guardian_code_status): + print("666666666666666666666666") + junior_queryset.guardian_code_status.remove('-') + print("guardian.guardian_code==>", guardian.guardian_code, '==>', type(guardian.guardian_code)) + print("junior_queryset.guardian_code==>", junior_queryset.guardian_code, '==>', type(junior_queryset.guardian_code)) + index = junior_queryset.guardian_code.index(guardian.guardian_code) + print("index==>", index, '==>', type(index)) + junior_queryset.guardian_code.remove(guardian.guardian_code) + data = junior_queryset.guardian_code_status.pop(index) + print("data==>", data, '==>', type(data)) junior_queryset.save() send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id) return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) diff --git a/junior/migrations/0030_remove_junior_guardian_code_status.py b/junior/migrations/0030_remove_junior_guardian_code_status.py new file mode 100644 index 0000000..6949e9a --- /dev/null +++ b/junior/migrations/0030_remove_junior_guardian_code_status.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.2 on 2023-08-26 08:59 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0029_junior_is_deleted'), + ] + + operations = [ + migrations.RemoveField( + model_name='junior', + name='guardian_code_status', + ), + ] diff --git a/junior/migrations/0031_junior_guardian_code_status.py b/junior/migrations/0031_junior_guardian_code_status.py new file mode 100644 index 0000000..c342f28 --- /dev/null +++ b/junior/migrations/0031_junior_guardian_code_status.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.2 on 2023-08-26 08:59 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0030_remove_junior_guardian_code_status'), + ] + + operations = [ + migrations.AddField( + model_name='junior', + name='guardian_code_status', + field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, default=None, max_length=10, null=True), null=True, size=None), + ), + ] diff --git a/junior/models.py b/junior/models.py index 773557b..559acfe 100644 --- a/junior/models.py +++ b/junior/models.py @@ -76,9 +76,9 @@ class Junior(models.Model): is_verified = models.BooleanField(default=False) """guardian code is approved or not""" guardian_code_approved = models.BooleanField(default=False) - # guardian code status""" - guardian_code_status = models.CharField(max_length=31, choices=GUARDIAN_CODE_STATUS, default='1', - null=True, blank=True) + # # guardian code status""" + guardian_code_status = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None), null=True, + ) # Profile created and updated time""" created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) diff --git a/junior/serializers.py b/junior/serializers.py index 6ba4218..3497b37 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -92,16 +92,23 @@ class CreateJuniorSerializer(serializers.ModelSerializer): # condition for guardian code if guardian_code: if not junior.guardian_code: + print("111111111") junior.guardian_code = [] + junior.guardian_code_status = [] junior.guardian_code.extend(guardian_code) + junior.guardian_code_status.extend(str(NUMBER['three'])) elif len(junior.guardian_code) < 3 and len(guardian_code) < 3: + print("2222222222") junior.guardian_code.extend(guardian_code) + junior.guardian_code_status.extend(str(NUMBER['three'])) else: raise serializers.ValidationError({"error":ERROR_CODE['2081'],"code":"400", "status":"failed"}) guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last() if guardian_data: JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) - junior.guardian_code_status = str(NUMBER['three']) + # print("junior.guardian_code.index(guardian_code)==>",junior.guardian_code.index(guardian_code),'===>',type(junior.guardian_code.index(guardian_code))) + print("junior.guardian_code==>", junior.guardian_code, '===>', type(junior.guardian_code)) + junior_approval_mail.delay(user.email, user.first_name) send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {}) @@ -296,9 +303,9 @@ class AddJuniorSerializer(serializers.ModelSerializer): referral_code=generate_code(ZOD, user_data.id), referral_code_used=guardian_data.referral_code, is_password_set=False, is_verified=True, - guardian_code_status=GUARDIAN_CODE_STATUS[1][0]) + guardian_code_status=[str(NUMBER['two'])]) JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data, - relationship=relationship) + relationship=relationship) total_junior = Junior.objects.all().count() JuniorPoints.objects.create(junior=junior_data, position=total_junior) """Generate otp""" @@ -326,9 +333,18 @@ class RemoveJuniorSerializer(serializers.ModelSerializer): if instance: guardian_code = self.context['guardian_code'] instance.is_invited = False + if instance.guardian_code and ('-' in instance.guardian_code): + print("1111111111111") + instance.guardian_code.remove('-') + print("instance.guardian_code==>",instance.guardian_code,'==>',type(instance.guardian_code),'===>', + len(instance.guardian_code)) + print("instance.guardian_code_status==>", instance.guardian_code_status, '==>', type(instance.guardian_code_status), '===>', + len(instance.guardian_code_status)) + index = instance.guardian_code.index(guardian_code) + print("index==>",index,'==>',type(index)) instance.guardian_code.remove(guardian_code) - if not instance.guardian_code: - instance.guardian_code_status = str(NUMBER['one']) + data = instance.guardian_code_status.pop(index) + print("data==>", data, '==>', type(data)) instance.save() return instance @@ -506,15 +522,19 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer): def update(self, instance, validated_data): guardian_code = self.context['guardian_code'] if guardian_code in instance.guardian_code: + if instance.guardian_code and ('-' in instance.guardian_code): + print("777777777777777") + instance.guardian_code.remove('-') + if instance.guardian_code_status and ('-' in instance.guardian_code_status): + print("666666666666666666666666") + instance.guardian_code_status.remove('-') + index = instance.guardian_code.index(guardian_code) + print("index===>",index,'===>',type(index)) instance.guardian_code.remove(guardian_code) + data = instance.guardian_code_status.pop(index) + print("data===>", data, '===>', type(data)) else: raise serializers.ValidationError({"error":ERROR_CODE['2082'],"code":"400", "status":"failed"}) - if not instance.guardian_code: - instance.guardian_code_status = str(NUMBER['one']) - elif instance.guardian_code and (len(instance.guardian_code) == 1 and '-' in instance.guardian_code): - instance.guardian_code_status = str(NUMBER['one']) - else: - instance.guardian_code_status = str(NUMBER['two']) instance.save() return instance diff --git a/junior/views.py b/junior/views.py index e7f5815..7e8831f 100644 --- a/junior/views.py +++ b/junior/views.py @@ -99,7 +99,10 @@ class UpdateJuniorProfile(viewsets.ModelViewSet): return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) except Exception as e: - error_detail = e.detail.get('error', None) + if e.detail: + error_detail = e.detail.get('error', None) + else: + error_detail = str(e) return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST) class ValidateGuardianCode(viewsets.ModelViewSet): @@ -191,7 +194,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet): return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST) elif not data: return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST) - if data == "Max": + elif data == "Max": return custom_error_response(ERROR_CODE['2081'], response_status=status.HTTP_400_BAD_REQUEST) return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK) # use AddJuniorSerializer serializer @@ -208,6 +211,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet): junior = Junior.objects.filter(auth__email=self.request.data['email']).first() guardian = Guardian.objects.filter(user=self.request.user).first() if junior.guardian_code and ('-' in junior.guardian_code): + print("1111111111111") junior.guardian_code.remove('-') if not junior: return none @@ -219,7 +223,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet): junior.guardian_code.append(guardian.guardian_code) else: return "Max" - junior.guardian_code_status = str(NUMBER['two']) + junior.guardian_code_status = [str(NUMBER['two'])] junior.save() JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior, relationship=str(self.request.data['relationship'])) @@ -736,24 +740,24 @@ class RemoveGuardianCodeAPIView(views.APIView): permission_classes = [IsAuthenticated] def put(self, request, format=None): - try: - guardian_code = self.request.data.get("guardian_code") - junior_queryset = Junior.objects.filter(auth=self.request.user).last() - if junior_queryset: - # use RemoveGuardianCodeSerializer serializer - serializer = RemoveGuardianCodeSerializer(junior_queryset, context = {"guardian_code":guardian_code}, - data=request.data, partial=True) - if serializer.is_valid(): - # save serializer - serializer.save() - return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK) - return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) - else: - # task in another state - return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) - except Exception as e: - error_detail = e.detail.get('error', None) - return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST) + # try: + guardian_code = self.request.data.get("guardian_code") + junior_queryset = Junior.objects.filter(auth=self.request.user).last() + if junior_queryset: + # use RemoveGuardianCodeSerializer serializer + serializer = RemoveGuardianCodeSerializer(junior_queryset, context = {"guardian_code":guardian_code}, + data=request.data, partial=True) + if serializer.is_valid(): + # save serializer + serializer.save() + return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK) + return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) + else: + # task in another state + return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) + # except Exception as e: + # error_detail = e.detail.get('error', None) + # return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST) class FAQViewSet(GenericViewSet, mixins.CreateModelMixin, diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 781df80..74ca6e9 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -181,32 +181,32 @@ AUTH_PASSWORD_VALIDATORS = [ # database query logs settings # Allows us to check db hits # useful to optimize db query and hit -LOGGING = { - "version": 1, - "filters": { - "require_debug_true": { - "()": "django.utils.log.RequireDebugTrue" - } - }, - "handlers": { - "console": { - "level": "DEBUG", - "filters": [ - "require_debug_true" - ], - "class": "logging.StreamHandler" - } - }, - # database logger - "loggers": { - "django.db.backends": { - "level": "DEBUG", - "handlers": [ - "console" - ] - } - } -} +# LOGGING = { +# "version": 1, +# "filters": { +# "require_debug_true": { +# "()": "django.utils.log.RequireDebugTrue" +# } +# }, +# "handlers": { +# "console": { +# "level": "DEBUG", +# "filters": [ +# "require_debug_true" +# ], +# "class": "logging.StreamHandler" +# } +# }, +# # database logger +# "loggers": { +# "django.db.backends": { +# "level": "DEBUG", +# "handlers": [ +# "console" +# ] +# } +# } +# } # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ From eb4e09964d05670cd4d0c56f5de54ddad48d046e Mon Sep 17 00:00:00 2001 From: jain Date: Sat, 26 Aug 2023 17:48:13 +0530 Subject: [PATCH 69/83] changes of guardian code status in necessary API --- guardian/serializers.py | 10 +------- guardian/views.py | 13 ++--------- junior/serializers.py | 26 ++++----------------- junior/views.py | 43 +++++++++++++++++----------------- zod_bank/settings.py | 52 ++++++++++++++++++++--------------------- 5 files changed, 56 insertions(+), 88 deletions(-) diff --git a/guardian/serializers.py b/guardian/serializers.py index 2914b79..4206d7a 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -403,11 +403,7 @@ class ApproveJuniorSerializer(serializers.ModelSerializer): """update guardian code""" instance = self.context['junior'] guardian_code = self.context['guardian_code'] - print("guardian_code==>", guardian_code, '==>', type(guardian_code)) - print("instance.guardian_code==>", instance.guardian_code, '==>', - type(instance.guardian_code)) index = instance.guardian_code.index(guardian_code) - print("index==>", index, '==>', type(index)) instance.guardian_code_status[index] = str(NUMBER['two']) instance.save() return instance @@ -516,11 +512,7 @@ class GuardianDetailListSerializer(serializers.ModelSerializer): def get_guardian_code_status(self,obj): """guardian code status""" - print("obj.guardian.guardian_code===>",obj.guardian.guardian_code,'===>',type(obj.guardian.guardian_code)) - print("obj.junior.guardian_code===>", obj.junior.guardian_code, '===>', type(obj.junior.guardian_code)) if obj.guardian.guardian_code in obj.junior.guardian_code: index = obj.junior.guardian_code.index(obj.guardian.guardian_code) - print("index===>", index, '===>', type(index)) data = obj.junior.guardian_code_status[index] - print("data===>", data, '===>', type(data)) - return obj.junior.guardian_code_status + return data diff --git a/guardian/views.py b/guardian/views.py index 73ce914..ddd6231 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -182,9 +182,7 @@ class CreateTaskAPIView(viewsets.ModelViewSet): if junior_id: guardian_data = Guardian.objects.filter(user=request.user).last() index = junior_id.guardian_code.index(guardian_data.guardian_code) - print("index===>", index, '===>', type(index)) status_index = junior_id.guardian_code_status[index] - print("status_index===>", status_index, '===>', type(status_index)) if status_index == str(NUMBER['three']): return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST) allowed_extensions = ['.jpg', '.jpeg', '.png'] @@ -308,20 +306,13 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet): junior_queryset.auth.id) return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) else: - print("#############################################") if junior_queryset.guardian_code and ('-' in junior_queryset.guardian_code): - print("777777777777777") junior_queryset.guardian_code.remove('-') if junior_queryset.guardian_code_status and ('-' in junior_queryset.guardian_code_status): - print("666666666666666666666666") junior_queryset.guardian_code_status.remove('-') - print("guardian.guardian_code==>", guardian.guardian_code, '==>', type(guardian.guardian_code)) - print("junior_queryset.guardian_code==>", junior_queryset.guardian_code, '==>', type(junior_queryset.guardian_code)) index = junior_queryset.guardian_code.index(guardian.guardian_code) - print("index==>", index, '==>', type(index)) junior_queryset.guardian_code.remove(guardian.guardian_code) - data = junior_queryset.guardian_code_status.pop(index) - print("data==>", data, '==>', type(data)) + junior_queryset.guardian_code_status.pop(index) junior_queryset.save() send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id) return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) @@ -372,7 +363,7 @@ class ApproveTaskAPIView(viewsets.ModelViewSet): return custom_error_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) - +# class GuardianListAPIView(viewsets.ModelViewSet): """Guardian list of assosicated junior""" diff --git a/junior/serializers.py b/junior/serializers.py index 3497b37..33210d8 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -92,13 +92,11 @@ class CreateJuniorSerializer(serializers.ModelSerializer): # condition for guardian code if guardian_code: if not junior.guardian_code: - print("111111111") junior.guardian_code = [] junior.guardian_code_status = [] junior.guardian_code.extend(guardian_code) junior.guardian_code_status.extend(str(NUMBER['three'])) elif len(junior.guardian_code) < 3 and len(guardian_code) < 3: - print("2222222222") junior.guardian_code.extend(guardian_code) junior.guardian_code_status.extend(str(NUMBER['three'])) else: @@ -106,9 +104,6 @@ class CreateJuniorSerializer(serializers.ModelSerializer): guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last() if guardian_data: JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) - # print("junior.guardian_code.index(guardian_code)==>",junior.guardian_code.index(guardian_code),'===>',type(junior.guardian_code.index(guardian_code))) - print("junior.guardian_code==>", junior.guardian_code, '===>', type(junior.guardian_code)) - junior_approval_mail.delay(user.email, user.first_name) send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {}) @@ -304,8 +299,8 @@ class AddJuniorSerializer(serializers.ModelSerializer): referral_code_used=guardian_data.referral_code, is_password_set=False, is_verified=True, guardian_code_status=[str(NUMBER['two'])]) - JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data, - relationship=relationship) + JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data, + relationship=relationship) total_junior = Junior.objects.all().count() JuniorPoints.objects.create(junior=junior_data, position=total_junior) """Generate otp""" @@ -334,17 +329,10 @@ class RemoveJuniorSerializer(serializers.ModelSerializer): guardian_code = self.context['guardian_code'] instance.is_invited = False if instance.guardian_code and ('-' in instance.guardian_code): - print("1111111111111") instance.guardian_code.remove('-') - print("instance.guardian_code==>",instance.guardian_code,'==>',type(instance.guardian_code),'===>', - len(instance.guardian_code)) - print("instance.guardian_code_status==>", instance.guardian_code_status, '==>', type(instance.guardian_code_status), '===>', - len(instance.guardian_code_status)) index = instance.guardian_code.index(guardian_code) - print("index==>",index,'==>',type(index)) instance.guardian_code.remove(guardian_code) - data = instance.guardian_code_status.pop(index) - print("data==>", data, '==>', type(data)) + instance.guardian_code_status.pop(index) instance.save() return instance @@ -463,7 +451,7 @@ class AddGuardianSerializer(serializers.ModelSerializer): user_type=str(NUMBER['two']), expired_at=expiry_time, is_verified=True) UserNotification.objects.get_or_create(user=user) - JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data, + JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data, relationship=relationship) """Notification email""" @@ -523,16 +511,12 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer): guardian_code = self.context['guardian_code'] if guardian_code in instance.guardian_code: if instance.guardian_code and ('-' in instance.guardian_code): - print("777777777777777") instance.guardian_code.remove('-') if instance.guardian_code_status and ('-' in instance.guardian_code_status): - print("666666666666666666666666") instance.guardian_code_status.remove('-') index = instance.guardian_code.index(guardian_code) - print("index===>",index,'===>',type(index)) instance.guardian_code.remove(guardian_code) - data = instance.guardian_code_status.pop(index) - print("data===>", data, '===>', type(data)) + instance.guardian_code_status.pop(index) else: raise serializers.ValidationError({"error":ERROR_CODE['2082'],"code":"400", "status":"failed"}) instance.save() diff --git a/junior/views.py b/junior/views.py index 7e8831f..37c4e01 100644 --- a/junior/views.py +++ b/junior/views.py @@ -211,7 +211,6 @@ class AddJuniorAPIView(viewsets.ModelViewSet): junior = Junior.objects.filter(auth__email=self.request.data['email']).first() guardian = Guardian.objects.filter(user=self.request.user).first() if junior.guardian_code and ('-' in junior.guardian_code): - print("1111111111111") junior.guardian_code.remove('-') if not junior: return none @@ -225,8 +224,10 @@ class AddJuniorAPIView(viewsets.ModelViewSet): return "Max" junior.guardian_code_status = [str(NUMBER['two'])] junior.save() - JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior, - relationship=str(self.request.data['relationship'])) + jun_data, created = JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior) + if jun_data: + jun_data.relationship = str(self.request.data['relationship']) + jun_data.save() return True @@ -740,24 +741,24 @@ class RemoveGuardianCodeAPIView(views.APIView): permission_classes = [IsAuthenticated] def put(self, request, format=None): - # try: - guardian_code = self.request.data.get("guardian_code") - junior_queryset = Junior.objects.filter(auth=self.request.user).last() - if junior_queryset: - # use RemoveGuardianCodeSerializer serializer - serializer = RemoveGuardianCodeSerializer(junior_queryset, context = {"guardian_code":guardian_code}, - data=request.data, partial=True) - if serializer.is_valid(): - # save serializer - serializer.save() - return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK) - return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) - else: - # task in another state - return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) - # except Exception as e: - # error_detail = e.detail.get('error', None) - # return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST) + try: + guardian_code = self.request.data.get("guardian_code") + junior_queryset = Junior.objects.filter(auth=self.request.user).last() + if junior_queryset: + # use RemoveGuardianCodeSerializer serializer + serializer = RemoveGuardianCodeSerializer(junior_queryset, context = {"guardian_code":guardian_code}, + data=request.data, partial=True) + if serializer.is_valid(): + # save serializer + serializer.save() + return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK) + return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) + else: + # task in another state + return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) + except Exception as e: + error_detail = e.detail.get('error', None) + return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST) class FAQViewSet(GenericViewSet, mixins.CreateModelMixin, diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 74ca6e9..781df80 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -181,32 +181,32 @@ AUTH_PASSWORD_VALIDATORS = [ # database query logs settings # Allows us to check db hits # useful to optimize db query and hit -# LOGGING = { -# "version": 1, -# "filters": { -# "require_debug_true": { -# "()": "django.utils.log.RequireDebugTrue" -# } -# }, -# "handlers": { -# "console": { -# "level": "DEBUG", -# "filters": [ -# "require_debug_true" -# ], -# "class": "logging.StreamHandler" -# } -# }, -# # database logger -# "loggers": { -# "django.db.backends": { -# "level": "DEBUG", -# "handlers": [ -# "console" -# ] -# } -# } -# } +LOGGING = { + "version": 1, + "filters": { + "require_debug_true": { + "()": "django.utils.log.RequireDebugTrue" + } + }, + "handlers": { + "console": { + "level": "DEBUG", + "filters": [ + "require_debug_true" + ], + "class": "logging.StreamHandler" + } + }, + # database logger + "loggers": { + "django.db.backends": { + "level": "DEBUG", + "handlers": [ + "console" + ] + } + } +} # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ From 8be8d56036b67e898adcdcefb8e6956e2b7fea62 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Sat, 26 Aug 2023 21:36:01 +0530 Subject: [PATCH 70/83] fixed approve junior notification issue --- guardian/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guardian/views.py b/guardian/views.py index ddd6231..e5b984a 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -303,7 +303,7 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet): # save serializer serializer.save() send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN, - junior_queryset.auth.id) + junior_queryset.auth.id, {}) return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) else: if junior_queryset.guardian_code and ('-' in junior_queryset.guardian_code): @@ -314,7 +314,7 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet): junior_queryset.guardian_code.remove(guardian.guardian_code) junior_queryset.guardian_code_status.pop(index) junior_queryset.save() - send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id) + send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id, {}) return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) From 235fc9baac4d5fa45ef620e4b6e08fda9ec743f4 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 28 Aug 2023 10:37:52 +0530 Subject: [PATCH 71/83] guardian code status in junior list API --- junior/serializers.py | 8 ++++++++ junior/views.py | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/junior/serializers.py b/junior/serializers.py index 33210d8..96f5fb6 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -171,6 +171,7 @@ class JuniorDetailListSerializer(serializers.ModelSerializer): rejected_task = serializers.SerializerMethodField('get_rejected_task') pending_task = serializers.SerializerMethodField('get_pending_task') position = serializers.SerializerMethodField('get_position') + guardian_code_status = serializers.SerializerMethodField('get_guardian_code_status') def get_auth(self, obj): @@ -215,6 +216,13 @@ class JuniorDetailListSerializer(serializers.ModelSerializer): def get_pending_task(self, obj): data = JuniorTask.objects.filter(junior=obj, task_status=PENDING).count() return data + + def get_guardian_code_status(self, obj): + if self.context['guardian_code'] in obj.guardian_code: + index = obj.guardian_code.index(self.context['guardian_code']) + if obj.guardian_code_status: + data = obj.guardian_code_status[index] + return data class Meta(object): """Meta info""" model = Junior diff --git a/junior/views.py b/junior/views.py index 37c4e01..7a95335 100644 --- a/junior/views.py +++ b/junior/views.py @@ -152,7 +152,8 @@ class JuniorListAPIView(viewsets.ModelViewSet): queryset = self.get_queryset() queryset = queryset.filter(guardian_code__icontains=str(guardian_data.guardian_code)) # use JuniorDetailListSerializer serializer - serializer = JuniorDetailListSerializer(queryset, many=True) + serializer = JuniorDetailListSerializer(queryset, context={"guardian_code": + guardian_data.guardian_code}, many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(ERROR_CODE['2045'], response_status=status.HTTP_200_OK) except Exception as e: From e3feab22a27ff32addc898072b41327517831843 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 28 Aug 2023 11:51:12 +0530 Subject: [PATCH 72/83] some changes --- guardian/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/guardian/views.py b/guardian/views.py index c565201..c1a03da 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -299,13 +299,14 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet): # save serializer serializer.save() send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN, - junior_queryset.auth.id) + junior_queryset.auth.id, {}) return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) else: junior_queryset.guardian_code = None junior_queryset.guardian_code_status = str(NUMBER['one']) junior_queryset.save() - send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id) + send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, + junior_queryset.auth.id, {}) return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) From c2c9210a9a4cc5bb2d9b7511549689437de395b5 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 28 Aug 2023 11:54:38 +0530 Subject: [PATCH 73/83] some changes --- guardian/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/guardian/views.py b/guardian/views.py index c1a03da..139f244 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -305,8 +305,7 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet): junior_queryset.guardian_code = None junior_queryset.guardian_code_status = str(NUMBER['one']) junior_queryset.save() - send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, - junior_queryset.auth.id, {}) + send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id, {}) return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) From 34359360e0e5dbb835af2346f75812b9edaa455f Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 28 Aug 2023 12:10:13 +0530 Subject: [PATCH 74/83] guardian_status update --- junior/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junior/views.py b/junior/views.py index 7a95335..c1c110d 100644 --- a/junior/views.py +++ b/junior/views.py @@ -223,7 +223,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet): junior.guardian_code.append(guardian.guardian_code) else: return "Max" - junior.guardian_code_status = [str(NUMBER['two'])] + junior.guardian_code_status.append(str(NUMBER['two'])) junior.save() jun_data, created = JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior) if jun_data: From 966f94eb1ec14cf78ba8a55a8e57e14f4e7669a1 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 28 Aug 2023 12:41:29 +0530 Subject: [PATCH 75/83] change message --- base/messages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/messages.py b/base/messages.py index b4abf7d..54c427f 100644 --- a/base/messages.py +++ b/base/messages.py @@ -109,7 +109,7 @@ ERROR_CODE = { # force update "2079": "Please update your app version for enjoying uninterrupted services", "2080": "Can not add App version", - "2081": "You can not add more than 3 guardian", + "2081": "A junior can only be associated with a maximum of 3 guardian", # guardian code not exist "2082": "Guardian code does not exist" From 8e529b292d8094bb7a4f35d0824643eff85fe6b1 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 28 Aug 2023 13:38:35 +0530 Subject: [PATCH 76/83] add non in delete API --- account/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account/utils.py b/account/utils.py index c24c833..60a5c44 100644 --- a/account/utils.py +++ b/account/utils.py @@ -94,7 +94,7 @@ def junior_account_update(user_tb): junior_data.is_active = False junior_data.is_verified = False junior_data.guardian_code = None - junior_data.guardian_code_status = str(NUMBER['one']) + junior_data.guardian_code_status = None junior_data.is_deleted = True junior_data.save() JuniorPoints.objects.filter(junior=junior_data).delete() From 5e17edcf3f8c47220b40c42b9677fae10e99e39d Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 28 Aug 2023 15:36:24 +0530 Subject: [PATCH 77/83] fixed add junior multiple device notification issue, changed send multiple user notification method, modified add fcm token method --- celerybeat-schedule | Bin 16384 -> 20480 bytes notifications/utils.py | 26 +++++++----------- notifications/views.py | 13 ++------- web_admin/serializers/analytics_serializer.py | 16 +++++++++-- web_admin/views/analytics.py | 4 +-- 5 files changed, 28 insertions(+), 31 deletions(-) diff --git a/celerybeat-schedule b/celerybeat-schedule index 4c54db222e0832de26f18d1b25337e66a5fdcf70..f457bb55d9731edd43b34c596341385d6757e2ec 100644 GIT binary patch delta 427 zcmZo@U~E{xI6;vofB_8t074*{LMiM`*?X7 z93bNCK+1HptlknnDPd(fmjBzPWQev+37X=~l_90k!)P$2hsn@l@&_~V$y+VNCtKND zOM;YrG=nLd0aTWu*~6@*Zv<2&xw*`?i)oSp2MfzoEvCu4jV<|mcv340GK*4E;!6^X zv!_fBF!yAaVK-J|F|C=rz&wzVb+UntCbLY2%;W;II1Nr`F81dfVEHJ`DH&p5r*LOT v!<{lE1E>_Jc&UXt&^0FNPz_uR42-EWVH)NFHDqXk9R*WA)joUUhHPE{l#E_c delta 193 zcmZozz}V2hI6;xefdLHu07OL)Tf#T_ nqn$82qrsFOCPRbCAI!uj?{M^VzT= diff --git a/notifications/utils.py b/notifications/utils.py index aad37c0..bd2a8bd 100644 --- a/notifications/utils.py +++ b/notifications/utils.py @@ -19,14 +19,13 @@ from junior.models import Junior from notifications.constants import NOTIFICATION_DICT from notifications.models import Notification - User = get_user_model() def register_fcm_token(user_id, registration_id, device_id, device_type): """ used to register the fcm device token""" - device, _ = FCMDevice.objects.update_or_create(device_id=device_id, - defaults={'user_id': user_id, 'type': device_type, + device, _ = FCMDevice.objects.update_or_create(user_id=user_id, + defaults={'device_id': device_id, 'type': device_type, 'active': True, 'registration_id': registration_id}) return device @@ -93,7 +92,7 @@ def get_notification_data(notification_type, from_user_id, from_user_type, to_us notification_data['from_user_image'] = from_user_image notification_data.update(extra_data) - to_user = User.objects.get(id=to_user_id) + to_user = User.objects.filter(id=to_user_id).first() return notification_data, push_data, from_user, to_user @@ -135,21 +134,18 @@ def send_notification_multiple_user(notification_type, from_user_id, from_user_t to_user_list = User.objects.filter(junior_profile__is_verified=True, is_superuser=False ).exclude(junior_profile__isnull=True, guardian_profile__isnull=True) - push_data = NOTIFICATION_DICT[notification_type].copy() - notification_data = push_data.copy() - points = extra_data.get('points', None) - from_user_name, from_user_image, from_user = get_from_user_details(from_user_id, from_user_type) - push_data['body'] = push_data['body'].format(from_user=from_user_name, points=points) - notification_data['body'] = notification_data['body'].format(from_user=from_user_name, - points=points) - notification_data['from_user'] = from_user_name - notification_data['from_user_image'] = from_user_image + notification_data, push_data, from_user, _ = get_notification_data(notification_type, from_user_id, + from_user_type, None, extra_data) + notification_list = [] for user in to_user_list: + notification_copy_data = notification_data.copy() + notification_copy_data.update( + {'badge': Notification.objects.filter(notification_to=user, is_read=False).count()}) notification_list.append(Notification(notification_type=notification_type, notification_to=user, notification_from=from_user, - data=notification_data)) + data=notification_copy_data)) Notification.objects.bulk_create(notification_list) to_user_list = to_user_list.filter(user_notification__push_notification=True) send_multiple_push(to_user_list, push_data) @@ -183,5 +179,3 @@ def send_notification_to_junior(notification_type, from_user_id, to_user_id, ext from_user = Guardian.objects.filter(user_id=from_user_id).first() extra_data['from_user_image'] = from_user.image send_notification(notification_type, from_user_id, to_user_id, extra_data) - - diff --git a/notifications/views.py b/notifications/views.py index 670635a..812502e 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -52,9 +52,11 @@ class NotificationViewSet(viewsets.GenericViewSet): @action(methods=['get'], detail=False, url_path='test', url_name='test') def send_test_notification(self, request): """ - to send test notification + to test send notification, task expiry, top junior :return: """ + notify_task_expiry() + notify_top_junior() send_notification(TEST_NOTIFICATION, None, None, request.auth.payload['user_id'], {}) return custom_response(SUCCESS_CODE["3000"]) @@ -67,12 +69,3 @@ class NotificationViewSet(viewsets.GenericViewSet): """ Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True) return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK) - - @action(methods=['get'], url_path='task', url_name='task', detail=False, - permission_classes=[AllowAny]) - def task(self, request, *args, **kwargs): - """ - notification list - """ - notify_top_junior() - return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK) diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py index b0177d7..7871615 100644 --- a/web_admin/serializers/analytics_serializer.py +++ b/web_admin/serializers/analytics_serializer.py @@ -9,7 +9,7 @@ from django.contrib.auth import get_user_model from account.utils import get_user_full_name # local imports -from base.constants import USER_TYPE +from base.constants import USER_TYPE, JUNIOR from junior.models import JuniorPoints, Junior @@ -37,7 +37,7 @@ class JuniorLeaderboardSerializer(serializers.ModelSerializer): :param obj: junior object :return: full name """ - return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name + return get_user_full_name(obj.auth) @staticmethod def get_first_name(obj): @@ -60,6 +60,8 @@ class LeaderboardSerializer(serializers.ModelSerializer): """ leaderboard serializer """ + user_id = serializers.SerializerMethodField() + user_type = serializers.SerializerMethodField() junior = JuniorLeaderboardSerializer() rank = serializers.IntegerField() @@ -68,7 +70,15 @@ class LeaderboardSerializer(serializers.ModelSerializer): meta class """ model = JuniorPoints - fields = ('total_points', 'rank', 'junior') + fields = ('user_id', 'user_type', 'total_points', 'rank', 'junior') + + @staticmethod + def get_user_id(obj): + return obj.junior.auth.id + + @staticmethod + def get_user_type(obj): + return JUNIOR class UserCSVReportSerializer(serializers.ModelSerializer): diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 4a010a3..c04bb98 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -128,7 +128,7 @@ class AnalyticsViewSet(GenericViewSet): :param request: :return: """ - queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( + queryset = JuniorPoints.objects.select_related('junior', 'junior__auth').annotate(rank=Window( expression=Rank(), order_by=[F('total_points').desc(), 'junior__created_at'] )).order_by('-total_points', 'junior__created_at') @@ -199,7 +199,7 @@ class AnalyticsViewSet(GenericViewSet): # sheet 3 for Juniors Leaderboard and rank elif sheet_name == 'Juniors Leaderboard': - queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( + queryset = JuniorPoints.objects.select_related('junior', 'junior__auth').annotate(rank=Window( expression=Rank(), order_by=[F('total_points').desc(), 'junior__created_at'] )).order_by('-total_points', 'junior__created_at')[:15] From d2242d4c647957c419a71e4e7dcc34b8cf8432b4 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 28 Aug 2023 18:39:29 +0530 Subject: [PATCH 78/83] remove guardian request and resend otp --- account/views.py | 1 - junior/serializers.py | 6 ++++-- junior/views.py | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/account/views.py b/account/views.py index 8dbb2f7..e208163 100644 --- a/account/views.py +++ b/account/views.py @@ -546,7 +546,6 @@ class UserEmailVerification(viewsets.ModelViewSet): class ReSendEmailOtp(viewsets.ModelViewSet): """Send otp on phone""" serializer_class = EmailVerificationSerializer - permission_classes = [IsAuthenticated] http_method_names = ('post',) def create(self, request, *args, **kwargs): """Param diff --git a/junior/serializers.py b/junior/serializers.py index 96f5fb6..d3d0b2f 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -91,6 +91,9 @@ class CreateJuniorSerializer(serializers.ModelSerializer): # Update guardian code""" # condition for guardian code if guardian_code: + if junior.guardian_code and guardian_code: + if guardian_code[0] in junior.guardian_code: + raise serializers.ValidationError({"error":ERROR_CODE['2076'],"code":"400", "status":"failed"}) if not junior.guardian_code: junior.guardian_code = [] junior.guardian_code_status = [] @@ -104,9 +107,8 @@ class CreateJuniorSerializer(serializers.ModelSerializer): guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last() if guardian_data: JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) + send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {}) junior_approval_mail.delay(user.email, user.first_name) - send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {}) - junior.dob = validated_data.get('dob', junior.dob) junior.passcode = validated_data.get('passcode', junior.passcode) junior.country_name = validated_data.get('country_name', junior.country_name) diff --git a/junior/views.py b/junior/views.py index c1c110d..d7a3d5f 100644 --- a/junior/views.py +++ b/junior/views.py @@ -326,6 +326,7 @@ class RemoveJuniorAPIView(views.APIView): if serializer.is_valid(): # save serializer serializer.save() + JuniorGuardianRelationship.objects.filter(guardian=guardian, junior=junior_queryset).delete() send_notification.delay(REMOVE_JUNIOR, None, None, junior_queryset.auth.id, {}) return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) @@ -744,6 +745,7 @@ class RemoveGuardianCodeAPIView(views.APIView): def put(self, request, format=None): try: guardian_code = self.request.data.get("guardian_code") + guardian_data = Guardian.objects.filter(guardian_code=guardian_code).last() junior_queryset = Junior.objects.filter(auth=self.request.user).last() if junior_queryset: # use RemoveGuardianCodeSerializer serializer @@ -752,6 +754,7 @@ class RemoveGuardianCodeAPIView(views.APIView): if serializer.is_valid(): # save serializer serializer.save() + JuniorGuardianRelationship.objects.filter(guardian=guardian_data, junior=junior_queryset).delete() return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) else: From 219bae792e4f8c887ea1c06f6ba281a26290db58 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 28 Aug 2023 19:39:39 +0530 Subject: [PATCH 79/83] added notification for existing junior add, modified leaderboard method at every places --- base/tasks.py | 12 ++++++++---- guardian/views.py | 12 ++++++++---- junior/serializers.py | 4 ++-- junior/utils.py | 10 ++++++---- junior/views.py | 5 +++-- notifications/constants.py | 29 +++++++++++++++++------------ web_admin/views/analytics.py | 20 ++++++++++++-------- 7 files changed, 56 insertions(+), 36 deletions(-) diff --git a/base/tasks.py b/base/tasks.py index 65dff3b..fbeca1d 100644 --- a/base/tasks.py +++ b/base/tasks.py @@ -68,10 +68,14 @@ def notify_top_junior(): task to send notification for top leaderboard junior to all junior's :return: """ - junior_points_qs = JuniorPoints.objects.select_related('junior', 'junior__auth').annotate(rank=Window( - expression=Rank(), - order_by=[F('total_points').desc(), 'junior__created_at'] - )).order_by('-total_points', 'junior__created_at') + junior_points_qs = JuniorPoints.objects.filter( + junior__is_verified=True + ).select_related( + 'junior', 'junior__auth' + ).annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at']) + ).order_by('-total_points', 'junior__created_at') prev_top_position = junior_points_qs.filter(position=1).first() new_top_position = junior_points_qs.filter(rank=1).first() diff --git a/guardian/views.py b/guardian/views.py index e5b984a..e120681 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -259,10 +259,14 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): return context def get_queryset(self): - queryset = JuniorPoints.objects.select_related('junior', 'junior__auth').annotate(rank=Window( - expression=Rank(), - order_by=[F('total_points').desc(), 'junior__created_at'] - )).order_by('-total_points', 'junior__created_at') + queryset = JuniorPoints.objects.filter( + junior__is_verified=True + ).select_related( + 'junior', 'junior__auth' + ).annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at']) + ).order_by('-total_points', 'junior__created_at') return queryset def list(self, request, *args, **kwargs): diff --git a/junior/serializers.py b/junior/serializers.py index d3d0b2f..de5f671 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -22,7 +22,7 @@ from account.models import UserEmailOtp, UserNotification from junior.utils import junior_notification_email, junior_approval_mail, get_junior_leaderboard_rank from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime from notifications.utils import send_notification -from notifications.constants import (ASSOCIATE_REQUEST, JUNIOR_ADDED, TASK_ACTION, +from notifications.constants import (ASSOCIATE_REQUEST, ASSOCIATE_JUNIOR, TASK_ACTION, ) from web_admin.models import ArticleCard @@ -323,7 +323,7 @@ class AddJuniorSerializer(serializers.ModelSerializer): """Notification email""" junior_notification_email.delay(email, full_name, email, password) # push notification - send_notification.delay(JUNIOR_ADDED, None, None, junior_data.auth.id, {}) + send_notification.delay(ASSOCIATE_JUNIOR, None, None, junior_data.auth.id, {}) return junior_data diff --git a/junior/utils.py b/junior/utils.py index 7533017..eac6ac9 100644 --- a/junior/utils.py +++ b/junior/utils.py @@ -70,10 +70,12 @@ def get_junior_leaderboard_rank(junior_obj): :param junior_obj: :return: junior's position/rank """ - queryset = JuniorPoints.objects.select_related('junior', 'junior__auth').annotate(rank=Window( - expression=Rank(), - order_by=[F('total_points').desc(), 'junior__created_at'] - )).order_by('-total_points', 'junior__created_at') + queryset = JuniorPoints.objects.filter( + junior__is_verified=True + ).select_related('junior', 'junior__auth').annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at'] + )).order_by('-total_points', 'junior__created_at') junior = next((query for query in queryset if query.junior == junior_obj), None) diff --git a/junior/views.py b/junior/views.py index d7a3d5f..cdb8874 100644 --- a/junior/views.py +++ b/junior/views.py @@ -42,12 +42,12 @@ from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, Ad from guardian.models import Guardian, JuniorTask from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer from base.messages import ERROR_CODE, SUCCESS_CODE -from base.constants import NUMBER, ARTICLE_STATUS, none +from base.constants import NUMBER, ARTICLE_STATUS, none, GUARDIAN from account.utils import custom_response, custom_error_response from guardian.utils import upload_image_to_alibaba from .utils import update_positions_based_on_points from notifications.utils import send_notification -from notifications.constants import REMOVE_JUNIOR, ARTICLE_REWARD_POINTS +from notifications.constants import REMOVE_JUNIOR, ARTICLE_REWARD_POINTS, ASSOCIATE_EXISTING_JUNIOR from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer, StartAssessmentSerializer) @@ -229,6 +229,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet): if jun_data: jun_data.relationship = str(self.request.data['relationship']) jun_data.save() + send_notification.delay(ASSOCIATE_EXISTING_JUNIOR, self.request.user.id, GUARDIAN, junior.auth.id, {}) return True diff --git a/notifications/constants.py b/notifications/constants.py index 503e0f2..85b0d2d 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -6,19 +6,20 @@ ASSOCIATE_REQUEST = 3 ASSOCIATE_REJECTED = 4 ASSOCIATE_APPROVED = 5 REFERRAL_POINTS = 6 -JUNIOR_ADDED = 7 +ASSOCIATE_JUNIOR = 7 +ASSOCIATE_EXISTING_JUNIOR = 8 -TASK_ASSIGNED = 8 -TASK_ACTION = 9 -TASK_REJECTED = 10 -TASK_APPROVED = 11 -PENDING_TASK_EXPIRING = 12 -IN_PROGRESS_TASK_EXPIRING = 13 -TOP_JUNIOR = 14 +TASK_ASSIGNED = 9 +TASK_ACTION = 10 +TASK_REJECTED = 11 +TASK_APPROVED = 12 +PENDING_TASK_EXPIRING = 13 +IN_PROGRESS_TASK_EXPIRING = 14 +TOP_JUNIOR = 15 -NEW_ARTICLE_PUBLISHED = 15 -ARTICLE_REWARD_POINTS = 16 -REMOVE_JUNIOR = 17 +NEW_ARTICLE_PUBLISHED = 16 +ARTICLE_REWARD_POINTS = 17 +REMOVE_JUNIOR = 18 TEST_NOTIFICATION = 99 @@ -53,10 +54,14 @@ NOTIFICATION_DICT = { }, # Juniors will receive notification # once any custodians add them in their account - JUNIOR_ADDED: { + ASSOCIATE_JUNIOR: { "title": "Profile already setup!", "body": "Your guardian has already setup your profile." }, + ASSOCIATE_EXISTING_JUNIOR: { + "title": "Associated to guardian", + "body": "Your are associated to your guardian {from_user}." + }, # Juniors will receive Notification # for every Task Assign by Custodians TASK_ASSIGNED: { diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index c04bb98..926cd47 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -128,10 +128,12 @@ class AnalyticsViewSet(GenericViewSet): :param request: :return: """ - queryset = JuniorPoints.objects.select_related('junior', 'junior__auth').annotate(rank=Window( - expression=Rank(), - order_by=[F('total_points').desc(), 'junior__created_at'] - )).order_by('-total_points', 'junior__created_at') + queryset = JuniorPoints.objects.filter( + junior__is_verified=True + ).select_related('junior', 'junior__auth').annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at'] + )).order_by('-total_points', 'junior__created_at') paginator = CustomPageNumberPagination() paginated_queryset = paginator.paginate_queryset(queryset, request) serializer = self.serializer_class(paginated_queryset, many=True) @@ -199,10 +201,12 @@ class AnalyticsViewSet(GenericViewSet): # sheet 3 for Juniors Leaderboard and rank elif sheet_name == 'Juniors Leaderboard': - queryset = JuniorPoints.objects.select_related('junior', 'junior__auth').annotate(rank=Window( - expression=Rank(), - order_by=[F('total_points').desc(), 'junior__created_at'] - )).order_by('-total_points', 'junior__created_at')[:15] + queryset = JuniorPoints.objects.filter( + junior__is_verified=True + ).select_related('junior', 'junior__auth').annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at'] + )).order_by('-total_points', 'junior__created_at')[:15] df_leaderboard = pd.DataFrame([ { 'Name': get_user_full_name(junior.junior.auth), From 63dfd731fc3e4728c0cbd9e77d7191428d8179b3 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 28 Aug 2023 20:09:39 +0530 Subject: [PATCH 80/83] condition in add junior --- junior/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/junior/views.py b/junior/views.py index d7a3d5f..5d23894 100644 --- a/junior/views.py +++ b/junior/views.py @@ -223,7 +223,10 @@ class AddJuniorAPIView(viewsets.ModelViewSet): junior.guardian_code.append(guardian.guardian_code) else: return "Max" - junior.guardian_code_status.append(str(NUMBER['two'])) + if not junior.guardian_code_status: + junior.guardian_code_status = [str(NUMBER['two'])] + else: + junior.guardian_code_status.append(str(NUMBER['two'])) junior.save() jun_data, created = JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior) if jun_data: From 37d191eef8685cc44b36bcecc1ea19a68dab086f Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 29 Aug 2023 10:41:44 +0530 Subject: [PATCH 81/83] add message for blank search --- base/messages.py | 3 ++- junior/views.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/base/messages.py b/base/messages.py index 54c427f..5a6e2e8 100644 --- a/base/messages.py +++ b/base/messages.py @@ -111,7 +111,8 @@ ERROR_CODE = { "2080": "Can not add App version", "2081": "A junior can only be associated with a maximum of 3 guardian", # guardian code not exist - "2082": "Guardian code does not exist" + "2082": "Guardian code does not exist", + "2083": "Search should not be blank" } """Success message code""" diff --git a/junior/views.py b/junior/views.py index 5ca50e0..a560f4d 100644 --- a/junior/views.py +++ b/junior/views.py @@ -223,6 +223,8 @@ class AddJuniorAPIView(viewsets.ModelViewSet): junior.guardian_code.append(guardian.guardian_code) else: return "Max" + if junior.guardian_code_status and ('-' in junior.guardian_code_status): + junior.guardian_code_status.remove('-') if not junior.guardian_code_status: junior.guardian_code_status = [str(NUMBER['two'])] else: @@ -355,6 +357,8 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): try: status_value = self.request.GET.get('status') search = self.request.GET.get('search') + if search.strip() == '': + return custom_error_response(ERROR_CODE['2083'], response_status=status.HTTP_400_BAD_REQUEST) if search and str(status_value) == '0': # search with title and for all task list queryset = JuniorTask.objects.filter(junior__auth=request.user, From e2c84eb83d42db13ebcce8fea20c0684e79cef6f Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 29 Aug 2023 11:49:59 +0530 Subject: [PATCH 82/83] search junior task --- junior/views.py | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/junior/views.py b/junior/views.py index a560f4d..64aaffb 100644 --- a/junior/views.py +++ b/junior/views.py @@ -346,9 +346,19 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): """Junior task list""" serializer_class = TaskDetailsjuniorSerializer permission_classes = [IsAuthenticated] + filter_backends = (SearchFilter,) + search_fields = ['task_name', ] pagination_class = PageNumberPagination http_method_names = ('get',) + def get_queryset(self): + queryset = JuniorTask.objects.filter(junior__auth=self.request.user + ).prefetch_related('junior', 'junior__auth' + ).order_by('due_date', 'created_at') + + queryset = self.filter_queryset(queryset) + return queryset + def list(self, request, *args, **kwargs): """Junior task list status=0 @@ -356,29 +366,14 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): page=1""" try: status_value = self.request.GET.get('status') - search = self.request.GET.get('search') - if search.strip() == '': - return custom_error_response(ERROR_CODE['2083'], response_status=status.HTTP_400_BAD_REQUEST) - if search and str(status_value) == '0': - # search with title and for all task list - queryset = JuniorTask.objects.filter(junior__auth=request.user, - task_name__icontains=search).order_by('due_date', 'created_at') - elif search and str(status_value) != '0': - # search with title and fetch task list with status wise - queryset = JuniorTask.objects.filter(junior__auth=request.user, task_name__icontains=search, - task_status=status_value).order_by('due_date', 'created_at') - if search is None and str(status_value) == '0': - # fetch all task list - queryset = JuniorTask.objects.filter(junior__auth=request.user).order_by('due_date', 'created_at') - elif search is None and str(status_value) != '0': - # fetch task list with status wise - queryset = JuniorTask.objects.filter(junior__auth=request.user, - task_status=status_value).order_by('due_date','created_at') + queryset = self.get_queryset() + if status_value and status_value != '0': + queryset = queryset.filter(task_status=status_value) paginator = self.pagination_class() # use Pagination paginated_queryset = paginator.paginate_queryset(queryset, request) - # use TaskDetailsSerializer serializer - serializer = TaskDetailsjuniorSerializer(paginated_queryset, many=True) + # use TaskDetails juniorSerializer serializer + serializer = self.serializer_class(paginated_queryset, many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) From a8d291474a7614ac66ece27b4c5c908e4da25ac3 Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 29 Aug 2023 11:52:47 +0530 Subject: [PATCH 83/83] search junior task --- base/messages.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/messages.py b/base/messages.py index 5a6e2e8..54c427f 100644 --- a/base/messages.py +++ b/base/messages.py @@ -111,8 +111,7 @@ ERROR_CODE = { "2080": "Can not add App version", "2081": "A junior can only be associated with a maximum of 3 guardian", # guardian code not exist - "2082": "Guardian code does not exist", - "2083": "Search should not be blank" + "2082": "Guardian code does not exist" } """Success message code"""