"""Trainee loop: win/lose board, weak-area analysis, user-generated personas. A user never re-chats a persona. To keep training, they generate new personas — either auto from their weak areas ("lock") or from a manual form. Generated personas are private to the user. """ from __future__ import annotations import datetime from pathlib import Path from typing import Any from ..storage.store import JsonStore, new_id from .store import ensure_persona_shape class MyPersonaStore: def __init__(self, data_dir: Path) -> None: self.personas = JsonStore(data_dir / "my_personas") def _path_key(self, user_id: str, pid: str) -> str: return f"{user_id}__{pid}" def create(self, *, user_id: str, persona: dict[str, Any]) -> dict[str, Any]: p = ensure_persona_shape(persona) if "id" not in p or not p["id"]: p["id"] = new_id("myp") record = { "key": self._path_key(user_id, p["id"]), "user_id": user_id, "persona": p, "created_at": datetime.datetime.now(datetime.timezone.utc).isoformat(), } return self.personas.create(record, key=record["key"]) def list_for(self, user_id: str) -> list[dict[str, Any]]: return [ r.get("persona") for r in self.personas.where(lambda x: x.get("user_id") == user_id) ] def analyze_weak_areas(sessions: list[dict[str, Any]]) -> dict[str, Any]: """Summarize which persona attributes a user tends to lose against.""" losses, wins = [], [] for ses in sessions: if ses.get("outcome") == "won": wins.append(ses) elif ses.get("outcome") == "lost": losses.append(ses) def tally(key: str, label: str) -> list[dict[str, Any]]: from collections import Counter c = Counter() for l in losses: meta = l.get("persona_meta") or {} v = meta.get(key) if v is not None: c[v] += 1 return [{"value": k, "losses": v} for k, v in c.most_common(3)] return { "total_sessions": len(sessions), "wins": len(wins), "losses": len(losses), "by_tier": tally("tier", "tier"), "by_initiation": tally("initiation_mode", "initiation"), "by_channel": tally("channel", "channel"), "top_loss_personas": [ { "persona_id": l.get("persona_id"), "persona_name": l.get("persona_name"), "score": (l.get("debrief") or {}).get("score", 0), "why": (l.get("debrief") or {}).get("why", ""), } for l in sorted( losses, key=lambda x: (x.get("debrief") or {}).get("score", 0) )[:5] ], }