Backend Aug 22-25: AI analysis, welcome-packet templating, LetterStream, DocuSeal, staff RBAC + tier gate
- analysis.py: deterministic claim scorer + /analyze /approve /letter /advance-tier endpoints (auto-runs on intake) - packet.py + packet_fields.json: welcome-packet templating engine (6 onboarding docs, field catalog) - letterstream.py + letters.py: certified-mail send pipeline + letter lifecycle (webhook verified) - docuseal.py: DocuSeal signing integration - staff.py/models.py/schema.sql/auth.py: approval actor from staff key, tier gate (APPROVED+ACTIVE+onboarding docs), onboarding_docs table - frontend/: dependency-free static portal (intake, magic-link login/verify, dashboard) - landing-mockups/: 4 design-stance mockups + favicons - legal/: aup/privacy/sms-terms/terms HTML - docs/: letter-queue scope, letterstream API contract, 6 welcome-packet templates - review-dre-landing-2026-08-21.md: 3-variant landing feedback sprint - compliance/DRE_Compliance_Manual.md: updated Source synced from deployed /opt/dre-portal/app/ (was 4 days ahead of git).
This commit is contained in:
+44
-1
@@ -44,10 +44,11 @@ def get_conn() -> sqlite3.Connection:
|
||||
|
||||
|
||||
def init_db() -> None:
|
||||
"""Create schema if absent and seed number_sequences for the current year."""
|
||||
"""Create schema if absent, migrate, and seed number_sequences for the current year."""
|
||||
with get_conn() as conn:
|
||||
schema_sql = _SCHEMA_FILE.read_text()
|
||||
conn.executescript(schema_sql)
|
||||
_migrate(conn)
|
||||
# Seed sequences for current year (idempotent)
|
||||
year = datetime.now(timezone.utc).year
|
||||
for prefix in ("DRE", "CLT"):
|
||||
@@ -58,6 +59,48 @@ def init_db() -> None:
|
||||
conn.commit()
|
||||
|
||||
|
||||
def _migrate(conn: sqlite3.Connection) -> None:
|
||||
"""Add analysis/approval/letter columns to `claims` if they don't already exist.
|
||||
|
||||
SQLite lacks `ADD COLUMN IF NOT EXISTS`, so we inspect PRAGMA table_info first.
|
||||
Idempotent — safe to run on every boot.
|
||||
"""
|
||||
existing = {row["name"] for row in conn.execute("PRAGMA table_info(claims)").fetchall()}
|
||||
additions = {
|
||||
"no_other_agency_at": "TEXT",
|
||||
"no_prior_action_at": "TEXT",
|
||||
"analysis_score": "INTEGER",
|
||||
"analysis_summary": "TEXT",
|
||||
"analysis_components": "TEXT",
|
||||
"analysis_at": "TEXT",
|
||||
"recommended_tier": "TEXT",
|
||||
"approval_status": "TEXT NOT NULL DEFAULT 'NONE'",
|
||||
"approval_decision_by": "TEXT",
|
||||
"approval_decision_at": "TEXT",
|
||||
"letter_subject": "TEXT",
|
||||
"letter_body": "TEXT",
|
||||
"letter_tier": "TEXT",
|
||||
"letter_updated_at": "TEXT",
|
||||
}
|
||||
for col, ddl in additions.items():
|
||||
if col not in existing:
|
||||
conn.execute(f"ALTER TABLE claims ADD COLUMN {col} {ddl}")
|
||||
|
||||
# onboarding_docs -> DocuSeal e-sign state (added later than the base schema)
|
||||
existing_od = {row["name"] for row in conn.execute("PRAGMA table_info(onboarding_docs)").fetchall()}
|
||||
od_additions = {
|
||||
"docuseal_submission_id": "TEXT",
|
||||
"docuseal_submitter_id": "TEXT",
|
||||
"docuseal_slug": "TEXT",
|
||||
"docuseal_embed_src": "TEXT",
|
||||
"docuseal_status": "TEXT",
|
||||
"docuseal_sent_at": "TEXT",
|
||||
}
|
||||
for col, ddl in od_additions.items():
|
||||
if col not in existing_od:
|
||||
conn.execute(f"ALTER TABLE onboarding_docs ADD COLUMN {col} {ddl}")
|
||||
|
||||
|
||||
def new_uuid() -> str:
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user