Files
legal-ai/web/paperclip_client.py
Chaim 0593fe9b01 Add interactive case creation wizard + document upload with auto-rename
New SPA UI with 4 views:
- Case list (#/) with status cards and document counts
- New case wizard (#/new) with 4-step form: details, parties, schedule, review+create
- Case view (#/case/:id) with grouped documents and drag-drop upload with tagging
- Legacy upload (#/upload) for backwards compatibility

Auto-creation pipeline in wizard step 4:
1. Creates case in legal-ai DB with local git repo
2. Creates Gitea repo in 'cases' org and pushes initial commit
3. Creates Paperclip project via direct DB insert

Document upload with smart rename:
- scan_001.pdf -> כתב-ערר-קובר-1130-25.pdf
- Based on doc_type + party_name + case_number

New files:
- web/gitea_client.py: Gitea REST API client
- web/paperclip_client.py: Paperclip embedded DB client

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 10:17:24 +00:00

78 lines
2.5 KiB
Python

"""Paperclip project creation via direct DB access (embedded PostgreSQL)."""
from __future__ import annotations
import logging
import uuid
import asyncpg
logger = logging.getLogger(__name__)
PAPERCLIP_DB_URL = "postgresql://paperclip:paperclip@127.0.0.1:54329/paperclip"
# Company IDs from Paperclip DB
COMPANIES = {
"licensing": "42a7acd0-30c5-4cbd-ac97-7424f65df294", # CMP — רישוי ובניה
"betterment": "8639e837-4c9d-47fa-a76b-95788d651896", # CMPA — היטלי השבחה
}
APPEAL_TYPE_TO_COMPANY = {
"רישוי": "licensing",
"licensing": "licensing",
"היטל השבחה": "betterment",
"betterment_levy": "betterment",
"פיצויים": "betterment",
"compensation": "betterment",
}
def _get_company_id(appeal_type: str) -> str:
key = APPEAL_TYPE_TO_COMPANY.get(appeal_type, "licensing")
return COMPANIES[key]
async def create_project(
case_number: str,
title: str,
description: str = "",
appeal_type: str = "רישוי",
color: str = "#6366f1",
) -> dict:
"""Create a project in the Paperclip embedded DB."""
company_id = _get_company_id(appeal_type)
project_id = str(uuid.uuid4())
conn = await asyncpg.connect(PAPERCLIP_DB_URL)
try:
await conn.execute(
"""INSERT INTO projects (id, company_id, name, description, status, color)
VALUES ($1, $2::uuid, $3, $4, 'backlog', $5)
ON CONFLICT DO NOTHING""",
project_id, company_id, f"ערר {case_number}{title}"[:200], description[:500] if description else "", color,
)
return {
"id": project_id,
"company_id": company_id,
"name": f"ערר {case_number}{title}",
"url": f"https://pc.nautilus.marcusgroup.org/{'CMP' if 'licensing' in appeal_type or appeal_type == 'רישוי' else 'CMPA'}/projects/{project_id}/issues",
}
finally:
await conn.close()
async def get_project_url(case_number: str) -> str | None:
"""Find existing Paperclip project for a case number."""
conn = await asyncpg.connect(PAPERCLIP_DB_URL)
try:
row = await conn.fetchrow(
"SELECT id, company_id FROM projects WHERE name LIKE $1",
f"%{case_number}%",
)
if row:
prefix = "CMP" if row["company_id"] == uuid.UUID(COMPANIES["licensing"]) else "CMPA"
return f"https://pc.nautilus.marcusgroup.org/{prefix}/projects/{row['id']}/issues"
return None
finally:
await conn.close()