Merge pull request #267 from KiwiTechLLC/dev

Dev
This commit is contained in:
Abu Talib
2023-08-25 12:53:12 +05:30
committed by GitHub
14 changed files with 183 additions and 82 deletions

View File

@ -0,0 +1,22 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
Account Deactivated
{% endblock %}
{% block plain %}
<tr>
<td style="padding: 0 27px 15px;">
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
Hi User,
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
Your account has been deactivated by admin. Please reach out to the admin for assistance.
</p>
</td>
</tr>
{% endblock %}

View File

@ -9,36 +9,20 @@ 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
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 +32,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,
@ -77,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

Binary file not shown.

View File

@ -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"""

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -14,6 +14,9 @@ TASK_REJECTED = 10
TASK_APPROVED = 11
PENDING_TASK_EXPIRING = 12
IN_PROGRESS_TASK_EXPIRING = 13
TOP_JUNIOR = 14
REMOVE_JUNIOR = 15
TEST_NOTIFICATION = 99
@ -23,38 +26,44 @@ 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
# 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
# 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
# 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}."
},
# Juniors will receive Notifications for every Points earned by referrals
# 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
# Juniors will receive notification
# once any custodians add them in their account
JUNIOR_ADDED: {
"title": "Profile already setup!",
"body": "Your guardian has already setup your profile."
},
# Juniors will receive Notification for every Task Assign by Custodians
# 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."
},
# Guardian will receive notification as soon as junior send task for approval
# Guardian will receive notification as soon
# as junior send task for approval
TASK_ACTION: {
"title": "Task completion approval!",
"body": "{from_user} completed her task {task_name}."
@ -83,6 +92,11 @@ NOTIFICATION_DICT = {
"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 related to Leaderboard progress
TOP_JUNIOR: {
"title": "Leaderboard topper!",
"body": "{from_user} is on top in leaderboard with {points} points."
},
# Juniors will receive notification as soon as their custodians remove them from account
REMOVE_JUNIOR: {
"title": "Disassociate by guardian!",

View File

@ -115,6 +115,39 @@ def send_push(user, data):
)
def send_multiple_push(queryset, data):
""" used to send same notification to multiple users """
FCMDevice.objects.filter(user__in=queryset, active=True).send_message(
Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
)
@shared_task()
def send_notification_multiple_user(notification_type, from_user_id, from_user_type,
to_user_list: list = [], extra_data: dict = {}):
"""
used to send notification to multiple user for the given notification type
"""
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_list = []
for user in to_user_list:
notification_list.append(Notification(notification_type=notification_type,
notification_to=user,
notification_from=from_user,
data=notification_data))
Notification.objects.bulk_create(notification_list)
send_multiple_push(to_user_list, push_data)
@shared_task()
def send_notification_to_guardian(notification_type, from_user_id, to_user_id, extra_data):
"""

View File

@ -11,7 +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.tasks import notify_task_expiry
from base.tasks import notify_task_expiry, notify_top_junior
from notifications.constants import TEST_NOTIFICATION
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
from notifications.utils import send_notification
@ -74,5 +74,5 @@ class NotificationViewSet(viewsets.GenericViewSet):
"""
notification list
"""
notify_task_expiry()
notify_top_junior()
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)

View File

@ -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,

View File

@ -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'])

View File

@ -28,8 +28,16 @@ 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='13'),
},
'notify_top_junior': {
'task': 'base.tasks.notify_top_junior',
'schedule': crontab(minute='0', hour='*/1'),
},
}
@ -39,14 +47,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),
},
}