# -*- coding: utf-8 -*- """Astraea form filling — overlay the user's answers onto official WA court forms with pymupdf (no PDF MCP exists; pymupdf is the editor). Field maps are auto-detected from the flat forms and cached in forms/fieldmap.json. """ import json import os import uuid import pymupdf FORMS_DIR = "/opt/astraea/forms" FIELDMAP_PATH = os.path.join(FORMS_DIR, "fieldmap.json") FORM_NAMES = { "fl200": {"name": "Summons (FL Divorce 200)", "file": "fl200.pdf", "description": "Notifies your spouse the divorce case has started."}, "fl201": {"name": "Petition for Dissolution (FL Divorce 201)", "file": "fl201.pdf", "description": "The main form that starts your divorce case."}, "fl001": {"name": "Confidential Information (FL All Family 001)", "file": "fl001.pdf", "description": "Private contact/safety info for the court."}, "fl140": {"name": "Parenting Plan (FL All Family 140)", "file": "fl140.pdf", "description": "Residential schedule and decision-making for your children."}, "wscss": {"name": "Child Support Worksheets (WSCSS)", "file": "wscss.pdf", "description": "Calculates child support under RCW 26.19."}, "fl131": {"name": "Financial Declaration (FL All Family 131)", "file": "fl131.pdf", "description": "Your income, assets, and debts."}, "fl231": {"name": "Findings & Conclusions (FL Divorce 231)", "file": "fl231.pdf", "description": "The court's factual findings."}, "fl241": {"name": "Final Divorce Order (FL Divorce 241)", "file": "fl241.pdf", "description": "The final decree ending your marriage."}, "fl211": {"name": "Response to Petition (FL Divorce 211)", "file": "fl211.pdf", "description": "Your reply to your spouse's petition."}, "fl223": {"name": "Motion for Temporary Order (FL Divorce 223)", "file": "fl223.pdf", "description": "Ask for temporary custody, support, or protection orders."}, } def _load_fieldmap(): try: with open(FIELDMAP_PATH) as f: return json.load(f) except Exception: return {} FIELDMAP = _load_fieldmap() def list_forms(): out = [] for fid, spec in FORM_NAMES.items(): fm = FIELDMAP.get(fid, []) fields = [{"key": f["key"], "label": f.get("label", f["key"]), "type": f.get("type", "text")} for f in fm] out.append({"id": fid, "name": spec["name"], "description": spec["description"], "fields": fields}) return out def fill_form(form_id, values): """Overlay `values` (dict field_key -> text) onto the form, return the filled PDF path.""" spec = FORM_NAMES.get(form_id) fm = FIELDMAP.get(form_id, []) if not spec: return None src = os.path.join(FORMS_DIR, spec["file"]) if not os.path.exists(src): return None fmap = {f["key"]: f for f in fm} doc = pymupdf.open(src) for key, text in values.items(): f = fmap.get(key) if not f or not text: continue page = doc[f.get("page", 0)] if f.get("type") == "checkbox": v = str(text).strip().lower() if v in ("x", "true", "yes", "1", "checked", "check", "on"): x, y = f["x"], f["y"] page.draw_line(pymupdf.Point(x + 1, y + 1), pymupdf.Point(x + 9, y + 9), color=(0, 0, 0), width=1.3) page.draw_line(pymupdf.Point(x + 9, y + 1), pymupdf.Point(x + 1, y + 9), color=(0, 0, 0), width=1.3) else: text = str(text).strip() if not text: continue page.insert_text((f["x"], f["y"]), text, fontsize=f.get("size", 11), fontname="helv", color=(0, 0, 0)) out = os.path.join("/tmp", f"filled_{form_id}_{uuid.uuid4().hex[:8]}.pdf") doc.save(out) doc.close() return out def available_forms(): return list(FORM_NAMES.keys())