perf: add indexed notification summaries and widget
This commit is contained in:
@@ -19,6 +19,14 @@ class NotificationMessage(Base, TimestampMixin):
|
||||
__table_args__ = (
|
||||
Index("ix_notification_messages_tenant_status", "tenant_id", "status"),
|
||||
Index("ix_notification_messages_tenant_recipient", "tenant_id", "recipient_type", "recipient_id"),
|
||||
Index(
|
||||
"ix_notification_messages_summary",
|
||||
"tenant_id",
|
||||
"recipient_id",
|
||||
"deleted_at",
|
||||
"status",
|
||||
"read_at",
|
||||
),
|
||||
Index("ix_notification_messages_source", "tenant_id", "source_module", "source_resource_type", "source_resource_id"),
|
||||
Index("ix_notification_messages_due", "tenant_id", "status", "not_before_at"),
|
||||
)
|
||||
|
||||
@@ -104,6 +104,13 @@ manifest = ModuleManifest(
|
||||
),
|
||||
),
|
||||
view_surfaces=(
|
||||
ViewSurface(
|
||||
id="notifications.widget.summary",
|
||||
module_id=MODULE_ID,
|
||||
kind="section",
|
||||
label="Notification summary widget",
|
||||
order=30,
|
||||
),
|
||||
ViewSurface(
|
||||
id="notifications.settings.preferences",
|
||||
module_id=MODULE_ID,
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
"""Add notification summary covering index.
|
||||
|
||||
Revision ID: 7a8b9c0d1e2f
|
||||
Revises: 6f7a8b9c0d1e
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
|
||||
|
||||
revision = "7a8b9c0d1e2f"
|
||||
down_revision = "6f7a8b9c0d1e"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_index(
|
||||
"ix_notification_messages_summary",
|
||||
"notification_messages",
|
||||
["tenant_id", "recipient_id", "deleted_at", "status", "read_at"],
|
||||
unique=False,
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(
|
||||
"ix_notification_messages_summary",
|
||||
table_name="notification_messages",
|
||||
)
|
||||
@@ -6,7 +6,7 @@ from email.message import EmailMessage
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import event, or_
|
||||
from sqlalchemy import and_, case, event, func, or_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from govoplan_core.core.notifications import NotificationDispatchRequest
|
||||
@@ -200,21 +200,75 @@ def notification_summary(
|
||||
user_id: str | None = None,
|
||||
recipient_ids: tuple[str, ...] | None = None,
|
||||
) -> dict[str, int | bool]:
|
||||
base_query = session.query(NotificationMessage).filter(
|
||||
filters = [
|
||||
NotificationMessage.tenant_id == tenant_id,
|
||||
NotificationMessage.deleted_at.is_(None),
|
||||
)
|
||||
]
|
||||
if recipient_ids is not None:
|
||||
base_query = base_query.filter(NotificationMessage.recipient_id.in_(recipient_ids))
|
||||
active_query = base_query.filter(NotificationMessage.status.notin_(["cancelled", "skipped"]))
|
||||
filters.append(NotificationMessage.recipient_id.in_(recipient_ids))
|
||||
active = NotificationMessage.status.notin_(["cancelled", "skipped"])
|
||||
total, unread, pending, failed = (
|
||||
session.query(
|
||||
func.count(NotificationMessage.id),
|
||||
func.coalesce(
|
||||
func.sum(
|
||||
case(
|
||||
(
|
||||
and_(
|
||||
active,
|
||||
NotificationMessage.read_at.is_(None),
|
||||
),
|
||||
1,
|
||||
),
|
||||
else_=0,
|
||||
)
|
||||
),
|
||||
0,
|
||||
),
|
||||
func.coalesce(
|
||||
func.sum(
|
||||
case(
|
||||
(
|
||||
and_(
|
||||
active,
|
||||
NotificationMessage.status.in_(
|
||||
sorted(PENDING_STATUSES)
|
||||
),
|
||||
),
|
||||
1,
|
||||
),
|
||||
else_=0,
|
||||
)
|
||||
),
|
||||
0,
|
||||
),
|
||||
func.coalesce(
|
||||
func.sum(
|
||||
case(
|
||||
(
|
||||
and_(
|
||||
active,
|
||||
NotificationMessage.status == "failed",
|
||||
),
|
||||
1,
|
||||
),
|
||||
else_=0,
|
||||
)
|
||||
),
|
||||
0,
|
||||
),
|
||||
)
|
||||
.filter(*filters)
|
||||
.one()
|
||||
)
|
||||
show_unread_badge = True
|
||||
if user_id:
|
||||
show_unread_badge = get_notification_preferences(session, tenant_id=tenant_id, user_id=user_id).show_unread_badge
|
||||
return {
|
||||
"total": base_query.count(),
|
||||
"unread": active_query.filter(NotificationMessage.read_at.is_(None)).count(),
|
||||
"pending": active_query.filter(NotificationMessage.status.in_(sorted(PENDING_STATUSES))).count(),
|
||||
"failed": active_query.filter(NotificationMessage.status == "failed").count(),
|
||||
"total": int(total or 0),
|
||||
"unread": int(unread or 0),
|
||||
"pending": int(pending or 0),
|
||||
"failed": int(failed or 0),
|
||||
"show_unread_badge": show_unread_badge,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user