diff --git a/lotto/app/db.py b/lotto/app/db.py index f12b188..0093ed5 100644 --- a/lotto/app/db.py +++ b/lotto/app/db.py @@ -259,6 +259,26 @@ def init_db() -> None: """) conn.execute("CREATE INDEX IF NOT EXISTS idx_briefings_draw ON lotto_briefings(draw_no DESC)") + # ── weekly_review 테이블 (큐레이터 자기 평가 + 사용자 패턴 갭) ──────── + conn.execute(""" + CREATE TABLE IF NOT EXISTS weekly_review ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + draw_no INTEGER UNIQUE NOT NULL, + curator_avg_match REAL, + curator_best_tier TEXT, + curator_best_match INTEGER, + curator_5plus_prizes INTEGER, + user_avg_match REAL, + user_best_match INTEGER, + user_5plus_prizes INTEGER, + user_pattern_summary TEXT, + draw_pattern_summary TEXT, + pattern_delta TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now','localtime')) + ) + """) + conn.execute("CREATE INDEX IF NOT EXISTS idx_review_draw ON weekly_review(draw_no DESC)") + @@ -1052,3 +1072,88 @@ def get_curator_usage(days: int = 30) -> Dict[str, Any]: "avg_latency_ms": round(float(r["avg_latency"] or 0), 1), } + +def save_review(data: Dict[str, Any]) -> int: + with _conn() as conn: + cur = conn.execute( + """ + INSERT INTO weekly_review ( + draw_no, + curator_avg_match, curator_best_tier, curator_best_match, curator_5plus_prizes, + user_avg_match, user_best_match, user_5plus_prizes, + user_pattern_summary, draw_pattern_summary, pattern_delta + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(draw_no) DO UPDATE SET + curator_avg_match=excluded.curator_avg_match, + curator_best_tier=excluded.curator_best_tier, + curator_best_match=excluded.curator_best_match, + curator_5plus_prizes=excluded.curator_5plus_prizes, + user_avg_match=excluded.user_avg_match, + user_best_match=excluded.user_best_match, + user_5plus_prizes=excluded.user_5plus_prizes, + user_pattern_summary=excluded.user_pattern_summary, + draw_pattern_summary=excluded.draw_pattern_summary, + pattern_delta=excluded.pattern_delta + """, + ( + data["draw_no"], + data.get("curator_avg_match"), data.get("curator_best_tier"), + data.get("curator_best_match"), data.get("curator_5plus_prizes"), + data.get("user_avg_match"), data.get("user_best_match"), + data.get("user_5plus_prizes"), + data.get("user_pattern_summary"), data.get("draw_pattern_summary"), + data.get("pattern_delta"), + ), + ) + return cur.lastrowid + + +def _review_row(r) -> Optional[Dict[str, Any]]: + if not r: + return None + return { + "id": r["id"], + "draw_no": r["draw_no"], + "curator_avg_match": r["curator_avg_match"], + "curator_best_tier": r["curator_best_tier"], + "curator_best_match": r["curator_best_match"], + "curator_5plus_prizes": r["curator_5plus_prizes"], + "user_avg_match": r["user_avg_match"], + "user_best_match": r["user_best_match"], + "user_5plus_prizes": r["user_5plus_prizes"], + "user_pattern_summary": r["user_pattern_summary"], + "draw_pattern_summary": r["draw_pattern_summary"], + "pattern_delta": r["pattern_delta"], + "created_at": r["created_at"], + } + + +def get_review(draw_no: int) -> Optional[Dict[str, Any]]: + with _conn() as conn: + r = conn.execute("SELECT * FROM weekly_review WHERE draw_no=?", (draw_no,)).fetchone() + return _review_row(r) + + +def get_latest_review() -> Optional[Dict[str, Any]]: + with _conn() as conn: + r = conn.execute("SELECT * FROM weekly_review ORDER BY draw_no DESC LIMIT 1").fetchone() + return _review_row(r) + + +def get_reviews_range(start_drw: int, end_drw: int) -> List[Dict[str, Any]]: + with _conn() as conn: + rows = conn.execute( + "SELECT * FROM weekly_review WHERE draw_no BETWEEN ? AND ? ORDER BY draw_no ASC", + (start_drw, end_drw), + ).fetchall() + return [_review_row(r) for r in rows] + + +def list_reviews(limit: int = 10) -> List[Dict[str, Any]]: + with _conn() as conn: + rows = conn.execute( + "SELECT * FROM weekly_review ORDER BY draw_no DESC LIMIT ?", + (limit,), + ).fetchall() + return [_review_row(r) for r in rows] +