Add settings page for tag-to-company mappings and auto-create Paperclip projects
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m22s

When a case is created, a Paperclip project is now automatically created in
the correct company based on the appeal_subtype tag. Tag-to-company mappings
are managed via a new Settings page that pulls companies from Paperclip DB.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-14 06:24:23 +00:00
parent 140a2e442d
commit 2faae002e7
4 changed files with 320 additions and 13 deletions

View File

@@ -27,19 +27,41 @@ COMPANIES = {
"betterment": "8639e837-4c9d-47fa-a76b-95788d651896", # CMPA — היטלי השבחה
}
APPEAL_TYPE_TO_COMPANY = {
"רישוי": "licensing",
"licensing": "licensing",
"היטל השבחה": "betterment",
"betterment_levy": "betterment",
"פיצויים": "betterment",
"compensation": "betterment",
# Fallback mapping — used only when DB lookup returns no results
_FALLBACK_APPEAL_TYPE_TO_COMPANY = {
"רישוי": COMPANIES["licensing"],
"היטל השבחה": COMPANIES["betterment"],
"פיצויים": COMPANIES["betterment"],
"building_permit": COMPANIES["licensing"],
"betterment_levy": COMPANIES["betterment"],
"compensation_197": COMPANIES["betterment"],
"compensation": COMPANIES["betterment"],
"licensing": COMPANIES["licensing"],
}
# Legal-AI DB URL for reading tag_company_mappings
_LEGAL_DB_URL = os.environ.get(
"DATABASE_URL", "postgresql://legal:legal@127.0.0.1:5432/legal_ai"
)
def _get_company_id(appeal_type: str) -> str:
key = APPEAL_TYPE_TO_COMPANY.get(appeal_type, "licensing")
return COMPANIES[key]
async def _get_company_id(appeal_type: str) -> str:
"""Resolve appeal_type tag to a Paperclip company ID via DB mappings, with fallback."""
try:
conn = await asyncpg.connect(_LEGAL_DB_URL)
try:
row = await conn.fetchrow(
"SELECT company_id FROM tag_company_mappings WHERE tag = $1 LIMIT 1",
appeal_type,
)
if row:
return row["company_id"]
finally:
await conn.close()
except Exception:
logger.debug("DB lookup for tag mapping failed, using fallback for '%s'", appeal_type)
return _FALLBACK_APPEAL_TYPE_TO_COMPANY.get(appeal_type, COMPANIES["licensing"])
async def create_project(
@@ -50,11 +72,16 @@ async def create_project(
color: str = "#6366f1",
) -> dict:
"""Create a project in the Paperclip embedded DB, or return existing one."""
company_id = _get_company_id(appeal_type)
prefix = "CMP" if _get_company_id(appeal_type) == COMPANIES["licensing"] else "CMPA"
company_id = await _get_company_id(appeal_type)
conn = await asyncpg.connect(PAPERCLIP_DB_URL)
try:
# Resolve prefix from company identifier in Paperclip DB
comp_row = await conn.fetchrow(
"SELECT identifier FROM companies WHERE id = $1::uuid", company_id,
)
prefix = comp_row["identifier"] if comp_row and comp_row["identifier"] else "CMP"
# Check for existing project with this case number
existing = await conn.fetchrow(
"SELECT id, name FROM projects WHERE name LIKE $1 AND company_id = $2::uuid",
@@ -216,7 +243,10 @@ async def get_project_url(case_number: str) -> str | None:
f"%{case_number}%",
)
if row:
prefix = "CMP" if row["company_id"] == uuid.UUID(COMPANIES["licensing"]) else "CMPA"
comp_row = await conn.fetchrow(
"SELECT identifier FROM companies WHERE id = $1::uuid", str(row["company_id"]),
)
prefix = comp_row["identifier"] if comp_row and comp_row["identifier"] else "CMP"
return f"https://pc.nautilus.marcusgroup.org/{prefix}/projects/{row['id']}/issues"
return None
finally: