mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-07-15 10:05:21 +00:00
notification set up and celery configuration
This commit is contained in:
@ -16,3 +16,22 @@ services:
|
|||||||
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
|
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
|
||||||
volumes:
|
volumes:
|
||||||
- .:/usr/src/app
|
- .:/usr/src/app
|
||||||
|
|
||||||
|
broker:
|
||||||
|
image: rabbitmq:3.7
|
||||||
|
container_name: rabbitmq
|
||||||
|
volumes:
|
||||||
|
- .:/usr/src/app
|
||||||
|
ports:
|
||||||
|
- 5673:5673
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build: .
|
||||||
|
image: celery
|
||||||
|
container_name: dev_celery
|
||||||
|
restart: "always"
|
||||||
|
command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E"
|
||||||
|
volumes:
|
||||||
|
- .:/usr/src/app
|
||||||
|
depends_on:
|
||||||
|
- broker
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
"""Views of Guardian"""
|
"""Views of Guardian"""
|
||||||
"""Third party Django app"""
|
|
||||||
|
# django imports
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework import viewsets, status
|
from rest_framework import viewsets, status
|
||||||
from rest_framework.pagination import PageNumberPagination
|
from rest_framework.pagination import PageNumberPagination
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
"""Import Django app"""
|
|
||||||
|
|
||||||
# Import guardian's model,
|
# Import guardian's model,
|
||||||
# Import junior's model,
|
# Import junior's model,
|
||||||
@ -19,7 +19,6 @@ from django.utils import timezone
|
|||||||
# Import account's serializer
|
# Import account's serializer
|
||||||
# Import account's task
|
# Import account's task
|
||||||
|
|
||||||
|
|
||||||
from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializer, TaskDetailsSerializer,
|
from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializer, TaskDetailsSerializer,
|
||||||
TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer)
|
TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer)
|
||||||
from .models import Guardian, JuniorTask
|
from .models import Guardian, JuniorTask
|
||||||
@ -31,6 +30,8 @@ from account.utils import custom_response, custom_error_response
|
|||||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
from base.constants import NUMBER
|
from base.constants import NUMBER
|
||||||
from .utils import upload_image_to_alibaba
|
from .utils import upload_image_to_alibaba
|
||||||
|
from notifications.constants import REGISTRATION
|
||||||
|
from notifications.utils import send_notification
|
||||||
|
|
||||||
""" Define APIs """
|
""" Define APIs """
|
||||||
# Define Signup API,
|
# Define Signup API,
|
||||||
@ -62,6 +63,7 @@ class SignupViewset(viewsets.ModelViewSet):
|
|||||||
user_type=str(request.data['user_type']), expired_at=expiry)
|
user_type=str(request.data['user_type']), expired_at=expiry)
|
||||||
"""Send email to the register user"""
|
"""Send email to the register user"""
|
||||||
send_otp_email(request.data['email'], otp)
|
send_otp_email(request.data['email'], otp)
|
||||||
|
send_notification(REGISTRATION, None, request.auth.payload['user_id'], {})
|
||||||
return custom_response(SUCCESS_CODE['3001'],
|
return custom_response(SUCCESS_CODE['3001'],
|
||||||
response_status=status.HTTP_200_OK)
|
response_status=status.HTTP_200_OK)
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
@ -3,4 +3,10 @@ notification admin file
|
|||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
# Register your models here.
|
from notifications.models import Notification
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Notification)
|
||||||
|
class NotificationAdmin(admin.ModelAdmin):
|
||||||
|
"""Notification Admin"""
|
||||||
|
list_display = ['id', 'notification_type', 'notification_to', 'data', 'is_read']
|
||||||
|
16
notifications/constants.py
Normal file
16
notifications/constants.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
notification constants file
|
||||||
|
"""
|
||||||
|
REGISTRATION = 1
|
||||||
|
TEST_NOTIFICATION = 99
|
||||||
|
|
||||||
|
NOTIFICATION_DICT = {
|
||||||
|
REGISTRATION: {
|
||||||
|
"title": "Successfully registered!",
|
||||||
|
"body": "You have registered successfully. Now login and complete your profile."
|
||||||
|
},
|
||||||
|
TEST_NOTIFICATION: {
|
||||||
|
"title": "Test Notification",
|
||||||
|
"body": "This notification is for testing purpose"
|
||||||
|
}
|
||||||
|
}
|
30
notifications/migrations/0001_initial.py
Normal file
30
notifications/migrations/0001_initial.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-07-19 07:40
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Notification',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('notification_type', models.CharField(blank=True, max_length=50, null=True)),
|
||||||
|
('data', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('is_read', models.BooleanField(default=False)),
|
||||||
|
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
|
||||||
|
('notification_from', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='from_notification', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('notification_to', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='to_notification', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
@ -1,6 +1,24 @@
|
|||||||
"""
|
"""
|
||||||
notification models file
|
notification models file
|
||||||
"""
|
"""
|
||||||
|
# django imports
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
# Create your models here.
|
USER = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class Notification(models.Model):
|
||||||
|
""" used to save the notifications """
|
||||||
|
notification_type = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
notification_to = models.ForeignKey(USER, related_name='to_notification', on_delete=models.CASCADE)
|
||||||
|
notification_from = models.ForeignKey(USER, related_name='from_notification', on_delete=models.SET_NULL,
|
||||||
|
blank=True, null=True)
|
||||||
|
data = models.JSONField(default=dict, blank=True, null=True)
|
||||||
|
is_read = models.BooleanField(default=False)
|
||||||
|
created_at = models.DateTimeField(default=timezone.now)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
""" string representation """
|
||||||
|
return f"{self.notification_to.id} | {self.notification_to.email}"
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
"""
|
"""
|
||||||
notifications utils file
|
notifications utils file
|
||||||
"""
|
"""
|
||||||
|
import constant as constant
|
||||||
# third party imports
|
# third party imports
|
||||||
from fcm_django.models import FCMDevice
|
from fcm_django.models import FCMDevice
|
||||||
|
from celery import shared_task
|
||||||
|
from firebase_admin.messaging import Message, Notification as FirebaseNotification
|
||||||
|
|
||||||
|
# django imports
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from account.models import UserNotification
|
||||||
|
from notifications.constants import NOTIFICATION_DICT
|
||||||
|
from notifications.models import Notification
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
|
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
def register_fcm_token(user_id, registration_id, device_id, device_type):
|
def register_fcm_token(user_id, registration_id, device_id, device_type):
|
||||||
""" used to register the fcm device token"""
|
""" used to register the fcm device token"""
|
||||||
device, _ = FCMDevice.objects.update_or_create(device_id=device_id,
|
device, _ = FCMDevice.objects.update_or_create(device_id=device_id,
|
||||||
@ -25,3 +38,35 @@ def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None:
|
|||||||
FCMDevice.objects.filter(user_id=user_id).delete()
|
FCMDevice.objects.filter(user_id=user_id).delete()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
def get_basic_detail(notification_type, from_user_id, to_user_id):
|
||||||
|
""" used to get the basic details """
|
||||||
|
notification_data = NOTIFICATION_DICT[notification_type]
|
||||||
|
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 notification_data, from_user, to_user
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task()
|
||||||
|
def send_notification(notification_type, from_user_id, to_user_id, extra_data):
|
||||||
|
""" used to send the push for the given notification type """
|
||||||
|
(notification_data, from_user, to_user) = get_basic_detail(notification_type, from_user_id, to_user_id)
|
||||||
|
print(notification_data, to_user)
|
||||||
|
user_notification_type = UserNotification.objects.filter(user=to_user).first()
|
||||||
|
# data = notification_data.data
|
||||||
|
data = notification_data
|
||||||
|
Notification.objects.create(notification_type=notification_type, notification_from=from_user,
|
||||||
|
notification_to=to_user, data=data)
|
||||||
|
if user_notification_type.push_notification:
|
||||||
|
data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()})
|
||||||
|
send_push(to_user, data)
|
||||||
|
|
||||||
|
|
||||||
|
def send_push(user, data):
|
||||||
|
""" used to send push notification to specific user """
|
||||||
|
# if user.push_notification:
|
||||||
|
notification_data = data.pop('data', None)
|
||||||
|
user.fcmdevice_set.filter(active=True).send_message(
|
||||||
|
Message(notification=FirebaseNotification(data['title'], data['body']), data=notification_data)
|
||||||
|
)
|
||||||
|
@ -8,9 +8,12 @@ from rest_framework.decorators import action
|
|||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
# local imports
|
||||||
from account.utils import custom_response
|
from account.utils import custom_response
|
||||||
from base.messages import SUCCESS_CODE
|
from base.messages import SUCCESS_CODE
|
||||||
|
from notifications.constants import TEST_NOTIFICATION
|
||||||
from notifications.serializers import RegisterDevice
|
from notifications.serializers import RegisterDevice
|
||||||
|
from notifications.utils import send_notification
|
||||||
|
|
||||||
|
|
||||||
class NotificationViewSet(viewsets.GenericViewSet):
|
class NotificationViewSet(viewsets.GenericViewSet):
|
||||||
@ -28,3 +31,12 @@ class NotificationViewSet(viewsets.GenericViewSet):
|
|||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return custom_response(SUCCESS_CODE["3000"])
|
return custom_response(SUCCESS_CODE["3000"])
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=False, url_path='test', url_name='test', serializer_class=None)
|
||||||
|
def send_test_notification(self, request):
|
||||||
|
"""
|
||||||
|
to send test notification
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
send_notification.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], {})
|
||||||
|
return custom_response(SUCCESS_CODE["3000"])
|
||||||
|
13
zod-bank-fcm-credentials.json
Normal file
13
zod-bank-fcm-credentials.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"type": "service_account",
|
||||||
|
"project_id": "zod-bank-ebb2a",
|
||||||
|
"private_key_id": "f1115f1b1fece77ba7a119b70e2502ce0777a7d4",
|
||||||
|
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCvLWEobyVN7D4+\nSZ4NwcugwuVk9MV7CjhQGB8lqzf/1Z0plBytjpjM75+orFk5n2tnOgTxsWCqR1R7\nLry4x2QH3HgJebd/TZTIyfepcAeuzUVhq9prgWVRsvxjpihMPZufA/Q1GEX5TBwX\nEasBW91Qwas2NBhUrzotnUBxOshVB4zCo3Ls9dbAN9O2O6paUMvcofSsRZ9XkB6P\nFFKy6nbQ3Bo+Lw3ntUfG1JQgkkxto2Vudiiq6J2dE6Eih2acEhEezQoJVpkMK+si\nlGp88T3j8nTx3o6ol99ke+3ZejPVE5sUbuhokSV/tS1Goy3whP+ys9lQtIyt3/mJ\nlmkoB9ShAgMBAAECggEAAk3H0GFF08C3EBWyDqH5dbLXbtH92e/UeNAMNcV4mGbY\n5GKGFywmEVmg2N22EPqa/s+Y6QxxrD9u9G1+EhbnIIx7olOBkScJ9C0c9CWztCon\ntPaofd1E1cI7I7UfVTg2ZLAHrBN4H70eadYc4va8bBtHj0EHYONz7S8sEBQ1Qna2\nIQuCEWY6MzhwCNEFIJd8ikd0GnkAJCuInK3F+2c37kugdgjRKxkTIfWmhNIfyWn3\ngoui5wltuuDKETVj9VUMyN6P7kffIJzS4mMRm/9F92scpPRbK+X1TrHpFsO826oP\nUuSBi5oOZYNyAvMBXGdnemoUNtAE+xg4kqwYJu5T0QKBgQDlbO08yH5MSLUxXZfL\nkCYg1mCUC4ySpL/qVFNuyvY+ex9gIg6gj4vEpK8s1lt0yvxrTng/MXxMMg7o9dO8\nmSlv/665twk/7NfkexJPWs1ph+54tud3Sa0TiVw6U5ObPMr1EYc7RnoELDR9Exc1\nUDJB+T3f3SGJicZn4pALyx132wKBgQDDd9lhqWLXJeG7JkInhLywuSVmnk077hY8\nxAfHqlO+/EEkM/YBtUvtC4KNiSkVftQwSM80NRiHlPvlWZ0G8l9RAfM/xxL/XUq6\nOu1fW1uJuukJgXFePV9SQLzfgd1fk1f/dKuiPSNhCzgTD7dFks0Ip6FD6/PCdN7x\neqRUqDccMwKBgQCTk1mW+7CiCTLkKjv2KScdgEhncnZd7bO1W8C/R7bVwgUQpVeb\nWDqjpvs3cDssCVYNAFDA9Wfq61hD6bzlV/AbpvARbfd5MzQ8OB4zBUmUVGfFJoIF\nbVLzeivlKNWNybETqs6+Bjt+a6Dnw1vuY0OwxE5Urb1g50rEkCvwKhsueQKBgQDB\nUt3rG46oX80cPkCbuUquNs/o6JRWu6m+u9s9/RYLBI6g8ctT8S2A6ytaNNgvbFsM\nzlYwunriTdW9Bp6p6jmfcyBUad4+NtTbz8BJ2Z91Xylwv1eS73xBa8nh/R0nlCEq\nhQfj1DgTmPcC0z5eT0z+TFzRQqK6JsEBcFzrZdvrxQKBgQDGEtWElG5XoDwnwO5e\nmfFLRKHfG+Xa6FChl2eeDIGOxv/Y6SMwT68t0gtnDFQGtcIMvC+kC/1Rv1v8yrOD\nCzEToh91Fw1c1eayNnsLq07qYnVWoMa7xFFSDhtBAnepqlU+81qaDvp+yILTIYSP\nwUuk3NpdJs2LkMetm5zJC9PJ2w==\n-----END PRIVATE KEY-----\n",
|
||||||
|
"client_email": "firebase-adminsdk-umcph@zod-bank-ebb2a.iam.gserviceaccount.com",
|
||||||
|
"client_id": "102629742363778142120",
|
||||||
|
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||||
|
"token_uri": "https://oauth2.googleapis.com/token",
|
||||||
|
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||||
|
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-umcph%40zod-bank-ebb2a.iam.gserviceaccount.com",
|
||||||
|
"universe_domain": "googleapis.com"
|
||||||
|
}
|
34
zod_bank/celery.py
Normal file
34
zod_bank/celery.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
"""
|
||||||
|
Celery Basic configuration
|
||||||
|
"""
|
||||||
|
|
||||||
|
# python imports
|
||||||
|
import os
|
||||||
|
|
||||||
|
# third party imports
|
||||||
|
from celery import Celery
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from celery.schedules import crontab
|
||||||
|
|
||||||
|
# OR, the same with increased verbosity:
|
||||||
|
load_dotenv(verbose=True)
|
||||||
|
|
||||||
|
env_path = os.path.join(os.path.abspath(os.path.join('.env', os.pardir)), '.env')
|
||||||
|
|
||||||
|
load_dotenv(dotenv_path=env_path)
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', os.getenv('SETTINGS'))
|
||||||
|
|
||||||
|
# celery app
|
||||||
|
app = Celery()
|
||||||
|
|
||||||
|
app.config_from_object('django.conf:settings')
|
||||||
|
|
||||||
|
# Load task modules from all registered Django apps.
|
||||||
|
app.autodiscover_tasks()
|
||||||
|
|
||||||
|
|
||||||
|
@app.task(bind=True)
|
||||||
|
def debug_task(self):
|
||||||
|
""" celery debug task """
|
||||||
|
print(f'Request: {self.request!r}')
|
Reference in New Issue
Block a user