Implement Manticore fully and re-theme
This commit is contained in:
96
core/management/commands/gia_analysis.py
Normal file
96
core/management/commands/gia_analysis.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from core.events.behavior import summarize_metrics
|
||||
from core.events.manticore import get_event_ledger_backend
|
||||
from core.util import logs
|
||||
|
||||
log = logs.get_logger("gia_analysis")
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Compute behavioral metrics from Manticore event rows into gia_metrics."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("--once", action="store_true", default=False)
|
||||
parser.add_argument("--user-id", type=int)
|
||||
parser.add_argument("--person-id")
|
||||
parser.add_argument("--sleep-seconds", type=float, default=60.0)
|
||||
parser.add_argument("--window-days", nargs="*", type=int, default=[1, 7, 30, 90])
|
||||
|
||||
def _run_cycle(
|
||||
self,
|
||||
*,
|
||||
user_id: int | None = None,
|
||||
person_id: str = "",
|
||||
window_days: list[int] | None = None,
|
||||
) -> int:
|
||||
backend = get_event_ledger_backend()
|
||||
now_ms = int(time.time() * 1000)
|
||||
baseline_since = now_ms - (90 * 86400000)
|
||||
windows = sorted({max(1, int(value)) for value in list(window_days or [1, 7, 30, 90])})
|
||||
|
||||
targets = backend.list_event_targets(user_id=user_id)
|
||||
if person_id:
|
||||
targets = [
|
||||
row
|
||||
for row in targets
|
||||
if str(row.get("person_id") or "").strip() == str(person_id).strip()
|
||||
]
|
||||
|
||||
written = 0
|
||||
for target in targets:
|
||||
target_user_id = int(target.get("user_id") or 0)
|
||||
target_person_id = str(target.get("person_id") or "").strip()
|
||||
if target_user_id <= 0 or not target_person_id:
|
||||
continue
|
||||
baseline_rows = backend.fetch_events(
|
||||
user_id=target_user_id,
|
||||
person_id=target_person_id,
|
||||
since_ts=baseline_since,
|
||||
)
|
||||
if not baseline_rows:
|
||||
continue
|
||||
for window in windows:
|
||||
since_ts = now_ms - (int(window) * 86400000)
|
||||
window_rows = [
|
||||
row
|
||||
for row in baseline_rows
|
||||
if int(row.get("ts") or 0) >= since_ts
|
||||
]
|
||||
metrics = summarize_metrics(window_rows, baseline_rows)
|
||||
for metric, values in metrics.items():
|
||||
backend.upsert_metric(
|
||||
user_id=target_user_id,
|
||||
person_id=target_person_id,
|
||||
window_days=int(window),
|
||||
metric=metric,
|
||||
value_ms=int(values.get("value_ms") or 0),
|
||||
baseline_ms=int(values.get("baseline_ms") or 0),
|
||||
z_score=float(values.get("z_score") or 0.0),
|
||||
sample_n=int(values.get("sample_n") or 0),
|
||||
computed_at=now_ms,
|
||||
)
|
||||
written += 1
|
||||
return written
|
||||
|
||||
def handle(self, *args, **options):
|
||||
once = bool(options.get("once"))
|
||||
sleep_seconds = max(1.0, float(options.get("sleep_seconds") or 60.0))
|
||||
user_id = options.get("user_id")
|
||||
person_id = str(options.get("person_id") or "").strip()
|
||||
window_days = list(options.get("window_days") or [1, 7, 30, 90])
|
||||
|
||||
while True:
|
||||
written = self._run_cycle(
|
||||
user_id=user_id,
|
||||
person_id=person_id,
|
||||
window_days=window_days,
|
||||
)
|
||||
self.stdout.write(f"gia-analysis wrote={written}")
|
||||
if once:
|
||||
return
|
||||
time.sleep(sleep_seconds)
|
||||
Reference in New Issue
Block a user