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:
@@ -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,6 +41,11 @@ def _confirm_submission_to_paperless(current: dict) -> dict:
|
||||
if value:
|
||||
fields[optional_key] = value
|
||||
artifact_kind = handoff.get("artifact_kind") or "document"
|
||||
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)
|
||||
@@ -67,6 +73,17 @@ def _confirm_submission_to_paperless(current: dict) -> dict:
|
||||
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": False,
|
||||
"status_code": 500,
|
||||
"mode": "direct-paperless" if artifact_kind == "source_bundle" else "webhook",
|
||||
"error": error_detail,
|
||||
}
|
||||
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
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
{% if request.query_params.get('submitted') %}
|
||||
<section class="card form-card notice-card success-card evidence-board">
|
||||
<div class="case-legend"><span class="section-label">Submission packet</span><span class="pill">Filed</span></div>
|
||||
<h2>Submission Created</h2>
|
||||
{% set paperless_status = request.query_params.get('paperless_status', 'uploaded-local') %}
|
||||
<section class="card form-card notice-card {{ 'success-card' if paperless_status == 'paperless-queued' else '' }} evidence-board">
|
||||
<div class="case-legend"><span class="section-label">Submission packet</span><span class="pill">{{ 'Filed' if paperless_status == 'paperless-queued' else 'Saved locally' }}</span></div>
|
||||
<h2>{{ 'Submission Created' if paperless_status == 'paperless-queued' else 'Submission Saved, Archive Pending' }}</h2>
|
||||
<p>Submission {{ request.query_params.get('submitted') }} was created successfully.</p>
|
||||
<p class="muted">Artifact: {{ request.query_params.get('artifact_kind', 'document') }} · Files: {{ request.query_params.get('file_count', '1') }}</p>
|
||||
<p class="muted">Paperless status: {{ request.query_params.get('paperless_status', 'uploaded-local') }}</p>
|
||||
<p class="muted">Paperless status: {{ paperless_status }}</p>
|
||||
{% if paperless_status != 'paperless-queued' %}
|
||||
<p class="muted">Schoolhouse kept the submission locally, but the archive handoff failed this time.</p>
|
||||
{% if request.query_params.get('paperless_error') %}
|
||||
<p class="muted">Error: {{ request.query_params.get('paperless_error') }}</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
<section class="card form-card evidence-board">
|
||||
|
||||
Reference in New Issue
Block a user