diff --git a/app.py b/app.py index 454f7cf..a6907a7 100644 --- a/app.py +++ b/app.py @@ -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")