Add structured court selector per jurisdiction - #219
Conversation
Replace the flat court dropdown with a multi-step court selector that
walks a filer through the right questions for each jurisdiction:
- Illinois routes by county using a regex group on the Tyler court list,
then optionally by division (Cook County gets sub-grouped by district).
- Massachusetts asks for department first, then uses MACourts to match a
typed town/address to the courts that serve it; the full list is always
shown alongside as a fallback.
- Vermont asks for Superior Court division, then uses VTCourts to look up
the unit from a town, county, or ZIP; the dropdown stays for those who
know their unit.
New services and files:
- efile/services/court_selection.py -- YAML-driven selector engine; parses
the court_selector block, evaluates when: conditions, filters courts by
name_pattern/exclude_name_pattern, and computes defaults.
- efile/services/court_location.py -- location-to-court matchers for MA
(MACourts) and VT (VTCourts), plus parse_place() for free-text input.
- efile/api/court_selector_views.py -- /dropdowns/court-selector/ endpoint
that serialises the selector state for the active jurisdiction.
- efile/static/js/court-selector.js + css/components/court-selector.css --
progressive-disclosure UI; each step appears only when its when: conditions
are met, and location steps fire the court-lookup API.
Supporting changes:
- is_non_filing_court() extracted from dropdown_views into court_selection
so the same filter applies to both the flat list and the selector.
- EXTRACTION_KEY_ALIASES + _normalized_key() added to extraction_fields so
LLM variants of party-name labels ("plaintiff name", "petitioner names" …)
all resolve to the canonical key.
- State YAML files (illinois, massachusetts, vermont) gain the court_selector
block documented in jurisdiction-config.md.
- Playwright test utilities updated with a selectCourt() helper that drives
the new selector UI; existing tests updated to use it.
There was a problem hiding this comment.
🟡 Changes recommended
The new Playwright guided-court helper can be flaky due to insufficient waiting/step-type handling, and the new git deps are pinned to a moving branch in pyproject, risking non-reproducible installs.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR introduces a YAML-driven, jurisdiction-aware “guided court selector” that replaces the single flat court dropdown with a progressive question flow (including optional place-based matching for MA/VT), adds a dedicated API endpoint to power the selector, and updates E2E/unit tests and partner documentation accordingly.
Changes:
- Added a selector engine (
court_selection) + location matcher layer (court_location) and exposed it viaGET /api/dropdowns/court-selector/. - Integrated the new selector UI into the extraction review + case lookup pages (new JS/CSS, template hooks), while keeping the underlying
<select>as the submitted value. - Updated jurisdiction YAML configs + partner docs, plus expanded unit/E2E test coverage and adjusted Playwright execution to avoid shared-draft conflicts.
File summaries
| File | Description |
|---|---|
| efile_app/uv.lock | Locks new git-based court-lookup dependencies and transitive packages (e.g., shapely). |
| efile_app/pyproject.toml | Adds MACourts/VTCourts dependencies for MA/VT location-based court matching. |
| Dockerfile | Installs git to support git-based Python dependencies during image builds. |
| efile_app/playwright.config.js | Forces serial Playwright execution to prevent shared test account/draft collisions. |
| efile_app/tests/test-utils.js | Adds E2E helpers for guided court selection and conditional step continuation. |
| efile_app/tests/test-optional-services-e2e.spec.js | Updates E2E flow to use the guided selector and new continuation helpers. |
| efile_app/tests/reorganized-filing-matrix.spec.js | Updates filing-matrix E2E flows to use guided court selection and new helpers. |
| efile_app/tests/document-classification-sentinel.spec.js | Updates extraction review assertions to match updated UI/field behavior. |
| efile_app/efile/tests/test_document_extractions.py | Adds tests ensuring party-name key normalization canonicalizes common variants. |
| efile_app/efile/tests/test_court_selection.py | Adds comprehensive unit tests for selector engine, endpoint behavior, and matchers. |
| efile_app/efile/templates/efile/workflow_base.html | Adds global inclusion of the court selector component stylesheet. |
| efile_app/efile/templates/efile/extraction_review.html | Adds selector mount point and loads the selector JS on extraction review. |
| efile_app/efile/templates/efile/case_lookup.html | Adds selector mount point, improves labeling, and loads selector JS on case lookup. |
| efile_app/efile/static/js/extraction-review.js | Mounts guided selector when available; improves handling of non-filing courts. |
| efile_app/efile/static/js/case-lookup.js | Mounts guided selector when available; adds explicit “court required” submit validation. |
| efile_app/efile/static/js/court-selector.js | Implements the progressive guided selector UI driven entirely by the API payload. |
| efile_app/efile/static/css/reorganized-flow.css | Adjusts extraction review layout when the selector is present. |
| efile_app/efile/static/css/components/court-selector.css | Adds component-scoped styles for the guided selector UI. |
| efile_app/efile/static/config/states/illinois.yaml | Adds Illinois court_selector config (county/division/appellate routing). |
| efile_app/efile/static/config/states/massachusetts.yaml | Adds Massachusetts court_selector config + court renaming rules + place matcher. |
| efile_app/efile/static/config/states/vermont.yaml | Adds Vermont court_selector config + unit selection and optional place matcher. |
| efile_app/efile/services/extraction_fields.py | Adds extraction key aliases to normalize party-name label variants consistently. |
| efile_app/efile/services/court_selection.py | New YAML-driven selector engine; includes shared “non-filing court” filtering. |
| efile_app/efile/services/court_location.py | New MA/VT location-to-court matching layer and place parsing utilities. |
| efile_app/efile/prompts/document_evidence_extraction.yaml | Strengthens prompt instructions to always extract party names from case captions. |
| efile_app/efile/api/urls.py | Registers the new /api/dropdowns/court-selector/ endpoint. |
| efile_app/efile/api/dropdown_views.py | Reuses is_non_filing_court() from the shared selector service. |
| efile_app/efile/api/court_selector_views.py | New endpoint implementation for guided selector state serialization. |
| docs/docs/partners-courts/jurisdiction-config.md | Documents court_selector configuration format and matcher behavior for partners. |
Review details
- Files reviewed: 28/29 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| "suffolklitlab-macourts @ git+https://github.com/SuffolkLITLab/MACourts@main", | ||
| # Vermont Superior Court unit lookup by town, county, or ZIP. | ||
| "suffolklitlab-vtcourts @ git+https://github.com/SuffolkLITLab/VTCourts@main", |
There was a problem hiding this comment.
This is fine for now, also we control both repos. will move to PyPI later
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Summary
Replaces the flat court dropdown with a multi-step, jurisdiction-aware court selector. Instead of scrolling through 200+ courts in one list, a filer answers a small number of progressive questions whose answers narrow the list to exactly the court they need.
Each state's
court_selectorblock in its YAML config file drives the entire flow — no new per-state Python or JS is needed to add a new one.What changed
New services
efile/services/court_selection.pycourt_selectorblock, evaluateswhen:conditions, filters courts byname_pattern/exclude_name_pattern, and computes step defaultsefile/services/court_location.pyparse_place()for free-text address inputNew API endpoint
GET /dropdowns/court-selector/— serialises the current selector state (steps, filtered court lists, active answers) for the active jurisdiction.New frontend
efile/static/js/court-selector.jswhen:conditions are met; location steps fire the court-lookup API and show matched courts alongside the full listefile/static/css/components/court-selector.cssPer-state routing logic
Illinois — routes by county. A regex groups the Tyler court list by county, and only counties with sub-courts (Cook above all) show a second division/courthouse selector. Appeals are identified by appellate district.
Massachusetts — routes by department, then by place. MACourts translates a typed town or address into the courts that serve it; the full department list is always shown beside the lookup as a fallback.
Vermont — routes by Superior Court division. VTCourts looks up the unit from a town, county, or ZIP; the dropdown remains for filers who know their unit already.
Other changes
is_non_filing_court()extracted fromdropdown_viewsintocourt_selectionso the same filter applies to both the flat list and the new selector.EXTRACTION_KEY_ALIASES+_normalized_key()added toextraction_fieldsso LLM variants of party-name labels ("plaintiff name", "petitioner names" …) resolve to the canonical key.court_selectorblock documented injurisdiction-config.md.selectCourt()helper that drives the new selector UI; existing E2E tests updated to use it.Testing
efile/tests/test_court_selection.pycover the selector engine andis_non_filing_court().Closes #120