feat(saju-lab): db.py — saju_records 3 컬럼 추가 (fortune_scores/lucky/monthly_flow) + 4 마이그레이션 테스트

This commit is contained in:
2026-05-26 08:05:41 +09:00
parent 030367da6c
commit f57c790437
2 changed files with 98 additions and 6 deletions

View File

@@ -46,6 +46,17 @@ def init_db() -> None:
CREATE INDEX IF NOT EXISTS idx_saju_created
ON saju_records(created_at DESC)
""")
# 신규 컬럼 ALTER (idempotent — 이미 있으면 OperationalError로 skip)
for col in (
"fortune_scores_json TEXT",
"lucky_json TEXT",
"monthly_flow_json TEXT",
):
try:
conn.execute(f"ALTER TABLE saju_records ADD COLUMN {col}")
except sqlite3.OperationalError:
pass
conn.execute("""
CREATE TABLE IF NOT EXISTS compat_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -76,8 +87,9 @@ def save_saju_record(data: Dict[str, Any]) -> int:
"""INSERT INTO saju_records
(birth_year, birth_month, birth_day, birth_hour, gender, calendar_type,
saju_data, analysis_data, daeun_data, interpretation_json,
model, tokens_in, tokens_out, cost_usd, latency_ms, reroll_count)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
model, tokens_in, tokens_out, cost_usd, latency_ms, reroll_count,
fortune_scores_json, lucky_json, monthly_flow_json)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
(
data["birth_year"], data["birth_month"], data["birth_day"],
data.get("birth_hour"), data["gender"], data.get("calendar_type", "solar"),
@@ -89,6 +101,9 @@ def save_saju_record(data: Dict[str, Any]) -> int:
data.get("tokens_in", 0), data.get("tokens_out", 0),
data.get("cost_usd", 0.0), data.get("latency_ms", 0),
data.get("reroll_count", 0),
json.dumps(data["fortune_scores_json"], ensure_ascii=False) if data.get("fortune_scores_json") else None,
json.dumps(data["lucky_json"], ensure_ascii=False) if data.get("lucky_json") else None,
json.dumps(data["monthly_flow_json"], ensure_ascii=False) if data.get("monthly_flow_json") else None,
),
)
return int(cur.lastrowid)
@@ -138,18 +153,29 @@ def delete_saju_record(record_id: int) -> None:
def _saju_row_to_dict(r) -> Dict[str, Any]:
def _safe_json(val):
if val is None:
return None
try:
return json.loads(val)
except (ValueError, TypeError):
return None
return {
"id": r["id"],
"created_at": r["created_at"],
"birth_year": r["birth_year"], "birth_month": r["birth_month"], "birth_day": r["birth_day"],
"birth_hour": r["birth_hour"], "gender": r["gender"], "calendar_type": r["calendar_type"],
"saju_data": json.loads(r["saju_data"]) if r["saju_data"] else None,
"analysis_data": json.loads(r["analysis_data"]) if r["analysis_data"] else None,
"daeun_data": json.loads(r["daeun_data"]) if r["daeun_data"] else None,
"interpretation_json": json.loads(r["interpretation_json"]) if r["interpretation_json"] else None,
"saju_data": _safe_json(r["saju_data"]),
"analysis_data": _safe_json(r["analysis_data"]),
"daeun_data": _safe_json(r["daeun_data"]),
"interpretation_json": _safe_json(r["interpretation_json"]),
"model": r["model"], "tokens_in": r["tokens_in"], "tokens_out": r["tokens_out"],
"cost_usd": r["cost_usd"], "latency_ms": r["latency_ms"], "reroll_count": r["reroll_count"],
"favorite": int(r["favorite"]), "memo": r["memo"],
"fortune_scores": _safe_json(r["fortune_scores_json"]) if "fortune_scores_json" in r.keys() else None,
"lucky": _safe_json(r["lucky_json"]) if "lucky_json" in r.keys() else None,
"monthly_flow": _safe_json(r["monthly_flow_json"]) if "monthly_flow_json" in r.keys() else None,
}