add submit_filing(): 3x web retry then fax fallback, fix success detection, shared attestation helper

This commit is contained in:
justin 2026-05-30 18:58:14 -05:00
parent 4a4bbd048e
commit b90d443667
2 changed files with 162 additions and 37 deletions

View file

@ -174,6 +174,141 @@ async def poll_fax_status(log_id: str | int, timeout: int = 300, interval: int =
}
async def submit_filing(
original_pdf_path: str,
pdf_url: str = "",
photo_id_path: str | None = None,
order_number: str = "",
dot_number: str = "",
mc_number: str = "",
entity_name: str = "",
document_type: str = "MCS-150 Biennial Update",
recipient_name: str = "Federal Motor Carrier Safety Administration (FMCSA)",
web_retries: int = 3,
web_retry_interval: int = 600,
) -> dict:
"""Submit a filing to FMCSA: try web submission first, fall back to fax.
Attempts electronic submission via ask.fmcsa.dot.gov up to `web_retries`
times, waiting `web_retry_interval` seconds between attempts. If all
web attempts fail, falls back to faxing to FMCSA at 202-366-3477.
After successful submission (either method), generates an attestation
cover page with digital signature and prepends it to the original PDF.
Returns:
dict with keys: success, method, attested_pdf, submitted_at,
screenshot_path, fax_log_id, error
"""
from scripts.workers.services.fmcsa_web_submitter import submit_mcs150
# ── Phase 1: Try web submission (up to 3 attempts, 10 min apart) ──
for attempt in range(1, web_retries + 1):
LOG.info("[filing] Web submission attempt %d/%d for DOT %s",
attempt, web_retries, dot_number)
web_result = await submit_mcs150(
pdf_path=original_pdf_path,
photo_id_path=photo_id_path,
dot_number=dot_number,
mc_number=mc_number,
entity_name=entity_name,
)
if web_result["success"]:
LOG.info("[filing] Web submission succeeded for DOT %s on attempt %d",
dot_number, attempt)
# Generate attestation
attested = await _generate_attestation(
original_pdf_path=original_pdf_path,
order_number=order_number,
dot_number=dot_number,
entity_name=entity_name,
document_type=document_type,
recipient_name=recipient_name,
submitted_at=datetime.fromisoformat(web_result["submitted_at"]),
)
return {
"success": True,
"method": "web",
"attested_pdf": attested,
"submitted_at": web_result["submitted_at"],
"screenshot_path": web_result.get("screenshot_path")
or web_result.get("pre_submit_screenshot"),
"fax_log_id": None,
"error": None,
}
LOG.warning("[filing] Web attempt %d failed: %s", attempt, web_result["error"])
if attempt < web_retries:
LOG.info("[filing] Waiting %ds before retry...", web_retry_interval)
await asyncio.sleep(web_retry_interval)
# ── Phase 2: Fall back to fax ──
LOG.info("[filing] All %d web attempts failed, falling back to fax for DOT %s",
web_retries, dot_number)
if not pdf_url:
return {
"success": False,
"method": "none",
"attested_pdf": None,
"submitted_at": None,
"screenshot_path": None,
"fax_log_id": None,
"error": "Web submission failed and no PDF URL provided for fax fallback",
}
fax_result = await send_and_attest(
pdf_url=pdf_url,
original_pdf_path=original_pdf_path,
order_number=order_number,
dot_number=dot_number,
entity_name=entity_name,
document_type=document_type,
recipient_name=recipient_name,
)
return {
"success": fax_result["success"],
"method": "fax" if fax_result["success"] else "none",
"attested_pdf": fax_result.get("attested_pdf"),
"submitted_at": fax_result.get("submitted_at"),
"screenshot_path": None,
"fax_log_id": fax_result.get("fax_log_id"),
"error": fax_result.get("error"),
}
async def _generate_attestation(
original_pdf_path: str,
order_number: str,
dot_number: str,
entity_name: str,
document_type: str,
recipient_name: str,
submitted_at: datetime,
) -> str | None:
"""Generate attestation cover page and prepend to original PDF."""
try:
from scripts.document_gen.templates.filing_attestation import (
generate_attestation_page,
prepend_attestation,
)
attest_pdf = generate_attestation_page(
order_number=order_number,
dot_number=dot_number,
entity_name=entity_name,
document_type=document_type,
submitted_at=submitted_at,
recipient_name=recipient_name,
)
return prepend_attestation(attest_pdf, original_pdf_path)
except Exception as exc:
LOG.error("[filing] Attestation generation failed: %s", exc)
return None
async def send_and_attest(
pdf_url: str,
original_pdf_path: str,
@ -216,39 +351,20 @@ async def send_and_attest(
# 3. Fax delivered — generate attestation
submitted_at = datetime.now(timezone.utc)
attested = await _generate_attestation(
original_pdf_path=original_pdf_path,
order_number=order_number,
dot_number=dot_number,
entity_name=entity_name,
document_type=document_type,
recipient_name=recipient_name,
submitted_at=submitted_at,
)
try:
from scripts.document_gen.templates.filing_attestation import (
generate_attestation_page,
prepend_attestation,
)
attest_pdf = generate_attestation_page(
order_number=order_number,
dot_number=dot_number,
entity_name=entity_name,
document_type=document_type,
submitted_at=submitted_at,
recipient_name=recipient_name,
)
combined_pdf = prepend_attestation(attest_pdf, original_pdf_path)
LOG.info("[fax] Attested copy generated: %s", combined_pdf)
return {
"success": True,
"attested_pdf": combined_pdf,
"fax_log_id": log_id,
"submitted_at": submitted_at.isoformat(),
"error": None,
}
except Exception as exc:
LOG.error("[fax] Attestation generation failed: %s", exc)
return {
"success": True, # Fax still succeeded
"attested_pdf": None,
"fax_log_id": log_id,
"submitted_at": submitted_at.isoformat(),
"error": f"Fax sent but attestation failed: {exc}",
}
return {
"success": True,
"attested_pdf": attested,
"fax_log_id": log_id,
"submitted_at": submitted_at.isoformat(),
"error": None if attested else "Fax sent but attestation generation failed",
}

View file

@ -229,10 +229,19 @@ async def submit_mcs150(
confirmation_text = await page.inner_text("body")
# Check for success indicators
success = any(kw in confirmation_text.lower() for kw in [
# Note: after successful submission, FMCSA redirects to www.fmcsa.dot.gov
# which may return 403 (Akamai WAF). The submission still went through —
# FMCSA sends a confirmation email to the address we provided.
# So a redirect away from ask.fmcsa.dot.gov IS a success signal.
redirected_away = "ask.fmcsa.dot.gov" not in page.url
has_confirmation_keywords = any(kw in confirmation_text.lower() for kw in [
"thank you", "submitted", "received", "confirmation",
"reference number", "ticket", "case"
"reference number", "ticket", "case",
])
has_error_keywords = any(kw in confirmation_text.lower() for kw in [
"please select an item", "required field", "invalid",
])
success = (redirected_away or has_confirmation_keywords) and not has_error_keywords
LOG.info("[fmcsa] Submission %s for DOT %s",
"succeeded" if success else "may have failed", dot_number)