fix(cli): stack pfs elements --dry-run reads the replica, never the writer (refs #684)
This commit is contained in:
@@ -66,6 +66,16 @@ def _classifier() -> Any:
|
|||||||
return closed_vocab_classifier(cfg, HostPool.from_config(cfg))
|
return closed_vocab_classifier(cfg, HostPool.from_config(cfg))
|
||||||
|
|
||||||
|
|
||||||
|
def _run_elements(
|
||||||
|
con: Any, store: Any, targets: list[str], classify: Any, *, write: bool
|
||||||
|
) -> None:
|
||||||
|
for c in targets:
|
||||||
|
x = extract_code(store, con, c, classify=classify)
|
||||||
|
if write:
|
||||||
|
write_elements(con, c, x.rows, x.reviews)
|
||||||
|
typer.echo(f"{c}: {len(x.rows)} elements, {len(x.reviews)} for review")
|
||||||
|
|
||||||
|
|
||||||
def _codes_for(con: Any, codes: list[str], family: str, all_payable: bool) -> list[str]:
|
def _codes_for(con: Any, codes: list[str], family: str, all_payable: bool) -> list[str]:
|
||||||
out: list[str] = [c.upper() for c in codes]
|
out: list[str] = [c.upper() for c in codes]
|
||||||
if family:
|
if family:
|
||||||
@@ -106,15 +116,21 @@ def elements(
|
|||||||
"""Extract typed elements for codes into pfs.code_element (+ review queue)."""
|
"""Extract typed elements for codes into pfs.code_element (+ review queue)."""
|
||||||
store = _store()
|
store = _store()
|
||||||
classify = None if no_llm else _classifier()
|
classify = None if no_llm else _classifier()
|
||||||
with _batch() as con:
|
if dry_run:
|
||||||
ensure_tables(con)
|
# A preview never contends for the DuckDB single-writer lock a
|
||||||
targets = _codes_for(con, code, family, all_payable)
|
# notebook may be holding (#508-#514) — read the replica, don't
|
||||||
for c in targets:
|
# open a batch writer.
|
||||||
x = extract_code(store, con, c, classify=classify)
|
con = _read()
|
||||||
if not dry_run:
|
try:
|
||||||
write_elements(con, c, x.rows, x.reviews)
|
targets = _codes_for(con, code, family, all_payable)
|
||||||
typer.echo(f"{c}: {len(x.rows)} elements, {len(x.reviews)} for review")
|
_run_elements(con, store, targets, classify, write=False)
|
||||||
if not dry_run:
|
finally:
|
||||||
|
con.close()
|
||||||
|
else:
|
||||||
|
with _batch() as con:
|
||||||
|
ensure_tables(con)
|
||||||
|
targets = _codes_for(con, code, family, all_payable)
|
||||||
|
_run_elements(con, store, targets, classify, write=True)
|
||||||
_publish()
|
_publish()
|
||||||
|
|
||||||
|
|
||||||
@@ -155,7 +171,7 @@ def families(write: bool = typer.Option(False, "--write")) -> None:
|
|||||||
"UNION SELECT DISTINCT hcpcs FROM pfs.rvu WHERE year = (SELECT max(year) FROM pfs.rvu) AND status_code IN ('A','R','T')"
|
"UNION SELECT DISTINCT hcpcs FROM pfs.rvu WHERE year = (SELECT max(year) FROM pfs.rvu) AND status_code IN ('A','R','T')"
|
||||||
).fetchall()
|
).fetchall()
|
||||||
]
|
]
|
||||||
elements = {c: read_elements(con, c) for c in codes}
|
elements_by_code = {c: read_elements(con, c) for c in codes}
|
||||||
events = {c: read_events(con, c) for c in codes}
|
events = {c: read_events(con, c) for c in codes}
|
||||||
descriptions = {
|
descriptions = {
|
||||||
r[0]: r[1]
|
r[0]: r[1]
|
||||||
@@ -163,7 +179,7 @@ def families(write: bool = typer.Option(False, "--write")) -> None:
|
|||||||
"SELECT hcpcs, arg_max(description, year) FROM pfs.rvu WHERE mod IS NULL OR mod = '' GROUP BY hcpcs"
|
"SELECT hcpcs, arg_max(description, year) FROM pfs.rvu WHERE mod IS NULL OR mod = '' GROUP BY hcpcs"
|
||||||
).fetchall()
|
).fetchall()
|
||||||
}
|
}
|
||||||
rows = derive_families(elements, events, descriptions)
|
rows = derive_families(elements_by_code, events, descriptions)
|
||||||
by_key: dict[str, list[str]] = {}
|
by_key: dict[str, list[str]] = {}
|
||||||
for r in rows:
|
for r in rows:
|
||||||
by_key.setdefault(r.key, []).append(f"{r.code}({r.role})")
|
by_key.setdefault(r.key, []).append(f"{r.code}({r.role})")
|
||||||
|
|||||||
@@ -121,10 +121,27 @@ class TestElements:
|
|||||||
"extract_code",
|
"extract_code",
|
||||||
lambda s, c, code, *, classify=None: Extraction(code, (), ()),
|
lambda s, c, code, *, classify=None: Extraction(code, (), ()),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def fail_batch():
|
||||||
|
raise AssertionError("--dry-run must not open a RW duckdb_batch connection")
|
||||||
|
|
||||||
|
monkeypatch.setattr(pfs_cli, "_batch", fail_batch)
|
||||||
|
|
||||||
|
read_calls = []
|
||||||
|
orig_read = pfs_cli._read
|
||||||
|
|
||||||
|
def spy_read():
|
||||||
|
read_calls.append(True)
|
||||||
|
return orig_read()
|
||||||
|
|
||||||
|
monkeypatch.setattr(pfs_cli, "_read", spy_read)
|
||||||
|
|
||||||
res = runner.invoke(
|
res = runner.invoke(
|
||||||
app, ["pfs", "elements", "--code", "G0556", "--no-llm", "--dry-run"]
|
app, ["pfs", "elements", "--code", "G0556", "--no-llm", "--dry-run"]
|
||||||
)
|
)
|
||||||
assert res.exit_code == 0 and con.published == []
|
assert res.exit_code == 0, res.output
|
||||||
|
assert con.published == []
|
||||||
|
assert read_calls == [True]
|
||||||
|
|
||||||
def test_all_payable_selects_art_status_in_newest_year(self, con, monkeypatch):
|
def test_all_payable_selects_art_status_in_newest_year(self, con, monkeypatch):
|
||||||
seen = []
|
seen = []
|
||||||
|
|||||||
Reference in New Issue
Block a user