130 lines
3.3 KiB
Python
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
|