"""Repair UTF-8 demo seed data (core display_name + risk_aml_list). Windows mysql CLI pipe may corrupt CJK; use SQLAlchemy + utf-8 file reads instead. Idempotent: safe to re-run after partial bootstrap. """ from __future__ import annotations import re import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from sqlalchemy import text from app.config.settings import settings from app.utils.db import get_engine CUSTOMER_SEED = ROOT / "scripts/core/03-seed-customers.sql" PRODUCT_SEED = ROOT / "scripts/core/02-seed-base.sql" AML_SEED = ROOT / "scripts/agent/seed-aml-list.sql" RISK_DEMO = ROOT / "scripts/demo/prepare_risk_demo.sql" _ROW_RE = re.compile(r"\('(CUST-[^']+)',\s*'([^']+)'") _PRODUCT_RE = re.compile(r"\('(PROD-[^']+)',\s*'([^']+)'") def _parse_customer_names() -> list[tuple[str, str]]: content = CUSTOMER_SEED.read_text(encoding="utf-8") block = content.split("INSERT INTO core_customer_risk")[0] return _ROW_RE.findall(block) def _parse_product_names() -> list[tuple[str, str]]: content = PRODUCT_SEED.read_text(encoding="utf-8") block = content.split("INSERT INTO core_product")[1] return _PRODUCT_RE.findall(block) def _parse_aml_rows() -> list[dict[str, str]]: content = AML_SEED.read_text(encoding="utf-8") rows: list[dict[str, str]] = [] for m in re.finditer( r"\('(AML-\d+)',\s*'(\w+)',\s*'([^']*)',\s*NULL,\s*NULL,\s*([\d.]+),\s*" r"'([^']*)',\s*'([^']*)',\s*'(\d{4}-\d{2}-\d{2})'\)", content, ): rows.append( { "list_id": m.group(1), "list_type": m.group(2), "full_name": m.group(3), "match_threshold": m.group(4), "source": m.group(5), "list_version": m.group(6), "effective_date": m.group(7), } ) if len(rows) != 8: raise RuntimeError(f"expected 8 AML rows, parsed {len(rows)}") return rows def _run_sql_file(engine, path: Path) -> None: statements = [s.strip() for s in path.read_text(encoding="utf-8").split(";") if s.strip()] with engine.begin() as conn: for stmt in statements: if stmt.upper().startswith("USE "): continue conn.execute(text(stmt)) def main() -> None: core = get_engine(settings.mysql_core_database) agent = get_engine(settings.mysql_database) names = _parse_customer_names() with core.begin() as conn: for customer_id, display_name in names: conn.execute( text("UPDATE core_customer SET display_name = :n WHERE customer_id = :id"), {"id": customer_id, "n": display_name}, ) print(f"updated {len(names)} core_customer.display_name") products = _parse_product_names() with core.begin() as conn: for product_id, product_name in products: conn.execute( text("UPDATE core_product SET product_name = :n WHERE product_id = :id"), {"id": product_id, "n": product_name}, ) print(f"updated {len(products)} core_product.product_name") aml_rows = _parse_aml_rows() with agent.begin() as conn: conn.execute(text("DELETE FROM risk_aml_list")) for row in aml_rows: conn.execute( text( """ INSERT INTO risk_aml_list (list_id, list_type, full_name, id_no, bank_card_no, match_threshold, source, list_version, effective_date, is_active) VALUES (:list_id, :list_type, :full_name, NULL, NULL, :match_threshold, :source, :list_version, :effective_date, 1) """ ), row, ) print(f"reseeded {len(aml_rows)} risk_aml_list rows") _run_sql_file(core, RISK_DEMO) print("ran prepare_risk_demo.sql") with core.connect() as conn: sample = conn.execute( text("SELECT customer_id, display_name FROM core_customer WHERE customer_id = 'CUST-3001'") ).one() prod = conn.execute( text("SELECT product_id, product_name FROM core_product WHERE product_id = 'PROD-110022'") ).one() print("verify CUST-3001:", sample) print("verify PROD-110022:", prod) if __name__ == "__main__": main()