95 lines
3.0 KiB
Python
95 lines
3.0 KiB
Python
"""Utility script to repair orphaned Case records.
|
|
|
|
This script attempts to link Case records to Client entries based on legacy
|
|
identifiers. It should be executed inside the running Docker container.
|
|
|
|
Usage:
|
|
docker compose exec delphi-db python scripts/fix_case_links.py
|
|
|
|
Safety:
|
|
- Prints a summary before making changes.
|
|
- Requires confirmation unless run with --yes.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from collections import defaultdict
|
|
|
|
from sqlalchemy.orm import joinedload
|
|
|
|
from app.database import SessionLocal
|
|
from app.models import Case, Client
|
|
|
|
|
|
def build_client_maps(session) -> tuple[dict[str, int], dict[str, list[int]]]:
|
|
rolodex_to_client: dict[str, int] = {}
|
|
file_to_client: dict[str, list[int]] = defaultdict(list)
|
|
|
|
for client in session.query(Client).options(joinedload(Client.cases)).all():
|
|
if client.rolodex_id:
|
|
rolodex_to_client[client.rolodex_id] = client.id
|
|
for case in client.cases:
|
|
file_to_client[case.file_no].append(client.id)
|
|
|
|
return rolodex_to_client, file_to_client
|
|
|
|
|
|
def find_orphaned_cases(session):
|
|
return session.query(Case).filter(Case.client_id == None).all() # noqa: E711
|
|
|
|
|
|
def main(confirm: bool) -> int:
|
|
session = SessionLocal()
|
|
try:
|
|
rolodex_map, file_map = build_client_maps(session)
|
|
orphans = find_orphaned_cases(session)
|
|
|
|
if not orphans:
|
|
print("No orphaned cases found. 🎉")
|
|
return 0
|
|
|
|
assignments: list[tuple[Case, int]] = []
|
|
for case in orphans:
|
|
candidate_client_id = None
|
|
if case.file_no in file_map and file_map[case.file_no]:
|
|
candidate_client_id = file_map[case.file_no][0]
|
|
elif case.file_no in rolodex_map:
|
|
candidate_client_id = rolodex_map[case.file_no]
|
|
|
|
if candidate_client_id:
|
|
assignments.append((case, candidate_client_id))
|
|
|
|
if not assignments:
|
|
print("No matching clients found for orphaned cases. Nothing to do.")
|
|
return 1
|
|
|
|
print("Orphaned cases detected:")
|
|
for case, client_id in assignments:
|
|
print(f" Case {case.file_no} (id={case.id}) → Client id {client_id}")
|
|
|
|
if not confirm:
|
|
response = input("Apply these fixes? [y/N]: ").strip().lower()
|
|
if response not in {"y", "yes"}:
|
|
print("Aborting with no changes.")
|
|
return 1
|
|
|
|
updated = 0
|
|
for case, client_id in assignments:
|
|
case.client_id = client_id
|
|
updated += 1
|
|
|
|
session.commit()
|
|
print(f"Updated {updated} cases with matching clients.")
|
|
return 0
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Repair orphaned Case links.")
|
|
parser.add_argument("--yes", action="store_true", help="Apply changes without confirmation")
|
|
args = parser.parse_args()
|
|
raise SystemExit(main(confirm=args.yes))
|
|
|