Implement notifications

This commit is contained in:
Keannu Christian Bernasol 2025-01-21 13:57:31 +08:00
parent 63f3bd0eab
commit 298501b973
16 changed files with 293 additions and 1 deletions

View file

@ -0,0 +1,16 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from .models import Notification
from unfold.contrib.filters.admin import RangeDateFilter
# Register your models here.
@admin.register(Notification)
class NotificationAdmin(ModelAdmin):
search_fields = ["id"]
list_display = ["id", "content", "type", "timestamp", "audience", "client"]
list_filter = [
("timestamp", RangeDateFilter),
]

View file

@ -0,0 +1,11 @@
from django.apps import AppConfig
class NotificationsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "notifications"
def ready(self) -> None:
import notifications.signals
return super().ready()

View file

@ -0,0 +1,60 @@
# Generated by Django 5.1.3 on 2025-01-21 04:14
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
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",
),
),
(
"timestamp",
models.DateTimeField(
default=django.utils.timezone.now, editable=False
),
),
("content", models.TextField(blank=True, max_length=512, null=True)),
(
"audience",
models.CharField(
choices=[
("client", "Client"),
("staff", "Staff"),
("head", "Head"),
],
default="staff",
max_length=16,
),
),
(
"client",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]

View file

@ -0,0 +1,22 @@
# Generated by Django 5.1.3 on 2025-01-21 04:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("notifications", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="notification",
name="type",
field=models.CharField(
choices=[("info", "Info"), ("warning", "Warning")],
default="info",
max_length=16,
),
),
]

View file

@ -0,0 +1,27 @@
from django.db import models
from django.utils.timezone import now
class Notification(models.Model):
client = models.ForeignKey(
"accounts.CustomUser", on_delete=models.CASCADE, null=True, blank=True)
timestamp = models.DateTimeField(default=now, editable=False)
content = models.TextField(max_length=512, blank=True, null=True)
AUDIENCE_CHOICES = (
("client", "Client"),
("staff", "Staff"),
("head", "Head")
)
TYPE_CHOICES = (
("info", "Info"),
("warning", "Warning"),
)
type = models.CharField(
max_length=16, choices=TYPE_CHOICES, default="info")
audience = models.CharField(
max_length=16, choices=AUDIENCE_CHOICES, default="staff")

View file

@ -0,0 +1,23 @@
from rest_framework import serializers
from accounts.models import CustomUser
from .models import Notification
class NotificationSerializer(serializers.ModelSerializer):
client = serializers.SlugRelatedField(
many=False, slug_field="id", queryset=CustomUser.objects.all(), required=False
)
timestamp = serializers.DateTimeField(
format="%m-%d-%Y %I:%M %p", read_only=True
)
class Meta:
model = Notification
fields = [
"id",
"client",
"timestamp",
"content",
"type",
"audience",
]

View file

@ -0,0 +1,14 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from datetime import timedelta
from notifications.models import Notification
from django.utils import timezone
@receiver(post_save, sender=Notification)
def notification_post_save(sender, instance, **kwargs):
# Calculate the time threshold (15 minutes ago)
threshold = timezone.now() - timedelta(minutes=15)
# Find and delete all notifications older than 15 minutes
Notification.objects.filter(timestamp__lt=threshold).delete()

View file

@ -0,0 +1,10 @@
from django.urls import path
from .views import (
NotificationListView,
NotificationDeleteView
)
urlpatterns = [
path("list/", NotificationListView.as_view()),
path("delete/<int:pk>/", NotificationDeleteView.as_view()),
]

View file

@ -0,0 +1,52 @@
from rest_framework import generics
from django.db.models import Q
from .serializers import NotificationSerializer
from .models import Notification
from rest_framework.permissions import IsAuthenticated
class NotificationListView(generics.ListAPIView):
"""
Used by all users to view notifications. Returns user-specific notifications for clients.
Returns role-wide notifications for staff and head roles
"""
http_method_names = ["get"]
serializer_class = NotificationSerializer
queryset = Notification.objects.all().order_by("-timestamp")
permission_classes = [IsAuthenticated]
def get_queryset(self):
user = self.request.user
if user.role == "client":
queryset = Notification.objects.filter(client=user)
elif user.role == "staff":
queryset = Notification.objects.filter(audience="staff")
elif user.role in ["head", "admin"]:
queryset = Notification.objects.filter(
Q(audience="staff") | Q(audience="head"))
return queryset
class NotificationDeleteView(generics.DestroyAPIView):
"""
Used by all users to dismiss or delete notifications.
"""
http_method_names = ["delete"]
serializer_class = NotificationSerializer
queryset = Notification.objects.all().order_by("-timestamp")
permission_classes = [IsAuthenticated]
def get_queryset(self):
user = self.request.user
if user.role == "client":
return Notification.objects.filter(client=user)
elif user.role == "staff":
return Notification.objects.filter(audience="staff")
else:
# For head or admin roles
return Notification.objects.filter(
Q(audience="head") | Q(audience="staff")
)