Aktivitäten: Zeitraum per seit_tagen/vor_tagen abrufbar

Nutzer-Wunsch 29.10.2026: Die Oberfläche zeigt nur die letzten 30 Tage,
Älteres auf Nachfrage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-29 07:38:01 +01:00
1 parent 86bde64fe6
commit a798040847
1 file changed
+22 -5
+22 -5
View File
@@ -22,7 +22,7 @@ import psycopg
from psycopg.types.json import Jsonb
from cryptography.fernet import Fernet, InvalidToken
from fastapi import FastAPI, Header, HTTPException, Request
from fastapi import FastAPI, Header, HTTPException, Query, Request
from pydantic import BaseModel
from fastapi.responses import FileResponse, Response
@@ -3623,12 +3623,18 @@ INSTALLATIONSEREIGNIS_TEXTE = {
}
def fetch_aktivitaeten(organization_id: str | None = None, limit: int = AKTIVITAETEN_LIMIT):
def fetch_aktivitaeten(
organization_id: str | None = None, limit: int = AKTIVITAETEN_LIMIT,
seit_tagen: int | None = None, vor_tagen: int | None = None,
):
"""
Aktivitätenliste (GUI-Runde 09.10.2026, Punkt 4): Installationsereignisse
einzeln, Agent-Läufe zusammengefasst. Ein Lauf meldet je Merkmal ein
Ereignis (rund 56) mit demselben Zeitstempel - die Liste bliebe sonst
unlesbar. Ohne organization_id über alle Organisationen (Admin-Dashboard).
Nutzer-Wunsch 29.10.2026: Die Oberfläche zeigt nur die letzten 30 Tage
(seit_tagen) und Älteres auf Nachfrage (vor_tagen).
"""
with get_database_connection() as conn:
@@ -3643,6 +3649,8 @@ def fetch_aktivitaeten(organization_id: str | None = None, limit: int = AKTIVITA
JOIN merkmale m ON m.id = e.merkmal_id
JOIN devices d ON d.id = e.device_id
WHERE (%(org)s::uuid IS NULL OR d.organization_id = %(org)s::uuid)
AND (%(seit)s::int IS NULL OR e.occurred_at >= now() - make_interval(days => %(seit)s::int))
AND (%(vor)s::int IS NULL OR e.occurred_at < now() - make_interval(days => %(vor)s::int))
GROUP BY e.device_id, e.occurred_at
ORDER BY e.occurred_at DESC
LIMIT %(limit)s
@@ -3652,6 +3660,8 @@ def fetch_aktivitaeten(organization_id: str | None = None, limit: int = AKTIVITA
FROM device_installation_events ie
JOIN devices d ON d.id = ie.device_id
WHERE (%(org)s::uuid IS NULL OR d.organization_id = %(org)s::uuid)
AND (%(seit)s::int IS NULL OR ie.occurred_at >= now() - make_interval(days => %(seit)s::int))
AND (%(vor)s::int IS NULL OR ie.occurred_at < now() - make_interval(days => %(vor)s::int))
ORDER BY ie.occurred_at DESC
LIMIT %(limit)s
)
@@ -3667,7 +3677,7 @@ def fetch_aktivitaeten(organization_id: str | None = None, limit: int = AKTIVITA
ORDER BY x.occurred_at DESC
LIMIT %(limit)s
""",
{"org": organization_id, "limit": limit},
{"org": organization_id, "limit": limit, "seit": seit_tagen, "vor": vor_tagen},
)
rows = cur.fetchall()
@@ -8729,11 +8739,18 @@ def set_hostname_konvention_endpoint(
@app.get("/api/v1/organizations/{organization_id}/aktivitaeten")
def list_aktivitaeten(organization_id: str, limit: int = AKTIVITAETEN_LIMIT, authorization: str | None = Header(default=None)):
def list_aktivitaeten(
organization_id: str, limit: int = AKTIVITAETEN_LIMIT,
seit_tagen: int | None = Query(default=None, ge=1), vor_tagen: int | None = Query(default=None, ge=1),
authorization: str | None = Header(default=None),
):
if not require_service_token(authorization):
return {"success": False, "error": "unauthorized", "message": "Fehlendes oder ungültiges Service-Token."}
return {"success": True, "aktivitaeten": fetch_aktivitaeten(organization_id, max(1, min(limit, 500)))}
return {
"success": True,
"aktivitaeten": fetch_aktivitaeten(organization_id, max(1, min(limit, 500)), seit_tagen, vor_tagen),
}
@app.get("/api/v1/organizations/{organization_id}/activation-codes")