Gov_Workflow_RL / app /state_machine.py
Siddharaj Shirke
deploy: clean code-only snapshot for HF Space
df97e68
"""
state_machine.py — Gov Workflow OpenEnv
Deterministic workflow transition engine aligned with Phase 1 schemas.
"""
from __future__ import annotations
from app.models import ApplicationCase, InternalSubstate, StageType
INTERNAL_TO_PUBLIC_STAGE: dict[InternalSubstate, StageType] = {
InternalSubstate.PRE_SCRUTINY: StageType.SUBMISSION,
InternalSubstate.DOC_VALIDATION: StageType.DOCUMENT_VERIFICATION,
InternalSubstate.SERVICE_SPECIFIC_VALIDATION: StageType.DOCUMENT_VERIFICATION,
InternalSubstate.FIELD_VERIFICATION_PENDING: StageType.FIELD_VERIFICATION,
InternalSubstate.DECISION_PENDING: StageType.APPROVAL,
InternalSubstate.ISSUANCE_READY: StageType.ISSUANCE,
InternalSubstate.BLOCKED_MISSING_DOCS: StageType.DOCUMENT_VERIFICATION,
InternalSubstate.COMPLETED: StageType.ISSUANCE,
InternalSubstate.REJECTED: StageType.APPROVAL,
}
def build_public_stage(substate: InternalSubstate) -> StageType:
return INTERNAL_TO_PUBLIC_STAGE.get(substate, StageType.SUBMISSION)
def transition_case(case: ApplicationCase, new_substate: InternalSubstate) -> None:
case.internal_substate = new_substate
case.public_stage = build_public_stage(new_substate)
case.days_in_current_stage = 0
def can_advance(case: ApplicationCase) -> bool:
if case.completed or case.rejected:
return False
if case.internal_substate == InternalSubstate.BLOCKED_MISSING_DOCS:
return False
return True
def advance_case(case: ApplicationCase, rng: object = None) -> tuple[bool, bool]:
"""
Returns (progressed, completed).
"""
if not can_advance(case):
return False, False
early_stages = {
InternalSubstate.PRE_SCRUTINY,
InternalSubstate.DOC_VALIDATION,
}
if case.has_missing_docs and case.internal_substate in early_stages:
transition_case(case, InternalSubstate.BLOCKED_MISSING_DOCS)
return True, False
current = case.internal_substate
if current == InternalSubstate.PRE_SCRUTINY:
transition_case(case, InternalSubstate.DOC_VALIDATION)
return True, False
if current == InternalSubstate.DOC_VALIDATION:
if case.field_verification_required:
transition_case(case, InternalSubstate.FIELD_VERIFICATION_PENDING)
else:
transition_case(case, InternalSubstate.DECISION_PENDING)
return True, False
if current == InternalSubstate.SERVICE_SPECIFIC_VALIDATION:
if case.field_verification_required:
transition_case(case, InternalSubstate.FIELD_VERIFICATION_PENDING)
else:
transition_case(case, InternalSubstate.DECISION_PENDING)
return True, False
if current == InternalSubstate.FIELD_VERIFICATION_PENDING:
return False, False
if current == InternalSubstate.DECISION_PENDING:
transition_case(case, InternalSubstate.ISSUANCE_READY)
return True, False
if current == InternalSubstate.ISSUANCE_READY:
transition_case(case, InternalSubstate.COMPLETED)
case.completed = True
return True, True
return False, False
def unblock_missing_docs(case: ApplicationCase) -> bool:
if case.internal_substate != InternalSubstate.BLOCKED_MISSING_DOCS:
return False
case.has_missing_docs = False
case.doc_resolution_day = None
transition_case(case, InternalSubstate.DOC_VALIDATION)
return True
def complete_field_verification(case: ApplicationCase) -> bool:
if case.internal_substate != InternalSubstate.FIELD_VERIFICATION_PENDING:
return False
case.field_verification_completion_day = None
transition_case(case, InternalSubstate.DECISION_PENDING)
return True