130 lines
3.3 KiB
Python

from django.core.cache import cache
from django.db.models import Count
from django.db.models import Q
UNREAD_SUMMARY_TIMEOUT = 60
def unread_summary_cache_key(user_id):
return f"notifications:unread_summary:user:{user_id}"
def _coerce_positive_int(value):
try:
integer_value = int(value)
except (TypeError, ValueError):
return None
return integer_value if integer_value > 0 else None
def _build_procurement_request_counts(user_id):
from django.contrib.contenttypes.models import ContentType
from procurement.models import ProcurementRequest
from .models import Notification
request_content_type = ContentType.objects.get_for_model(
ProcurementRequest,
)
request_content_type_id = request_content_type.id
counts = {}
notifications = (
Notification.objects.filter(
recipient_id=user_id,
unread=True,
deleted=False,
)
.filter(
Q(verb__startswith="procurement_")
| Q(verb__startswith="equipment_")
)
.only(
"data",
"target_content_type_id",
"target_object_id",
"action_object_content_type_id",
"action_object_object_id",
)
)
for notification in notifications:
data = notification.data if isinstance(notification.data, dict) else {}
request_id = _coerce_positive_int(data.get("request_id"))
if request_id is None and (
notification.target_content_type_id == request_content_type_id
):
request_id = _coerce_positive_int(notification.target_object_id)
if request_id is None and (
notification.action_object_content_type_id == request_content_type_id
):
request_id = _coerce_positive_int(
notification.action_object_object_id,
)
if request_id is not None:
counts[request_id] = counts.get(request_id, 0) + 1
return [
{"request_id": request_id, "unread": unread}
for request_id, unread in sorted(counts.items())
]
def build_unread_summary(user_id):
from .models import Notification
rows = (
Notification.objects.filter(
recipient_id=user_id,
unread=True,
deleted=False,
)
.values("verb")
.annotate(unread=Count("id"))
.order_by()
)
verbs = [
{"verb": row["verb"], "unread": row["unread"]}
for row in rows
]
return {
"total": sum(item["unread"] for item in verbs),
"verbs": verbs,
"procurement_requests": _build_procurement_request_counts(user_id),
}
def get_unread_summary(user_id):
key = unread_summary_cache_key(user_id)
try:
cached = cache.get(key)
except Exception:
cached = None
if cached is not None:
return cached
summary = build_unread_summary(user_id)
try:
cache.set(key, summary, UNREAD_SUMMARY_TIMEOUT)
except Exception:
pass
return summary
def get_unread_count(user_id):
return get_unread_summary(user_id)["total"]
def invalidate_unread_summary(user_id):
if not user_id:
return
try:
cache.delete(unread_summary_cache_key(user_id))
except Exception:
pass