""" Deterministic completion evaluation engine. Single source of truth for onboarding completion. """ from typing import Dict, Any, List def field_status(field_val: Any) -> str: """ Returns field status: 'not_collected' | 'reported' | 'confirmed_none' | 'unknown' | 'declined' """ if field_val is None: return "not_collected" if isinstance(field_val, dict): status = field_val.get("status") if status in ["reported", "confirmed_none", "unknown", "declined"]: return status items = field_val.get("items", []) return "reported" if items else "not_collected" if isinstance(field_val, list): return "reported" if len(field_val) > 0 else "not_collected" if isinstance(field_val, str) and field_val.strip(): return "reported" return "not_collected" def check_required_missing(state: Dict[str, Any]) -> List[str]: """ Single source of truth evaluating missing clinical fields for onboarding completion. Required fields: reason_for_visit, symptoms, duration, medical_history, medications, allergies. """ if not isinstance(state, dict): state = {} missing: List[str] = [] # 1. Reason for Visit if field_status(state.get("reason_for_visit")) == "not_collected": missing.append("reason_for_visit") # 2. Symptoms symptoms = state.get("symptoms", []) sym_status = field_status(symptoms) if sym_status == "not_collected": missing.append("symptoms") # 3. Duration has_duration = False if isinstance(symptoms, list) and symptoms: for s in symptoms: if isinstance(s, dict) and s.get("duration"): has_duration = True break if not has_duration and state.get("duration"): has_duration = True if sym_status != "not_collected" and not has_duration: missing.append("duration") # 4. Medical History if field_status(state.get("medical_history")) == "not_collected": missing.append("medical_history") # 5. Medications if field_status(state.get("medications")) == "not_collected": missing.append("medications") # 6. Allergies if field_status(state.get("allergies")) == "not_collected": missing.append("allergies") return missing def is_onboarding_complete(state: Dict[str, Any]) -> bool: """ Returns True ONLY if all required fields are collected or confirmed_none. """ missing = check_required_missing(state) return len(missing) == 0