from __future__ import annotations from typing import Any VALID_TYPES = {"first_submission", "revised_submission", "final_submission", "model_only_review"} def normalize_submission_type(value: str) -> str: key = str(value or "first_submission").strip().lower().replace(" ", "_") aliases = { "first": "first_submission", "revision": "revised_submission", "revised": "revised_submission", "final": "final_submission", "model": "model_only_review", } key = aliases.get(key, key) if key not in VALID_TYPES: raise ValueError(f"submission_type must be one of {sorted(VALID_TYPES)}") return key def _missing(checks: list[dict[str, Any]], stages: set[str]) -> list[dict[str, Any]]: return [ row for row in checks if row.get("status") == "Missing" and row.get("stage") in stages ] def readiness_register( *, submission_type: str, has_model: bool, has_results: bool, has_pcswmm_package: bool, has_revision: bool, has_city_comments: bool, response_matrix: list[dict[str, Any]] | None = None, certified_checklist_attached: bool = False, no_objections_letter_attached: bool = False, prior_approved_report_attached: bool = False, correspondence_appendix_attached: bool = False, clean_copy: bool = False, ) -> dict[str, Any]: """Return draft, engineer-review, and final-submission readiness. The professional engineer-certified checklist is required for final municipal submission, but it does not block deterministic draft generation. """ stype = normalize_submission_type(submission_type) checks: list[dict[str, Any]] = [ { "check": "SWMM model available", "status": "Complete" if has_model else "Missing", "stage": "draft", }, { "check": "Usable simulation results", "status": "Complete" if has_results else "Missing", "stage": "draft", }, { "check": "Deterministic engineering evidence package", "status": "Complete" if has_pcswmm_package else "Missing", "stage": "draft", }, { "check": "Professional engineer-certified SWMR checklist", "status": "Complete" if certified_checklist_attached else "Missing", "stage": "submission", }, ] if stype in {"revised_submission", "final_submission"}: checks.extend([ { "check": "Baseline-to-revised comparison", "status": "Complete" if has_revision else "Missing", "stage": "engineer_review", }, { "check": "City review comments", "status": "Complete" if has_city_comments else "Missing", "stage": "engineer_review", }, ]) matrix = response_matrix or [] unresolved = sum( 1 for row in matrix if row.get("status") not in {"Resolved", "Not applicable"} ) checks.append({ "check": "Comment response closure", "status": "Complete" if matrix and unresolved == 0 else f"Review ({unresolved} unresolved)", "stage": "submission", }) if stype == "final_submission": checks.extend([ { "check": "Clean report copy without mark-ups", "status": "Complete" if clean_copy else "Missing", "stage": "submission", }, { "check": "No Objections letter as first page", "status": "Complete" if no_objections_letter_attached else "Missing", "stage": "submission", }, { "check": "Relevant correspondence appendix", "status": "Complete" if correspondence_appendix_attached else "Missing", "stage": "submission", }, ]) if has_revision: checks.append({ "check": "Previously approved report included as appendix", "status": "Complete" if prior_approved_report_attached else "Missing", "stage": "submission", }) draft_blocking = _missing(checks, {"draft"}) review_blocking = _missing(checks, {"draft", "engineer_review"}) submission_blocking = _missing(checks, {"draft", "engineer_review", "submission"}) ready_for_draft = not draft_blocking ready_for_engineer_review = not review_blocking ready_for_submission = not submission_blocking if ready_for_submission: overall_status = "SUBMISSION READY" elif ready_for_engineer_review: overall_status = "ENGINEER-REVIEW READY" elif ready_for_draft: overall_status = "DRAFT READY" else: overall_status = "NOT READY" return { "submission_type": stype, "ready_for_draft": ready_for_draft, "ready_for_engineer_review": ready_for_engineer_review, "ready_for_submission": ready_for_submission, "status": overall_status, "checks": checks, "draft_blocking_items": [row["check"] for row in draft_blocking], "engineer_review_blocking_items": [row["check"] for row in review_blocking], "submission_blocking_items": [row["check"] for row in submission_blocking], # Backward compatibility for clients expecting blocking_items. "blocking_items": [row["check"] for row in submission_blocking], "disclaimer": ( "Draft and engineer-review readiness indicate workflow completeness only. " "Final submission requires responsible professional authentication and City acceptance." ), }