Summary #
- Modularized
ms_extractlabs.sas: the nested macros are now standalone, the triplicated post-extraction and write/append logic is consolidated into helpers, and the hiddennewrunstate is removed. LABCODSMAP,PATIDLEN, andLABPXLENare explicit parameters with empty defaults;LABTABLEandNUMPARTITIONSremain ambient globals by design.- All 10 in-repo call sites across 9 files pass the new parameters.
Changes #
getlabres(labresult=)andcreateseqadate(labdatetype=)extracted to top level with explicit parameter lists; bodies unchanged apart from the parameter references.- New
ms_lab_filterresults(indata=, outdata=, labresult=, labdatetype=, caresettingvar=, mindate=, ptlocfilter=)consolidates the triplicated criteria/date/patient-location filter step. LAB01 calls it withptlocfilter=N(no pt_loc filter, exactly as before); LAB02/LABXX call it withptlocfilter=Y. - New
ms_lab_writeorappend(outfile=, data=, cleanup=)replaces thenewrun%IF/%ELSEblocks: it appends when%isdatareports that OUTFILE already holds observations, and replaces otherwise. Equivalence argument: in the old codenewrunflipped to 1 exactly when a prior replace executed with at least one observation — i.e. exactly when OUTFILE holds observations at decision time (OUTFILE is always created empty up front; a replace of an empty extraction leaves it empty). The old LAB03-only%let newrun=1;had no reader and is dropped. - Entry validation: empty
PATIDLEN/LABPXLENnow fail loudly withERROR:lines and skip the body. EmptyLABTABLE(global) and emptyLABCODSMAP(parameter) remain a supported no-labs state that still creates the empty attributed OUTFILE. - Internal symbols are
%local-declared. - Header and comments (later rounds): usage example and
@paramcorrected to the actual parameter spellingLABCODSMAP;PATIDLENdocumented as a required numeric length in bytes (3–8, per the SCDM PatID column) and the example value corrected from 18 to 8; the never-readindata.&PROCTABLE.input line dropped; the two closing%ENDguards relabeled (*is labtable;labeled the datafile check); paraphrase comments removed and the load-bearing warnings (caller-localMESSAGEcontract,NOBSreset) moved beside the statements they explain. No executable statement changed in these rounds. - Callers updated:
ms_cidanum.sas(x2),t4_cidanum.sas,t4_scenarioloop.sas,combo.sas,ms_cidacov_codeextraction.sas,t4_cidacov_codeextraction.sas,ms_concomitantuse.sas,ms_createmicohorts.sas,ms_switching.sas— each passeslabcodsmap=,patidlen=,labpxlen=resolved from the same values the old macro read ambiently (resolution-equivalent: the old code resolved them one frame deeper, in a scope that never declared them locally). - Known log-only delta: each extraction block emits extra
%isdatabanner lines fromms_lab_writeorappend, and the globalNOBSholds OUTFILE's observation count at macro exit (no in-repo consumer reads it before resetting it).
Validation #
- Statement-level equivalence: a whitespace-normalized diff of the pre-refactor macro against the new file contains exactly the intended changes (helpers moved to top level,
newrunblocks replaced by helpers, parameter renames,%local+ entry validation) and nothing else; the extracted helper bodies are byte-identical to the old nested macros modulo the parameter substitution. - Structural checks (all pass): 5 top-level macros with parameter lists, no nested definitions, every
%mendnamed, nonewrun/patid_len/labpx_lenstragglers,ptlocfilter=Nexactly once andptlocfilter=Yexactly twice, all 10 call sites carry the three new parameters. - Adversarial implementation review over the full diff found no behavioral issues in the macro or the caller updates; findings in the test harness itself were fixed.
- Not run — no SAS runtime in this environment. A full old-vs-new equivalence harness (
ms_extractlabs_equivalence_test.sas: scenarios S1-S14 covering every criteria operator, both init branches, partitioned/unpartitioned guards, replace-with-empty,Lab_var=Raw,covlabs,datafilewhere, empty lookfile, empty lab table, no-labs state, and the new entry validation) is prepared for the operator's SAS environment. It compares PROC COMPARE all-values-equal,dictionary.columnsstructure (name/varnum/type/length/format), and log-scraped WARNING lines. Merge is gated on its results.
Review notes #
- Merge stays with the operator, gated on the SAS equivalence harness results.
- The four commits include one reverted experiment (round 3 added
%symexist(LABCODESMAP)guards at six call sites after an unconfirmed warning report; round 4 reverted them when it did not reproduce). The net caller change equals round 2. call symput('message',...)insidems_lab_filterresultsdeliberately writes the missing-Adate warning into the caller's%local MESSAGE; the%let message=;at the top ofMS_EXTRACTLABSis load-bearing (commented in code).ms_mfu.sascontains near-duplicate lab logic with a different output shape and join targets; a possible future follow-up, untouched here.