fix(schoolhouse): handle Paperless upload failures gracefully

- catch Paperless handoff exceptions during assignment intake
- preserve local submission records when archive handoff fails
- redirect HTML uploads back to the form with readable status details
- show archive-pending messaging instead of a raw 500 page
This commit is contained in:
Fizzlepoof
2026-06-06 02:37:20 +00:00
parent db8dce8c8b
commit 7a67a2febc
2 changed files with 59 additions and 30 deletions

View File

@@ -1,6 +1,7 @@
import hashlib
from pathlib import Path
from typing import Any
from urllib.parse import quote_plus
from fastapi import APIRouter, File, Form, HTTPException, Request, UploadFile
from fastapi.responses import RedirectResponse
@@ -40,33 +41,49 @@ def _confirm_submission_to_paperless(current: dict) -> dict:
if value:
fields[optional_key] = value
artifact_kind = handoff.get("artifact_kind") or "document"
if artifact_kind == "source_bundle":
paperless_response = paperless.upload_source_bundle_receipt(current)
enrichment = paperless.enrich_direct_submission(current, paperless_response)
response: dict[str, Any]
body_obj: dict[str, Any]
ok = False
error_detail = None
try:
if artifact_kind == "source_bundle":
paperless_response = paperless.upload_source_bundle_receipt(current)
enrichment = paperless.enrich_direct_submission(current, paperless_response)
response = {
"ok": True,
"status_code": 200,
"body": paperless_response,
"mode": "direct-paperless",
"enrichment": enrichment,
}
task_id = paperless_response.get("task_id") or paperless_response.get("id")
body_obj = {
"paperless_document_id": enrichment.get("document_id"),
"paperless_title": enrichment.get("title") or paperless.build_submission_title(current),
"paperless_task_id": task_id,
"paperless_metadata": enrichment.get("metadata_payload"),
}
ok = True
else:
if handoff.get("artifact_kind"):
fields["artifact_kind"] = str(handoff["artifact_kind"])
if handoff.get("file_count"):
fields["file_count"] = str(handoff["file_count"])
response = webhooks.post_multipart(handoff["target_url"], fields, "document", file_path)
body = response.get("body", {}) if isinstance(response, dict) else {}
body_obj = body[0] if isinstance(body, list) and body and isinstance(body[0], dict) else (body if isinstance(body, dict) else {})
ok = bool(response.get("ok", False)) if isinstance(response, dict) else False
if not ok:
error_detail = str(response.get("error") or response.get("body") or "Paperless confirmation failed.")
except Exception as exc:
error_detail = f"{type(exc).__name__}: {exc}"
response = {
"ok": True,
"status_code": 200,
"body": paperless_response,
"mode": "direct-paperless",
"enrichment": enrichment,
"ok": False,
"status_code": 500,
"mode": "direct-paperless" if artifact_kind == "source_bundle" else "webhook",
"error": error_detail,
}
task_id = paperless_response.get("task_id") or paperless_response.get("id")
body_obj = {
"paperless_document_id": enrichment.get("document_id"),
"paperless_title": enrichment.get("title") or paperless.build_submission_title(current),
"paperless_task_id": task_id,
"paperless_metadata": enrichment.get("metadata_payload"),
}
ok = True
else:
if handoff.get("artifact_kind"):
fields["artifact_kind"] = str(handoff["artifact_kind"])
if handoff.get("file_count"):
fields["file_count"] = str(handoff["file_count"])
response = webhooks.post_multipart(handoff["target_url"], fields, "document", file_path)
body = response.get("body", {}) if isinstance(response, dict) else {}
body_obj = body[0] if isinstance(body, list) and body and isinstance(body[0], dict) else (body if isinstance(body, dict) else {})
ok = bool(response.get("ok", False)) if isinstance(response, dict) else False
body_obj = {}
updates = {
"status": "paperless-queued" if ok else "paperless-error",
"confirmed_at": store.now(),
@@ -76,6 +93,7 @@ def _confirm_submission_to_paperless(current: dict) -> dict:
"paperless_task_id": body_obj.get("paperless_task_id"),
}
updated = repo.update_submission(str(current.get("id")), updates) or current
updated["paperless_error_detail"] = error_detail
grade_note = None
if ok and updated.get("assignment_id"):
grade_note = paperless.append_grade_note_if_available(str(updated.get("assignment_id")))
@@ -87,6 +105,7 @@ def _confirm_submission_to_paperless(current: dict) -> dict:
"webhook": [response],
"grade_note": grade_note,
"file_count": handoff.get("file_count") or 1,
"error_detail": error_detail,
}
@@ -220,7 +239,10 @@ async def intake_assignment(
"backend": repo.backend,
}
if "text/html" in accept:
redirect_url = "/assignments/upload?submitted=" + str(created["id"]) + "&file_count=" + str(bundle["file_count"]) + "&artifact_kind=" + str(created.get("artifact_kind", "document")) + "&paperless_status=" + str(created.get("status", "uploaded-local"))
status_value = str(created.get("status", "uploaded-local"))
redirect_url = "/assignments/upload?submitted=" + str(created["id"]) + "&file_count=" + str(bundle["file_count"]) + "&artifact_kind=" + str(created.get("artifact_kind", "document")) + "&paperless_status=" + status_value
if auto_confirmed and auto_confirmed.get("error_detail"):
redirect_url += "&paperless_error=" + quote_plus(str(auto_confirmed["error_detail"]))
return RedirectResponse(url=redirect_url, status_code=303)
return response_payload