"""Web API layer — wraps orchestrator with 202+ polling pattern.""" import uuid, json, sys, os from pathlib import Path from datetime import datetime from fastapi import FastAPI, UploadFile, File, Form, HTTPException from fastapi.responses import HTMLResponse, JSONResponse from fastapi.staticfiles import StaticFiles sys.path.insert(0, str(Path(__file__).parent.parent)) from config import Config from orchestrator import run_pipeline app = FastAPI(title="COBOL->Java Verify") BASE = Path(__file__).parent app.mount("/static", StaticFiles(directory=str(BASE / "static")), name="static") TASKS_DIR = Path("tasks"); TASKS_DIR.mkdir(exist_ok=True) UPLOAD_DIR = Path("uploads"); UPLOAD_DIR.mkdir(exist_ok=True) MAX_SIZE = 10 * 1024 * 1024 @app.get("/", response_class=HTMLResponse) async def index(): return (BASE / "templates" / "upload.html").read_text(encoding="utf-8") @app.post("/verify") async def verify( copybook: UploadFile = File(...), cobol_src: UploadFile = File(...), java_src: UploadFile = File(...), mapping: UploadFile = File(...), runner: str = Form("native"), ): task_id = str(uuid.uuid4())[:8] task_dir = UPLOAD_DIR / task_id; task_dir.mkdir(parents=True, exist_ok=True) for f, name in [(copybook,"copybook.cpy"),(cobol_src,"program.cbl"), (java_src,"java"),(mapping,"mapping.yaml")]: content = await f.read() if len(content) > MAX_SIZE: raise HTTPException(413, f"{f.filename} exceeds 10MB") (task_dir / name).write_bytes(content) (TASKS_DIR / f"{task_id}.json").write_text(json.dumps({ "id":task_id,"status":"queued","copybook":str(task_dir/"copybook.cpy"), "cobol_src":str(task_dir/"program.cbl"),"java_src":str(task_dir/"java"), "mapping":str(task_dir/"mapping.yaml"),"runner":runner, "created":datetime.now().isoformat()})) return JSONResponse({"task_id":task_id,"status":"queued"}, status_code=202) @app.get("/status/{task_id}") async def status(task_id: str): tf = TASKS_DIR / f"{task_id}.json" if not tf.exists(): raise HTTPException(404, "task not found") data = json.loads(tf.read_text()) return JSONResponse({"task_id":task_id,"status":data.get("status","unknown"), "result":data.get("result"),"fields":data.get("fields",[])}) @app.get("/fields/{task_id}") async def fields(task_id: str): tf = TASKS_DIR / f"{task_id}.json" if not tf.exists(): raise HTTPException(404, "task not found") data = json.loads(tf.read_text()) return JSONResponse({"task_id":task_id,"fields":data.get("fields",[]), "debug":data.get("debug",{}), "build_log":data.get("build_log","")}) @app.get("/result/{task_id}", response_class=HTMLResponse) async def result(task_id: str): tf = TASKS_DIR / f"{task_id}.json" if not tf.exists(): raise HTTPException(404, "task not found") data = json.loads(tf.read_text()) html = (BASE / "templates" / "result.html").read_text(encoding="utf-8") html = html.replace("{{ task.id }}", data.get("id", task_id)) if data.get("status") == "done" and data.get("result"): r = data["result"] html = html.replace("{{ task.status }}", "done") html = html.replace("{{ task.result.status }}", r.get("status","")) html = html.replace("{{ task.result.program }}", r.get("program","")) html = html.replace("{{ task.result.matched }}", str(r.get("matched",0))) html = html.replace("{{ task.result.mismatched }}", str(r.get("mismatched",0))) html = html.replace("{{ task.result.runner }}", r.get("runner","")) html = html.replace("{{ task.result.duration }}", str(r.get("duration",0))) elif data.get("status") == "error": html = html.replace("{{ task.status }}", "error") html = html.replace("{{ task.result.status }}", data.get("result","")) else: html = html.replace("{{ task.status }}", data.get("status","queued")) return HTMLResponse(html)