Files
microdao-daarion/services/oneok-calc-adapter/app.py

97 lines
3.3 KiB
Python

import os
from typing import Any, Dict, List, Optional
from fastapi import Depends, FastAPI, Header, HTTPException
API_KEY = os.getenv("ONEOK_ADAPTER_API_KEY", "").strip()
BASE_RATE_PER_M2 = float(os.getenv("ONEOK_BASE_RATE_PER_M2", "3200"))
INSTALL_RATE_PER_M2 = float(os.getenv("ONEOK_INSTALL_RATE_PER_M2", "900"))
CURRENCY = os.getenv("ONEOK_CURRENCY", "UAH")
app = FastAPI(title="1OK Calc Adapter", version="1.0.0")
def _auth(authorization: Optional[str] = Header(default=None)) -> None:
if not API_KEY:
return
expected = f"Bearer {API_KEY}"
if authorization != expected:
raise HTTPException(status_code=401, detail="Unauthorized")
def _normalize_units(payload: Dict[str, Any]) -> List[Dict[str, Any]]:
units = payload.get("window_units")
if isinstance(units, list) and units:
return [u for u in units if isinstance(u, dict)]
# Compatibility alias used by some LLM tool calls.
units = payload.get("windows")
if isinstance(units, list) and units:
return [u for u in units if isinstance(u, dict)]
# fallback to one unit in root payload
if "width_mm" in payload and "height_mm" in payload:
return [payload]
return []
@app.get("/health")
def health() -> Dict[str, Any]:
return {"status": "ok", "service": "oneok-calc-adapter"}
@app.post("/calc/window_quote")
def calc_window_quote(input_payload: Dict[str, Any], _: None = Depends(_auth)) -> Dict[str, Any]:
units = _normalize_units(input_payload)
if not units:
raise HTTPException(status_code=400, detail="window_units/windows or width_mm/height_mm required")
line_items: List[Dict[str, Any]] = []
subtotal = 0.0
for idx, unit in enumerate(units, 1):
width_mm = float(unit.get("width_mm") or 0)
height_mm = float(unit.get("height_mm") or 0)
if width_mm <= 0 or height_mm <= 0:
continue
area_m2 = (width_mm * height_mm) / 1_000_000.0
base = round(area_m2 * BASE_RATE_PER_M2, 2)
install = round(area_m2 * INSTALL_RATE_PER_M2, 2)
total = round(base + install, 2)
subtotal += total
line_items.append(
{
"item": unit.get("label") or f"window_{idx}",
"width_mm": width_mm,
"height_mm": height_mm,
"area_m2": round(area_m2, 3),
"base_price": base,
"install_price": install,
"total": total,
}
)
if not line_items:
raise HTTPException(status_code=400, detail="No valid window units for calculation")
assumptions = [
f"Базова ставка: {BASE_RATE_PER_M2} {CURRENCY}/м2",
f"Монтаж: {INSTALL_RATE_PER_M2} {CURRENCY}/м2",
"ОЦІНКА: без виїзного підтвердженого заміру ціна попередня.",
]
lead_days = 10
if str(input_payload.get("urgency", "")).lower() in {"urgent", "терміново"}:
lead_days = 7
return {
"currency": CURRENCY,
"line_items": line_items,
"totals": {
"subtotal": round(subtotal, 2),
"discount": 0.0,
"grand_total": round(subtotal, 2),
},
"assumptions": assumptions,
"lead_time_if_known": f"{lead_days} днів",
}