Phase 1: FastAPI backend with async job model
- backend/app: FastAPI API wrapping the CAD skill modules - upload -> job -> poll -> model / BOM / artifacts -> geometry query - SQLite via SQLModel (Model, Job, BomRow, QueryLog) - ThreadPoolExecutor worker, serialized, with live stage updates - docker-compose.yml: dev server (mounts source, --reload) on :8000 - api-test.sh: end-to-end live validation script - requirements.txt: add fastapi, uvicorn, python-multipart, sqlmodel - external_diagram.py: port active-area detection OCC.Core -> OCP - .gitignore, PHASE1.md Validated live: MR16 round-trip passes (28 BOM rows, 12 artifacts, bounding-box query, xlsx download; active-area detection working). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# Phase 1 live validation — exercises the API end to end against a running server
|
||||
# (docker compose up). Upload a sample, poll the job, read model detail, download
|
||||
# an artifact, run a geometry query.
|
||||
#
|
||||
# docker compose up -d && ./api-test.sh
|
||||
set -uo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
BASE="${BASE:-http://localhost:8000}"
|
||||
SAMPLE="${SAMPLE:-skill.src/MR16s Gen1_EN.step}"
|
||||
PYJ() { python3 -c "import sys,json$1"; } # tiny JSON helper
|
||||
|
||||
echo "== health =="
|
||||
curl -fsS "$BASE/api/health"; echo
|
||||
|
||||
echo "== upload ($SAMPLE) =="
|
||||
resp=$(curl -fsS -F "file=@${SAMPLE}" -F "diagram=true" "$BASE/api/upload")
|
||||
echo "$resp"
|
||||
job_id=$(PYJ ";print(json.load(sys.stdin)['job_id'])" <<<"$resp")
|
||||
model_id=$(PYJ ";print(json.load(sys.stdin)['model_id'])" <<<"$resp")
|
||||
|
||||
echo "== poll job $job_id =="
|
||||
status=""
|
||||
for i in $(seq 1 200); do
|
||||
j=$(curl -fsS "$BASE/api/jobs/$job_id")
|
||||
status=$(PYJ ";print(json.load(sys.stdin)['status'])" <<<"$j")
|
||||
stage=$(PYJ ";print(json.load(sys.stdin).get('stage'))" <<<"$j")
|
||||
echo " [$i] status=$status stage=$stage"
|
||||
[ "$status" = "done" ] && break
|
||||
[ "$status" = "error" ] && { echo "$j"; exit 1; }
|
||||
sleep 3
|
||||
done
|
||||
[ "$status" = "done" ] || { echo "TIMEOUT waiting for job"; exit 1; }
|
||||
|
||||
echo "== model $model_id detail =="
|
||||
detail=$(curl -fsS "$BASE/api/models/$model_id")
|
||||
echo "$detail" | PYJ "
|
||||
d=json.load(sys.stdin); m=d['model']
|
||||
print(' backend:', m['backend'], '| faces:', m['face_count'], '| parts:', m['part_count'])
|
||||
print(' bbox(mm):', m['bbox_x_mm'], m['bbox_y_mm'], m['bbox_z_mm'], '| chinese:', m['has_chinese'])
|
||||
print(' bom rows:', len(d['bom']))
|
||||
print(' artifacts:', d['artifacts'])
|
||||
"
|
||||
|
||||
echo "== download .xlsx artifact =="
|
||||
art=$(echo "$detail" | PYJ ";print(next((a for a in json.load(sys.stdin)['artifacts'] if a.endswith('.xlsx')),''))")
|
||||
if [ -n "$art" ]; then
|
||||
enc=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1]))" "$art")
|
||||
curl -fsS -o /tmp/api_test_bom.xlsx "$BASE/api/models/$model_id/artifacts/$enc"
|
||||
echo " downloaded '$art' -> $(wc -c < /tmp/api_test_bom.xlsx) bytes"
|
||||
else
|
||||
echo " no .xlsx artifact found"; exit 1
|
||||
fi
|
||||
|
||||
echo "== geometry query =="
|
||||
curl -fsS -X POST -H 'Content-Type: application/json' \
|
||||
-d '{"query":"bounding box"}' "$BASE/api/models/$model_id/query" \
|
||||
| PYJ ";print(json.load(sys.stdin)['result'][:500])"
|
||||
|
||||
echo "== API TEST PASSED =="
|
||||
Reference in New Issue
Block a user