98 lines
3.9 KiB
Python
98 lines
3.9 KiB
Python
# -*- 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"])} 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())
|