Coverage for src/lexigram/auth/admin/handlers/active_sessions.py: 95%
21 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 00:58 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 00:58 +0800
1"""Active sessions widget handler."""
3from __future__ import annotations
5from datetime import UTC, datetime
7from lexigram.contracts.admin import (
8 SessionCountProtocol,
9 Stat,
10 StatContent,
11 WidgetParams,
12)
13from lexigram.contracts.admin.errors import AdminError
14from lexigram.contracts.auth import SessionRepositoryProtocol
15from lexigram.result import Ok, Result
18class ActiveSessionsWidgetHandler:
19 """Handler for the active sessions widget.
21 Args:
22 session_repository: optional session repository. Degrades to a zero
23 count when absent or when it lacks the session-count capability.
24 """
26 def __init__(
27 self, session_repository: SessionRepositoryProtocol | None = None
28 ) -> None:
29 """Initialize handler with the session repository.
31 Args:
32 session_repository: repository implementing SessionRepositoryProtocol.
33 When None, the widget reports a zero count.
34 """
35 self._session_repository = session_repository
37 async def get_data(self, params: WidgetParams) -> Result[StatContent, AdminError]:
38 """Fetch active sessions data.
40 Mirrors the widget template: the count is rendered statically with
41 neutral styling and the peak line is shown only when non-zero — no
42 tone/threshold ladder exists in the template.
44 Infrastructure failures propagate as exceptions.
46 Args:
47 params: Widget parameters (unused for this widget).
49 Returns:
50 Result containing StatContent or AdminError.
51 """
52 count = 0
53 if isinstance(self._session_repository, SessionCountProtocol):
54 cutoff = datetime.now(UTC)
55 count = await self._session_repository.count_active(cutoff)
56 return Ok(self._build_content(count=count, peak_today=0))
58 def _build_content(self, count: int, peak_today: int) -> StatContent:
59 """Build the StatContent mirroring the widget template.
61 Args:
62 count: Number of currently active sessions.
63 peak_today: Peak session count today.
65 Returns:
66 StatContent with neutral styling and a conditional peak stat.
67 """
68 stats: list[Stat] = [
69 Stat(label="Currently Active", value=str(count)),
70 ]
71 if peak_today > 0:
72 stats.append(Stat(label="Peak today", value=str(peak_today)))
73 return StatContent(stats=tuple(stats))
76__all__ = ["ActiveSessionsWidgetHandler"]