All checks were successful
CI / lint (push) Successful in 29s
CI / notebooks-smoke (push) Successful in 1m25s
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Deploy / docs (push) Has been skipped
Deploy / api (push) Has been skipped
Deploy / mc (push) Has been skipped
Infra CI / notebooks (push) Successful in 56s
Infra CI / zotero (push) Successful in 13s
Infra CI / docs (push) Successful in 1m14s
Infra CI / api (push) Successful in 50s
Infra CI / mc (push) Successful in 19s
Deploy / report (push) Successful in 13s
CI / test (push) Successful in 14m27s
Harden / build-scan-report (push) Successful in 26m15s
Renovate / renovate (push) Successful in 15s
Notebooks Integration / notebooks-integration (push) Successful in 7m16s
Zotero Sync / zotero-sync (push) Successful in 53s
Package Supply Chain / pkg-supply-chain (push) Successful in 58s
The M5 close-out missed half the issue's scope: #514 says 'OPPS/PFS reference data' and I cut over only OPPS, leaving PFS — the largest reference domain, 23.5M rows across 8 tables — entirely on the monolith, including pfs.* queries in the very notebook whose OPPS query was migrated. This completes PFS the same way: - publish_opps_to_lake.py → publish_reference_to_lake.py with a schema registry (opps: 3 tables, pfs: 8); host-side docker-exec wrapper extracted to dev/scripts/_lake.py, shared by the ingests. - PFS published to the lake and read-back verified: carrier_locality 21,863,770 rows in 10.1s, plus rvu/gpci/clinical_labor/medical_ equipment/medical_supply/physician_work_time/zip_carrier_locality. - New dev/scripts/ingest_pfs.py wraps pfs.pipe.load_all (previously ad-hoc, no entrypoint) with the standard plumbing: duckdb_batch preflight, replica refresh, lake publish. - 5 notebooks migrated: pfs_calcs, pfs_reconciliation, skin_sub_budget_neutrality read the lake as their primary connection; skin_sub_pricing and skin_sub_cost_sharing switch their pure-pfs cells to the lake. The one cross-source join (pfs × skin_subs) stays on the monolith mirror, annotated. All 5 headless-verified in prod: zero cell errors. - pfs_calcs leaves the pre-commit host-run safe list (the lake catalog is compose-internal); the nightly integration covers it in-container.
87 lines
2.6 KiB
Python
87 lines
2.6 KiB
Python
"""M4 pilot (#513): publish OPPS reference tables to the DuckLake lakehouse.
|
|
|
|
Reads the OPPS tables from the local DuckDB (read replica when present)
|
|
and writes them to DuckLake via ``aco.lake.DuckLakeContext.save`` — the
|
|
Context write path this pilot exists to exercise. Config comes from
|
|
``stack.toml [lake.ducklake]``; the postgres catalog password from the
|
|
``POSTGRES_PASSWORD`` env var.
|
|
|
|
The catalog host and RustFS are compose-internal, so this runs inside a
|
|
data-network container:
|
|
|
|
docker exec -e POSTGRES_PASSWORD=... notebooks \\
|
|
env PYTHONPATH=/home/kert/src uv run --project /home/kert/workspace \\
|
|
python /tmp/publish_opps_to_lake.py
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import time
|
|
|
|
SCHEMAS: dict[str, tuple[str, ...]] = {
|
|
"opps": ("addendum_b", "apc_weight", "skin_sub_addendum_b"),
|
|
"pfs": (
|
|
"carrier_locality",
|
|
"clinical_labor",
|
|
"gpci",
|
|
"medical_equipment",
|
|
"medical_supply",
|
|
"physician_work_time",
|
|
"rvu",
|
|
"zip_carrier_locality",
|
|
),
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--schemas",
|
|
nargs="*",
|
|
default=list(SCHEMAS),
|
|
choices=list(SCHEMAS),
|
|
help="reference schemas to publish",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
import narwhals as nw
|
|
|
|
from aco.lake import DuckLakeContext
|
|
from conf import cfg, connect
|
|
|
|
pw = os.environ.get("POSTGRES_PASSWORD", "")
|
|
dsn = cfg.lake.ducklake.catalog
|
|
if dsn.startswith("postgres:") and pw:
|
|
dsn = f"{dsn} password={pw}"
|
|
|
|
ctx = DuckLakeContext(
|
|
catalog_dsn=dsn,
|
|
data_path=cfg.lake.ducklake.data_path,
|
|
s3_endpoint=cfg.lake.ducklake.s3_endpoint,
|
|
read_only=False,
|
|
)
|
|
|
|
src = connect.duckdb("aco") # read-only; resolves to the replica
|
|
for schema in args.schemas:
|
|
for table in SCHEMAS[schema]:
|
|
t = time.time()
|
|
df = src.execute(f"SELECT * FROM {schema}.{table}").pl() # noqa: S608
|
|
ctx.save(f"{schema}.{table}", nw.from_native(df), mode="replace")
|
|
back = nw.to_native(ctx.load(f"{schema}.{table}"))
|
|
status = "OK" if back.height == df.height else "MISMATCH"
|
|
print(
|
|
f" {schema}.{table}: {df.height} rows → lake "
|
|
f"({back.height} read back) [{status}] in {time.time() - t:.1f}s"
|
|
)
|
|
if status != "OK":
|
|
return 1
|
|
src.close()
|
|
print("lake publish complete")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|