Skip to content

Commit d0b55dd

Browse files
Changes for staging data and testing for non part D claim pruning,
naming refactor for accuracy, refactor query logic into base_model.py to match current pruning
1 parent 26a0142 commit d0b55dd

4 files changed

Lines changed: 118 additions & 70 deletions

File tree

apps/bfd-pipeline-idr/model/base_model.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,3 +865,44 @@ def stale_phase_1_claims_query(
865865
""",
866866
(cutoff_date, cutoff_date),
867867
)
868+
869+
870+
def non_latest_non_part_d_claim_items_query(
871+
item_table: str,
872+
cutoff_date: datetime,
873+
) -> tuple[str, tuple[datetime]]:
874+
part_d_codes = ",".join(str(code) for code in PART_D_CLAIM_TYPE_CODES)
875+
876+
return (
877+
f"""
878+
SELECT item.clm_uniq_id, item.bfd_row_id
879+
FROM {item_table} item
880+
JOIN {IDR_CLAIM_TABLE} clm ON clm.clm_uniq_id = item.clm_uniq_id
881+
WHERE clm.clm_ltst_clm_ind = 'N'
882+
AND clm.clm_type_cd NOT IN ({part_d_codes})
883+
AND clm.clm_idr_ld_dt < %s
884+
ORDER BY item.clm_uniq_id, item.bfd_row_id
885+
LIMIT {PHASE_1_PRUNE_BATCH_LIMIT}
886+
""",
887+
(cutoff_date,),
888+
)
889+
890+
891+
def non_latest_non_part_d_parent_claims_query(
892+
claim_table: str,
893+
cutoff_date: datetime,
894+
) -> tuple[str, tuple[datetime]]:
895+
part_d_codes = ",".join(str(code) for code in PART_D_CLAIM_TYPE_CODES)
896+
897+
return (
898+
f"""
899+
SELECT clm.clm_uniq_id
900+
FROM {claim_table} clm
901+
WHERE clm.clm_ltst_clm_ind = 'N'
902+
AND clm.clm_type_cd NOT IN ({part_d_codes})
903+
AND clm.clm_idr_ld_dt < %s
904+
ORDER BY clm.clm_uniq_id
905+
LIMIT {PHASE_1_PRUNE_BATCH_LIMIT}
906+
""",
907+
(cutoff_date,),
908+
)

apps/bfd-pipeline-idr/pipeline_stages.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
prune_bene_lis_cmbnd,
4242
prune_bene_ma_part_d,
4343
prune_bene_ma_part_d_rx,
44-
prune_non_latest_part_d_ss_claim_items,
45-
prune_non_latest_part_d_ss_parent_claims,
44+
prune_non_latest_non_part_d_ss_claim_items,
45+
prune_non_latest_non_part_d_ss_parent_claims,
4646
prune_phase_1_ss_claims,
4747
)
4848
from settings import enable_prior_auth_ingestion
@@ -178,13 +178,13 @@ def _stage5_prune_obsolete_rows(self) -> Stage[bool]:
178178
self.start_time,
179179
)
180180
yield functools.partial(
181-
prune_non_latest_part_d_ss_claim_items,
181+
prune_non_latest_non_part_d_ss_claim_items,
182182
model,
183183
self.load_mode,
184184
self.start_time,
185185
)
186186
yield functools.partial(
187-
prune_non_latest_part_d_ss_parent_claims,
187+
prune_non_latest_non_part_d_ss_parent_claims,
188188
model,
189189
self.load_mode,
190190
self.start_time,

apps/bfd-pipeline-idr/pipeline_utils.py

Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
CLAIM_PROFESSIONAL_SS_TABLE,
1616
DEFAULT_MAX_DATE,
1717
DEFAULT_PARTITION,
18-
IDR_CLAIM_TABLE,
19-
PART_D_CLAIM_TYPE_CODES,
2018
PHASE_1_CUTOFF,
2119
)
2220
from extractor import PostgresExtractor, SnowflakeExtractor, Source
@@ -25,6 +23,8 @@
2523
from model.base_model import (
2624
LoadMode,
2725
T,
26+
non_latest_non_part_d_claim_items_query,
27+
non_latest_non_part_d_parent_claims_query,
2828
stale_phase_1_claims_query,
2929
)
3030
from model.idr_beneficiary_low_income_subsidy_cmbnd import IdrBeneficiaryLowIncomeSubsidyCmbnd
@@ -171,7 +171,7 @@ def prune_phase_1_ss_claims(
171171
return True
172172

173173

174-
def prune_non_latest_part_d_ss_claim_items(
174+
def prune_non_latest_non_part_d_ss_claim_items(
175175
cls: type[T],
176176
load_mode: LoadMode,
177177
job_start: datetime,
@@ -182,37 +182,29 @@ def prune_non_latest_part_d_ss_claim_items(
182182
return True
183183

184184
prune_cutoff_date = job_start - timedelta(days=PHASE_1_CUTOFF)
185-
part_d_codes = ",".join(str(code) for code in PART_D_CLAIM_TYPE_CODES)
186185

187186
logger.info("pruning non-latest non-Part-D ss claim items older than {}", prune_cutoff_date)
188187

189-
with psycopg.connect(get_connection_string(load_mode)) as conn, conn.transaction():
188+
prune_query, params = non_latest_non_part_d_claim_items_query(item_table, prune_cutoff_date)
189+
190+
with psycopg.connect(get_connection_string(load_mode)) as conn:
190191
while True:
191-
# Claim items can exist even when the non-latest parent claim was filtered
192-
# before final claim-table load, so use the source claim table.
193-
res = conn.execute(
194-
f"""
195-
DELETE FROM {item_table}
196-
WHERE (clm_uniq_id, bfd_row_id) IN (
197-
SELECT item.clm_uniq_id, item.bfd_row_id
198-
FROM {item_table} item
199-
JOIN {IDR_CLAIM_TABLE} clm ON clm.clm_uniq_id = item.clm_uniq_id
200-
WHERE clm.clm_ltst_clm_ind = 'N'
201-
AND clm.clm_type_cd NOT IN ({part_d_codes})
202-
AND clm.clm_idr_ld_dt < %s
203-
LIMIT %s
192+
with conn.transaction():
193+
res = conn.execute(
194+
f"""
195+
DELETE FROM {item_table}
196+
WHERE (clm_uniq_id, bfd_row_id) IN ({prune_query})
197+
""", # type: ignore
198+
params,
204199
)
205-
""", # type: ignore
206-
(prune_cutoff_date, PHASE_1_PRUNE_BATCH_LIMIT),
207-
)
208-
logger.info("pruned {} rows from {}", res.rowcount, item_table)
209-
if res.rowcount < PHASE_1_PRUNE_BATCH_LIMIT:
210-
break
200+
logger.info("pruned {} rows from {}", res.rowcount, item_table)
201+
if res.rowcount < PHASE_1_PRUNE_BATCH_LIMIT:
202+
break
211203

212204
return True
213205

214206

215-
def prune_non_latest_part_d_ss_parent_claims(
207+
def prune_non_latest_non_part_d_ss_parent_claims(
216208
cls: type[T],
217209
load_mode: LoadMode,
218210
job_start: datetime,
@@ -222,28 +214,24 @@ def prune_non_latest_part_d_ss_parent_claims(
222214
return True
223215

224216
prune_cutoff_date = job_start - timedelta(days=PHASE_1_CUTOFF)
225-
part_d_codes = ",".join(str(code) for code in PART_D_CLAIM_TYPE_CODES)
226217

227218
logger.info("pruning non-latest non-Part-D ss parent claims older than {}", prune_cutoff_date)
228219

229-
with psycopg.connect(get_connection_string(load_mode)) as conn, conn.transaction():
220+
prune_query, params = non_latest_non_part_d_parent_claims_query(claim_table, prune_cutoff_date)
221+
222+
with psycopg.connect(get_connection_string(load_mode)) as conn:
230223
while True:
231-
res = conn.execute(
232-
f"""
233-
DELETE FROM {claim_table}
234-
WHERE clm_uniq_id IN (
235-
SELECT clm.clm_uniq_id FROM {claim_table} clm
236-
WHERE clm.clm_ltst_clm_ind = 'N'
237-
AND clm.clm_type_cd NOT IN ({part_d_codes})
238-
AND clm.clm_idr_ld_dt < %s
239-
LIMIT %s
224+
with conn.transaction():
225+
res = conn.execute(
226+
f"""
227+
DELETE FROM {claim_table}
228+
WHERE clm_uniq_id IN ({prune_query})
229+
""", # type: ignore
230+
params,
240231
)
241-
""", # type: ignore
242-
(prune_cutoff_date, PHASE_1_PRUNE_BATCH_LIMIT),
243-
)
244-
logger.info("pruned {} rows from {}", res.rowcount, claim_table)
245-
if res.rowcount < PHASE_1_PRUNE_BATCH_LIMIT:
246-
break
232+
logger.info("pruned {} rows from {}", res.rowcount, claim_table)
233+
if res.rowcount < PHASE_1_PRUNE_BATCH_LIMIT:
234+
break
247235

248236
return True
249237

apps/bfd-pipeline-idr/test_pipeline.py

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,42 @@ def _do_test_pipeline(conn: Connection[DictRow], load_type: LoadType) -> None:
9393
rows = cur.fetchmany(1)
9494
assert rows[0]["mbi_num"] == "1OX4Y88RV68"
9595

96+
# Seed a parent claim so the prune job has a row to delete.
97+
# CSV covers item pruning because non-latest parents do not load.
98+
if load_type == LoadType.INCREMENTAL:
99+
claim_table = sql.Identifier("idr", "claim_institutional_ss")
100+
cur = conn.execute(
101+
"select * from idr.claim_institutional_ss where clm_uniq_id = 123359318723"
102+
)
103+
assert cur.rowcount == 1
104+
row = cur.fetchone()
105+
assert row is not None
106+
107+
non_latest_parent_claim = dict(row)
108+
non_latest_parent_claim["clm_uniq_id"] = 999999434801
109+
non_latest_parent_claim["clm_ltst_clm_ind"] = "N"
110+
non_latest_parent_claim["clm_type_cd"] = 2081
111+
# Make the seeded claim old enough to be picked up by the prune job.
112+
non_latest_parent_claim["clm_idr_ld_dt"] = datetime(2019, 6, 13, tzinfo=UTC).date()
113+
114+
if "bfd_row_id" in non_latest_parent_claim:
115+
non_latest_parent_claim["bfd_row_id"] = 999999434801
116+
117+
if "clm_dt_sgntr_sk" in non_latest_parent_claim:
118+
non_latest_parent_claim["clm_dt_sgntr_sk"] = 999999434801
119+
120+
conn.execute(
121+
t"""
122+
INSERT INTO {claim_table:i} (
123+
{sql.SQL(", ").join(sql.Identifier(k) for k in non_latest_parent_claim):q}
124+
)
125+
VALUES (
126+
{sql.SQL(", ").join(non_latest_parent_claim.values()):q}
127+
)
128+
"""
129+
)
130+
conn.commit()
131+
96132
cur = conn.execute("select max(last_ts) as max_ts from idr.load_progress")
97133
row = cur.fetchone()
98134
assert row is not None
@@ -215,22 +251,17 @@ def _do_test_pipeline(conn: Connection[DictRow], load_type: LoadType) -> None:
215251
rows = cur.fetchmany(1)
216252
assert rows[0]["clm_uniq_id"] == 113370100080
217253

218-
# Phase 1 SS (PAC) claims older than 60 days will be pruned on incremental loads
219-
if load_type == LoadType.INITIAL:
220-
cur = conn.execute("select * from idr.claim_institutional_ss order by clm_uniq_id")
221-
assert cur.rowcount == 21
222-
rows = cur.fetchmany(1)
223-
assert rows[0]["clm_uniq_id"] == 123359318723
224-
else:
225-
cur = conn.execute("select * from idr.claim_institutional_ss order by clm_uniq_id")
226-
assert cur.rowcount == 9
227-
rows = cur.fetchmany(1)
228-
assert rows[0]["clm_uniq_id"] == 849348853948
229-
230254
# Non-latest non-Part-D SS parent claims are filtered before final claim-table load
231255
cur = conn.execute("select * from idr.claim_institutional_ss where clm_uniq_id = 999999434800")
232256
assert cur.rowcount == 0
233257

258+
# Existing non-latest non-Part-D SS parent claims are pruned on incremental loads
259+
if load_type == LoadType.INCREMENTAL:
260+
cur = conn.execute(
261+
"select * from idr.claim_institutional_ss where clm_uniq_id = 999999434801"
262+
)
263+
assert cur.rowcount == 0
264+
234265
cur = conn.execute("select * from idr.claim_professional_nch order by clm_uniq_id")
235266
assert cur.rowcount == 51
236267
rows = cur.fetchmany(1)
@@ -251,18 +282,6 @@ def _do_test_pipeline(conn: Connection[DictRow], load_type: LoadType) -> None:
251282
rows = cur.fetchmany(1)
252283
assert rows[0]["clm_uniq_id"] == 113370100080
253284

254-
# Phase 1 SS (PAC) claims older than 60 days will be pruned on incremental loads
255-
if load_type == LoadType.INITIAL:
256-
cur = conn.execute("select * from idr.claim_item_institutional_ss order by clm_uniq_id")
257-
assert cur.rowcount == 328
258-
rows = cur.fetchmany(1)
259-
assert rows[0]["clm_uniq_id"] == 123359318723
260-
else:
261-
cur = conn.execute("select * from idr.claim_item_institutional_ss order by clm_uniq_id")
262-
assert cur.rowcount == 151
263-
rows = cur.fetchmany(1)
264-
assert rows[0]["clm_uniq_id"] == 849348853948
265-
266285
# Items for non-latest non-Part-D SS claims are pruned on incremental loads
267286
cur = conn.execute(
268287
"select * from idr.claim_item_institutional_ss where clm_uniq_id = 999999434800"

0 commit comments

Comments
 (0)