DEV-41601: ms_extractlabs changes and rationale. Marp source with a Rosé Pine color scheme.

6.8 kB · 219 lines · Created 3d ago
Markdown


Making ms_extractlabs easier to maintain #

DEV-41601 · Code changes and rationale

Move repeated work into helpers. Make inputs explicit. Keep the distinct extraction rules visible.

inputfiles/macros/ms_extractlabs.sas


Make dependencies visible #

The macro now receives three inputs explicitly:

%MACRO MS_EXTRACTLABS(datafile=, lookfile=, mindate=,
                     outfile=, Lab_var=, datafilewhere=,
                     labcodsmap=, patidlen=, labpxlen=);

labcodsmap names the map in INFOLDER. patidlen and labpxlen set the storage lengths of PatID and PX.

Why: empty defaults avoid hidden references to global variables.

LABTABLE and NUMPARTITIONS remain documented global inputs.


Give each helper one job #

Two nested helpers become top-level macros with explicit inputs. The shared filter uses them in sequence:

keep=0;
%getlabres(labresult=&labresult.);
if keep=1;
%createseqadate(labdatetype=&labdatetype.);

getlabres checks result criteria. createseqadate selects Adate using the configured date priority.

Two new helpers, ms_lab_filterresults and ms_lab_writeorappend, replace repeated filter and output blocks.

Why: each shared step has one definition, in the same file.


Share repeated steps, not distinct joins #

LAB01 matches mapped attributes. LAB02 matches LOINC codes. Other code types match procedure codes and their code types.

After the LAB01 join, the macro delegates the repeated work:

%ms_lab_filterresults(indata=_lab01, outdata=_lab01,
    labresult=&LABRESULT., labdatetype=&labdatetype.,
    caresettingvar=&caresettingvar., mindate=&mindate.,
    ptlocfilter=N);
%ms_lab_writeorappend(outfile=&outfile., data=_lab01,
    cleanup=_lab01 _map);

The other paths use ptlocfilter=Y for their care-setting checks.

Why: share repeated steps without hiding different matching rules.


Use output data instead of a flag #

The helper checks OUTFILE instead of tracking newrun. Core logic, with cleanup omitted:

%isdata(dataset=&outfile.);
%if %eval(&nobs.>0) %then %do;
    proc datasets library=work nolist;
        append base=&outfile. data=&data. FORCE;
    quit;
%end;
%else %do;
    data &outfile.;
        set &data.;
    run;
%end;

Why: the row count replaces the flag. The first populated result determines the output's column order and attributes.


Keep internal state local and reject missing lengths #

%local message labdatetype labresult caresettingvar part;
%let message=;

These variables belong to the main macro's current invocation. The entry check rejects blank lengths before output creation:

%if %str(&patidlen.) eq %str() or
    %str(&labpxlen.) eq %str() %then %do;
    %put ERROR: (Sentinel) ms_extractlabs: missing required parameter(s).;
    %return;
%end;

Excerpt omits diagnostic detail lines. Empty LABTABLE and labcodsmap remain valid for the supported no-lab-data path.

Why: missing required lengths and absent lab data are different conditions.


Keep the warning inside the invocation #

The main macro declares and initializes its local message variable. The filter helper writes the missing-date warning into that variable through macro scope resolution.

At the end, the main macro prints it:

%put &message.;

The log keeps the warning. The local variable ends with the invocation.

This prevents the macro from overwriting an outer message variable. The helper still depends on the main macro's local declaration.


Recognize the remaining global side effect #

The output helper checks the row count before choosing an action:

%isdata(dataset=&outfile.);
%if %eval(&nobs.>0) %then %do;

%isdata updates the global macro variable NOBS. This value describes the output before the next write or append.

Later writes and duplicate removal can change the output count without updating NOBS.

Do not interpret NOBS at macro exit as the final output count.

Sign up or Sign in to add to the discussion.