diff --git a/account/views.py b/account/views.py index a76693e..45e662e 100644 --- a/account/views.py +++ b/account/views.py @@ -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,6 +103,11 @@ 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( diff --git a/base/tasks.py b/base/tasks.py index 24893fc..ebc6672 100644 --- a/base/tasks.py +++ b/base/tasks.py @@ -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 @@ -53,7 +53,7 @@ def notify_task_expiry(): (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: diff --git a/notifications/admin.py b/notifications/admin.py index c7cc895..4df035a 100644 --- a/notifications/admin.py +++ b/notifications/admin.py @@ -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'] diff --git a/notifications/constants.py b/notifications/constants.py index c22f246..6917327 100644 --- a/notifications/constants.py +++ b/notifications/constants.py @@ -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}." } diff --git a/notifications/migrations/0002_notification_updated_at.py b/notifications/migrations/0002_notification_updated_at.py new file mode 100644 index 0000000..7a075fa --- /dev/null +++ b/notifications/migrations/0002_notification_updated_at.py @@ -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), + ), + ] diff --git a/notifications/models.py b/notifications/models.py index 5d6d02c..1fb99e9 100644 --- a/notifications/models.py +++ b/notifications/models.py @@ -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 """ diff --git a/notifications/serializers.py b/notifications/serializers.py index e4fdb05..2c7dfec 100644 --- a/notifications/serializers.py +++ b/notifications/serializers.py @@ -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): diff --git a/notifications/utils.py b/notifications/utils.py index a118cd7..0ad6cda 100644 --- a/notifications/utils.py +++ b/notifications/utils.py @@ -88,6 +88,7 @@ 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) + notification_data['from_user'] = from_user_name notification_data['from_user_image'] = from_user_image @@ -104,14 +105,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 +131,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) ) diff --git a/notifications/views.py b/notifications/views.py index cec50e4..5fcafe8 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -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,11 @@ 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) + send_notification(int(notification_type), None, None, 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 +71,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)