main.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. from pathlib import Path
  2. from urllib.parse import urlencode
  3. from urllib.request import Request, urlopen
  4. import json
  5. from fastapi import FastAPI, HTTPException
  6. from fastapi.responses import FileResponse
  7. from fastapi.staticfiles import StaticFiles
  8. app = FastAPI(title="格士立青岛出口本体接口")
  9. ROOT = Path(__file__).resolve().parents[1]
  10. QUERIES = ROOT / "queries"
  11. STATIC = ROOT / "app" / "static"
  12. FUSEKI_ENDPOINT = "http://localhost:3030/gesli/sparql"
  13. app.mount("/static", StaticFiles(directory=STATIC), name="static")
  14. @app.get("/health")
  15. def health():
  16. return {"status": "ok"}
  17. @app.get("/")
  18. def index():
  19. return FileResponse(STATIC / "index.html")
  20. @app.get("/api/orders")
  21. def orders():
  22. return query_file("order-status.rq")
  23. @app.get("/api/risks")
  24. def risks():
  25. return query_file("risk-orders.rq")
  26. @app.get("/api/flow")
  27. def flow():
  28. return query_file("operation-flow.rq")
  29. @app.get("/api/node-times")
  30. def node_times():
  31. return query_file("node-time-status.rq")
  32. @app.get("/api/order-cutoffs")
  33. def order_cutoffs():
  34. return query_file("order-cutoff-times.rq")
  35. @app.get("/api/exception-map")
  36. def exception_map():
  37. return query_file("exception-risk-map.rq")
  38. def query_file(filename: str):
  39. path = QUERIES / filename
  40. if not path.exists():
  41. raise HTTPException(status_code=404, detail=f"Query file not found: {filename}")
  42. return run_sparql(path.read_text(encoding="utf-8"))
  43. def run_sparql(query: str):
  44. data = urlencode({"query": query}).encode("utf-8")
  45. request = Request(
  46. FUSEKI_ENDPOINT,
  47. data=data,
  48. headers={
  49. "Accept": "application/sparql-results+json",
  50. "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
  51. },
  52. method="POST",
  53. )
  54. try:
  55. with urlopen(request, timeout=10) as response:
  56. payload = json.loads(response.read().decode("utf-8"))
  57. except Exception as exc:
  58. raise HTTPException(
  59. status_code=502,
  60. detail="无法连接 Fuseki。请确认 Docker 和 http://localhost:3030/gesli/sparql 正常运行。",
  61. ) from exc
  62. variables = payload.get("head", {}).get("vars", [])
  63. rows = []
  64. for binding in payload.get("results", {}).get("bindings", []):
  65. rows.append({
  66. variable: binding.get(variable, {}).get("value", "")
  67. for variable in variables
  68. })
  69. return {"rows": rows}