feat(proceeding-type): explicit ערר/בל"מ field for cases + corpus
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m40s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m40s
Same case_number can exist as both a regular appeal (ערר) and an extension-of-time request (בל"מ), and we were inferring the difference from appeal_subtype prefixes — fragile, and case-number lookups weren't disambiguated. Now stored as a first-class field on both case_law (corpus) and cases (live cases), with partial unique indexes on (case_number, proceeding_type). - SCHEMA_V15: column + CHECK constraints + backfill from appeal_subtype LIKE 'extension_request_%' + partial unique indexes replace the old global UNIQUE(case_number). - derive_proceeding_type() centralizes the inference rule (extension_request_* → בל"מ; subject regex fallback; default ערר). - Metadata extractor prompt asks Claude to populate the new field explicitly; apply_to_record writes it for internal_committee rows. - internal_decision_upload, case_create, case_update accept an optional proceeding_type; FastAPI request models expose it. - Wizard + edit dialog get a sided Select; case header renders the resolved label (ערר / בל"מ). - Uploaded the 2 staged בל"מ decisions on betterment levy: 8126/24 (סופר נוח, 13 chunks), 8047/23 (הרנון, 48 chunks). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -823,6 +823,58 @@ CREATE TABLE IF NOT EXISTS legal_argument_propositions (
|
||||
"""
|
||||
|
||||
|
||||
# proceeding_type — מבחין בין הליך ערר עיקרי לבל"מ (בקשה להארכת מועד).
|
||||
# חל גם על case_law (קורפוס) וגם על cases (תיקים חיים). שני הסוגים
|
||||
# יכולים לחלוק אותו case_number, ולכן ה-uniqueness עוברת ל-(case_number,
|
||||
# proceeding_type). בקורפוס: רק internal_committee מקבלים ערך מאוכלס;
|
||||
# פסיקה חיצונית נשארת עם ''.
|
||||
SCHEMA_V15_SQL = """
|
||||
-- ------- case_law (קורפוס) -------
|
||||
ALTER TABLE case_law ADD COLUMN IF NOT EXISTS proceeding_type TEXT NOT NULL DEFAULT '';
|
||||
|
||||
ALTER TABLE case_law DROP CONSTRAINT IF EXISTS case_law_proceeding_type_check;
|
||||
ALTER TABLE case_law ADD CONSTRAINT case_law_proceeding_type_check
|
||||
CHECK (proceeding_type IN ('', 'ערר', 'בל"מ'));
|
||||
|
||||
-- Backfill לפי appeal_subtype הקיים
|
||||
UPDATE case_law SET proceeding_type = 'בל"מ'
|
||||
WHERE source_kind = 'internal_committee' AND proceeding_type = ''
|
||||
AND appeal_subtype LIKE 'extension_request_%';
|
||||
|
||||
UPDATE case_law SET proceeding_type = 'ערר'
|
||||
WHERE source_kind = 'internal_committee' AND proceeding_type = '';
|
||||
|
||||
ALTER TABLE case_law DROP CONSTRAINT IF EXISTS case_law_internal_proceeding_check;
|
||||
ALTER TABLE case_law ADD CONSTRAINT case_law_internal_proceeding_check
|
||||
CHECK (source_kind != 'internal_committee' OR proceeding_type IN ('ערר', 'בל"מ'));
|
||||
|
||||
-- החלפת UNIQUE(case_number) ב-partial unique לפי source_kind
|
||||
ALTER TABLE case_law DROP CONSTRAINT IF EXISTS case_law_case_number_key;
|
||||
DROP INDEX IF EXISTS case_law_case_number_key;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_case_law_internal_number_proc
|
||||
ON case_law (case_number, proceeding_type)
|
||||
WHERE source_kind = 'internal_committee';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_case_law_external_number
|
||||
ON case_law (case_number)
|
||||
WHERE source_kind <> 'internal_committee';
|
||||
|
||||
-- ------- cases (תיקים חיים) -------
|
||||
ALTER TABLE cases ADD COLUMN IF NOT EXISTS proceeding_type TEXT NOT NULL DEFAULT 'ערר';
|
||||
|
||||
ALTER TABLE cases DROP CONSTRAINT IF EXISTS cases_proceeding_type_check;
|
||||
ALTER TABLE cases ADD CONSTRAINT cases_proceeding_type_check
|
||||
CHECK (proceeding_type IN ('ערר', 'בל"מ'));
|
||||
|
||||
UPDATE cases SET proceeding_type = 'בל"מ'
|
||||
WHERE proceeding_type = 'ערר' AND appeal_subtype LIKE 'extension_request_%';
|
||||
|
||||
ALTER TABLE cases DROP CONSTRAINT IF EXISTS cases_case_number_key;
|
||||
DROP INDEX IF EXISTS cases_case_number_key;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_cases_number_proc
|
||||
ON cases (case_number, proceeding_type);
|
||||
"""
|
||||
|
||||
|
||||
async def _run_schema_migrations(pool: asyncpg.Pool) -> None:
|
||||
async with pool.acquire() as conn:
|
||||
await conn.execute(SCHEMA_SQL)
|
||||
@@ -840,7 +892,8 @@ async def _run_schema_migrations(pool: asyncpg.Pool) -> None:
|
||||
await conn.execute(SCHEMA_V12_SQL)
|
||||
await conn.execute(SCHEMA_V13_SQL)
|
||||
await conn.execute(SCHEMA_V14_SQL)
|
||||
logger.info("Database schema initialized (v1-v14)")
|
||||
await conn.execute(SCHEMA_V15_SQL)
|
||||
logger.info("Database schema initialized (v1-v15)")
|
||||
|
||||
|
||||
async def init_schema() -> None:
|
||||
@@ -867,6 +920,7 @@ async def create_case(
|
||||
# from the case_number prefix before calling here.
|
||||
practice_area: str = "",
|
||||
appeal_subtype: str = "",
|
||||
proceeding_type: str = "ערר",
|
||||
) -> dict:
|
||||
pool = await get_pool()
|
||||
case_id = uuid4()
|
||||
@@ -875,14 +929,14 @@ async def create_case(
|
||||
"""INSERT INTO cases (id, case_number, title, appellants, respondents,
|
||||
subject, property_address, permit_number, committee_type,
|
||||
hearing_date, notes, expected_outcome,
|
||||
practice_area, appeal_subtype)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)""",
|
||||
practice_area, appeal_subtype, proceeding_type)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)""",
|
||||
case_id, case_number, title,
|
||||
json.dumps(appellants or []),
|
||||
json.dumps(respondents or []),
|
||||
subject, property_address, permit_number, committee_type,
|
||||
hearing_date, notes, expected_outcome,
|
||||
practice_area, appeal_subtype,
|
||||
practice_area, appeal_subtype, proceeding_type,
|
||||
)
|
||||
return await get_case(case_id)
|
||||
|
||||
@@ -2024,18 +2078,22 @@ async def create_internal_committee_decision(
|
||||
summary: str = "",
|
||||
is_binding: bool = True,
|
||||
document_id: UUID | None = None,
|
||||
proceeding_type: str = "ערר",
|
||||
) -> dict:
|
||||
"""Upsert an appeals-committee decision as source_kind='internal_committee'.
|
||||
|
||||
If a row with this case_number already exists as cited_only, promotes it.
|
||||
Idempotent: calling again updates metadata in-place.
|
||||
Idempotency key: (case_number, proceeding_type) — the same number can
|
||||
exist as both 'ערר' and 'בל"מ' (an extension-of-time request can be
|
||||
filed against an existing appeal with the same number).
|
||||
"""
|
||||
pool = await get_pool()
|
||||
tags_json = json.dumps(subject_tags or [], ensure_ascii=False)
|
||||
async with pool.acquire() as conn:
|
||||
existing = await conn.fetchrow(
|
||||
"SELECT id FROM case_law WHERE case_number = $1",
|
||||
case_number,
|
||||
"SELECT id FROM case_law "
|
||||
"WHERE case_number = $1 AND proceeding_type = $2 "
|
||||
" AND source_kind = 'internal_committee'",
|
||||
case_number, proceeding_type,
|
||||
)
|
||||
if existing:
|
||||
row = await conn.fetchrow(
|
||||
@@ -2072,19 +2130,20 @@ async def create_internal_committee_decision(
|
||||
subject_tags, summary, full_text,
|
||||
source_kind, source_type, document_id,
|
||||
extraction_status, halacha_extraction_status,
|
||||
practice_area, appeal_subtype, is_binding
|
||||
practice_area, appeal_subtype, is_binding, proceeding_type
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6,
|
||||
$7, $8, $9,
|
||||
'internal_committee', 'appeals_committee', $10,
|
||||
'processing', 'pending',
|
||||
$11, $12, $13
|
||||
$11, $12, $13, $14
|
||||
)
|
||||
RETURNING *
|
||||
""",
|
||||
case_number, case_name, court, decision_date, chair_name, district,
|
||||
tags_json, summary, full_text,
|
||||
document_id, practice_area, appeal_subtype, is_binding,
|
||||
proceeding_type,
|
||||
)
|
||||
return _row_to_case_law(row)
|
||||
|
||||
@@ -2100,6 +2159,7 @@ async def update_case_law(case_law_id: UUID, **fields) -> dict | None:
|
||||
"case_number", "case_name", "court", "date", "practice_area", "appeal_subtype",
|
||||
"subject_tags", "summary", "headnote", "key_quote", "source_url",
|
||||
"source_type", "precedent_level", "is_binding", "district", "chair_name",
|
||||
"proceeding_type",
|
||||
}
|
||||
updates = {k: v for k, v in fields.items() if k in allowed}
|
||||
if not updates:
|
||||
|
||||
Reference in New Issue
Block a user