Neuer Reiter zwischen Audit-Log und Verbrauchsrechner, Anlass war die Dashboard-Kachel API-Kosten gesamt, die nirgendwo hinführte (sie springt jetzt hierher). Kennzahlen-Set nach Betreiber-Best-Practices für nutzungsbasierte KI-Produkte (Kostenverlauf, Marge je Kunde, Betrieb). - GET /api/statistik?months=N liefert alles in einem JSON. Kosten und Läufe je Monat und Lagentyp (führende Quelle refresh_log mit lückenlosem incidents-Join), Quellen-Split aus token_usage_monthly, Erfolgsquote, normalisiertes Fehler-Ranking, Dauer je Typ, Wachstum, MAU/DAU aus user_activity_days und je Organisation Ist-Kosten plus Margen-Schätzung zu heutigen Sätzen (Recherche-Anlage zählt dreifach, weil der Multi-Pass nur eine Log-Zeile erzeugt). Aggregation als pure, unit-getestete Funktionen, Monatsachse lückenlos gefüllt, Mediane nur über Läufe mit Kosten (Erfassung begann erst 03/2026). - statistik.js injiziert sich selbst nach dem rechner.js-Muster, Platzierung doppelt gesichert (Ladereihenfolge mit async=false plus insertBefore). Charts als Inline-SVG ohne Bibliothek, eine Achse je Chart, Hover-Titel mit großen Trefferflächen, Legenden, Serienfarben je Theme mit dem dataviz-Palette-Validator geprüft (alle Checks PASS). Zeitraum 6/12/Alles, Datenstand-Hinweis, ehrliche Fußnote unter der Margen-Tabelle, Leerzustand für die junge Aktivitäts-Erfassung. - Migration user_activity_days für die Portal-Staging-DB (auf Live legt sie der Monitor an, dort läuft die Erfassung in get_current_user). - 10 neue Unit-Tests plus Auth-Smoke für den Endpoint, 96 Tests grün (die zwei bekannten shared-Import-Fehlschläge bestehen unverändert). Gegenstück im Monitor: Commit d367c60 (Aktivitäts-Erfassung). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
47 Zeilen
1.5 KiB
Python
47 Zeilen
1.5 KiB
Python
"""Migration 2026-07-25: Tabelle user_activity_days anlegen (MAU/DAU).
|
|
|
|
Der Monitor legt die Tabelle beim Start selbst an und schreibt je Nutzer und
|
|
Kalendertag einen Eintrag (auth.get_current_user). Die Portal-Staging-Umgebung
|
|
nutzt eine EIGENE Datenbank ohne laufenden Monitor, deshalb dieses Skript.
|
|
Idempotent (CREATE TABLE IF NOT EXISTS), es werden keine Daten eingefügt.
|
|
|
|
Ausführung:
|
|
DB_PATH=/home/claude-dev/AegisSight-Monitor-staging/data/osint.db python3 migrations/2026-07-25_user_activity.py
|
|
DB_PATH=/home/claude-dev/osint-data/osint.db python3 migrations/2026-07-25_user_activity.py
|
|
"""
|
|
import os
|
|
import sqlite3
|
|
import sys
|
|
|
|
|
|
def main(db_path: str) -> int:
|
|
if not os.path.exists(db_path):
|
|
print(f"FEHLER: DB nicht gefunden: {db_path}", file=sys.stderr)
|
|
return 1
|
|
|
|
conn = sqlite3.connect(db_path, timeout=60)
|
|
conn.execute("PRAGMA busy_timeout = 60000")
|
|
conn.execute("PRAGMA journal_mode = WAL")
|
|
|
|
print(f"Migration auf {db_path}")
|
|
|
|
conn.execute("""
|
|
CREATE TABLE IF NOT EXISTS user_activity_days (
|
|
user_id INTEGER NOT NULL,
|
|
day TEXT NOT NULL,
|
|
tenant_id INTEGER,
|
|
PRIMARY KEY (user_id, day)
|
|
)
|
|
""")
|
|
print(" + user_activity_days angelegt (oder vorhanden)")
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
print("Migration abgeschlossen.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
db_path = os.environ.get("DB_PATH", "/home/claude-dev/osint-data/osint.db")
|
|
sys.exit(main(db_path))
|