Compare commits

...
21 Commits
Author SHA1 Message Date
abutalib-kiwi d1927b24ee to user type added 2023-10-06 18:10:56 +05:30
abutalib-kiwi a1f9f93654 added to user type as per required changes, handled apple signup for existing users 2023-10-06 17:29:41 +05:30
abutalib-kiwi 13665f4c9a fixed user notificatio setting issue 2023-10-05 17:38:57 +05:30
abutalib-kiwi 6e3166967e fixed login into another device issue 2023-10-05 13:07:42 +05:30
abutalib-kiwi ad4d782e72 added mail for user activation, handled fcm token for deleted user 2023-10-03 18:54:47 +05:30
abutalib-kiwi 18a53e1c48 handled deactivated users for social login 2023-09-29 16:09:22 +05:30
abutalib-kiwi bd7eddb275 notification mark as read api modified for clear all, list sorting set to updated at field, added same field 2023-09-29 14:58:06 +05:30
abutalib-kiwi e121c92fb4 notification create modified to update or create 2023-09-28 19:09:57 +05:30
abutalib-kiwi 18143e0219 modified send push method 2023-09-27 19:31:09 +05:30
abutalib-kiwi 251a912948 revert access token time to 3 hrs 2023-09-27 12:36:28 +05:30
abutalib-kiwi f7bb83cebb added notification type in serializer 2023-09-26 19:11:04 +05:30
dilipshrivastwa-kiwi 994e9a270e Merge pull request #339 from KiwiTechLLC/ZBKBCK-54
added notification type in push data
2023-09-26 17:27:10 +05:30
abutalib-kiwi 32c35f8649 added notification type in push data 2023-09-26 17:24:40 +05:30
abutalib-kiwi ea02d7f5bb token expire time 1 min for testing 2023-09-25 11:45:50 +05:30
abutalib-kiwi 648628d3ea Merge branch 'dev' of https://github.com/KiwiTechLLC/ZODBank-Backend into dev 2023-09-25 11:44:57 +05:30
abutalib-kiwi 6ba3d7d8db token expire time 1 min for testing 2023-09-25 11:44:04 +05:30
dilipshrivastwa-kiwi 651405ddef Merge pull request #336 from KiwiTechLLC/ZBKBCK-52
feedback changes and added mark all read
2023-09-22 23:29:38 +05:30
dilipshrivastwa-kiwi c484669a2d Merge pull request #335 from KiwiTechLLC/ZBKBCK-52
fixed guardian reject issue, updated ids in notification method, remo…
2023-09-13 18:18:07 +05:30
dilipshrivastwa-kiwi a3c2b68a0d Merge pull request #332 from KiwiTechLLC/ZBKBCK-52
fixed article reward points notification for 0 points earned
2023-09-13 14:50:24 +05:30
dilipshrivastwa-kiwi af25dc4e82 Merge pull request #331 from KiwiTechLLC/ZBKBCK-52
modified get article card current page method
2023-09-13 14:05:55 +05:30
dilipshrivastwa-kiwi 71bbef84aa Merge pull request #330 from KiwiTechLLC/ZBKBCK-52
modified get article card current page method
2023-09-13 13:40:10 +05:30
15 changed files with 142 additions and 29 deletions
+8 -9
View File
@@ -373,17 +373,16 @@ class UpdateUserNotificationSerializer(serializers.ModelSerializer):
fields = ['push_notification', 'email_notification', 'sms_notification']
def create(self, validated_data):
instance = UserNotification.objects.filter(user=self.context).last()
if instance:
# change notification status
instance.push_notification = validated_data.get('push_notification',instance.push_notification)
instance.email_notification = validated_data.get('email_notification', instance.email_notification)
instance.sms_notification = validated_data.get('sms_notification', instance.sms_notification)
instance.save()
else:
instance = UserNotification.objects.create(user=self.context)
instance, _ = UserNotification.objects.update_or_create(
user=self.context,
defaults={
'push_notification': validated_data.get('push_notification'),
'email_notification': validated_data.get('email_notification'),
'sms_notification': validated_data.get('sms_notification'),
})
return instance
class UserPhoneOtpSerializer(serializers.ModelSerializer):
"""User Phone serializers"""
class Meta(object):
@@ -0,0 +1,22 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
Account Activated
{% 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;">
We're pleased to inform you that your account has been successfully reactivated by our admin team. Welcome back to ZOD ! <br><br> You can now access all the features and services as before. If you have any questions or need assistance, please feel free to reach out to our support team. <br><br> Thank you for being a valued member of our community.
</p>
</td>
</tr>
{% endblock %}
+19 -1
View File
@@ -90,6 +90,11 @@ class GoogleLoginMixin(object):
ERROR_CODE["2071"],
response_status=status.HTTP_400_BAD_REQUEST
)
if not junior_query.is_active:
return custom_error_response(
ERROR_CODE["2075"],
response_status=status.HTTP_404_NOT_FOUND
)
serializer = JuniorSerializer(junior_query)
elif str(user_type) == '2':
guardian_query = Guardian.objects.filter(user=user_data.last()).last()
@@ -98,12 +103,21 @@ class GoogleLoginMixin(object):
ERROR_CODE["2070"],
response_status=status.HTTP_400_BAD_REQUEST
)
if not guardian_query.is_active:
return custom_error_response(
ERROR_CODE["2075"],
response_status=status.HTTP_404_NOT_FOUND
)
serializer = GuardianSerializer(guardian_query)
else:
return custom_error_response(
ERROR_CODE["2069"],
response_status=status.HTTP_400_BAD_REQUEST
)
device_detail, created = UserDeviceDetails.objects.get_or_create(user=user_data.last())
if device_detail:
device_detail.device_id = device_id
device_detail.save()
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
@@ -170,7 +184,7 @@ class SigninWithApple(views.APIView):
user_data = {"email": decoded_data.get('email'), "username": decoded_data.get('email'), "is_active": True}
if decoded_data.get("email"):
try:
user = User.objects.get(email=decoded_data.get("email"))
user = User.objects.get(email__iexact=decoded_data.get("email"))
if str(user_type) == '1':
junior_data = Junior.objects.filter(auth=user).last()
if not junior_data:
@@ -192,6 +206,10 @@ class SigninWithApple(views.APIView):
ERROR_CODE["2069"],
response_status=status.HTTP_400_BAD_REQUEST
)
device_detail, created = UserDeviceDetails.objects.get_or_create(user=user)
if device_detail:
device_detail.device_id = device_id
device_detail.save()
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
+3 -2
View File
@@ -13,7 +13,7 @@ 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 base.constants import PENDING, IN_PROGRESS, JUNIOR, GUARDIAN
from guardian.models import JuniorTask
from junior.models import JuniorPoints
from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING, NOTIFICATION_DICT, TOP_JUNIOR
@@ -48,12 +48,13 @@ def notify_task_expiry():
:return:
"""
all_pending_tasks = JuniorTask.objects.filter(
junior__is_verified=True,
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,
send_notification(PENDING_TASK_EXPIRING, task.guardian.user_id, GUARDIAN, 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:
+2 -2
View File
@@ -11,7 +11,7 @@ import tempfile
# Import date time module's function
from datetime import datetime, time
# import Number constant
from base.constants import NUMBER
from base.constants import NUMBER, GUARDIAN
# Import Junior's model
from junior.models import Junior, JuniorPoints
# Import guardian's model
@@ -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.delay(REFERRAL_POINTS, None, None, junior_queryset.auth_id, {})
send_notification.delay(REFERRAL_POINTS, None, GUARDIAN, junior_queryset.auth_id, {})
+2 -2
View File
@@ -16,7 +16,7 @@ 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, JUNIOR)
GUARDIAN_CODE_STATUS, JUNIOR, GUARDIAN)
from guardian.models import Guardian, JuniorTask
from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail, get_junior_leaderboard_rank
@@ -323,7 +323,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
"""Notification email"""
junior_notification_email.delay(email, full_name, email, special_password)
# push notification
send_notification.delay(ASSOCIATE_JUNIOR, None, None, junior_data.auth_id, {})
send_notification.delay(ASSOCIATE_JUNIOR, None, GUARDIAN, junior_data.auth_id, {})
return junior_data
+2 -2
View File
@@ -362,7 +362,7 @@ class RemoveJuniorAPIView(views.APIView):
# save serializer
serializer.save()
JuniorGuardianRelationship.objects.filter(guardian=guardian, junior=junior_queryset).delete()
send_notification.delay(REMOVE_JUNIOR, None, None, junior_queryset.auth_id, {})
send_notification.delay(REMOVE_JUNIOR, None, GUARDIAN, 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:
@@ -712,7 +712,7 @@ class CompleteArticleAPIView(views.APIView):
total_earn_points=Sum('earn_points'))['total_earn_points']
data = {"total_earn_points":total_earn_points}
if total_earn_points:
send_notification.delay(ARTICLE_REWARD_POINTS, None, None,
send_notification.delay(ARTICLE_REWARD_POINTS, None, GUARDIAN,
request.user.id, {'points': total_earn_points})
return custom_response(SUCCESS_CODE['3042'], data, response_status=status.HTTP_200_OK)
except Exception as e:
+1
View File
@@ -11,3 +11,4 @@ class NotificationAdmin(admin.ModelAdmin):
"""Notification Admin"""
list_display = ['id', 'notification_type', 'notification_to', 'data', 'is_read']
list_filter = ['notification_type']
search_fields = ['notification_to']
+19
View File
@@ -22,79 +22,93 @@ ARTICLE_REWARD_POINTS = 17
REMOVE_JUNIOR = 18
TEST_NOTIFICATION = 99
# notification dictionary
NOTIFICATION_DICT = {
REGISTRATION: {
"notification_type": REGISTRATION,
"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: {
"notification_type": 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: {
"notification_type": 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: {
"notification_type": 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
REFERRAL_POINTS: {
"notification_type": 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
ASSOCIATE_JUNIOR: {
"notification_type": ASSOCIATE_JUNIOR,
"title": "Profile already setup!",
"body": "Your guardian has already setup your profile."
},
ASSOCIATE_EXISTING_JUNIOR: {
"notification_type": ASSOCIATE_EXISTING_JUNIOR,
"title": "Associated to guardian",
"body": "Your are associated to your guardian {from_user}."
},
# Juniors will receive Notification
# for every Task Assign by Custodians
TASK_ASSIGNED: {
"notification_type": 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
TASK_ACTION: {
"notification_type": TASK_ACTION,
"title": "Task completion approval!",
"body": "{from_user} completed their task {task_name}."
},
# Juniors will receive notification as soon
# as their task is rejected by custodians
TASK_REJECTED: {
"notification_type": TASK_REJECTED,
"title": "Task completion rejected!",
"body": "Your task completion request has been rejected by {from_user}."
},
# Juniors will receive notification as soon as their task is approved by custodians
# and for every Points earned by Task completion
TASK_APPROVED: {
"notification_type": TASK_APPROVED,
"title": "Task completion approved!",
"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: {
"notification_type": 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: {
"notification_type": 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."
@@ -102,27 +116,32 @@ NOTIFICATION_DICT = {
# Juniors will receive Notification
# related to Leaderboard progress
TOP_JUNIOR: {
"notification_type": TOP_JUNIOR,
"title": "Leaderboard topper!",
"body": "{from_user} is on top in leaderboard with {points} points."
},
# Juniors will receive notification
# when admin add any new financial learnings
NEW_ARTICLE_PUBLISHED: {
"notification_type": NEW_ARTICLE_PUBLISHED,
"title": "Time to read!",
"body": "A new article has been published."
},
# Juniors will receive notification when they earn points by reading financial Learning
ARTICLE_REWARD_POINTS: {
"notification_type": ARTICLE_REWARD_POINTS,
"title": "Article reward points!",
"body": "You are rewarded with {points} points for reading article and answering questions. "
},
# Juniors will receive notification as soon as their custodians remove them from account
REMOVE_JUNIOR: {
"notification_type": REMOVE_JUNIOR,
"title": "Disassociate by guardian!",
"body": "Your guardian has disassociated you."
},
# Test notification
TEST_NOTIFICATION: {
"notification_type": TEST_NOTIFICATION,
"title": "Test Notification",
"body": "This notification is for testing purpose from {from_user}."
}
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-09-29 07:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('notifications', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='notification',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
]
+1
View File
@@ -18,6 +18,7 @@ class Notification(models.Model):
data = models.JSONField(default=dict, blank=True, null=True)
is_read = models.BooleanField(default=False)
created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
""" string representation """
+1 -1
View File
@@ -36,7 +36,7 @@ class NotificationListSerializer(serializers.ModelSerializer):
class Meta(object):
"""meta info"""
model = Notification
fields = ['id', 'data', 'badge', 'is_read', 'created_at']
fields = ['id', 'notification_type', 'data', 'badge', 'is_read', 'updated_at']
@staticmethod
def get_badge(obj):
+20 -2
View File
@@ -24,6 +24,7 @@ User = get_user_model()
def register_fcm_token(user_id, registration_id, device_id, device_type):
""" used to register the fcm device token"""
FCMDevice.objects.filter(registration_id=registration_id).delete()
device, _ = FCMDevice.objects.update_or_create(user_id=user_id,
defaults={'device_id': device_id, 'type': device_type,
'active': True,
@@ -88,9 +89,14 @@ def get_notification_data(notification_type, from_user_id, from_user_type, to_us
push_data['body'] = push_data['body'].format(from_user=from_user_name, task_name=task_name, points=points)
notification_data['body'] = notification_data['body'].format(from_user=from_user_name,
task_name=task_name, points=points)
push_data['to_user_type'] = GUARDIAN if from_user_type == JUNIOR else JUNIOR
notification_data['to_user_type'] = GUARDIAN if from_user_type == JUNIOR else JUNIOR
notification_data['from_user'] = from_user_name
notification_data['from_user_image'] = from_user_image
notification_data.update(extra_data)
to_user = User.objects.filter(id=to_user_id).first()
return notification_data, push_data, from_user, to_user
@@ -104,14 +110,25 @@ def send_notification(notification_type, from_user_id, from_user_type, to_user_i
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.objects.create(notification_type=notification_type, notification_from=from_user,
notification_to=to_user, data=notification_data)
# notification create method changed on 28sep as per changes required
task_id = extra_data['task_id'] if 'task_id' in extra_data else None
Notification.objects.update_or_create(data__has_key='task_id', data__task_id=task_id,
notification_from=from_user, notification_to=to_user,
defaults={
'notification_type': notification_type,
'notification_from': from_user,
'notification_to': to_user,
'data': notification_data
})
if user_notification_type and user_notification_type.push_notification:
send_push(to_user, push_data)
def send_push(user, data):
""" used to send push notification to specific user """
data['notification_type'] = str(data['notification_type'])
user.fcmdevice_set.filter(active=True).send_message(
Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
)
@@ -119,6 +136,7 @@ def send_push(user, data):
def send_multiple_push(queryset, data):
""" used to send same notification to multiple users """
data['notification_type'] = str(data['notification_type'])
FCMDevice.objects.filter(user__in=queryset, active=True).send_message(
Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
)
+15 -5
View File
@@ -33,7 +33,7 @@ class NotificationViewSet(viewsets.GenericViewSet):
:return:
"""
queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id']
).select_related('notification_to').order_by('-id')
).select_related('notification_to').order_by('-updated_at', '-id')
paginator = CustomPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
@@ -58,8 +58,12 @@ class NotificationViewSet(viewsets.GenericViewSet):
"""
notify_task_expiry()
notify_top_junior()
send_notification(TEST_NOTIFICATION, None, None, request.auth.payload['user_id'],
notification_type = request.query_params.get('type', TEST_NOTIFICATION)
from_user_type = request.query_params.get('from_user_type')
send_notification(int(notification_type), None, from_user_type, request.auth.payload['user_id'],
{})
if notification_type and request.query_params.get('clear_all'):
Notification.objects.filter(notification_type=notification_type).delete()
return custom_response(SUCCESS_CODE["3000"])
@action(methods=['patch'], url_path='mark-as-read', url_name='mark-as-read', detail=False,
@@ -68,8 +72,14 @@ class NotificationViewSet(viewsets.GenericViewSet):
"""
notification list
"""
if request.query_params.get('all'):
Notification.objects.filter(notification_to_id=request.auth.payload['user_id']).update(is_read=True)
elif request.data.get('id'):
if request.data.get('id'):
Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True)
elif request.query_params.get('mark_all'):
Notification.objects.filter(notification_to_id=request.auth.payload['user_id']).update(is_read=True)
elif request.query_params.get('clear_all'):
Notification.objects.filter(notification_to_id=request.auth.payload['user_id']).delete()
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
+9 -3
View File
@@ -120,17 +120,23 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
if user_type not in [GUARDIAN, JUNIOR]:
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
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 not obj:
return custom_error_response(ERROR_CODE['2004'], status.HTTP_400_BAD_REQUEST)
if obj.is_active:
deactivate_email_template = 'user_deactivate.email'
obj.is_active = False
send_email([obj.user.email if user_type == GUARDIAN else obj.auth.email], email_template)
send_email([obj.user.email if user_type == GUARDIAN else obj.auth.email],
deactivate_email_template)
else:
activate_email_template = 'user_activate.email'
obj.is_active = True
send_email([obj.user.email if user_type == GUARDIAN else obj.auth.email],
activate_email_template)
obj.save()
return custom_response(SUCCESS_CODE['3038'])