"""FastAPI: two Excel VVs (original + new) → comparison report Excel.""" import logging import os import uuid from pathlib import Path from fastapi import FastAPI, File, HTTPException, UploadFile from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from vv_compare import compare, write_compare_report logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI(title="VV Compare") app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) WORK_DIR = Path(os.getenv("WORK_DIR", "/tmp/vv-compare")) WORK_DIR.mkdir(parents=True, exist_ok=True) jobs: dict[str, dict] = {} @app.get("/") async def root(): return FileResponse("static/index.html") @app.post("/api/compare") async def do_compare( original: UploadFile = File(...), new: UploadFile = File(...), ): for f in (original, new): suffix = Path(f.filename or "").suffix.lower() if suffix not in (".xlsx", ".xlsm"): raise HTTPException(400, "Podporované formáty: .xlsx, .xlsm") job_id = str(uuid.uuid4()) job_dir = WORK_DIR / job_id job_dir.mkdir() orig_path = job_dir / f"original{Path(original.filename).suffix}" new_path = job_dir / f"new{Path(new.filename).suffix}" orig_path.write_bytes(await original.read()) new_path.write_bytes(await new.read()) logger.info("Job %s: original=%s, new=%s", job_id, original.filename, new.filename) try: result = compare(orig_path, new_path) except Exception as exc: logger.exception("Compare failed") raise HTTPException(500, f"Porovnání selhalo: {exc}") jobs[job_id] = { "original_filename": original.filename, "new_filename": new.filename, "job_dir": str(job_dir), "result": result, } return {"job_id": job_id, "result": result} @app.get("/api/report/{job_id}") async def report(job_id: str): if job_id not in jobs: raise HTTPException(404, "Úloha nenalezena") job = jobs[job_id] out_path = Path(job["job_dir"]) / "report.xlsx" write_compare_report( job["result"], job.get("original_filename") or "puvodni.xlsx", job.get("new_filename") or "novy.xlsx", out_path, ) return FileResponse( str(out_path), media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename="Porovnani_VV_puvodni_vs_novy.xlsx", ) @app.get("/health") async def health(): return {"status": "ok"} app.mount("/static", StaticFiles(directory="static"), name="static")