changed notification method, added celery task to notify expiring task

This commit is contained in:
abutalib-kiwi
2023-08-24 12:10:18 +05:30
parent 5b9f7b1828
commit f541608656
12 changed files with 153 additions and 71 deletions

View File

@ -282,8 +282,9 @@ def generate_code(value, user_id):
OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1)
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.last_name else user_obj.first_name
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.first_name or user_obj.last_name else "User"

View File

@ -1,6 +1,8 @@
"""
web_admin tasks file
"""
import datetime
# third party imports
from celery import shared_task
from templated_email import send_templated_mail
@ -8,6 +10,12 @@ from templated_email import send_templated_mail
# django imports
from django.conf import settings
# 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
@shared_task
def send_email_otp(email, verification_code):
@ -27,3 +35,24 @@ def send_email_otp(email, verification_code):
}
)
return True
@shared_task()
def notify_task_expiry():
"""
task to send notification for those task which expiring soon
:return:
"""
all_pending_tasks = JuniorTask.objects.filter(
task_status__in=[PENDING, IN_PROGRESS],
due_date__range=[datetime.datetime.now().date(),
(datetime.datetime.now().date() + datetime.timedelta(days=1))])
if pending_tasks := all_pending_tasks.filter(task_status=PENDING):
for task in pending_tasks:
send_notification(PENDING_TASK_EXPIRING, None, None, task.junior.auth.id,
{'task_id': task.id})
if in_progress_tasks := all_pending_tasks.filter(task_status=IN_PROGRESS):
for task in in_progress_tasks:
send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth.id, JUNIOR, task.guardian.user.id,
{'task_id': task.id})
return True

Binary file not shown.

View File

@ -24,13 +24,13 @@ from account.models import UserProfile, UserEmailOtp, UserNotification
from account.utils import generate_code
from junior.serializers import JuniorDetailSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user
from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user, GUARDIAN
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
# send notification function
from notifications.utils import send_notification, send_notification_to_junior
from notifications.utils import send_notification
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
@ -420,8 +420,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update complete time of task
# instance.completed_on = real_time()
instance.completed_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_APPROVED, instance.guardian.user.id, junior_details.auth.id,
{'task_id': instance.id})
send_notification.delay(TASK_APPROVED, instance.guardian.user.id, GUARDIAN,
junior_details.auth.id, {'task_id': instance.id})
else:
# reject the task
instance.task_status = str(NUMBER['three'])
@ -429,8 +429,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update reject time of task
# instance.rejected_on = real_time()
instance.rejected_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_REJECTED, instance.guardian.user.id, junior_details.auth.id,
{'task_id': instance.id})
send_notification.delay(TASK_REJECTED, instance.guardian.user.id, GUARDIAN,
junior_details.auth.id, {'task_id': instance.id})
instance.save()
junior_data.save()
return junior_details

View File

@ -21,7 +21,7 @@ from zod_bank.celery import app
# notification's constant
from notifications.constants import REFERRAL_POINTS
# send notification function
from notifications.utils import send_notification, send_notification_to_junior
from notifications.utils import send_notification
# Define upload image on
@ -117,7 +117,7 @@ def update_referral_points(referral_code, referral_code_used):
junior_query.total_points = junior_query.total_points + NUMBER['five']
junior_query.referral_points = junior_query.referral_points + NUMBER['five']
junior_query.save()
send_notification_to_junior.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {})
send_notification.delay(REFERRAL_POINTS, None, None, junior_queryset.auth.id, {})

View File

@ -36,10 +36,10 @@ 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 base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, GUARDIAN_CODE_STATUS
from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN
from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_ASSIGNED, ASSOCIATE_APPROVED, ASSOCIATE_REJECTED
from notifications.utils import send_notification_to_junior
from notifications.utils import send_notification
""" Define APIs """
# Define Signup API,
@ -202,8 +202,8 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
# save serializer
task = serializer.save()
send_notification_to_junior.delay(TASK_ASSIGNED, request.auth.payload['user_id'],
junior_id.auth.id, {'task_id': task.id})
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)
except Exception as e:
@ -292,13 +292,14 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet):
if serializer.is_valid():
# save serializer
serializer.save()
send_notification_to_junior.delay(ASSOCIATE_APPROVED, guardian.user.id, junior_queryset.auth.id)
send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN,
junior_queryset.auth.id)
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
else:
junior_queryset.guardian_code = None
junior_queryset.guardian_code_status = str(NUMBER['one'])
junior_queryset.save()
send_notification_to_junior.delay(ASSOCIATE_REJECTED, guardian.user.id, junior_queryset.auth.id)
send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id)
return custom_response(SUCCESS_CODE['3024'], 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

@ -16,12 +16,12 @@ from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, Juni
from guardian.tasks import generate_otp
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED,
GUARDIAN_CODE_STATUS)
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 guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian
from notifications.utils import send_notification
from notifications.constants import (ASSOCIATE_REQUEST, JUNIOR_ADDED, TASK_ACTION,
)
from web_admin.models import ArticleCard
@ -98,7 +98,7 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior)
junior.guardian_code_status = str(NUMBER['three'])
junior_approval_mail.delay(user.email, user.first_name)
send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior.auth.id, guardian_data.user.id, {})
send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {})
junior.dob = validated_data.get('dob', junior.dob)
junior.passcode = validated_data.get('passcode', junior.passcode)
@ -307,7 +307,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
"""Notification email"""
junior_notification_email.delay(email, full_name, email, password)
# push notification
send_notification_to_junior.delay(JUNIOR_ADDED, None, junior_data.auth.id, {})
send_notification.delay(JUNIOR_ADDED, None, None, junior_data.auth.id, {})
return junior_data
@ -339,8 +339,8 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
send_notification_to_guardian.delay(TASK_ACTION, instance.junior.auth.id,
instance.guardian.user.id, {'task_id': instance.id})
send_notification.delay(TASK_ACTION, instance.junior.auth.id, JUNIOR,
instance.guardian.user.id, {'task_id': instance.id})
return instance
class JuniorPointsSerializer(serializers.ModelSerializer):
@ -450,7 +450,7 @@ class AddGuardianSerializer(serializers.ModelSerializer):
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail.delay(email, full_name)
send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior_data.auth.id, guardian_data.user.id, {})
send_notification.delay(ASSOCIATE_REQUEST, junior_data.auth.id, JUNIOR, guardian_data.user.id, {})
return guardian_data
class StartTaskSerializer(serializers.ModelSerializer):

View File

@ -46,7 +46,7 @@ from base.constants import NUMBER, ARTICLE_STATUS, none
from account.utils import custom_response, custom_error_response
from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points
from notifications.utils import send_notification, send_notification_to_junior
from notifications.utils import send_notification
from notifications.constants import REMOVE_JUNIOR
from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer,
@ -312,7 +312,7 @@ class RemoveJuniorAPIView(views.APIView):
if serializer.is_valid():
# save serializer
serializer.save()
send_notification_to_junior.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {})
send_notification.delay(REMOVE_JUNIOR, None, None, junior_queryset.auth.id, {})
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:

View File

@ -12,8 +12,9 @@ TASK_ASSIGNED = 8
TASK_ACTION = 9
TASK_REJECTED = 10
TASK_APPROVED = 11
REMOVE_JUNIOR = 13
PENDING_TASK_EXPIRING = 12
IN_PROGRESS_TASK_EXPIRING = 13
REMOVE_JUNIOR = 15
TEST_NOTIFICATION = 99
@ -22,16 +23,18 @@ 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
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
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
ASSOCIATE_APPROVED: {
"title": "Associate request approved!",
"body": "Your request to associate has been approved by {from_user}."
@ -54,7 +57,7 @@ NOTIFICATION_DICT = {
# Guardian will receive notification as soon as junior send task for approval
TASK_ACTION: {
"title": "Task completion approval!",
"body": "You have request from {from_user} for task completion."
"body": "{from_user} completed her task {task_name}."
},
# Juniors will receive notification as soon as their task is rejected by custodians
TASK_REJECTED: {
@ -68,11 +71,24 @@ NOTIFICATION_DICT = {
"body": "Your task completion request has been approved by {from_user}. "
"Also you earned 5 points for successful completion."
},
# Juniors will receive notification when their task end date about to end
PENDING_TASK_EXPIRING: {
"title": "Task expiring soon!",
"body": "Your task {task_name} is expiring soon. Please complete it."
},
# User will receive notification when their assigned task is about to end
# and juniors have not performed any action
IN_PROGRESS_TASK_EXPIRING: {
"title": "Task expiring soon!",
"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 as soon as their custodians remove them from account
REMOVE_JUNIOR: {
"title": "Disassociate by guardian!",
"body": "Your guardian has disassociated you."
},
# Test notification
TEST_NOTIFICATION: {
"title": "Test Notification",
"body": "This notification is for testing purpose from {from_user}."

View File

@ -11,7 +11,8 @@ from django.contrib.auth import get_user_model
from account.models import UserNotification
from account.utils import get_user_full_name
from guardian.models import Guardian
from base.constants import GUARDIAN, JUNIOR
from guardian.models import Guardian, JuniorTask
from junior.models import Junior
from notifications.constants import NOTIFICATION_DICT
from notifications.models import Notification
@ -42,44 +43,64 @@ def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None:
print(e)
def get_basic_detail(from_user_id, to_user_id):
def get_from_user_details(from_user_id, from_user_type):
"""
used to get the basic details
used to get from user details
"""
from_user = User.objects.get(id=from_user_id) if from_user_id else None
to_user = User.objects.get(id=to_user_id)
return from_user, to_user
from_user = None
from_user_name = None
from_user_image = None
if from_user_id:
if from_user_type == GUARDIAN:
guardian = Guardian.objects.filter(user_id=from_user_id).select_related('user').first()
from_user = guardian.user
from_user_name = get_user_full_name(from_user)
from_user_image = guardian.image
elif from_user_type == JUNIOR:
junior = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first()
from_user = junior.auth
from_user_name = get_user_full_name(from_user)
from_user_image = junior.image
return from_user_name, from_user_image, from_user
def get_notification_data(notification_type, from_user, to_user, extra_data):
def get_notification_data(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
"""
get notification and push data
:param from_user_type: GUARDIAN or JUNIOR
:param notification_type: notification_type
:param from_user: from_user obj
:param to_user: to_user obj
:param from_user_id: from_user obj
:param to_user_id: to_user obj
:param extra_data: any extra data provided
:return: notification and push data
"""
push_data = NOTIFICATION_DICT[notification_type].copy()
notification_data = push_data.copy()
notification_data['to_user'] = get_user_full_name(to_user)
if from_user:
from_user_name = get_user_full_name(from_user)
push_data['body'] = push_data['body'].format(from_user=from_user_name)
notification_data['body'] = notification_data['body'].format(from_user=from_user_name)
notification_data['from_user'] = from_user_name
task_name = None
if 'task_id' in extra_data:
task = JuniorTask.objects.filter(id=extra_data.get('task_id')).first()
task_name = task.task_name
extra_data['task_image'] = task.image if task.image else task.default_image
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, task_name=task_name)
notification_data['body'] = notification_data['body'].format(from_user=from_user_name, task_name=task_name)
notification_data['from_user'] = from_user_name
notification_data['from_user_image'] = from_user_image
notification_data.update(extra_data)
return notification_data, push_data
to_user = User.objects.get(id=to_user_id)
return notification_data, push_data, from_user, to_user
def send_notification(notification_type, from_user, to_user, extra_data):
@shared_task()
def send_notification(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
"""
used to send the push for the given notification type
"""
# (from_user, to_user) = get_basic_detail(from_user_id, to_user_id)
notification_data, push_data = get_notification_data(notification_type, from_user, to_user, extra_data)
notification_data, push_data, from_user, to_user = get_notification_data(notification_type, from_user_id,
from_user_type, to_user_id, extra_data)
user_notification_type = UserNotification.objects.filter(user=to_user).first()
notification_data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()})
Notification.objects.create(notification_type=notification_type, notification_from=from_user,
@ -104,14 +125,10 @@ def send_notification_to_guardian(notification_type, from_user_id, to_user_id, e
:param extra_data:
:return:
"""
from_user = None
if from_user_id:
from_user = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first()
from_user = Junior.objects.filter(auth_id=from_user_id).first()
extra_data['from_user_image'] = from_user.image
from_user = from_user.auth
to_user = Guardian.objects.filter(user_id=to_user_id).select_related('user').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.user, extra_data)
send_notification(notification_type, from_user_id, to_user_id, extra_data)
@shared_task()
@ -123,13 +140,9 @@ def send_notification_to_junior(notification_type, from_user_id, to_user_id, ext
:param extra_data:
:return:
"""
from_user = None
if from_user_id:
from_user = Guardian.objects.filter(user_id=from_user_id).select_related('user').first()
from_user = Guardian.objects.filter(user_id=from_user_id).first()
extra_data['from_user_image'] = from_user.image
from_user = from_user.user
to_user = Junior.objects.filter(auth_id=to_user_id).select_related('auth').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.auth, extra_data)
send_notification(notification_type, from_user_id, to_user_id, extra_data)

View File

@ -4,27 +4,35 @@ notifications views file
# django imports
from django.db.models import Q
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework import viewsets, status, views
# local imports
from account.utils import custom_response, custom_error_response
from base.constants import JUNIOR, GUARDIAN
from base.messages import SUCCESS_CODE, ERROR_CODE
from base.tasks import notify_task_expiry
from guardian.models import Guardian
from notifications.constants import TEST_NOTIFICATION
# Import serializer
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
from notifications.utils import send_notification, send_notification_to_guardian, send_notification_to_junior
# Import model
from notifications.utils import send_notification
from notifications.models import Notification
class NotificationViewSet(viewsets.GenericViewSet):
""" used to do the notification actions """
"""
used to do the notification actions
"""
serializer_class = NotificationListSerializer
permission_classes = [IsAuthenticated, ]
def list(self, request, *args, **kwargs) -> Response:
""" list the notifications """
"""
to list user's notifications
:param request:
:return:
"""
queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id']
).select_related('notification_to').order_by('-id')
paginator = self.pagination_class()
@ -49,10 +57,8 @@ class NotificationViewSet(viewsets.GenericViewSet):
to send test notification
:return:
"""
send_notification_to_guardian.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'],
{'task_id': None})
send_notification_to_junior.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'],
{'task_id': None})
send_notification(TEST_NOTIFICATION, None, None, request.auth.payload['user_id'],
{})
return custom_response(SUCCESS_CODE["3000"])
@action(methods=['patch'], url_path='mark-as-read', url_name='mark-as-read', detail=False,
@ -63,3 +69,12 @@ class NotificationViewSet(viewsets.GenericViewSet):
"""
Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True)
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
@action(methods=['get'], url_path='task', url_name='task', detail=False,
permission_classes=[AllowAny])
def task(self, request, *args, **kwargs):
"""
notification list
"""
notify_task_expiry()
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)

View File

@ -27,6 +27,13 @@ app.config_from_object('django.conf:settings')
# Load task modules from all registered Django apps.
app.autodiscover_tasks()
app.conf.beat_schedule = {
'notify_task_expiry': {
'task': 'base.tasks.notify_task_expiry',
'schedule': crontab(minute='0', hour='*/1'),
},
}
@app.task(bind=True)
def debug_task(self):