feat: link related precedents across court instances (SCHEMA_V11)
Add ability to mark case_law records as related (e.g. same appeal
through ועדת ערר → מנהלי → עליון):
- DB: case_law_relations join table (bidirectional, V11 migration)
- DB CRUD: add/remove/get_case_law_relations
- Service: get_precedent() now returns related_cases[]
- MCP: precedent_link_cases + precedent_unlink_cases tools
- REST: POST/DELETE /api/precedent-library/{id}/relations
- UI: RelatedCasesSection on detail page with search dialog and unlink
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -700,6 +700,20 @@ CREATE INDEX IF NOT EXISTS idx_case_law_chair ON case_law(chair_name) WHERE chai
|
||||
CREATE INDEX IF NOT EXISTS idx_case_law_district ON case_law(district) WHERE district <> '';
|
||||
"""
|
||||
|
||||
SCHEMA_V11_SQL = """
|
||||
CREATE TABLE IF NOT EXISTS case_law_relations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
case_law_id UUID NOT NULL REFERENCES case_law(id) ON DELETE CASCADE,
|
||||
related_id UUID NOT NULL REFERENCES case_law(id) ON DELETE CASCADE,
|
||||
relation_type TEXT NOT NULL DEFAULT 'same_case_chain',
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
UNIQUE(case_law_id, related_id),
|
||||
CHECK (case_law_id <> related_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_clr_a ON case_law_relations(case_law_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_clr_b ON case_law_relations(related_id);
|
||||
"""
|
||||
|
||||
|
||||
async def _run_schema_migrations(pool: asyncpg.Pool) -> None:
|
||||
async with pool.acquire() as conn:
|
||||
@@ -714,7 +728,8 @@ async def _run_schema_migrations(pool: asyncpg.Pool) -> None:
|
||||
await conn.execute(SCHEMA_V8_SQL)
|
||||
await conn.execute(SCHEMA_V9_SQL)
|
||||
await conn.execute(SCHEMA_V10_SQL)
|
||||
logger.info("Database schema initialized (v1-v10)")
|
||||
await conn.execute(SCHEMA_V11_SQL)
|
||||
logger.info("Database schema initialized (v1-v11)")
|
||||
|
||||
|
||||
async def init_schema() -> None:
|
||||
@@ -1735,6 +1750,59 @@ async def get_case_law(case_law_id: UUID) -> dict | None:
|
||||
return _row_to_case_law(row) if row else None
|
||||
|
||||
|
||||
async def add_case_law_relation(
|
||||
a_id: UUID, b_id: UUID, relation_type: str = "same_case_chain"
|
||||
) -> None:
|
||||
"""Link two case_law records bidirectionally. Idempotent (ON CONFLICT DO NOTHING)."""
|
||||
pool = await get_pool()
|
||||
async with pool.acquire() as conn:
|
||||
await conn.executemany(
|
||||
"""
|
||||
INSERT INTO case_law_relations(case_law_id, related_id, relation_type)
|
||||
VALUES($1, $2, $3)
|
||||
ON CONFLICT (case_law_id, related_id) DO NOTHING
|
||||
""",
|
||||
[(a_id, b_id, relation_type), (b_id, a_id, relation_type)],
|
||||
)
|
||||
|
||||
|
||||
async def remove_case_law_relation(a_id: UUID, b_id: UUID) -> None:
|
||||
"""Remove a bidirectional link between two case_law records."""
|
||||
pool = await get_pool()
|
||||
await pool.execute(
|
||||
"""
|
||||
DELETE FROM case_law_relations
|
||||
WHERE (case_law_id = $1 AND related_id = $2)
|
||||
OR (case_law_id = $2 AND related_id = $1)
|
||||
""",
|
||||
a_id,
|
||||
b_id,
|
||||
)
|
||||
|
||||
|
||||
async def get_case_law_relations(case_law_id: UUID) -> list[dict]:
|
||||
"""Return all case_law records linked to case_law_id, ordered by date asc."""
|
||||
pool = await get_pool()
|
||||
rows = await pool.fetch(
|
||||
"""
|
||||
SELECT cl.*, r.relation_type
|
||||
FROM case_law_relations r
|
||||
JOIN case_law cl ON cl.id = r.related_id
|
||||
WHERE r.case_law_id = $1
|
||||
ORDER BY cl.date ASC NULLS LAST
|
||||
""",
|
||||
case_law_id,
|
||||
)
|
||||
results = []
|
||||
for row in rows:
|
||||
d = dict(row)
|
||||
relation_type = d.pop("relation_type")
|
||||
normalized = _row_to_case_law(d)
|
||||
normalized["relation_type"] = relation_type
|
||||
results.append(normalized)
|
||||
return results
|
||||
|
||||
|
||||
async def get_case_law_by_citation(case_number: str) -> dict | None:
|
||||
pool = await get_pool()
|
||||
row = await pool.fetchrow(
|
||||
|
||||
Reference in New Issue
Block a user