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 fef85ad..49e8728 100644 Binary files a/celerybeat-schedule and b/celerybeat-schedule differ 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), - }, -}