import json import time from app.healthie import healthie from app.schemas import ModuleData, ModuleSubField, SectionDefinition MOD_TYPE_MAP = { "radio_button": "radio", "radio_buttons": "radio", "check_box": "checkbox", "check_boxes": "checkbox", "checkboxes": "checkbox", "sex": "radio", "text_area": "textarea", "date_picker": "date", "dob": "date", "phone_number": "phone", "email_address": "email", } US_STATES = [ ("AL", "Alabama"), ("AK", "Alaska"), ("AZ", "Arizona"), ("AR", "Arkansas"), ("CA", "California"), ("CO", "Colorado"), ("CT", "Connecticut"), ("DE", "Delaware"), ("FL", "Florida"), ("GA", "Georgia"), ("HI", "Hawaii"), ("ID", "Idaho"), ("IL", "Illinois"), ("IN", "Indiana"), ("IA", "Iowa"), ("KS", "Kansas"), ("KY", "Kentucky"), ("LA", "Louisiana"), ("ME", "Maine"), ("MD", "Maryland"), ("MA", "Massachusetts"), ("MI", "Michigan"), ("MN", "Minnesota"), ("MS", "Mississippi"), ("MO", "Missouri"), ("MT", "Montana"), ("NE", "Nebraska"), ("NV", "Nevada"), ("NH", "New Hampshire"), ("NJ", "New Jersey"), ("NM", "New Mexico"), ("NY", "New York"), ("NC", "North Carolina"), ("ND", "North Dakota"), ("OH", "Ohio"), ("OK", "Oklahoma"), ("OR", "Oregon"), ("PA", "Pennsylvania"), ("RI", "Rhode Island"), ("SC", "South Carolina"), ("SD", "South Dakota"), ("TN", "Tennessee"), ("TX", "Texas"), ("UT", "Utah"), ("VT", "Vermont"), ("VA", "Virginia"), ("WA", "Washington"), ("WV", "West Virginia"), ("WI", "Wisconsin"), ("WY", "Wyoming"), ] MULTI_FIELD_MODULES = { "name": [ {"key": "first", "label": "First name", "required": True}, {"key": "last", "label": "Last name", "required": True}, ], "location": [ {"key": "line1", "label": "Street address line 1", "required": True}, {"key": "line2", "label": "Street address line 2", "required": False}, {"key": "city", "label": "City", "required": True}, {"key": "state", "label": "State", "required": True}, {"key": "zip", "label": "ZIP code", "required": True}, ], } class FormEngine: """Fetches form definition from Healthie, parses into sections.""" def __init__(self): self._sections: list[SectionDefinition] = [] self._last_fetch: float = 0 self._refresh_interval: int = 300 def get_sections(self, refresh_interval: int = 300) -> list[SectionDefinition]: if not self._sections or time.time() - self._last_fetch > refresh_interval: self._refresh(refresh_interval) return self._sections def _refresh(self, refresh_interval: int = 300) -> None: modules = healthie.fetch_form_definition() modules.sort(key=lambda m: float(m.get("position", 0))) self._sections = self._parse_sections(modules) self._last_fetch = time.time() self._refresh_interval = refresh_interval def _parse_sections(self, modules: list[dict]) -> list[SectionDefinition]: """Parse modules into sections delimited by label modules.""" sections: list[SectionDefinition] = [] current: SectionDefinition | None = None self._module_map: dict[str, ModuleData] = {} for mod in modules: meta = self._parse_metadata(mod.get("metadata")) is_label = mod["mod_type"] == "label" or meta.get("section_label") is_agreement = mod["mod_type"] in ("read_only", "agree_to_above") raw_options = mod.get("options") if isinstance(raw_options, list): options = raw_options content_html = None elif isinstance(raw_options, str): stripped = raw_options.strip() if stripped.startswith("<"): options = None content_html = raw_options else: options = [l.strip() for l in stripped.split("\n") if l.strip()] content_html = None else: options = None content_html = None raw_mod_type = mod.get("mod_type", "text") normalized_type = MOD_TYPE_MAP.get(raw_mod_type, raw_mod_type) sub_fields = [] for sf in MULTI_FIELD_MODULES.get(raw_mod_type, []): sub_fields.append(ModuleSubField(**sf)) module_data = ModuleData( id=mod["id"], label=mod.get("label", ""), mod_type=normalized_type, options=options, content_html=content_html, sub_fields=sub_fields, helper_text=mod.get("sublabel"), metadata=meta, required=mod.get("required", False), ) self._module_map[mod["id"]] = module_data if is_label: if current: sections.append(current) current = SectionDefinition( id=len(sections) + 1, label=mod["label"], conditional=meta.get("conditional"), ) continue if current is None: current = SectionDefinition( id=1, label="Introduction", is_agreement=True ) current.modules.append(module_data) if is_agreement: current.is_agreement = True if current: sections.append(current) return sections @staticmethod def _parse_metadata(raw: str | None) -> dict: if not raw: return {} try: data = json.loads(raw) return data.get("access_intake", {}) except (json.JSONDecodeError, TypeError): return {} def get_module(self, module_id: str) -> ModuleData | None: """Find a specific module by ID.""" if not self._sections: self._refresh() return self._module_map.get(module_id) form_engine = FormEngine()