From 2d0e987803a0b126085b79d676bc380a27735723 Mon Sep 17 00:00:00 2001 From: Chaim Date: Mon, 13 Apr 2026 18:44:50 +0000 Subject: [PATCH] Add missing case_precedents CRUD functions to db module Four functions were called by tools/precedents.py but never implemented in services/db.py: create_case_precedent, list_case_precedents, delete_case_precedent, search_precedent_library. This caused 500 errors on the /api/cases/{n}/precedents endpoint. Co-Authored-By: Claude Opus 4.6 (1M context) --- mcp-server/src/legal_mcp/services/db.py | 85 +++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/mcp-server/src/legal_mcp/services/db.py b/mcp-server/src/legal_mcp/services/db.py index 756041a..000adee 100644 --- a/mcp-server/src/legal_mcp/services/db.py +++ b/mcp-server/src/legal_mcp/services/db.py @@ -1066,6 +1066,91 @@ async def search_precedents( return results[:limit] +# ── Case precedents (CRUD) ──────────────────────────────────────── + + +async def create_case_precedent( + case_id: UUID, + quote: str, + citation: str, + section_id: str | None = None, + chair_note: str = "", + pdf_document_id: UUID | None = None, + practice_area: str | None = None, +) -> dict: + """Insert a new precedent attached to a case.""" + pool = await get_pool() + row = await pool.fetchrow( + """ + INSERT INTO case_precedents + (case_id, section_id, quote, citation, chair_note, pdf_document_id, practice_area) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING * + """, + case_id, section_id, quote, citation, chair_note, pdf_document_id, practice_area, + ) + return dict(row) + + +async def list_case_precedents(case_id: UUID) -> list[dict]: + """List all precedents attached to a case, ordered by section then creation time.""" + pool = await get_pool() + rows = await pool.fetch( + """ + SELECT id, case_id, section_id, quote, citation, chair_note, + pdf_document_id, practice_area, created_at, updated_at + FROM case_precedents + WHERE case_id = $1 + ORDER BY section_id NULLS LAST, created_at + """, + case_id, + ) + return [dict(r) for r in rows] + + +async def delete_case_precedent(precedent_id: UUID) -> bool: + """Delete a precedent attachment by ID. Returns True if deleted.""" + pool = await get_pool() + result = await pool.execute( + "DELETE FROM case_precedents WHERE id = $1", precedent_id + ) + return result == "DELETE 1" + + +async def search_precedent_library( + query: str, practice_area: str = "", limit: int = 10, +) -> list[dict]: + """Search all precedents across cases by citation or quote text.""" + pool = await get_pool() + pattern = f"%{query}%" + if practice_area: + rows = await pool.fetch( + """ + SELECT id, case_id, section_id, quote, citation, chair_note, + practice_area, created_at + FROM case_precedents + WHERE (citation ILIKE $1 OR quote ILIKE $1) + AND practice_area = $2 + ORDER BY created_at DESC + LIMIT $3 + """, + pattern, practice_area, limit, + ) + else: + rows = await pool.fetch( + """ + SELECT id, case_id, section_id, quote, citation, chair_note, + practice_area, created_at + FROM case_precedents + WHERE citation ILIKE $1 OR quote ILIKE $1 + ORDER BY created_at DESC + LIMIT $2 + """, + pattern, limit, + ) + return [dict(r) for r in rows] + + # ── Chair feedback ──────────────────────────────────────────────── async def record_chair_feedback(