Files
stack/tests/pfs/test_codetables.py
kert 297eef504b
Some checks failed
CI / lint (push) Successful in 30s
CI / notebooks-smoke (push) Successful in 1m25s
Infra CI / notebooks (push) Failing after 1m5s
Infra CI / zotero (push) Successful in 16s
Infra CI / docs (push) Successful in 1m32s
Infra CI / api (push) Successful in 1m5s
Infra CI / llm (push) Successful in 52s
Infra CI / mc (push) Successful in 13s
CI / lint (pull_request) Successful in 31s
CI / test (push) Successful in 17m29s
CI / notebooks-smoke (pull_request) Successful in 1m27s
Infra CI / notebooks (pull_request) Failing after 1m9s
Infra CI / zotero (pull_request) Successful in 14s
Infra CI / docs (pull_request) Successful in 13s
Infra CI / api (pull_request) Successful in 19s
Infra CI / llm (pull_request) Successful in 13s
Infra CI / mc (pull_request) Successful in 13s
CI / test (pull_request) Successful in 28m53s
fix(cli,pfs): I4/I9/I6/M3 — families/lineage read without --write, bulk element/event reads, --all-payable marked experimental, unknown-family error trimmed
I4: `stack pfs families` and `stack pfs lineage` opened a RW
`duckdb_batch` connection even without `--write`, so a plain lookup
contended for the DuckDB single-writer lock a notebook may be holding
(#508-#514). Mirror the `elements --dry-run` shape: `_read()` when
`write` is false, `_batch()` + `ensure_tables` only when true. A
replica that hasn't had `--write` run yet (no `pfs.code_element`/
`code_event`/`code_family`) prints "no derived tables yet — run with
--write" and exits 0 instead of raising.

I9: `families --write` ran `read_elements`/`read_events` once per code
(~2xN queries) inside the write lock. Added `read_all_elements`/
`read_all_events` to `pfs.codetables` — one full-table read each,
grouped in Python, same per-code ordering as the existing readers.

I6: `--all-payable` isn't viable as written (the loop isn't inverted in
this slice per the controller's ruling) — help text now says
"experimental: ~20 min of SQL before any model call" and a
`log.warning` logs the target count when it's used.

M3: the unknown-`--family` error listed every key in `FAMILIES`
(~15k once `refresh_from` has run) — list only `HAND_FAMILIES`.

Claude-Session: https://claude.ai/code/session_01Aum3pEMAM3yQVdFSdVe6Gc
2026-09-09 14:11:21 -04:00

182 lines
4.9 KiB
Python

"""pfs.codetables — DDL + writers for the code element/event/family tables."""
from __future__ import annotations
import duckdb
import pytest
from pfs.codetables import (
ElementRow,
EventRow,
FamilyRow,
ReviewRow,
ensure_tables,
read_all_elements,
read_all_events,
read_elements,
read_events,
read_families,
write_elements,
write_events,
write_families,
)
@pytest.fixture
def con():
c = duckdb.connect(":memory:")
ensure_tables(c)
yield c
c.close()
def _el(code="99490", value="consent", year=2015):
return ElementRow(
code, year, "activity", value, "", "Consent;", "DE2VH9PD", 1245, 67716, "fr"
)
class TestDDL:
def test_idempotent(self, con):
ensure_tables(con)
names = {
r[0]
for r in con.execute(
"SELECT table_name FROM information_schema.tables WHERE table_schema='pfs'"
).fetchall()
}
assert {
"code_element",
"code_element_review",
"code_event",
"code_family",
} <= names
class TestElements:
def test_write_replaces_per_code(self, con):
assert (
write_elements(
con,
"99490",
[_el(), _el(value="24-7-access")],
[ReviewRow("99490", "odd line", "activity", "", "DE2VH9PD", 1246)],
)
== 2
)
assert write_elements(con, "99490", [_el()], []) == 1
rows = read_elements(con, "99490")
assert [r.value for r in rows] == ["consent"]
assert (
con.execute(
"SELECT count(*) FROM pfs.code_element_review WHERE code='99490'"
).fetchone()[0]
== 0
)
def test_other_codes_untouched(self, con):
write_elements(con, "99490", [_el()], [])
write_elements(con, "G0556", [_el(code="G0556")], [])
write_elements(con, "99490", [], [])
assert read_elements(con, "99490") == []
assert len(read_elements(con, "G0556")) == 1
class TestReadAll:
def test_read_all_elements_matches_per_code_reader(self, con):
write_elements(
con, "99490", [_el(value="24-7-access"), _el(value="consent")], []
)
write_elements(con, "G0556", [_el(code="G0556")], [])
out = read_all_elements(con)
assert set(out) == {"99490", "G0556"}
assert out["99490"] == read_elements(con, "99490")
assert out["G0556"] == read_elements(con, "G0556")
def test_read_all_elements_empty(self, con):
assert read_all_elements(con) == {}
def test_read_all_events_matches_per_code_reader(self, con):
rows = [
EventRow(
"99439",
2021,
"replaces",
"G2058",
"99439",
"YBM4IZUS",
1578,
84639,
"fr",
True,
"",
),
EventRow("99439", 2021, "appeared", "", "", "", 0, 0, "rvu", True, ""),
]
write_events(con, "99439", rows)
write_events(
con,
"G2058",
[EventRow("G2058", 2020, "appeared", "", "", "", 0, 0, "rvu", True, "")],
)
out = read_all_events(con)
assert set(out) == {"99439", "G2058"}
assert out["99439"] == read_events(con, "99439")
assert out["G2058"] == read_events(con, "G2058")
def test_read_all_events_empty(self, con):
assert read_all_events(con) == {}
class TestEvents:
def test_roundtrip_sorted_by_year(self, con):
rows = [
EventRow(
"99439",
2021,
"replaces",
"G2058",
"99439",
"YBM4IZUS",
1578,
84639,
"fr",
True,
"",
),
EventRow("99439", 2021, "appeared", "", "", "", 0, 0, "rvu", True, ""),
]
assert write_events(con, "99439", rows) == 2
got = read_events(con, "99439")
assert [e.kind for e in got] == ["appeared", "replaces"]
assert got[1].anchored is True
class TestFamilies:
def test_full_replace(self, con):
write_families(
con,
[
FamilyRow(
"CCM", "Chronic Care Management", "99490", "base", 2015, None, "", 0
)
],
)
write_families(
con,
[
FamilyRow(
"CCM",
"Chronic Care Management",
"99439",
"add-on",
2021,
None,
"YBM4IZUS",
1578,
)
],
)
fams = read_families(con)
assert len(fams) == 1 and fams[0].code == "99439" and fams[0].role == "add-on"