|
@@ -84,11 +84,12 @@ class DashboardStore:
|
|
|
hours: float = 24,
|
|
hours: float = 24,
|
|
|
limit: int = 20,
|
|
limit: int = 20,
|
|
|
offset: int = 0,
|
|
offset: int = 0,
|
|
|
- ) -> list[dict[str, Any]]:
|
|
|
|
|
|
|
+ ) -> dict[str, Any]:
|
|
|
"""Paginated cluster listing filtered by payload.timestamp (event time).
|
|
"""Paginated cluster listing filtered by payload.timestamp (event time).
|
|
|
|
|
|
|
|
payload.timestamp is guaranteed ISO 8601 UTC — uses _read_ts from
|
|
payload.timestamp is guaranteed ISO 8601 UTC — uses _read_ts from
|
|
|
sqlite_store. Do NOT filter by updated_at (row mod time).
|
|
sqlite_store. Do NOT filter by updated_at (row mod time).
|
|
|
|
|
+ Returns {"clusters": [...], "total": int}.
|
|
|
"""
|
|
"""
|
|
|
cutoff_ts = (datetime.now(timezone.utc) - timedelta(hours=hours)).timestamp()
|
|
cutoff_ts = (datetime.now(timezone.utc) - timedelta(hours=hours)).timestamp()
|
|
|
|
|
|
|
@@ -106,22 +107,25 @@ class DashboardStore:
|
|
|
filtered.sort(key=lambda c: _read_ts(c.get("timestamp")) or 0.0, reverse=True)
|
|
filtered.sort(key=lambda c: _read_ts(c.get("timestamp")) or 0.0, reverse=True)
|
|
|
page = filtered[offset:offset + limit]
|
|
page = filtered[offset:offset + limit]
|
|
|
|
|
|
|
|
- return [
|
|
|
|
|
- {
|
|
|
|
|
- "cluster_id": c.get("cluster_id", ""),
|
|
|
|
|
- "headline": c.get("headline", ""),
|
|
|
|
|
- "topic": c.get("topic", ""),
|
|
|
|
|
- "sentiment": c.get("sentiment", "neutral"),
|
|
|
|
|
- "sentimentScore": c.get("sentimentScore"),
|
|
|
|
|
- "importance": c.get("importance", 0),
|
|
|
|
|
- "entities": c.get("entities", []),
|
|
|
|
|
- "sources": c.get("sources", []),
|
|
|
|
|
- "timestamp": c.get("timestamp", ""),
|
|
|
|
|
- "keywords": c.get("keywords", []),
|
|
|
|
|
- "article_count": len(c.get("articles", [])),
|
|
|
|
|
- }
|
|
|
|
|
- for c in page
|
|
|
|
|
- ]
|
|
|
|
|
|
|
+ return {
|
|
|
|
|
+ "clusters": [
|
|
|
|
|
+ {
|
|
|
|
|
+ "cluster_id": c.get("cluster_id", ""),
|
|
|
|
|
+ "headline": c.get("headline", ""),
|
|
|
|
|
+ "topic": c.get("topic", ""),
|
|
|
|
|
+ "sentiment": c.get("sentiment", "neutral"),
|
|
|
|
|
+ "sentimentScore": c.get("sentimentScore"),
|
|
|
|
|
+ "importance": c.get("importance", 0),
|
|
|
|
|
+ "entities": c.get("entities", []),
|
|
|
|
|
+ "sources": c.get("sources", []),
|
|
|
|
|
+ "timestamp": c.get("timestamp", ""),
|
|
|
|
|
+ "keywords": c.get("keywords", []),
|
|
|
|
|
+ "article_count": len(c.get("articles", [])),
|
|
|
|
|
+ }
|
|
|
|
|
+ for c in page
|
|
|
|
|
+ ],
|
|
|
|
|
+ "total": len(filtered),
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
def get_cluster_detail(self, cluster_id: str) -> dict[str, Any] | None:
|
|
def get_cluster_detail(self, cluster_id: str) -> dict[str, Any] | None:
|
|
|
with self._store._conn() as conn:
|
|
with self._store._conn() as conn:
|