feat(import): add real-time progress tracking for CSV imports
This commit is contained in:
@@ -1639,6 +1639,108 @@ async def get_import_progress(
|
||||
}
|
||||
|
||||
|
||||
@router.get("/current-batch")
|
||||
async def get_current_batch(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Return the most recent running batch import for the current user, if any."""
|
||||
try:
|
||||
row = (
|
||||
db.query(ImportAudit)
|
||||
.filter(ImportAudit.status == "running")
|
||||
.filter(ImportAudit.initiated_by_user_id == getattr(current_user, "id", None))
|
||||
.order_by(ImportAudit.started_at.desc())
|
||||
.first()
|
||||
)
|
||||
if not row:
|
||||
return {"running": False}
|
||||
return {
|
||||
"running": True,
|
||||
"audit_id": row.id,
|
||||
"started_at": row.started_at.isoformat() if row.started_at else None,
|
||||
"total_files": row.total_files,
|
||||
"message": row.message,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get current batch: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/batch-progress/{audit_id}")
|
||||
async def get_batch_progress(
|
||||
audit_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Return real-time progress for a batch import using audit tables as the source of truth."""
|
||||
audit = db.query(ImportAudit).filter(ImportAudit.id == audit_id).first()
|
||||
if not audit:
|
||||
raise HTTPException(status_code=404, detail="Batch not found")
|
||||
|
||||
# Authorization: allow only the initiating user or admins to view progress
|
||||
try:
|
||||
from app.utils.enhanced_auth import is_admin_user
|
||||
is_admin = is_admin_user(current_user)
|
||||
except Exception:
|
||||
is_admin = False
|
||||
if not is_admin and getattr(current_user, "id", None) != getattr(audit, "initiated_by_user_id", None):
|
||||
raise HTTPException(status_code=403, detail="Not authorized to view this batch progress")
|
||||
|
||||
# Aggregate per-file results to compute progress
|
||||
processed_files = db.query(ImportAuditFile).filter(ImportAuditFile.audit_id == audit.id).count()
|
||||
successful_files = db.query(ImportAuditFile).filter(
|
||||
ImportAuditFile.audit_id == audit.id,
|
||||
ImportAuditFile.status.in_(["success", "completed_with_errors", "skipped"])
|
||||
).count()
|
||||
failed_files = db.query(ImportAuditFile).filter(
|
||||
ImportAuditFile.audit_id == audit.id,
|
||||
ImportAuditFile.status == "failed"
|
||||
).count()
|
||||
|
||||
total_files = audit.total_files or 0
|
||||
percent_complete: float = 0.0
|
||||
if total_files > 0:
|
||||
try:
|
||||
percent_complete = round((processed_files / total_files) * 100, 1)
|
||||
except Exception:
|
||||
percent_complete = 0.0
|
||||
|
||||
data = {
|
||||
"audit_id": audit.id,
|
||||
"status": audit.status,
|
||||
"total_files": total_files,
|
||||
"processed_files": processed_files,
|
||||
"successful_files": successful_files,
|
||||
"failed_files": failed_files,
|
||||
"started_at": audit.started_at.isoformat() if audit.started_at else None,
|
||||
"finished_at": audit.finished_at.isoformat() if audit.finished_at else None,
|
||||
"percent": percent_complete,
|
||||
"message": audit.message,
|
||||
}
|
||||
|
||||
# Include a brief summary of last processed file if desired (best-effort)
|
||||
try:
|
||||
last_file = (
|
||||
db.query(ImportAuditFile)
|
||||
.filter(ImportAuditFile.audit_id == audit.id)
|
||||
.order_by(ImportAuditFile.id.desc())
|
||||
.first()
|
||||
)
|
||||
if last_file:
|
||||
data["last_file"] = {
|
||||
"file_type": last_file.file_type,
|
||||
"status": last_file.status,
|
||||
"imported_count": last_file.imported_count,
|
||||
"errors": last_file.errors,
|
||||
"message": last_file.message,
|
||||
"created_at": last_file.created_at.isoformat() if last_file.created_at else None,
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@router.post("/batch-validate")
|
||||
async def batch_validate_csv_files(
|
||||
files: List[UploadFile] = UploadFileForm(...),
|
||||
|
||||
Reference in New Issue
Block a user