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),
- },
-}