Initial commit: CMM Report Analyzer
FastAPI app that parses CMM inspection reports (PDF/Excel/CSV), computes SPC metrics (Cp/Cpk/Pp/Ppk, control limits, Shapiro-Wilk), generates interactive Plotly charts, and provides AI-powered quality summaries via Azure OpenAI with graceful fallback. Includes 21 passing tests covering parsers, SPC calculations, and API endpoints.
This commit is contained in:
46
app/routers/upload.py
Normal file
46
app/routers/upload.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, HTTPException, UploadFile
|
||||
|
||||
from app.config import settings
|
||||
from app.services.batch import process_batch
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
ALLOWED_EXTENSIONS = {".pdf", ".xlsx", ".xls", ".csv"}
|
||||
|
||||
|
||||
@router.post("/upload")
|
||||
async def upload_files(files: list[UploadFile]):
|
||||
if not files:
|
||||
raise HTTPException(400, "No files provided")
|
||||
|
||||
saved: list[Path] = []
|
||||
tmp_dir = Path(tempfile.mkdtemp(prefix="cmm_"))
|
||||
|
||||
for f in files:
|
||||
if not f.filename:
|
||||
continue
|
||||
ext = Path(f.filename).suffix.lower()
|
||||
if ext not in ALLOWED_EXTENSIONS:
|
||||
raise HTTPException(
|
||||
400, f"Unsupported file type: {ext}. Allowed: {ALLOWED_EXTENSIONS}"
|
||||
)
|
||||
size = 0
|
||||
dest = tmp_dir / f.filename
|
||||
with open(dest, "wb") as out:
|
||||
while chunk := await f.read(1024 * 64):
|
||||
size += len(chunk)
|
||||
if size > settings.max_upload_mb * 1024 * 1024:
|
||||
raise HTTPException(400, f"File too large (max {settings.max_upload_mb} MB)")
|
||||
out.write(chunk)
|
||||
saved.append(dest)
|
||||
|
||||
if not saved:
|
||||
raise HTTPException(400, "No valid files uploaded")
|
||||
|
||||
batch_id = await process_batch(saved)
|
||||
return {"batch_id": batch_id, "file_count": len(saved)}
|
||||
Reference in New Issue
Block a user