diff --git a/base/tasks.py b/base/tasks.py index 2056493..98d0df0 100644 --- a/base/tasks.py +++ b/base/tasks.py @@ -9,12 +9,16 @@ from templated_email import send_templated_mail # django imports from django.conf import settings +from django.db.models import F, Window +from django.db.models.functions.window import Rank # local imports from base.constants import PENDING, IN_PROGRESS, JUNIOR from guardian.models import JuniorTask -from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING -from notifications.utils import send_notification +from junior.models import JuniorPoints +from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING, NOTIFICATION_DICT, TOP_JUNIOR +from notifications.models import Notification +from notifications.utils import send_notification, get_from_user_details, send_notification_multiple_user @shared_task @@ -56,3 +60,26 @@ def notify_task_expiry(): send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth.id, JUNIOR, task.guardian.user.id, {'task_id': task.id}) return True + + +@shared_task() +def notify_top_junior(): + """ + task to send notification for top leaderboard junior to all junior's + :return: + """ + junior_points_qs = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( + expression=Rank(), + order_by=[F('total_points').desc(), 'junior__created_at'] + )).order_by('-total_points', 'junior__created_at') + + prev_top_position = junior_points_qs.filter(position=1).first() + new_top_position = junior_points_qs.filter(rank=1).first() + if prev_top_position != new_top_position: + to_user_list = [junior_point.junior.auth for junior_point in junior_points_qs] + send_notification_multiple_user(TOP_JUNIOR, new_top_position.junior.auth.id, JUNIOR, + to_user_list, {'points': new_top_position.total_points}) + for junior_point in junior_points_qs: + junior_point.position = junior_point.rank + junior_point.save() + return True diff --git a/celerybeat-schedule b/celerybeat-schedule index 49e8728..4ebacd4 100644 Binary files a/celerybeat-schedule and b/celerybeat-schedule differ diff --git a/notifications/constants.py b/notifications/constants.py index f94e4aa..e2ae430 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -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!", diff --git a/notifications/utils.py b/notifications/utils.py index 7623673..ed7e350 100644 --- a/notifications/utils.py +++ b/notifications/utils.py @@ -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): """ diff --git a/notifications/views.py b/notifications/views.py index 4546317..54adf5a 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -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) diff --git a/zod_bank/celery.py b/zod_bank/celery.py index 5a3de03..571b1e0 100644 --- a/zod_bank/celery.py +++ b/zod_bank/celery.py @@ -34,7 +34,11 @@ app.conf.beat_schedule = { }, 'notify_task_expiry': { 'task': 'base.tasks.notify_task_expiry', - 'schedule': crontab(minute='30', hour='19'), + 'schedule': crontab(minute='0', hour='13'), + }, + 'notify_top_junior': { + 'task': 'base.tasks.notify_top_junior', + 'schedule': crontab(minute='0', hour='*/1'), }, }