Files
guilan-ace-backend/apps/communications/api/views.py
Amirhossein Khalili 3ec28638d1 feat: replace email flows with in-app notifications
Add recipient notification detail and filtering, a queued admin composer, and contextual automatic titles. Retire email delivery, obsolete auth routes, newsletter data, and related templates and settings.
2026-09-26 12:23:10 +03:30

175 lines
6.5 KiB
Python

from typing import List
from django.db.models import Count, Q
from django.shortcuts import get_object_or_404
from django.utils import timezone
from ninja import Router
from ninja.pagination import paginate
from apps.communications.api.schemas import (
AnnouncementCreateSchema,
AnnouncementListSchema,
AnnouncementSchema,
AnnouncementStatsSchema,
MessageResponseSchema,
PushDeviceCreateSchema,
PushDeviceSchema,
PushDeviceUpdateSchema,
)
from apps.communications.models import Announcement, AnnouncementPriority, AnnouncementType, PushNotificationDevice
from core.authentication import jwt_auth
communications_router = Router()
def _is_staff_user(user) -> bool:
return bool(user and (user.is_staff or user.is_superuser))
@communications_router.get("/announcements/", response=List[AnnouncementListSchema])
@paginate
def list_announcements(request, published_only: bool = True):
queryset = Announcement.objects.select_related("author").filter(is_deleted=False)
if published_only:
queryset = queryset.filter(is_published=True, publish_date__lte=timezone.now())
items = []
for announcement in queryset.order_by("-created_at"):
items.append(
{
"id": announcement.id,
"title": announcement.title,
"content": announcement.content,
"announcement_type": announcement.announcement_type,
"priority": announcement.priority,
"author": announcement.author,
"is_published": announcement.is_published,
"publish_date": announcement.publish_date,
"created_at": announcement.created_at,
}
)
return items
@communications_router.get("/announcements/{announcement_id}/", response=AnnouncementSchema)
def get_announcement(request, announcement_id: int):
announcement = get_object_or_404(
Announcement.objects.select_related("author").filter(is_deleted=False),
id=announcement_id,
)
if not announcement.is_published and not _is_staff_user(getattr(request, "auth", None)):
return {"error": "Announcement not found"}, 404
return announcement
@communications_router.post("/announcements/", response=AnnouncementSchema, auth=jwt_auth)
def create_announcement(request, payload: AnnouncementCreateSchema):
user = request.auth
if not _is_staff_user(user):
return {"error": "Permission denied"}, 403
announcement = Announcement.objects.create(
author=user,
title=payload.title,
content=payload.content,
announcement_type=payload.announcement_type,
priority=payload.priority,
is_published=payload.is_published,
publish_date=payload.publish_date,
)
return announcement
@communications_router.put("/announcements/{announcement_id}/", response=AnnouncementSchema, auth=jwt_auth)
def update_announcement(request, announcement_id: int, payload: AnnouncementCreateSchema):
user = request.auth
if not _is_staff_user(user):
return {"error": "Permission denied"}, 403
announcement = get_object_or_404(Announcement, id=announcement_id, is_deleted=False)
announcement.title = payload.title
announcement.content = payload.content
announcement.announcement_type = payload.announcement_type
announcement.priority = payload.priority
announcement.is_published = payload.is_published
announcement.publish_date = payload.publish_date
announcement.save()
return announcement
@communications_router.delete("/announcements/{announcement_id}/", response=MessageResponseSchema, auth=jwt_auth)
def delete_announcement(request, announcement_id: int):
user = request.auth
if not _is_staff_user(user):
return {"error": "Permission denied"}, 403
announcement = get_object_or_404(Announcement, id=announcement_id, is_deleted=False)
announcement.soft_delete()
return {"message": "Announcement deleted successfully"}
@communications_router.get("/announcements/stats/", response=AnnouncementStatsSchema, auth=jwt_auth)
def get_announcement_stats(request):
user = request.auth
if not _is_staff_user(user):
return {"error": "Permission denied"}, 403
stats = Announcement.objects.filter(is_deleted=False).aggregate(
total_announcements=Count("id"),
published_announcements=Count("id", filter=Q(is_published=True)),
draft_announcements=Count("id", filter=Q(is_published=False)),
urgent_announcements=Count("id", filter=Q(priority="urgent")),
)
return stats
@communications_router.post("/push-devices/", response=PushDeviceSchema, auth=jwt_auth)
def register_push_device(request, payload: PushDeviceCreateSchema):
user = request.auth
device, created = PushNotificationDevice.objects.get_or_create(
user=user,
device_token=payload.device_token,
defaults={"device_type": payload.device_type, "is_active": True},
)
if not created:
device.is_active = True
device.device_type = payload.device_type
device.save()
return device
@communications_router.delete("/push-devices/", response=MessageResponseSchema, auth=jwt_auth)
def unregister_push_device(request, device_token: str):
user = request.auth
try:
device = PushNotificationDevice.objects.get(user=user, device_token=device_token)
device.delete()
return {"message": "Device unregistered successfully"}
except PushNotificationDevice.DoesNotExist:
return {"message": "Device not found"}, 404
@communications_router.get("/push-devices/", response=List[PushDeviceSchema], auth=jwt_auth)
def list_user_push_devices(request):
return PushNotificationDevice.objects.filter(user=request.auth, is_deleted=False).order_by("-created_at")
@communications_router.put("/push-devices/{device_id}/", response=PushDeviceSchema, auth=jwt_auth)
def update_push_device(request, device_id: int, payload: PushDeviceUpdateSchema):
device = get_object_or_404(PushNotificationDevice, id=device_id, user=request.auth, is_deleted=False)
device.is_active = payload.is_active
device.save()
return device
@communications_router.get("/announcement-types/", response=List[dict])
def get_announcement_types(request):
return [{"value": choice[0], "label": choice[1]} for choice in AnnouncementType.choices]
@communications_router.get("/announcement-priorities/", response=List[dict])
def get_announcement_priorities(request):
return [{"value": choice[0], "label": choice[1]} for choice in AnnouncementPriority.choices]