From 0af2a352066fb63eb75dbe769e1f4f4fd5d383d7 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 1 Sep 2023 12:14:15 +0530 Subject: [PATCH 01/13] task assign to multiple junior --- guardian/serializers.py | 24 ++++++++++-- guardian/views.py | 83 ++++++++++++++++++++++++----------------- zod_bank/settings.py | 52 +++++++++++++------------- 3 files changed, 94 insertions(+), 65 deletions(-) diff --git a/guardian/serializers.py b/guardian/serializers.py index 4206d7a..c26ad53 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -218,7 +218,7 @@ class TaskSerializer(serializers.ModelSerializer): class Meta(object): """Meta info""" model = JuniorTask - fields = ['id', 'task_name','task_description','points', 'due_date', 'junior', 'default_image'] + fields = ['id', 'task_name','task_description','points', 'due_date','default_image'] def validate_due_date(self, value): """validation on due date""" @@ -229,11 +229,27 @@ class TaskSerializer(serializers.ModelSerializer): return value def create(self, validated_data): """create default task image data""" - validated_data['guardian'] = Guardian.objects.filter(user=self.context['user']).last() + guardian = Guardian.objects.filter(user=self.context['user']).last() # update image of the task images = self.context['image'] - validated_data['default_image'] = images - instance = JuniorTask.objects.create(**validated_data) + junior_ids = self.context['junior_data'] + print("junior_ids==>", junior_ids, '==>', type(junior_ids)) + print() + junior_data = junior_ids[0].split(',') + print("junior_data[0==>", junior_data, '==>', type(junior_data)) + tasks_created = [] + + for junior_id in junior_data: + print("junior_id==>",junior_id,'==>',type(junior_id)) + task_data = validated_data.copy() + task_data['guardian'] = guardian + task_data['default_image'] = images + task_data['junior'] = Junior.objects.filter(id=junior_id).last() + print("task_data===>", task_data, '===>', type(task_data)) + print("task_data['junior']===>", task_data['junior'], '===>', type(task_data['junior'])) + instance = JuniorTask.objects.create(**task_data) + tasks_created.append(instance) + print("tasks_created==>", tasks_created, '==>', type(tasks_created)) return instance class GuardianDetailSerializer(serializers.ModelSerializer): diff --git a/guardian/views.py b/guardian/views.py index e120681..2502a18 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -177,42 +177,55 @@ class CreateTaskAPIView(viewsets.ModelViewSet): """ try: image = request.data['default_image'] - junior = request.data['junior'] - junior_id = Junior.objects.filter(id=junior).last() - if junior_id: - guardian_data = Guardian.objects.filter(user=request.user).last() - index = junior_id.guardian_code.index(guardian_data.guardian_code) - status_index = junior_id.guardian_code_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() + juniors = request.data['junior'].split(',') + print("juniors===>", juniors, '===>', type(juniors)) + print() - 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) + for junior in juniors: + print("junior===>", junior, '===>', type(junior)) + junior_id = Junior.objects.filter(id=junior).last() + print("junior_id===>", junior_id, '===>', type(junior_id)) + if junior_id: + guardian_data = Guardian.objects.filter(user=request.user).last() + index = junior_id.guardian_code.index(guardian_data.guardian_code) + status_index = junior_id.guardian_code_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) + print("request.data===>", request.data, '===>', type(request.data)) + 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') + print("data===>",data,'===>',type(data)) + junior_data = data.pop('junior') + print() + print("data===>", data, '===>', type(data)) + print("junior_data===>", junior_data, '===>', type(junior_data)) + # use TaskSerializer serializer + serializer = TaskSerializer(context={"user":request.user, "image":image_data, + "junior_data":junior_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) + 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) 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 4f79a690c1584822c43d9f3a85e02c667940aa00 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 1 Sep 2023 16:25:05 +0530 Subject: [PATCH 02/13] unrestrict logout and refresh token api while login in multiple device --- account/custom_middleware.py | 3 ++- base/messages.py | 4 +-- guardian/serializers.py | 7 ----- guardian/views.py | 12 +-------- zod_bank/settings.py | 52 ++++++++++++++++++------------------ 5 files changed, 31 insertions(+), 47 deletions(-) diff --git a/account/custom_middleware.py b/account/custom_middleware.py index b3cc750..705cf49 100644 --- a/account/custom_middleware.py +++ b/account/custom_middleware.py @@ -45,11 +45,12 @@ class CustomMiddleware(object): device_type = str(request.META.get('HTTP_TYPE')) api_endpoint = request.path + unrestricted_api = ('/api/v1/user/login/', '/api/v1/logout/', '/api/v1/generate-token/') if request.user.is_authenticated: # device details 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/': + if not device_details and api_endpoint not in unrestricted_api: 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']): diff --git a/base/messages.py b/base/messages.py index 54c427f..feed804 100644 --- a/base/messages.py +++ b/base/messages.py @@ -147,8 +147,8 @@ SUCCESS_CODE = { "3018": "Task created successfully", "3019": "Support Email sent successfully", "3020": "Logged out successfully.", - "3021": "Add junior successfully", - "3022": "Remove junior successfully", + "3021": "Added junior successfully", + "3022": "Removed junior successfully", "3023": "Junior is approved successfully", "3024": "Junior request is rejected successfully", "3025": "Task is approved successfully", diff --git a/guardian/serializers.py b/guardian/serializers.py index c26ad53..bd2e43d 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -233,23 +233,16 @@ class TaskSerializer(serializers.ModelSerializer): # update image of the task images = self.context['image'] junior_ids = self.context['junior_data'] - print("junior_ids==>", junior_ids, '==>', type(junior_ids)) - print() junior_data = junior_ids[0].split(',') - print("junior_data[0==>", junior_data, '==>', type(junior_data)) tasks_created = [] for junior_id in junior_data: - print("junior_id==>",junior_id,'==>',type(junior_id)) task_data = validated_data.copy() task_data['guardian'] = guardian task_data['default_image'] = images task_data['junior'] = Junior.objects.filter(id=junior_id).last() - print("task_data===>", task_data, '===>', type(task_data)) - print("task_data['junior']===>", task_data['junior'], '===>', type(task_data['junior'])) instance = JuniorTask.objects.create(**task_data) tasks_created.append(instance) - print("tasks_created==>", tasks_created, '==>', type(tasks_created)) return instance class GuardianDetailSerializer(serializers.ModelSerializer): diff --git a/guardian/views.py b/guardian/views.py index 2502a18..b212faf 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -178,13 +178,8 @@ class CreateTaskAPIView(viewsets.ModelViewSet): try: image = request.data['default_image'] juniors = request.data['junior'].split(',') - print("juniors===>", juniors, '===>', type(juniors)) - print() - for junior in juniors: - print("junior===>", junior, '===>', type(junior)) junior_id = Junior.objects.filter(id=junior).last() - print("junior_id===>", junior_id, '===>', type(junior_id)) if junior_id: guardian_data = Guardian.objects.filter(user=request.user).last() index = junior_id.guardian_code.index(guardian_data.guardian_code) @@ -197,7 +192,6 @@ class CreateTaskAPIView(viewsets.ModelViewSet): if not junior.isnumeric(): """junior value must be integer""" return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) - print("request.data===>", request.data, '===>', type(request.data)) data = request.data if 'https' in str(image): image_data = image @@ -208,11 +202,7 @@ class CreateTaskAPIView(viewsets.ModelViewSet): image_url = upload_image_to_alibaba(image, filename) image_data = image_url data.pop('default_image') - print("data===>",data,'===>',type(data)) junior_data = data.pop('junior') - print() - print("data===>", data, '===>', type(data)) - print("junior_data===>", junior_data, '===>', type(junior_data)) # use TaskSerializer serializer serializer = TaskSerializer(context={"user":request.user, "image":image_data, "junior_data":junior_data}, data=data) @@ -222,7 +212,7 @@ class CreateTaskAPIView(viewsets.ModelViewSet): 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_response(SUCCESS_CODE['3018'], 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) 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 7b75a3233ca657a7cd4489f969df64f0a9d4867c Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 4 Sep 2023 12:52:04 +0530 Subject: [PATCH 03/13] add current page and total page in task list API --- account/utils.py | 5 +++-- guardian/views.py | 7 ++++++- junior/views.py | 7 ++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/account/utils.py b/account/utils.py index 60a5c44..f77e8e5 100644 --- a/account/utils.py +++ b/account/utils.py @@ -186,12 +186,13 @@ def send_support_email(name, sender, subject, message): return name -def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count=None): +def custom_response(detail, data=None, total_pages=None, current_page=None, response_status=status.HTTP_200_OK, count=None): """Custom response code""" if not data: """when data is none""" data = None - return Response({"data": data, "message": detail, "status": "success", "code": response_status, "count": count}) + return Response({"data": data, "message": detail, "total_pages":total_pages, "current_page":current_page, + "status": "success", "code": response_status, "count": count}) def custom_error_response(detail, response_status): diff --git a/guardian/views.py b/guardian/views.py index b212faf..e115065 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -1,4 +1,5 @@ """Views of Guardian""" +import math # django imports # Import IsAuthenticated @@ -154,15 +155,19 @@ class TaskListAPIView(viewsets.ModelViewSet): def list(self, request, *args, **kwargs): """Create guardian profile""" status_value = self.request.GET.get('status') + current_page = self.request.GET.get('page') queryset = self.get_queryset() if status_value and status_value != '0': queryset = queryset.filter(task_status=status_value) paginator = self.pagination_class() + total_count = len(queryset) + total_pages = math.ceil(total_count/10) # use Pagination paginated_queryset = paginator.paginate_queryset(queryset, request) # use TaskDetailsSerializer serializer serializer = self.serializer_class(paginated_queryset, many=True) - return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) + return custom_response(None, serializer.data, total_pages=total_pages, current_page=current_page, + response_status=status.HTTP_200_OK) class CreateTaskAPIView(viewsets.ModelViewSet): diff --git a/junior/views.py b/junior/views.py index 3e9eeed..1d2cac8 100644 --- a/junior/views.py +++ b/junior/views.py @@ -12,6 +12,7 @@ import datetime import requests from rest_framework.viewsets import GenericViewSet, mixins +import math """Django app import""" from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi @@ -366,15 +367,19 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): page=1""" try: status_value = self.request.GET.get('status') + current_page = self.request.GET.get('page') queryset = self.get_queryset() if status_value and status_value != '0': queryset = queryset.filter(task_status=status_value) paginator = self.pagination_class() + total_count = len(queryset) + total_pages = math.ceil(total_count / 10) # use Pagination paginated_queryset = paginator.paginate_queryset(queryset, request) # use TaskDetails juniorSerializer serializer serializer = self.serializer_class(paginated_queryset, many=True) - return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) + return custom_response(None, serializer.data, total_pages=total_pages, current_page=current_page, + response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) From d62efa21395ad07963a90e050d986bb1b6791bda Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 4 Sep 2023 13:09:54 +0530 Subject: [PATCH 04/13] added and modifid pagination --- base/pagination.py | 46 ++++++++++++++++++++++++++++++++++++ web_admin/pagination.py | 18 -------------- web_admin/views/analytics.py | 4 ++-- 3 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 base/pagination.py delete mode 100644 web_admin/pagination.py diff --git a/base/pagination.py b/base/pagination.py new file mode 100644 index 0000000..085579c --- /dev/null +++ b/base/pagination.py @@ -0,0 +1,46 @@ +""" +web_admin pagination file +""" +# third party imports +from collections import OrderedDict +from rest_framework.pagination import PageNumberPagination + +from account.utils import custom_response +from base.constants import NUMBER + + +class CustomPageNumberPagination(PageNumberPagination): + """ + custom paginator class + """ + # Set the desired page size + page_size = NUMBER['ten'] + page_size_query_param = 'page_size' + # Set a maximum page size if needed + max_page_size = NUMBER['hundred'] + + def get_paginated_response(self, data): + """ + :param data: queryset to be paginated + :return: return a OrderedDict + """ + return custom_response(None, OrderedDict([ + ('count', self.page.paginator.count), + ('data', data), + ('current_page', self.page.number), + ('total_pages', self.page.paginator.num_pages), + + + ])) + + def get_paginated_dict_response(self, data): + """ + :param data: queryset to be paginated + :return: return a simple dict obj + """ + return { + 'current': self.page.number, + 'data': data, + 'total': self.page.paginator.count, + 'total_pages': self.page.paginator.num_pages, + } diff --git a/web_admin/pagination.py b/web_admin/pagination.py deleted file mode 100644 index c2eed5e..0000000 --- a/web_admin/pagination.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -web_admin pagination file -""" -# third party imports -from rest_framework.pagination import PageNumberPagination - -from base.constants import NUMBER - - -class CustomPageNumberPagination(PageNumberPagination): - """ - custom paginator class - """ - # Set the desired page size - page_size = NUMBER['ten'] - page_size_query_param = 'page_size' - # Set a maximum page size if needed - max_page_size = NUMBER['hundred'] diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 926cd47..b4f228f 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -23,11 +23,11 @@ from django.http import HttpResponse # local imports 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 base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, 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 base.pagination import CustomPageNumberPagination from web_admin.permission import AdminPermission from web_admin.serializers.analytics_serializer import LeaderboardSerializer, UserCSVReportSerializer from web_admin.utils import get_dates From ec585d35f3ec8cb7e30fe3264c76d302ef920280 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 4 Sep 2023 13:11:25 +0530 Subject: [PATCH 05/13] added and modifid pagination --- base/pagination.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/pagination.py b/base/pagination.py index 085579c..e060447 100644 --- a/base/pagination.py +++ b/base/pagination.py @@ -39,8 +39,8 @@ class CustomPageNumberPagination(PageNumberPagination): :return: return a simple dict obj """ return { - 'current': self.page.number, + 'count': self.page.paginator.count, 'data': data, - 'total': self.page.paginator.count, + 'current_page': self.page.number, 'total_pages': self.page.paginator.num_pages, } From 20fa6e43dadcd7f4640aa58a39595604bd5bf94f Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 4 Sep 2023 15:02:47 +0530 Subject: [PATCH 06/13] changes in task list API and add special character in password for added junior --- account/utils.py | 26 +++++++++++++++++++++++++- guardian/views.py | 15 ++++++++++++--- junior/serializers.py | 8 ++++---- junior/views.py | 9 +++++++-- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/account/utils.py b/account/utils.py index f77e8e5..63dfc17 100644 --- a/account/utils.py +++ b/account/utils.py @@ -1,6 +1,6 @@ """Account utils""" from celery import shared_task - +import random """Import django""" from django.conf import settings from rest_framework import viewsets, status @@ -289,3 +289,27 @@ 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.first_name or user_obj.last_name else "User" + +def make_special_password(length=10): + # Define character sets + lowercase_letters = string.ascii_lowercase + uppercase_letters = string.ascii_uppercase + digits = string.digits + special_characters = '!@#$%^&*()_-+=<>?/[]{}|' + + # Combine character sets + all_characters = lowercase_letters + uppercase_letters + digits + special_characters + + # Create a password with random characters + password = ( + random.choice(lowercase_letters) + + random.choice(uppercase_letters) + + random.choice(digits) + + random.choice(special_characters) + + ''.join(random.choice(all_characters) for _ in range(length - 4)) + ) + + # Shuffle the characters to make it more random + password_list = list(password) + random.shuffle(password_list) + return ''.join(password_list) diff --git a/guardian/views.py b/guardian/views.py index e115065..66d65e6 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -136,7 +136,8 @@ class TaskListAPIView(viewsets.ModelViewSet): Params status search - page""" + page + junior""" serializer_class = TaskDetailsSerializer permission_classes = [IsAuthenticated] filter_backends = (SearchFilter,) @@ -156,9 +157,17 @@ class TaskListAPIView(viewsets.ModelViewSet): """Create guardian profile""" status_value = self.request.GET.get('status') current_page = self.request.GET.get('page') + junior = self.request.GET.get('junior') queryset = self.get_queryset() - if status_value and status_value != '0': - queryset = queryset.filter(task_status=status_value) + task_status = ['1'] + if str(status_value) == '2': + task_status = ['2', '4'] + elif str(status_value) == '3': + task_status = ['3', '5', '6'] + if status_value and not junior: + queryset = queryset.filter(task_status__in=task_status) + elif status_value and junior: + queryset = queryset.filter(task_status__in=task_status,junior=int(junior)) paginator = self.pagination_class() total_count = len(queryset) total_pages = math.ceil(total_count/10) diff --git a/junior/serializers.py b/junior/serializers.py index de5f671..aba9b12 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -11,7 +11,7 @@ from django.utils import timezone from rest_framework_simplejwt.tokens import RefreshToken # local imports -from account.utils import send_otp_email, generate_code +from account.utils import send_otp_email, generate_code, make_special_password from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, FAQ from guardian.tasks import generate_otp from base.messages import ERROR_CODE, SUCCESS_CODE @@ -297,8 +297,8 @@ class AddJuniorSerializer(serializers.ModelSerializer): user_data = User.objects.create(username=email, email=email, first_name=self.context['first_name'], last_name=self.context['last_name']) - password = User.objects.make_random_password() - user_data.set_password(password) + special_password = make_special_password() + user_data.set_password(special_password) user_data.save() junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'), image=profile_image, @@ -321,7 +321,7 @@ class AddJuniorSerializer(serializers.ModelSerializer): # add push notification UserNotification.objects.get_or_create(user=user_data) """Notification email""" - junior_notification_email.delay(email, full_name, email, password) + junior_notification_email.delay(email, full_name, email, special_password) # push notification send_notification.delay(ASSOCIATE_JUNIOR, None, None, junior_data.auth.id, {}) return junior_data diff --git a/junior/views.py b/junior/views.py index 1d2cac8..8d1390c 100644 --- a/junior/views.py +++ b/junior/views.py @@ -369,8 +369,13 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): status_value = self.request.GET.get('status') current_page = self.request.GET.get('page') queryset = self.get_queryset() - if status_value and status_value != '0': - queryset = queryset.filter(task_status=status_value) + task_status = ['1'] + if str(status_value) == '2': + task_status = ['2', '4'] + elif str(status_value) == '3': + task_status = ['3', '5', '6'] + if status_value: + queryset = queryset.filter(task_status__in=task_status) paginator = self.pagination_class() total_count = len(queryset) total_pages = math.ceil(total_count / 10) From 116fb003588f18f36f2f1896c14b5f4add75b27e Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 4 Sep 2023 15:22:15 +0530 Subject: [PATCH 07/13] optimized task status code --- account/utils.py | 9 +++++++++ guardian/views.py | 8 ++------ junior/views.py | 8 ++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/account/utils.py b/account/utils.py index 63dfc17..353042a 100644 --- a/account/utils.py +++ b/account/utils.py @@ -313,3 +313,12 @@ def make_special_password(length=10): password_list = list(password) random.shuffle(password_list) return ''.join(password_list) + +def task_status_fun(status_value): + """task status""" + task_status_value = ['1'] + if str(status_value) == '2': + task_status_value = ['2', '4'] + elif str(status_value) == '3': + task_status_value = ['3', '5', '6'] + return task_status_value diff --git a/guardian/views.py b/guardian/views.py index 66d65e6..082a9d7 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -37,7 +37,7 @@ from .models import Guardian, JuniorTask from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship 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 account.utils import custom_response, custom_error_response, send_otp_email, task_status_fun from base.messages import ERROR_CODE, SUCCESS_CODE from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN from .utils import upload_image_to_alibaba @@ -159,11 +159,7 @@ class TaskListAPIView(viewsets.ModelViewSet): current_page = self.request.GET.get('page') junior = self.request.GET.get('junior') queryset = self.get_queryset() - task_status = ['1'] - if str(status_value) == '2': - task_status = ['2', '4'] - elif str(status_value) == '3': - task_status = ['3', '5', '6'] + task_status = task_status_fun(status_value) if status_value and not junior: queryset = queryset.filter(task_status__in=task_status) elif status_value and junior: diff --git a/junior/views.py b/junior/views.py index 8d1390c..fd917d2 100644 --- a/junior/views.py +++ b/junior/views.py @@ -44,7 +44,7 @@ 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, GUARDIAN -from account.utils import custom_response, custom_error_response +from account.utils import custom_response, custom_error_response, task_status_fun from guardian.utils import upload_image_to_alibaba from .utils import update_positions_based_on_points from notifications.utils import send_notification @@ -369,11 +369,7 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): status_value = self.request.GET.get('status') current_page = self.request.GET.get('page') queryset = self.get_queryset() - task_status = ['1'] - if str(status_value) == '2': - task_status = ['2', '4'] - elif str(status_value) == '3': - task_status = ['3', '5', '6'] + task_status = task_status_fun(status_value) if status_value: queryset = queryset.filter(task_status__in=task_status) paginator = self.pagination_class() From a211baa10ac52b7f54406a093b4bbf5a3b4d99b8 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 4 Sep 2023 15:46:36 +0530 Subject: [PATCH 08/13] added cors Allow specific origins setting, unpublish article api, pagination in notification list --- notifications/views.py | 5 +++-- web_admin/serializers/article_serializer.py | 2 +- web_admin/views/article.py | 16 ++++++++++++++++ zod_bank/settings.py | 14 ++++++++++++-- zod_bank/urls.py | 6 ++++-- 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/notifications/views.py b/notifications/views.py index 812502e..c3a6751 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -11,6 +11,7 @@ from rest_framework import viewsets, status, views # local imports from account.utils import custom_response, custom_error_response from base.messages import SUCCESS_CODE, ERROR_CODE +from base.pagination import CustomPageNumberPagination from base.tasks import notify_task_expiry, notify_top_junior from notifications.constants import TEST_NOTIFICATION from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer @@ -33,10 +34,10 @@ class NotificationViewSet(viewsets.GenericViewSet): """ queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id'] ).select_related('notification_to').order_by('-id') - paginator = self.pagination_class() + paginator = CustomPageNumberPagination() paginated_queryset = paginator.paginate_queryset(queryset, request) serializer = self.serializer_class(paginated_queryset, many=True) - return custom_response(None, serializer.data, count=queryset.count()) + return paginator.get_paginated_response(serializer.data) @action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice) def fcm_registration(self, request): diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index 7e94e9b..d030d41 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -81,7 +81,7 @@ class ArticleSerializer(serializers.ModelSerializer): meta class """ model = Article - fields = ('id', 'title', 'description', 'article_cards', 'article_survey') + fields = ('id', 'title', 'description', 'is_published', 'article_cards', 'article_survey') def validate(self, attrs): """ diff --git a/web_admin/views/article.py b/web_admin/views/article.py index 902f579..c9d4426 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -130,6 +130,22 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel return custom_response(SUCCESS_CODE["3029"]) return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST) + @action(methods=['get'], url_name='status-change', url_path='status-change', + detail=True) + def article_status_change(self, request, *args, **kwargs): + """ + article un-publish or publish api method + :param request: article id + :return: success message + """ + try: + article = Article.objects.filter(id=kwargs['pk']).first() + article.is_published = False if article.is_published else True + article.save(update_fields=['is_published']) + return custom_response(SUCCESS_CODE["3038"]) + except AttributeError: + return custom_error_response(ERROR_CODE["2041"], response_status=status.HTTP_400_BAD_REQUEST) + @action(methods=['get'], url_name='remove-card', url_path='remove-card', detail=True) def remove_article_card(self, request, *args, **kwargs): diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 781df80..7ea1f74 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -37,7 +37,17 @@ SECRET_KEY = os.getenv('SECRET_KEY') DEBUG = os.getenv('DEBUG') # cors allow setting -CORS_ORIGIN_ALLOW_ALL = True +CORS_ORIGIN_ALLOW_ALL = False + +# Allow specific origins +CORS_ALLOWED_ORIGINS = [ + "https://dev-api.zodqaapp.com", + "https://qa-api.zodqaapp.com", + "https://stage-api.zodqaapp.com", + # Add more trusted origins as needed +] +# if DEBUG: +# CORS_ALLOWED_ORIGINS += ["http://localhost:3000"] # allow all host ALLOWED_HOSTS = ['*'] @@ -53,7 +63,7 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - # Add Django rest frame work apps here + # Add Django rest framework apps here 'django_extensions', 'storages', 'drf_yasg', diff --git a/zod_bank/urls.py b/zod_bank/urls.py index a34ef93..ba78b8f 100644 --- a/zod_bank/urls.py +++ b/zod_bank/urls.py @@ -20,12 +20,11 @@ from django.urls import path, include from drf_yasg import openapi from drf_yasg.views import get_schema_view from django.urls import path - +from django.conf import settings schema_view = get_schema_view(openapi.Info(title="Zod Bank API", default_version='v1'), public=True, ) urlpatterns = [ - path('apidoc/', schema_view.with_ui('swagger', cache_timeout=None), name='schema-swagger-ui'), path('admin/', admin.site.urls), path('', include(('account.urls', 'account'), namespace='account')), path('', include('guardian.urls')), @@ -33,3 +32,6 @@ urlpatterns = [ path('', include(('notifications.urls', 'notifications'), namespace='notifications')), path('', include(('web_admin.urls', 'web_admin'), namespace='web_admin')), ] + +if settings.DEBUG: + urlpatterns += [(path('apidoc/', schema_view.with_ui('swagger', cache_timeout=None), name='schema-swagger-ui'))] From 9b14eedb1876286bdf7e96be30176bd58ec71c68 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 4 Sep 2023 16:49:18 +0530 Subject: [PATCH 09/13] handle scenerio for task after disassociate --- account/templates/templated_email/support_mail.email | 2 +- account/utils.py | 3 +-- account/views.py | 5 ++--- base/messages.py | 5 ++++- guardian/views.py | 2 ++ junior/views.py | 9 +++++++-- 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/account/templates/templated_email/support_mail.email b/account/templates/templated_email/support_mail.email index 34d6156..20fcdb1 100644 --- a/account/templates/templated_email/support_mail.email +++ b/account/templates/templated_email/support_mail.email @@ -1,7 +1,7 @@ {% extends "templated_email/email_base.email" %} {% block subject %} - {{subject}} + Support Mail {% endblock %} {% block plain %} diff --git a/account/utils.py b/account/utils.py index 353042a..9ef0106 100644 --- a/account/utils.py +++ b/account/utils.py @@ -167,7 +167,7 @@ def user_device_details(user, device_id): return False -def send_support_email(name, sender, subject, message): +def send_support_email(name, sender, message): """Send otp on email with template""" to_email = [settings.EMAIL_FROM_ADDRESS] from_email = settings.DEFAULT_ADDRESS @@ -179,7 +179,6 @@ def send_support_email(name, sender, subject, message): context={ 'name': name.title(), 'sender': sender, - 'subject': subject, 'message': message } ) diff --git a/account/views.py b/account/views.py index ff0a6e6..a76693e 100644 --- a/account/views.py +++ b/account/views.py @@ -689,11 +689,10 @@ class SendSupportEmail(views.APIView): def post(self, request): name = request.data.get('name') sender = request.data.get('email') - subject = request.data.get('subject') message = request.data.get('message') - if name and sender and subject and message: + if name and sender and message: try: - send_support_email(name, sender, subject, message) + send_support_email(name, sender, message) return custom_response(SUCCESS_CODE['3019'], 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 feed804..0af15a1 100644 --- a/base/messages.py +++ b/base/messages.py @@ -111,7 +111,10 @@ 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": "You can not start this task because guardian is not associate with you", + "2084": "You can not complete this task because guardian is not associate with you", + "2085": "You can not take action on this task because junior is not associate with you" } """Success message code""" diff --git a/guardian/views.py b/guardian/views.py index 082a9d7..e083708 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -357,6 +357,8 @@ class ApproveTaskAPIView(viewsets.ModelViewSet): 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 guardian.guardian_code not in task_queryset.junior.guardian_code: + return custom_error_response(ERROR_CODE['2084'], response_status=status.HTTP_400_BAD_REQUEST) 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 diff --git a/junior/views.py b/junior/views.py index fd917d2..9a49345 100644 --- a/junior/views.py +++ b/junior/views.py @@ -409,10 +409,12 @@ 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 or not task_queryset.junior.is_active: + if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code: + return custom_error_response(ERROR_CODE['2085'], response_status=status.HTTP_400_BAD_REQUEST) + elif 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'])]: + elif task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]: """Already request send """ return custom_error_response(ERROR_CODE['2049'], response_status=status.HTTP_400_BAD_REQUEST) serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True) @@ -517,7 +519,10 @@ class StartTaskAPIView(views.APIView): try: task_id = self.request.data.get('task_id') task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last() + print("task_queryset==>",task_queryset) if task_queryset and task_queryset.task_status == str(NUMBER['one']): + if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code: + return custom_error_response(ERROR_CODE['2083'], response_status=status.HTTP_400_BAD_REQUEST) # use StartTaskSerializer serializer serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True) if serializer.is_valid(): From 65d0932893abe3bcb1dd1b511a1a67f43cd59431 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 4 Sep 2023 16:52:50 +0530 Subject: [PATCH 10/13] sonar issues --- guardian/serializers.py | 1 + guardian/views.py | 3 ++- notifications/constants.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/guardian/serializers.py b/guardian/serializers.py index bd2e43d..0058bdd 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -237,6 +237,7 @@ class TaskSerializer(serializers.ModelSerializer): tasks_created = [] for junior_id in junior_data: + # create task task_data = validated_data.copy() task_data['guardian'] = guardian task_data['default_image'] = images diff --git a/guardian/views.py b/guardian/views.py index e083708..e832ec5 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -208,7 +208,8 @@ class CreateTaskAPIView(viewsets.ModelViewSet): 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) + 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') diff --git a/notifications/constants.py b/notifications/constants.py index 85b0d2d..c22f246 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -22,7 +22,7 @@ ARTICLE_REWARD_POINTS = 17 REMOVE_JUNIOR = 18 TEST_NOTIFICATION = 99 - +# notification dictionary NOTIFICATION_DICT = { REGISTRATION: { "title": "Successfully registered!", From be9f600bccfe8debeca22d5f668b5212d9271adc Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 4 Sep 2023 16:45:29 +0530 Subject: [PATCH 11/13] modified cord allowed origin values, added yml file for qa stage and prod --- docker-compose-prod.yml | 39 +++++++++++++++++++++++++++++++++++++++ docker-compose-qa.yml | 39 +++++++++++++++++++++++++++++++++++++++ docker-compose-stage.yml | 39 +++++++++++++++++++++++++++++++++++++++ zod_bank/settings.py | 13 +++++++++++-- 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 docker-compose-prod.yml create mode 100644 docker-compose-qa.yml create mode 100644 docker-compose-stage.yml diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml new file mode 100644 index 0000000..436833b --- /dev/null +++ b/docker-compose-prod.yml @@ -0,0 +1,39 @@ +version: '3' +services: + nginx: + image: nginx:latest + container_name: nginx + restart: always + ports: + - "8000:8000" + volumes: + - ./nginx:/etc/nginx/conf.d + - .:/usr/src/app + depends_on: + - web + web: + build: . + container_name: prod_django + restart: always + command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info" + volumes: + - .:/usr/src/app + + broker: + image: rabbitmq:3.7 + container_name: prod_rabbitmq + volumes: + - .:/usr/src/app + ports: + - 5673:5673 + + worker: + build: . + image: celery + container_name: prod_celery + restart: "always" + command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E" + volumes: + - .:/usr/src/app + depends_on: + - broker diff --git a/docker-compose-qa.yml b/docker-compose-qa.yml new file mode 100644 index 0000000..6e3f4d5 --- /dev/null +++ b/docker-compose-qa.yml @@ -0,0 +1,39 @@ +version: '3' +services: + nginx: + image: nginx:latest + container_name: nginx + restart: always + ports: + - "8000:8000" + volumes: + - ./nginx:/etc/nginx/conf.d + - .:/usr/src/app + depends_on: + - web + web: + build: . + container_name: qa_django + restart: always + command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info" + volumes: + - .:/usr/src/app + + broker: + image: rabbitmq:3.7 + container_name: qa_rabbitmq + volumes: + - .:/usr/src/app + ports: + - 5673:5673 + + worker: + build: . + image: celery + container_name: qa_celery + restart: "always" + command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E" + volumes: + - .:/usr/src/app + depends_on: + - broker diff --git a/docker-compose-stage.yml b/docker-compose-stage.yml new file mode 100644 index 0000000..39db221 --- /dev/null +++ b/docker-compose-stage.yml @@ -0,0 +1,39 @@ +version: '3' +services: + nginx: + image: nginx:latest + container_name: nginx + restart: always + ports: + - "8000:8000" + volumes: + - ./nginx:/etc/nginx/conf.d + - .:/usr/src/app + depends_on: + - web + web: + build: . + container_name: stage_django + restart: always + command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info" + volumes: + - .:/usr/src/app + + broker: + image: rabbitmq:3.7 + container_name: stage_rabbitmq + volumes: + - .:/usr/src/app + ports: + - 5673:5673 + + worker: + build: . + image: celery + container_name: stage_celery + restart: "always" + command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E" + volumes: + - .:/usr/src/app + depends_on: + - broker diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 7ea1f74..590bc9d 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -35,19 +35,28 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SECRET_KEY = os.getenv('SECRET_KEY') # SECURITY WARNING: don't run with debug turned on in production! DEBUG = os.getenv('DEBUG') +ENV = os.getenv('ENV') # cors allow setting CORS_ORIGIN_ALLOW_ALL = False # Allow specific origins +# if ENV in ['dev', 'qa', 'stage']: CORS_ALLOWED_ORIGINS = [ + # backend base url "https://dev-api.zodqaapp.com", "https://qa-api.zodqaapp.com", "https://stage-api.zodqaapp.com", + + # frontend url + "http://localhost:3000", + "https://zod-dev.zodqaapp.com", + "https://zod-qa.zodqaapp.com", + "https://zod-stage.zodqaapp.com", # Add more trusted origins as needed ] -# if DEBUG: -# CORS_ALLOWED_ORIGINS += ["http://localhost:3000"] +if ENV == "prod": + CORS_ALLOWED_ORIGINS = [] # allow all host ALLOWED_HOSTS = ['*'] From 5524eeed643f4d6594ae27f9a2a1a692d1927440 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Tue, 5 Sep 2023 13:54:58 +0530 Subject: [PATCH 12/13] modified pagination in task list and junior task list api --- account/utils.py | 6 +++--- guardian/views.py | 19 ++++++++----------- junior/views.py | 15 +++++++-------- web_admin/views/article.py | 2 +- zod_bank/settings.py | 26 +++++++++++++------------- 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/account/utils.py b/account/utils.py index 9ef0106..9122477 100644 --- a/account/utils.py +++ b/account/utils.py @@ -185,13 +185,13 @@ def send_support_email(name, sender, message): return name -def custom_response(detail, data=None, total_pages=None, current_page=None, response_status=status.HTTP_200_OK, count=None): +def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count=None): """Custom response code""" if not data: """when data is none""" data = None - return Response({"data": data, "message": detail, "total_pages":total_pages, "current_page":current_page, - "status": "success", "code": response_status, "count": count}) + return Response({"data": data, "message": detail, "status": "success", + "code": response_status, "count": count}) def custom_error_response(detail, response_status): diff --git a/guardian/views.py b/guardian/views.py index e832ec5..9d6af06 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -17,6 +17,7 @@ from base.constants import guardian_code_tuple from rest_framework.filters import SearchFilter from django.utils import timezone +from base.pagination import CustomPageNumberPagination # Import guardian's model, # Import junior's model, # Import account's model, @@ -147,8 +148,8 @@ class TaskListAPIView(viewsets.ModelViewSet): def get_queryset(self): queryset = JuniorTask.objects.filter(guardian__user=self.request.user - ).prefetch_related('junior', 'junior__auth' - ).order_by('due_date', 'created_at') + ).select_related('junior', 'junior__auth' + ).order_by('due_date', 'created_at') queryset = self.filter_queryset(queryset) return queryset @@ -156,23 +157,19 @@ class TaskListAPIView(viewsets.ModelViewSet): def list(self, request, *args, **kwargs): """Create guardian profile""" status_value = self.request.GET.get('status') - current_page = self.request.GET.get('page') junior = self.request.GET.get('junior') queryset = self.get_queryset() task_status = task_status_fun(status_value) - if status_value and not junior: + if status_value: queryset = queryset.filter(task_status__in=task_status) - elif status_value and junior: - queryset = queryset.filter(task_status__in=task_status,junior=int(junior)) - paginator = self.pagination_class() - total_count = len(queryset) - total_pages = math.ceil(total_count/10) + if junior: + queryset = queryset.filter(junior=int(junior)) + paginator = CustomPageNumberPagination() # use Pagination paginated_queryset = paginator.paginate_queryset(queryset, request) # use TaskDetailsSerializer serializer serializer = self.serializer_class(paginated_queryset, many=True) - return custom_response(None, serializer.data, total_pages=total_pages, current_page=current_page, - response_status=status.HTTP_200_OK) + return paginator.get_paginated_response(serializer.data) class CreateTaskAPIView(viewsets.ModelViewSet): diff --git a/junior/views.py b/junior/views.py index 9a49345..fb4771d 100644 --- a/junior/views.py +++ b/junior/views.py @@ -13,6 +13,9 @@ import requests from rest_framework.viewsets import GenericViewSet, mixins import math + +from base.pagination import CustomPageNumberPagination + """Django app import""" from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi @@ -354,8 +357,8 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): def get_queryset(self): queryset = JuniorTask.objects.filter(junior__auth=self.request.user - ).prefetch_related('junior', 'junior__auth' - ).order_by('due_date', 'created_at') + ).select_related('junior', 'junior__auth' + ).order_by('due_date', 'created_at') queryset = self.filter_queryset(queryset) return queryset @@ -367,20 +370,16 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): page=1""" try: status_value = self.request.GET.get('status') - current_page = self.request.GET.get('page') queryset = self.get_queryset() task_status = task_status_fun(status_value) if status_value: queryset = queryset.filter(task_status__in=task_status) - paginator = self.pagination_class() - total_count = len(queryset) - total_pages = math.ceil(total_count / 10) + paginator = CustomPageNumberPagination() # use Pagination paginated_queryset = paginator.paginate_queryset(queryset, request) # use TaskDetails juniorSerializer serializer serializer = self.serializer_class(paginated_queryset, many=True) - return custom_response(None, serializer.data, total_pages=total_pages, current_page=current_page, - response_status=status.HTTP_200_OK) + return paginator.get_paginated_response(serializer.data) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/web_admin/views/article.py b/web_admin/views/article.py index c9d4426..ab55d16 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -230,7 +230,7 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m :param request: :return: default article card images """ - queryset = self.queryset + queryset = self.get_queryset() serializer = self.serializer_class(queryset, many=True) return custom_response(None, data=serializer.data) diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 590bc9d..cde1918 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -41,20 +41,20 @@ ENV = os.getenv('ENV') CORS_ORIGIN_ALLOW_ALL = False # Allow specific origins -# if ENV in ['dev', 'qa', 'stage']: -CORS_ALLOWED_ORIGINS = [ - # backend base url - "https://dev-api.zodqaapp.com", - "https://qa-api.zodqaapp.com", - "https://stage-api.zodqaapp.com", +if ENV in ['dev', 'qa', 'stage']: + CORS_ALLOWED_ORIGINS = [ + # backend base url + "https://dev-api.zodqaapp.com", + "https://qa-api.zodqaapp.com", + "https://stage-api.zodqaapp.com", - # frontend url - "http://localhost:3000", - "https://zod-dev.zodqaapp.com", - "https://zod-qa.zodqaapp.com", - "https://zod-stage.zodqaapp.com", - # Add more trusted origins as needed -] + # frontend url + "http://localhost:3000", + "https://zod-dev.zodqaapp.com", + "https://zod-qa.zodqaapp.com", + "https://zod-stage.zodqaapp.com", + # Add more trusted origins as needed + ] if ENV == "prod": CORS_ALLOWED_ORIGINS = [] From 8f214d11a7ec074ec33f3f44f458f98d88b13d42 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Tue, 5 Sep 2023 19:29:57 +0530 Subject: [PATCH 13/13] modified create task api, added badge count in notification list --- celerybeat-schedule | Bin 20480 -> 20480 bytes guardian/serializers.py | 13 ++++--- guardian/views.py | 71 +++++++++++++++++------------------ notifications/serializers.py | 7 +++- 4 files changed, 48 insertions(+), 43 deletions(-) diff --git a/celerybeat-schedule b/celerybeat-schedule index f457bb55d9731edd43b34c596341385d6757e2ec..be892edea3778b38a0afe5c729adddc781b59706 100644 GIT binary patch delta 77 zcmZozz}T>Wal;%34o+4cK1IILkjV?o0~y&S8`x-0mUm1Ogb2?)Hzh-?ZA#D-Z=T81 T9E+f$i+fWal;%34h|W1V>K4jn#l{y0~uK-8`x-0mUm1Oga}7zPRS5!n-Vm|n|ty! W$0DF87Xt%h>P(pE+|80s&v^k;9Tw~W diff --git a/guardian/serializers.py b/guardian/serializers.py index 0058bdd..f78c237 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -28,7 +28,7 @@ from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user, GUARDIA 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 +from notifications.constants import TASK_APPROVED, TASK_REJECTED, TASK_ASSIGNED # send notification function from notifications.utils import send_notification from django.core.exceptions import ValidationError @@ -229,21 +229,22 @@ class TaskSerializer(serializers.ModelSerializer): return value def create(self, validated_data): """create default task image data""" - guardian = Guardian.objects.filter(user=self.context['user']).last() + guardian = self.context['guardian'] # update image of the task images = self.context['image'] - junior_ids = self.context['junior_data'] - junior_data = junior_ids[0].split(',') + junior_data = self.context['junior_data'] tasks_created = [] - for junior_id in junior_data: + for junior in junior_data: # create task task_data = validated_data.copy() task_data['guardian'] = guardian task_data['default_image'] = images - task_data['junior'] = Junior.objects.filter(id=junior_id).last() + task_data['junior'] = junior instance = JuniorTask.objects.create(**task_data) tasks_created.append(instance) + send_notification.delay(TASK_ASSIGNED, guardian.user.id, GUARDIAN, + junior.auth.id, {'task_id': instance.id}) return instance class GuardianDetailSerializer(serializers.ModelSerializer): diff --git a/guardian/views.py b/guardian/views.py index 9d6af06..4a8a804 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -184,49 +184,48 @@ class CreateTaskAPIView(viewsets.ModelViewSet): """ try: image = request.data['default_image'] - juniors = request.data['junior'].split(',') - for junior in juniors: - junior_id = Junior.objects.filter(id=junior).last() - if junior_id: - guardian_data = Guardian.objects.filter(user=request.user).last() - index = junior_id.guardian_code.index(guardian_data.guardian_code) - status_index = junior_id.guardian_code_status[index] + junior_ids = request.data['junior'].split(',') + # if not junior.isnumeric(): + # """junior value must be integer""" + # return custom_error_response(ERROR_CODE['2047'], 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 '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_data = upload_image_to_alibaba(image, filename) + request.data.pop('default_image') + + guardian = Guardian.objects.filter(user=request.user).select_related('user').last() + junior_data = Junior.objects.filter(id__in=junior_ids).select_related('auth') + + for junior in junior_data: + if junior: + index = junior.guardian_code.index(guardian.guardian_code) + status_index = junior.guardian_code_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') - junior_data = data.pop('junior') - # use TaskSerializer serializer - serializer = TaskSerializer(context={"user":request.user, "image":image_data, - "junior_data":junior_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'], 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) + + # use TaskSerializer serializer + serializer = TaskSerializer(context={"guardian": guardian, "image": image_data, + "junior_data": junior_data}, data=request.data) + if serializer.is_valid(): + # save serializer + serializer.save() + # removed send notification method and used in serializer + return custom_response(SUCCESS_CODE['3018'], 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) + class SearchTaskListAPIView(viewsets.ModelViewSet): """Filter task""" serializer_class = TaskDetailsSerializer diff --git a/notifications/serializers.py b/notifications/serializers.py index 2f0222f..e4fdb05 100644 --- a/notifications/serializers.py +++ b/notifications/serializers.py @@ -31,11 +31,16 @@ class RegisterDevice(serializers.Serializer): class NotificationListSerializer(serializers.ModelSerializer): """List of notification""" + badge = serializers.SerializerMethodField() class Meta(object): """meta info""" model = Notification - fields = ['id', 'data', 'is_read', 'created_at'] + fields = ['id', 'data', 'badge', 'is_read', 'created_at'] + + @staticmethod + def get_badge(obj): + return Notification.objects.filter(notification_to=obj.notification_to, is_read=False).count() class ReadNotificationSerializer(serializers.ModelSerializer):