"""compat API — /api/saju/compat/* 5 endpoints.""" from fastapi import APIRouter, HTTPException from typing import Optional from ..models import ( CompatInterpretRequest, CompatInterpretResponse, CompatPatchRequest, ) from ..interpret import pipeline from ..calculator.core import calculate_saju from ..calculator.analysis import perform_full_analysis from ..calculator.compatibility import calculate_compatibility from ..calculator.lunar import lunar_to_solar from .. import db as db_module router = APIRouter(prefix="/api/saju/compat") def _calc_one(p) -> tuple[dict, dict]: """한 사람의 입력 → (saju, analysis).""" if p.calendar_type == "lunar": sy, sm, sd = lunar_to_solar(p.year, p.month, p.day, p.is_leap_month) else: sy, sm, sd = p.year, p.month, p.day saju = calculate_saju(sy, sm, sd, p.hour, p.gender) analysis = perform_full_analysis(saju, 2026) return saju, analysis @router.post("/interpret", response_model=CompatInterpretResponse) async def interpret_compat_endpoint(req: CompatInterpretRequest): try: saju_a, analysis_a = _calc_one(req.person_a) saju_b, analysis_b = _calc_one(req.person_b) compat = calculate_compatibility(saju_a, saju_b) except Exception as e: raise HTTPException(status_code=400, detail=f"계산 실패: {e}") try: interp_result = await pipeline.interpret_compat( saju_a, saju_b, analysis_a, analysis_b, compat["score"], compat["breakdown"], ) except pipeline.SajuError as e: raise HTTPException(status_code=500, detail=str(e)) from e rid = db_module.save_compat_record({ "person_a": req.person_a.model_dump(), "person_b": req.person_b.model_dump(), "saju_a": saju_a, "saju_b": saju_b, "score": compat["score"], "breakdown": compat["breakdown"], "interpretation_json": interp_result["interpretation_json"], "model": interp_result["model"], "tokens_in": interp_result["tokens_in"], "tokens_out": interp_result["tokens_out"], "cost_usd": interp_result["cost_usd"], "latency_ms": interp_result["latency_ms"], "reroll_count": interp_result["reroll_count"], }) return { "saju_a": saju_a, "saju_b": saju_b, "score": compat["score"], "breakdown": compat["breakdown"], "interpretation_json": interp_result["interpretation_json"], "reading_id": rid, "model": interp_result["model"], "tokens_in": interp_result["tokens_in"], "tokens_out": interp_result["tokens_out"], "cost_usd": interp_result["cost_usd"], "latency_ms": interp_result["latency_ms"], "reroll_count": interp_result["reroll_count"], } @router.get("/readings") async def list_readings(page: int = 1, size: int = 20, favorite: Optional[bool] = None): return db_module.list_compat_records(page=page, size=size, favorite=favorite) @router.get("/readings/{reading_id}") async def get_reading(reading_id: int): row = db_module.get_compat_record(reading_id) if not row: raise HTTPException(status_code=404, detail="reading not found") return row @router.patch("/readings/{reading_id}") async def patch_reading(reading_id: int, req: CompatPatchRequest): row = db_module.get_compat_record(reading_id) if not row: raise HTTPException(status_code=404, detail="reading not found") db_module.update_compat_record(reading_id, **req.model_dump(exclude_none=True)) return {"ok": True} @router.delete("/readings/{reading_id}") async def delete_reading(reading_id: int): row = db_module.get_compat_record(reading_id) if not row: raise HTTPException(status_code=404, detail="reading not found") db_module.delete_compat_record(reading_id) return {"ok": True}