feat(graph): metadata filters + facets (corpus graph PR A)
Adds legal-metadata filtering and the payload to color by it (foundation for the color-by selector in the analytics PR). Backend (web/graph_api.py, web/app.py) — read-only, G2: - GraphNode += court, date (ISO) — precedents carry them for filter/color-by. - build_corpus_graph += server-side WHERE filters (G5): court, precedent_level, chair, district, year_from, year_to (EXTRACT(YEAR FROM date)). Neighborhood query also selects court/date. - New GET /api/graph/facets (response_model GraphFacets, UI2) → distinct courts/levels/chairs/districts so the UI doesn't hardcode Hebrew strings. Frontend: - graph.ts: GraphNode += court/date; GraphFilters += the six params; buildParams; useGraphFacets() hook. - graph-filter-panel: an "advanced" Accordion with court/precedent_level/chair/ district Selects (from facets) + year-from/year-to Selects. - graph-view: new controls wired into filters; facets fetched and passed down. Verified read-only against the live DB (precedent_level=עליון&year_from=2015 filters correctly; facets populated: 36 courts / 3 levels / 19 chairs / 4 districts). web-ui build + lint pass. Invariants: G2 (SELECT-only via db.get_pool), G5 (filters server-side), UI2 (explicit response_models). api:types to be regenerated post-deploy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
19
web/app.py
19
web/app.py
@@ -5774,6 +5774,12 @@ async def graph_corpus(
|
||||
min_citations: int = 0,
|
||||
limit: int = graph_api.NODE_CAP_DEFAULT,
|
||||
q: str = "",
|
||||
court: str = "",
|
||||
precedent_level: str = "",
|
||||
chair: str = "",
|
||||
district: str = "",
|
||||
year_from: int = 0,
|
||||
year_to: int = 0,
|
||||
):
|
||||
"""Full corpus graph under the given filters (most-cited nodes survive the cap)."""
|
||||
if practice_area and practice_area not in _PRACTICE_AREAS:
|
||||
@@ -5787,9 +5793,22 @@ async def graph_corpus(
|
||||
min_citations=min_citations,
|
||||
limit=limit,
|
||||
q=q,
|
||||
court=court,
|
||||
precedent_level=precedent_level,
|
||||
chair=chair,
|
||||
district=district,
|
||||
year_from=year_from,
|
||||
year_to=year_to,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/api/graph/facets", response_model=graph_api.GraphFacets)
|
||||
async def graph_facets():
|
||||
"""Distinct filter values (courts / levels / chairs / districts) for the UI."""
|
||||
pool = await db.get_pool()
|
||||
return await graph_api.build_facets(pool)
|
||||
|
||||
|
||||
@app.get("/api/graph/node/{node_id}/neighborhood", response_model=graph_api.CorpusGraph)
|
||||
async def graph_node_neighborhood(node_id: str, depth: int = 1, node_types: str = ""):
|
||||
"""Local-graph focus: the node + its neighbors out to ``depth`` (1-2)."""
|
||||
|
||||
Reference in New Issue
Block a user