/*-------------------------------------------------------------------------------------*\
|  PROGRAM NAME: scdm_standard_macros.sas                                               |
|                                                                                       |
|---------------------------------------------------------------------------------------|
|  PURPOSE:                                                                             |
|     The purpose of the program is to store macros used repeatedly in QA programs      |
|---------------------------------------------------------------------------------------|
|  PROGRAM INPUT:                                                                       |
|     see 00.0_scdm_data_qa_review_master_file.sas                                      |
|                                                                                       |
|  PROGRAM OUTPUT:                                                                      |
|     see Workplan PDF                                                                  |
|---------------------------------------------------------------------------------------|
|  CONTACT:                                                                             |
|     Sentinel Coordinating Center                                                      |
|     info@sentinelsystem.org                                                           |
\*-------------------------------------------------------------------------------------*/

*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-;
*  PLEASE DO NOT EDIT BELOW WITHOUT CONTACTING THE SENTINEL OPERATIONS CENTER           ;
*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-;

/*********************************************************************************/
/* START ==> %isBlank                                                            */
/*********************************************************************************/
/*  Checks if a macro variable is blank: outputs 1 if blank and 0 if not blank   */
/*-------------------------------------------------------------------------------*/
%macro isBlank(param);
  %sysevalf(%superq(param)=,boolean)
%mend isBlank;
/*-------------------------------------------------------------------------------*/
/* END ==> %isBlank                                                              */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %kill_directory                                                     */
/*********************************************************************************/
/*  Deletes all existing SAS datasets if {dir}=y in Master program               */
/*-------------------------------------------------------------------------------*/
%macro kill_directory (kill_list=);
  %local k;
  %do k=1 %to %sysfunc(countw(&kill_list.));
    %let dir=%scan(&kill_list.,&k.);
    %let kill=&&kill_dir_&dir.;
    %put &kill;
    %if %lowcase(&kill.)=%str(y) %then %do;
      proc datasets kill lib=&dir. memtype=data nowarn nolist nodetails;
      quit;
    %end;
  %end;
%mend kill_directory;
/*-------------------------------------------------------------------------------*/
/* END ==> %kill_directory                                                       */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %table_size                                                         */
/*********************************************************************************/
/*  Used in the L1 module retrieve the File Size of each SCDM table              */
/*-------------------------------------------------------------------------------*/
%macro table_size (libin=, dsin=, libout=, dsout=);
  %local lib ds;
  %let lib=%upcase(&libin.);
  %let ds=%upcase(&dsin.);

  proc sql noprint;
    create table %bquote(&libout..&dsout.) as
    select distinct( upcase("&tabid.") )as tabid length=3
         , PRXCHANGE('s/\d+$//', 1, trim(upcase(memname))) as memname length 32
         , sum(filesize) as filesize
         , obslen
         , nvar
         , sum(npage) as npage
         , compress
         , pcompress
    from dictionary.tables
    where libname="&lib." and calculated memname = "&ds."
    group by calculated memname
    ;
  quit;
%mend table_size;


/*********************************************************************************/
/* START ==> %remove_labels                                                      */
/*********************************************************************************/
/*  Used to delete all variable labels from any dataset                          */
/*-------------------------------------------------------------------------------*/
%macro remove_labels(lib, ds);
  proc datasets nolist lib=&lib. memtype=data;
    modify &ds.;
    attrib _all_ label=' ';
  quit;
%mend remove_labels;
/*-------------------------------------------------------------------------------*/
/* END ==> %remove_labels                                                        */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %redirect_libs                                                      */
/*********************************************************************************/
/*  Redirect subdirectory macro variables and libraries back to specified root   */
/*   folder                                                                      */
/*-------------------------------------------------------------------------------*/
%macro redirect_libs(root);

/* Define request specific subdirectories */
%let DPLOCAL=&root./dplocal/ ;
%let MSOC= &root./msoc/ ;
%let INFOLDER= &root./inputfiles/;
%let SASPROGRAMS= &root./sasprograms/ ;

/* Assign libnames */
%soc_lib(DPLOCAL,&DPLOCAL)
%soc_lib(MSOC,&MSOC)
%soc_lib(INFOLDER,&INFOLDER, options=%str(access=readonly))

%mend redirect_libs;
/*-------------------------------------------------------------------------------*/
/* END ==> %redirect_libs                                                        */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %dircopy                                                            */
/*********************************************************************************/
/*  Copy all files from one directory to another                                 */
/*                                                                               */
/*  Code to read directories recursively (outputting dirs_found and files_found  */
/*    datasets) pulled from https://www.lexjansen.com/wuss/2012/55.pdf           */
/*-------------------------------------------------------------------------------*/
%macro dircopy (indir=,outdir=);

/* Obtain list of all directories within &indir, and list of files within each directory */
data dirs_found (compress=no);
 length Root $256.;
 root="&indir.";
 output;
run;

data dirs_found files_found (compress=no);
  keep Path FileName;
  length fref $8 Filename $256;
  /* Read the name of a directory to search. */
  modify dirs_found;
  /* Make a copy of the name, because we might reset root. */
  Path = root;
  rc = filename(fref, path);
  if rc = 0 then do;
    did = dopen(fref);
    rc = filename(fref);
  end;
  else do;
    length msg $200.;
    msg = sysmsg();
    putlog msg=;
    did = .;
  end;
  if did <= 0 then do;
    putlog 'ERR' 'OR: Unable to open ' Path=;
    return;
  end;
  dnum = dnum(did);
  do i = 1 to dnum;
    filename = dread(did, i);
    fid = mopen(did, filename);
    if fid > 0 then do;
  output files_found;
  end;
  else do;
/*  A directory name was found; calculate the complete */
/*  path, and add it to the dirs_found data set, */
/*  where it will be read in the next iteration of this */
/*  data step. */
    root = catt(path, "/", filename);
    output dirs_found;
  end;
  end;
  rc = dclose(did);
run;

/* Obtain list of output directories to copy files to */
data dirs_all;
  set dirs_found;
  outpath=tranwrd(root,"&indir","&outdir");
run;

/* Create output directory folders if they don't exist */
options dlcreatedir;
data _null_;
  set dirs_all;
  call execute("libname folder " ||'"'||trim(outpath)||'";');
run;
options nodlcreatedir;

/* Create macro variable list for each directory and output directory */
proc sql noprint;
  select root, count(root), outpath
  into  :dirlist separated by '*', :dircnt, :outlist separated by '*'
  from dirs_all
  ;
quit;

%do i=1 %to &dircnt;

  %let dir=%scan(&dirlist.,&i.,'*');
  %let copydir=%scan(&outlist.,&i.,'*');

  /* Obtain list of files in directory to copy */
  proc sql noprint;
    select filename, count(filename)  into :flist separated by '*', :fcnt
    from files_found
    where path="&dir."
    ;
  quit;

  %do j=1 %to &fcnt;
    %let file=%scan(&flist.,&j.,'*');

    filename source "&dir./&file" recfm=n;
    filename dest "&copydir./&file" recfm=n;

    data _null_;
      length msg $500.;
      rc=fcopy('source', 'dest');
         if rc=0 then
          put "Copied source file &dir./&file to destination &copydir.";
       else do;
          msg=sysmsg();
          put rc= msg=;
       end;
    run;

  %end;
%end;

%mend dircopy;
/*-------------------------------------------------------------------------------*/
/* END ==> %dircopy                                                              */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %set_ds                                                             */
/*********************************************************************************/
/*  Used in all table modules to combine datasets based on the specified prefix  */
/*-------------------------------------------------------------------------------*/
%macro set_ds (libin=, dsin_prefix=, libout=, dsout=);
  %let libin=%upcase(&libin.);
  %let dsin_pre=%upcase(&dsin_prefix.);
  proc sql noprint;
    select count(memname) into :exist trimmed
    from dictionary.members
    where libname="&libin." and memtype='DATA' and memname contains "&dsin_pre.";
  quit;

  %if &exist. > 0 %then %do;
    data &libout..&dsout.;
      set &libin..&dsin_prefix.:;
      format coun: comma15.;
    run;
    proc datasets lib=&libin. nowarn nolist nodetails;
      delete &dsin_prefix.:;
    quit;
  %end;
%mend;
/*-------------------------------------------------------------------------------*/
/* END ==> %set_ds                                                               */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %set_ds_varlength                                                   */
/*********************************************************************************/
/*  Used in all table modules to combine datasets based on the specified prefix  */
/*  but change column length of variable to the max length of all tables         */
/*-------------------------------------------------------------------------------*/
%macro set_ds_varlength (dsin_prefix=, dsout=);
  %local vlen;
  %let vlen = 0;
  proc sql noprint;
    select max(length) into :vlen
    from dictionary.columns
    where libname="DPLOCAL" and memtype='DATA' and memname contains "%upcase(&dsin_prefix.)" and upper(name)='VALUE'
    group by name;
  quit;

  %if &vlen ^= 0 %then %do;
    data dplocal.&dsout. (drop=temp);
        length Value $&vlen. temp $&vlen.;
        set dplocal.&dsin_prefix.: (rename=(value=temp));
        value=temp;
        format coun: comma15.;
    run;

    proc datasets lib=dplocal nowarn nolist nodetails;
      delete &dsin_prefix.:;
    quit;
  %end; /* End if datasets exists */
%mend;
/*-------------------------------------------------------------------------------*/
/* END ==> %set_ds_varlength;                                                    */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %get_flagid                                                         */
/*********************************************************************************/
/*  Used in L2 and table modules to add flagid, etc. to the flags dataset.       */
/*    ntabs=number of SCDM tables referenced in FlagID (1 or 2)                  */
/*    nvars=number of variables referenced in FlagID (1 or 2)                    */
/*    checkid=CheckID value for flagid                                           */
/*    tabid1=tabid or table1 value                                               */
/*    tabid2=table2 value                                                        */
/*    var1=variable1 value                                                       */
/*    var2=variable2 value                                                       */
/*    i=row number used to append to dataset name                                */
/*    Note:  Currently, this will only work for up to 2 variable checks.         */
/*-------------------------------------------------------------------------------*/
%macro get_flagid (ntabs=, nvars=, checkid=, tabid1=, tabid2=, var1=, var2=, i=);

  %let countvar=;
  %let temp_nobs=0;
  data _null_;
    length dummy 3;
    if 0 then set flag nobs=flagid_n;
    /* If there is no count variable, create dummy to prevent array warning */
    dummy=1;
    array numerics {*} _numeric_;
    do i = 1 to dim(numerics);
      if index(lowcase(vname(numerics{i})),'count') then call symputx('countvar',vname(numerics{i}));
    end;
    call symputx('temp_nobs',flagid_n);
    stop ;
  run;

  /*continue only if there is at least one row in the flags dataset */
  %if %eval(&temp_nobs. gt 0) %then %do;

/* Join existing temp flags dataset with the flags lkp table to get the following
     variables: flagid, flag_descr, flagtype, abortyn */
    proc sql noprint;
      create table
    /* name of new flags dataset will depend on macro variable ntabs */
    %if %eval(&ntabs.=1) %then %do; %str(flag_l2_&checkid._&tabid1._&i) %end;
    %else %if %eval(&ntabs.=2) %then %do; %str(flag_l2_&checkid._&tabid1._&tabid2._&i) %end;
      as
      select b.flagid
           , b.flag_descr
           , b.flagtype
           , b.abortyn
  /* if a 'count' variable already exists in temp dataset, sum the values and rename as 'count' */
    %if %length(&countvar.) > 0 %then %do;
      , sum(a.&countvar.) as count
    %end;
  /* else count the rows and create variable 'count' */
    %else %do;
      , count(*) as count
    %end;
      from (select *, "&checkid." as checkid from flag) as a
  /* perform the join based on the number of SCDM tables and variables specified in ntabs and nvars */
    /* if the flagid only references one SCDM table (e.g. Tabid=ENR) */
    %if %eval(&ntabs.=1) %then %do;
                    , temp_l2_flags(where=(checkid="&checkid." and lowcase(tableid)=lowcase("&tabid1.")
      %if %eval(&nvars. gt 0) %then %do;
        and lowcase(variable1)=lowcase("&var1.")
      %end;
      %if %eval(&nvars.=2) %then %do;
        and lowcase(variable2)=lowcase("&var2.")
      %end;
      )) as b
    where a.checkid=b.checkid
    %end;
    /* if the flagid references 2 SCDM table (e.g. Tabid=DEM-ENR) */
    %else %if %eval(&ntabs.=2) %then %do;
      left join temp_l2_flags(where=(checkid="&checkid."
      %if %eval(&nvars. gt 0) %then %do;
        and lowcase(variable1)=lowcase("&var1.")
      %end;
      %if %eval(&nvars.=2) %then %do;
        and lowcase(variable2)=lowcase("&var2.")
      %end;
      )) as b
      on a.table1=b.table1 and a.table2=b.table2
    %end;
    group by b.flagid,b.flag_descr,b.flagtype,b.abortyn;

    drop table flag;
    quit;
  %end;

  %else %do;
    proc datasets lib=work nolist nodetails nowarn;
      delete flag;
    quit;
  %end;
%mend get_flagid;
/*-------------------------------------------------------------------------------*/
/*  END ==> %get_flagid                                                          */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %ds_nodupkey - subset of l2_ds_nodupkey                             */
/*********************************************************************************/
%macro ds_nodupkey;

    %let keyvarlist=;
    %let dupct=0 ;

    %local tabid_exc_lc;
    %let tabid_exc_lc=%lowcase(%bquote(&tabid_exc)) ;

    proc sql noprint;
      select lowcase(variable)
      into :keyvarlist separated by ' '
      from infolder.lkp_all_l1(where=(lowcase(tabid)="&tabid."))
      where upcase(keyvar)='K';
    quit;

    %if %length(&keyvarlist) > 0 %then %do;
        %if %lowcase(&tabid.)=enr %then %do;

          data _null_;
            count_obs = &&&tabid._nobs ;
            call symputx('nobs',put(count_obs,best12.));
          run;

          data _null_;
            if 0 then set dplocal.l2_distinctkey_enr nobs=flag_nobs;
            call symputx('distinctnobs',flag_nobs);
            stop ;
          run;

          %let dupct=%eval(&nobs-&distinctnobs);
        %end;
        %else %do;

          %if &do_partitions and not %eval(%lowcase(&tabid.) in &tabid_exc_lc) %then %do ;
            data t_view / view=t_view ;
              set qadata.&&&tabid.table.: (keep=&keyvarlist);
            ;run;
            proc sort data = t_view nodupkey out=_null_ dupout=_dup_data;
              by &keyvarlist;
            run;
            proc sql noprint;
              drop view t_view ;
            ;quit;
          %end;
          %else %do ;
            proc sort data = qadata.&&&tabid.table (keep=&keyvarlist) nodupkey out=_null_ dupout=_dup_data;
              by &keyvarlist;
            run;
          %end ;

          data _null_;
            if 0 then set _dup_data nobs=dupnobs;
            call symputx('dupct', dupnobs);
            stop ;
          run;

        %end;
      %put **** Number of duplicate rows in the &tabid. table: &dupct.;

    %end;

    data dplocal.nobs_dup_&tabid.;
      length TabID $3 count_Dup 8;
      tabid=upcase("&tabid.");
      count_dup=&dupct;
      format count_dup comma15.;
    run;

%mend ds_nodupkey;
/*-------------------------------------------------------------------------------*/
/* END ==> %ds_nodupkey                                                          */
/*-------------------------------------------------------------------------------*/



/*********************************************************************************/
/* START ==> %l2_ds_enr_overlap                                                  */
/*********************************************************************************/
/*  Identify and capture invalid enrollment spans                                */
/*  Create intermediate dataset with valid enrollment spans                      */
/*
/*-------------------------------------------------------------------------------*/
%macro l2_ds_enr_overlap;
    %if %sysfunc(exist(dplocal.l2_distinctkey_enr)) %then %do;

        data dplocal.l2_nodup_enr (drop = lag_:  dateconflict )
             dplocal.enr_overlap_patid_dates ;

            length dateconflict $ 12;
            format lag_: mmddyy10.;

            set dplocal.l2_distinctkey_enr ;
            by patid;

            dateconflict=' ';
            lag_start=lag(enr_start);
            lag_end=lag(enr_end);

            %*exclude records with invalid dates from output - identified via check 226 that sources directly from MSCDM table;
            if enr_end lt enr_start then delete;

            %* output first record per patid;
            else if first.patid then do;
              lag_start=.;
              lag_end=.;
              output dplocal.l2_nodup_enr  ;
            end;

            %* evaluate remaining records to identify invalid spans and separately output from valid records;
            else do;

                %* categorize invalid types and output to overlap dataset;;
                if enr_start le lag_end and enr_end le lag_end    then dateconflict="Subset"; /* subset span */
                else if (enr_start le lag_end) or (enr_end le lag_end) then dateconflict="Overlap"; /*overlapping span*/
                if not missing(dateconflict) then output dplocal.enr_overlap_patid_dates; /* output records with populated date conflict to select dataset */

                %* output valid spans separately;
                else output dplocal.l2_nodup_enr;
            end;
        run;
    %end;
%mend l2_ds_enr_overlap;
/*-------------------------------------------------------------------------------*/
/* END ==> %l2_ds_enr_overlap                                                    */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_201                                                           */
/*********************************************************************************/
%macro flag_201;
  data _null_;
    set temp_l2_flags(where=(checkid='201'));
    if _n_ > 0 then do;
    i=put(_N_,3. -L);
    call execute(cat('proc sql noprint;
                  create table flag as
                  select ', quote(strip(table1)), ' as table1, ', quote(strip(table2)),' as table2, sum(count) as count',
                  ' from dplocal.all_l2_',strip(variable1),'_match',
                  ' where ',strip(table1),'="1" and ',strip(table2),'="0" and count>0',
                  ' group by table1, table2; quit;'));
    call execute(cats('%nrstr(%get_flagid(ntabs=2,nvars=1,checkid=201,tabid1=',strip(table1),',tabid2=',strip(table2),',var1=',strip(variable1),',i=',i,'));'));
    end;
  run;
%mend flag_201;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_201                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_203                                                           */
/*********************************************************************************/
%macro flag_203;
  proc sql noprint;
    create table tmp as
      select a.*, b.VarLength2
    from (select c.*, d.dp_length as VarLength1 label= ' '
          from temp_l2_flags(drop=variable2--variable4 test: data: look: ms: result_type) as c
          left join dplocal.all_l1_scdm_comp(keep=TabID var dp_length) as d
          on lower(c.table1)=lower(d.TabID) and lower(c.variable1)=lower(d.var)
          where c.checkid = '203') as a
    full join
          (select c.*, d.dp_length as VarLength2 label= ' '
          from temp_l2_flags(drop=variable2--variable4 test: data: look: ms: result_type) as c
          left join dplocal.all_l1_scdm_comp(keep=TabID var dp_length) as d
          on lower(c.table2)=lower(d.TabID) and lower(c.variable1)=lower(d.var)
          where c.checkid = '203') as b
    on a.flagid=b.flagid;
  quit;

  data dplocal.all_l2_crosstab_length_var(keep=table1 table2 variable1 Varlength1 Varlength2
                                          rename=(table1=TabID1 table2=TabID2 variable1=Variable))
       flag_l2_203(where=(VarLength1^=Varlength2));
    set tmp;
    count=99999;
  run;

  proc sort data = dplocal.all_l2_crosstab_length_var;
    by Variable TabID1 TabID2;
  run;

  proc datasets lib=work nolist nowarn nodetails memtype=data;
    delete tmp;
  quit;
%mend flag_203;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_203                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************
* START ==> %flag_209
* Description: if varlength for ID variables is consistent across tables, checks
*              that length of var is not larger than needed capacity
* FlagType: Warn / No Abort
**********************************************************************************/
%macro flag_209;
  proc sql noprint;
    create table flag_l2_209 as
    select *, 99999 as count
    from (select a.*
           , max(b.req_length) as maxReqLength label=' '
           , b.dp_length as dpLength label=' '
    from temp_l2_flags (keep = Flag: AbortYN Level VarID CheckID Variable1 Table1
                        where=(checkid="209")) as a
    left join dplocal.all_l1_scdm_comp (keep=TabID var dp_length req_length) as b
    on lowcase(a.table1)=lowcase(b.TabID) and lowcase(a.variable1)=lowcase(b.var)
    group by variable1
    )
    where dplength > maxReqLength
    order by variable1, table1;
  quit;
%mend flag_209;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_209                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_205                                                           */
/*********************************************************************************/
%macro flag_205;
  %local sql_tabid_exc_lc;
  %let sql_tabid_exc_lc=%lowcase(%bquote(&sql_tabid_exc)) ;
  data _null_;
    set temp_l2_flags(where=(checkid='205'));
    length firsttable $45 secondtable $45;
    if _n_ > 0 then do;
    i=put(_N_,3. -L);

    if lowcase(table1) = 'dem' and exist("dplocal.patid2bdate") then firsttable='dplocal.patid2bdate';
    else do; 
      if &do_partitions and lowcase(table1) not in (&sql_tabid_exc_lc) then do ;
        call execute(cat('data t_view1 /view=t_view1;  set ',  strip(cats('qadata.','&',table1,'table.:'))
                         , '(keep=patid ' , strip(variable1),' where=(',strip(variable1),' ne .)) ;'
                      )) ;
        firsttable='t_view1' ;
      end ;
      else do ;
        firsttable = cats('qadata.','&',table1,'table (keep=patid ' , strip(variable1)
                           ,' where=(',strip(variable1),' ne .)) ');
      end;
    end ;
      

    if lowcase(table2) = 'enr' and exist("dplocal.l2_nodup_enr") then secondtable='dplocal.l2_nodup_enr';
    else do; 
      if &do_partitions and lowcase(table2) not in (&sql_tabid_exc_lc) then do ;
        call execute(cat('data t_view2 /view=t_view2;  set ',  strip(cats('qadata.','&',table2,'table.:'))
                         , '(keep=patid ' , strip(variable2),' where=(',strip(variable2),' ne .)) ;'
                      )) ;
        secondtable='t_view2' ;
      end ;
      else do ;
        secondtable = cats('qadata.','&',table2,'table (keep=patid ' , strip(variable2)
                           ,' where=(',strip(variable2),' ne .)) ');
      end;
    end ;

    call execute(cat('proc sql noprint magic=102;
                  create table flag as
                  select ', quote(strip(variable1)), ' as variable1, ',
                            quote(strip(variable2)),' as variable2, ',
                            quote(strip(table1)),' as table1, ',
                            quote(strip(table2)),' as table2 from ',
                            strip(firsttable),  ' as a, ',
                            strip(secondtable), ' as b ',
                            ' where a.patid=b.patid and a.',strip(variable1),' >', ' b.',strip(variable2), ';'));
    call execute(cats('%nrstr(%get_flagid(ntabs=2,nvars=2,checkid=205,tabid1=',strip(table1),',tabid2=',strip(table2),
                      ',var1=',strip(variable1),',var2=',strip(variable2),',i=',i,'));'));
    end;
  run;
%mend;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_205                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_206                                                           */
/*********************************************************************************/
%macro flag_206;
    %local sql_tabid_exc_lc;
    %let sql_tabid_exc_lc=%lowcase(%bquote(&sql_tabid_exc)) ;
    data _null_;
    set temp_l2_flags(where=(checkid = '206'));
    length firsttable secondtable jointable $750;
    if _n_ > 0 then do;
    i=put(_N_,3. -L);
      if lowcase(table1) = 'prs' or lowcase(table2) = 'prs' then do;
        if &do_partitions and lowcase(table1) not in (&sql_tabid_exc_lc) then do ;
          call execute(cat('data t_view1 /view=t_view1;  set qadata.&',table1,'table.:'
                     , '(keep=prm_code surveyID questionID questionVerID ',strip(variable1)
                     , ' where=(',strip(variable1),' ne .) );'
                  )) ;
          firsttable = 't_view1' ;
        end;
        else do;
          firsttable = cat( strip(cats('qadata.&',table1,'table.A'))
                     , '(keep=prm_code surveyID questionID questionVerID ',strip(variable1)
                     , ' where=(',strip(variable1),' ne .) )'
                  );
        end;

        if &do_partitions and lowcase(table2) not in (&sql_tabid_exc_lc) then do ;
          call execute(cat('data t_view2 /view=t_view2;  set ',  strip(cats('qadata.','&',table2,'table.:'))
                     , '(keep=prm_code surveyID questionID questionVerID ',strip(variable2)
                     , ' where=(',strip(variable2),' ne .) );'
                  )) ;

          secondtable='t_view2';
        end;
        else do;
          secondtable = cat( strip(cats('qadata.','&',table2,'table.'))
                     , '(keep=prm_code surveyID questionID questionVerID ',strip(variable2)
                     , ' where=(',strip(variable2),' ne .) )'
                  );
        end;

        jointable=cat(strip(firsttable), ' a,', strip(secondtable), ' b '
                    , " where a.prm_code=b.prm_code and
                       a.surveyID=b.surveyID and
                       a.questionID=b.questionID and
                       a.questionVerID=b.questionVerID and
                       a.",strip(variable1), ' < ', "b.",strip(variable2),';');
      end;
      else do;


        if &do_partitions and lowcase(table1) not in (&sql_tabid_exc_lc) then do ;
          call execute(cat('data t_view1 /view=t_view1;  set ',  strip(cats('qadata.','&',table1,'table.:'))
                     , '(keep=patid ',strip(variable1)
                     , ' where=(',strip(variable1),' ne .) );'
                  )) ;
          firsttable = 't_view1' ;
        end;
        else do;
          firsttable = cat(strip(cats('qadata.','&',table1,'table.'))
                     , '(keep=patid ',strip(variable1)
                     , ' where=(',strip(variable1),' ne .) )'
                   );
        end;
  
        if lowcase(table2) = 'enr' and exist("dplocal.l2_nodup_enr") then secondtable='dplocal.l2_nodup_enr';
        else do;
          if &do_partitions and lowcase(table2) not in (&sql_tabid_exc_lc) then do ;
            call execute(cat('data t_view2 /view=t_view2;  set ',  strip(cats('qadata.','&',table2,'table.:'))
                       , '(keep=patid ',strip(variable2)
                       , ' where=(',strip(variable2),' ne .) );'
                    )) ;
  
            secondtable='t_view2';
          end;
          else do;
            secondtable = cat( strip(cats('qadata.','&',table2,'table.'))
                       , '(keep=patid ',strip(variable2)
                       , ' where=(',strip(variable2),' ne .) )'
                    );
          end;
        end;

        jointable=cat(strip(firsttable), ' a, ', strip(secondtable),' b '
                    , " where a.patid=b.patid and a.",strip(variable1), ' < ',"b.",strip(variable2),";");
      end;

      call execute(cat('proc sql noprint magic=102;
                        create table flag as
                        select ', quote(strip(variable1)), ' as variable1, ',
                              quote(strip(variable2)),' as variable2, ',
                              quote(strip(table1)),' as table1, ',
                              quote(strip(table2)),' as table2 from ',
                              jointable,';'));
      call execute(cats('%nrstr(%get_flagid(ntabs=2,nvars=2,checkid=206,tabid1=',strip(table1),',tabid2=',strip(table2),
                        ',var1=',strip(variable1),',var2=',strip(variable2),',i=',i,'));'));
    end;
  run;

%mend;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_206                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_211                                                           */
/*********************************************************************************/
/* Check 211 - Perform duplicate record by key variables check                   */
/*-------------------------------------------------------------------------------*/
%macro flag_211;
  data _null_;
    set temp_l2_flags(where=(checkid='211'));
    if _n_ > 0 then do;
    i=put(_N_,3. -L);
    call execute(cat('proc sql noprint;
                      create table flag as
                      select b.flagid, b.flag_descr, b.flagtype, b.abortyn, a.count_dup as count
                      from dplocal.all_l2_nobs_dup a inner join temp_l2_flags b
                      on a.TabID=b.tableid
                      where b.checkid="211" and a.TabID=',quote(strip(tableid)),';quit;'));
    call execute(cat('%nrstr(%get_flagid(ntabs=1,nvars=0,checkid=211,tabid1=',strip(tableid),',i=',i,'));'));
    end;
  run;
%mend flag_211;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_211                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_215                                                           */
/*********************************************************************************/
/* Overlapping enrollment spans by PatID, enr_start, enr_end;                    */
/* includes enrollment spans that are subset of another                          */
/* Regardless of coverage type (must be 2nd L2 flag macro run)                   */
/*-------------------------------------------------------------------------------*/
%macro flag_215;

  proc sql noprint;
    select dataset into :ds trimmed
    from temp_l2_flags (where=(checkid="215"))
    ;
  quit;

  /* defensive coding: ensure specified ds from lkp exists prior to running check */
  %if %sysfunc(exist(&ds.)) %then %do;
    proc sql noprint;
      select count(*) into :recct trimmed
      from &ds.
      ;
    quit;

    proc sql noprint;
      create table flag_l2_215 as
      select flagid
           , flag_descr
           , flagtype
           , abortyn
           , checkid
           , &recct as count format=comma15. informat=comma15.
      from temp_l2_flags (where=(checkid="215"))
      ;
    quit;
  %end;

%mend flag_215;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_215                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_216                                                           */
/*********************************************************************************/
/* Identify non-bridged enrollment spans by non-date key variables               */
/*-------------------------------------------------------------------------------*/
%macro flag_216;

%if %sysfunc(exist(dplocal.L2_NODUP_ENR)) %then %do;

  /* Extract variables dynamically for check */
  proc sql noprint;
    select name, cats('lag_',name)
    into :flag_216_varlist separated by ' ', :flag_216_laglist separated by ' '
    from dictionary.columns
    where LIBNAME = 'DPLOCAL' and MEMNAME = 'L2_NODUP_ENR' and upper(NAME) not in ('DP', 'PATID');

    select cats(name,'=prev_',name)
    into :flag_216_cond separated by ' and '
    from dictionary.columns
    where LIBNAME = 'DPLOCAL' and MEMNAME = 'L2_NODUP_ENR' and upper(NAME) not in ('DP', 'PATID', 'ENR_START', 'ENR_END');
  quit;

  data flag;
    set dplocal.l2_nodup_enr(where=(not missing(enr_start) and not missing(enr_end)));
    by patid;
    retain &flag_216_laglist.;
    if first.patid then do;
      %do i = 1 %to %sysfunc(countw(&FLAG_216_VARLIST));
        %let enrvar = %scan(&FLAG_216_VARLIST,&i);
      lag_&enrvar = &enrvar;
      %end;
      diff=0;
    end;
    else do;
      %do i = 1 %to %sysfunc(countw(&FLAG_216_VARLIST));
        %let enrvar = %scan(&FLAG_216_VARLIST,&i);
      prev_&enrvar = lag_&enrvar;
      lag_&enrvar = &enrvar;
      %end;
      diff = enr_start - prev_enr_end;
    end;
    if diff=1 and &flag_216_cond. then output flag;
    drop lag_: prev_: diff;
  run;

  proc sql noprint;
    create table flag_l2_216 as
    select b.flagid
         , b.flag_descr
         , b.flagtype
         , b.abortyn
         , a.count
    from (select count(*) as count, "216" as checkid from flag) a, temp_l2_flags (where=(checkid="216")) b
    where a.checkid=b.checkid
    ;
  quit;
%end;
%mend flag_216;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_216                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_220                                                           */
/*********************************************************************************/
/* Intra-table LAB check for missing data relationship across 2 variables        */
/* LAB Specific: LOINC should not be missing when ms_test_name = UNMAPPED        */
/*-------------------------------------------------------------------------------*/
%macro flag_220;
  data _null_;
    set temp_l2_flags(where=(checkid='220'));
    if _n_ > 0 then do;
    i=put(_N_,3. -L);
    call execute(cat('data flag; set ',strip(dataset),'(where=(missing(',strip(variable1),') and upcase(',strip(variable2),')="UNMAPPED")); run;'));
    call execute(cat('%nrstr(%get_flagid(ntabs=1,nvars=2,checkid=220,tabid1=',strip(tableid),',var1=',strip(variable1),',var2=',strip(variable2),',i=',i,'));'));
    end;
  run;
%mend;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_220                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_222                                                           */
/*********************************************************************************/
/* Intra-table LAB check for invalid data relationship across 2 variables        */
/*-------------------------------------------------------------------------------*/
%macro flag_222;

  data _null_;
    set temp_l2_flags(where=(checkid='222'));
    if _n_ > 0 and exist("infolder.lkp_all_l2") and lookup_table = "lkp_all_l2" then do;
    i=put(_N_,3. -L);
    call execute(cat('proc sql noprint; select flagcondition into :where separated by " or " from ',
                      cats('infolder.',strip(lookup_table)), '(where=(lowcase(tabid)=',quote(strip(lowcase(tableid))),
                      ' and lowcase(variable1)=',quote(strip(lowcase(variable1))),
                      ' and lowcase(variable2)=',quote(strip(lowcase(variable2))),
                      ' and lowcase(abortyn)=',quote(strip(lowcase(abortyn))),
                      ' and checkid="222"));'));
    call execute(cat('%nrstr(data flag; set ', strip(dataset), '(where=(&where) keep= ',variable1, ' ', variable2,');',' run;)'));
    call execute(cat('%nrstr(%get_flagid(ntabs=1,nvars=2,checkid=222,tabid1=',strip(tableid),',var1=',strip(variable1),',var2=',strip(variable2),',i=',i,'));'));
    end;
  run;

%mend flag_222;
/*********************************************************************************/
/* END ==> %flag_222                                                             */
/*********************************************************************************/

/*********************************************************************************/
/* START ==> %flag_223                                                           */
/*********************************************************************************/
/* Intra-table check for invalid data relationship across 2 variables            */
/*-------------------------------------------------------------------------------*/

%macro flag_223;
  data _null_;
    set temp_l2_flags(where=(checkid="223"));
    if _n_ > 0 then do;
      i=put(_N_,3. -L);
      if exist(dataset) then do;
        call execute(cat('proc sql noprint;
                      select flagcondition into :where separated by " or " from ', cats("infolder.",lookup_table),
                      ' where lowcase(tabid)=lowcase(',quote(strip(table1)),')',' and lowcase(variable1)=lowcase(',quote(strip(variable1)),')',
                      ' and lowcase(variable2)=lowcase(',quote(strip(variable2)),')',' and checkID = "223";'));
        call execute(cat('%nrstr(data flag; set ',strip(dataset),'(where=(&where));',' run;)'));
        call execute(cat('%nrstr(%get_flagid(ntabs=1,nvars=2,checkid=223,tabid1=',strip(table1),',var1=',strip(variable1),',var2=',strip(variable2),',i=',i,'));'));
      end;
    end;
  run;
%mend flag_223;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_223                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_224                                                       */
/*********************************************************************************/
%macro flag_224(tab=&module.);

  %put ====> MACRO CALLED: flag_224;
  %local tabid_exc_lc;
  %let tabid_exc_lc=%lowcase(%bquote(&tabid_exc)) ;

  %local qa_table ;
    %if &do_partitions and not %eval(%lowcase(&tab.) in &tabid_exc_lc) %then %do ;
      data t_view_224 /view=t_view_224 ;
        set qadata.&&&&&tab.table.: ;
      run;
      %let qa_table = t_view_224 ;
    %end;
    %else %do;
      %let qa_table = qadata.&&&&&tab.table. ;
    %end;

  data dplocal.l2_flags_&tab._224;
    length count 8.;
    keep Flag: TableID AbortYN Level VarID CheckID Variable1 Variable2 count;
    set infolder.lkp_all_flags;
    where TableID = upcase("&tab.") and checkid = "224";
    if _N_ > 0 then do;
    rc = dosubl(cat('proc sql noprint; create table dplocal.flag224_nomatch_', _N_, ' as
      select patid, ', variable1, ', ', variable2, '
      from ', "&qa_table.", ' (keep=Patid ', variable1, ' ', variable2, ')
      where not missing(', variable2, ') and compare(', variable1, ', ', variable2, ') ne 0;
    quit;'));
      length dsin $ 32. dsout $ 32.;
      dsin = catx("_", "flag224_nomatch", _N_);
      dsout = catx("_", "&tab.", Variable1, "vars_match");
      rc = dosubl('data _null_;
                   if 0 then set dplocal.'||dsin|| ' nobs=n;
                      call symputx("nrows", n);
                      stop;
                   run;');
    count = input(symget("nrows"), COMMA15.);
      if count = 0 then do;
        rc = dosubl('proc datasets library=dplocal; delete '||dsin||'; run;');
      end;
      else do;
        rc= dosubl('proc datasets library=dplocal; change '||dsin||'='||dsout||'; run;');
      end;
    end;
  run;

  %put ******** NOTE: END OF MACRO flag_224;

%mend flag_224;

/*********************************************************************************/
/* START ==> %flag_226                                                           */
/*********************************************************************************/
/* Intra-table check for invalid date relationship (var1 gt var2)                */
/*-------------------------------------------------------------------------------*/
%macro flag_226;
  data _null_;
    set temp_l2_flags(where=(checkid ='226'));
    if _n_ > 0 then do;
    i=put(_N_,3. -L);
    call execute(cat('data flag; set ', cats("qadata.","&",strip(tableid),"table:"),
                                 '(keep=',variable1, variable2,' where=(not missing(',strip(variable1),')
                                 and not missing(',strip(variable2),')
                                 and ',strip(variable1),' gt ',strip(variable2),')); run;'));
    call execute(cat('%nrstr(%get_flagid(ntabs=1,nvars=2,checkid=226,tabid1=',strip(tableid),',var1=',strip(variable1),',var2=',strip(variable2),',i=',i,'));'));
    end;
  run;
%mend flag_226;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_226                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_227                                                           */
/*********************************************************************************/
/* Intra-table check for invalid date relationship (var1 lt var2)                */
/*-------------------------------------------------------------------------------*/
%macro flag_227;
  data _null_;
    set temp_l2_flags(where=(checkid ='227'));
    if _n_ > 0 then do;
    i=put(_N_,3. -L);
    call execute(cat('data flag; set ', cats("qadata.","&",strip(tableid),"table:"),
                                 '(keep=',variable1, variable2,' where=(not missing(',strip(variable1),')
                                 and not missing(',strip(variable2),')
                                 and ',strip(variable1),' lt ',strip(variable2),')); run;'));
    call execute(cat('%nrstr(%get_flagid(ntabs=1,nvars=2,checkid=227,tabid1=',strip(tableid),',var1=',strip(variable1),',var2=',strip(variable2),',i=',i,'));'));
    end;
  run;
%mend flag_227;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_227                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_228                                                           */
/*********************************************************************************/
/* Intra-table check for invalid data value relationship across 2 variables      */
/*-------------------------------------------------------------------------------*/
%macro flag_228;
  data _null_;
    set temp_l2_flags(where=(checkid="228"));
    if _n_ > 0 then do;
      i=put(_N_,3. -L);
      if exist(dataset) then do;
        call execute(cat('proc sql noprint;
                      select flagcondition into :where separated by " or " from ', cats("infolder.",lookup_table),
                      ' where lowcase(tabid)=lowcase(',quote(strip(table1)),')',' and lowcase(variable1)=lowcase(',quote(strip(variable1)),')',
                      ' and lowcase(variable2)=lowcase(',quote(strip(variable2)),')',' and checkID = "228";'));
        call execute(cat('%nrstr(data flag; set ',strip(dataset),'(where=(&where));',' run;)'));
        call execute(cat('%nrstr(%get_flagid(ntabs=1,nvars=2,checkid=228,tabid1=',strip(table1),',var1=',strip(variable1),',var2=',strip(variable2),',i=',i,'));'));
      end;
    end;
  run;
%mend flag_228;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_228                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_229                                                           */
/*********************************************************************************/
/* Intra-table check for invalid variable value based on specified date          */
/*-------------------------------------------------------------------------------*/
%macro flag_229;
  data _null_;
    set temp_l2_flags(where=(checkid="229"));
    if _n_ > 0 then do;
      i=put(_N_,3. -L);
      if exist(dataset) then do;
        call execute(cat('proc sql noprint;
                      select flagcondition into :where separated by " or " from ', cats("infolder.",lookup_table),
                      ' where lowcase(tabid)=lowcase(',quote(strip(table1)),')',' and lowcase(variable1)=lowcase(',quote(strip(variable1)),')',
                      ' and lowcase(variable2)=lowcase(',quote(strip(variable2)),')',' and checkID = "229";'));
        call execute(cat('%nrstr(data flag; set ',strip(dataset),'(where=(&where));',' run;)'));
        call execute(cat('%nrstr(%get_flagid(ntabs=1,nvars=2,checkid=229,tabid1=',strip(table1),',var1=',strip(variable1),',var2=',strip(variable2),',i=',i,'));'));
      end;
    end;
  run;
%mend flag_229;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_229                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_236                                                           */
/*********************************************************************************/
/* Check for 0 CauseType=U records per PatID in COD table                        */
/*-------------------------------------------------------------------------------*/
%macro flag_236;
    /*check to see if COD table exists*/
    %local qa_table ;
    %let qa_table = ;

    %if &do_partitions and %sysfunc(exist(qadata.&CODTABLE.1)) %then %do;
      data t_view /view=t_view ;
        set qadata.&CODTABLE: ;
      run;
      %let qa_table = t_view ;
    %end ;
    %else %if %sysfunc(exist(qadata.&CODTABLE)) %then %do;
      %let qa_table = qadata.&CODTABLE ;
    %end ;

    %if %length(&qa_table) %then %do ;
      proc sql noprint;
        select count(distinct patid) into :count_236 trimmed
        from &qa_table
        where PatID not in (
                         select patid
                         from &qa_table
                         where upper(causetype) = "U"
                          ) ;

        create table flag_l2_236 as
        select flagid
             , flag_descr
             , flagtype
             , abortyn
             , &count_236 as count
        from temp_l2_flags
        where checkid="236"
        ;
      quit;

    %end;

%mend flag_236;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_236                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_237                                                           */
/*********************************************************************************/
/* Check for >1 CauseType=U record per PatID in COD table                      */
/*-------------------------------------------------------------------------------*/
%macro flag_237;
  %local pn qa_table ;
    %if &do_partitions %then %let pn=1 ;  /* use 1st partition as proxy for all partition */
                       %else %let pn= ;
    /*check to see COD table exists*/
    %if %sysfunc(exist(qadata.&CODTABLE.&pn)) %then %do;
       %if &do_partitions %then %do ;
         data t_view /view=t_view ;
           set qadata.&CODTABLE.: ;
         ;run;
         %let qa_table = t_view ;
       %end ;
       %else %do ;
         %let qa_table = qadata.&CODTABLE ;
       %end ;

      proc sql noprint;
        select count(distinct patid) into :count_237 trimmed
        from &qa_table.
        where PatID in (
            select patid
            from &qa_table.
            where upper(causetype) = "U"
            group by patid
            having count(*) gt 1
            );

        create table flag_l2_237 as
        select flagid
             , flag_descr
             , flagtype
             , abortyn
             , &count_237 as count
        from temp_l2_flags
        where checkid="237"
        ;
      quit;

    %end;

%mend flag_237;
/*-------------------------------------------------------------------------------*/
/* END ==> %flag_237                                                             */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %flag_244                                                           */
/*********************************************************************************/
/* Used to create dataset to check invalid combinations of variables             */
/*-------------------------------------------------------------------------------*/
%macro flag_244;

  data _null_;
    set temp_l2_flags(where=(checkid ='244'));
    if _n_ > 0 then do;
    i=put(_N_,3. -L);
    call execute(cat('data temp_244; if 0 then set infolder.',strip(lookup_table),';',
                                 'declare hash existing (hashexp:16, dataset:"infolder.',strip(lookup_table),'");',
                                 'existing.definekey(all:"yes");',
                                 'existing.definedone();',
                                 'do until (done);',
                                 'set ',strip(dataset), ' end=done;',
                                 'if existing.check() ne 0 then output;end;stop;run;'));

    call execute(cat('proc sql noprint; create table flag_l2_244_',i,' as ',
                      'select b.flagid, b.flag_descr, b.flagtype, b.abortyn, a.count ',
                      'from (select sum(count) as count, "244" as checkid from temp_244) as a, temp_l2_flags(where=(checkid="244")) as b ',
                      'where a.checkid=b.checkid;'));
    end;
  run;

%mend flag_244;


/*********************************************************************************/
/* START ==> %flag_245                                                           */
/*********************************************************************************/
/* Used to create dataset to check invalid combinations of variables meeting     */
/* rate threshold                                                                */
/*-------------------------------------------------------------------------------*/
%macro flag_245;

  data _null_;
    set temp_l2_flags(where=(checkid ='245'));
    length fmtname $10;
    if _n_ > 0 then do;
    i=put(_N_,3. -L);
    fmtname=upcase(cats(TableID, CheckID, compress(TestID,'-'),'F'));
    call execute(cat('data ', strip(fmtname),';', 'set ', 'infolder.',strip(lookup_table),'; where FMTNAME = ',quote(strip(fmtname)),';run;'));
    call execute(cat('proc format cntlin = work.',strip(fmtname),';','quit;'));
    call execute(cat('data flag_l2_245_',i,';',
                      'length checkid $3 flagid $21 flagtype $4 abortyn $1 flag_descr $255;',
                      'set ',strip(dataset),';',
                      'checkid="245";',
                      'flagid=',quote(strip(flagid)),';',
                      'flag_descr=',quote(strip(flag_descr)),';',
                      'flagtype=',quote(strip(flagtype)),';',
                      'abortyn=',quote(strip(abortyn)),';',
                      'if upcase(put(rate,',strip(fmtname),'.)) = "FLAGGED";',
                      'rename rate=count;keep checkid flagid flag_descr flagtype abortyn rate; run;'));
    end;
  run;

%mend flag_245;

/*********************************************************************************/
/* START ==> %level2                                                             */
/*********************************************************************************/
%macro level2;
  /* Exclude 230, 231, 232, 234 and 235 because it is called in the lab module separately */
  data temp_l2_flags;
    set infolder.lkp_all_flags(where=(level='2' and abortyn='N' and
             ^index(tableid,'-') and
             substr(lowcase(tableid),1,3) = "&module" and
             substr(lowcase(tableid),5,3) in (' ', "&module") and
             checkid ^= '209'
       %if &module = lab %then %do; and checkid not in ('230', '231', '232', '234','235') %end; ));
    table1=substr(tableid,1,3);
    table2=substr(tableid,5,3);
  run;

  proc sort data = temp_l2_flags nodupkey out=unique_l2_flags;
    by checkid;
  run;

  data _null_;
    set unique_l2_flags;
    call execute(cats('%nrstr(%flag_',checkid,';)'));
  run;

  data dplocal.l2_flags_&module.;
    set flag_l2_:;
  run;

  proc datasets kill lib=work memtype=data nolist nowarn nodetails;
  quit;
%mend level2;
/*-------------------------------------------------------------------------------*/
/* END ==> %level2                                                               */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %date_percentiles                                                   */
/*********************************************************************************/
%macro date_percentiles (libin=, dsin=, libout=, dsout=, vars=);
  proc means nolabels data=&libin..&dsin. (keep=&vars. n) missing vardef=weight StackODSOutput
       n min p1 P5 P25 median P75 P95 p99 max;
    var &vars.;
    weight n;
    ods output summary=&libout..&dsout.;
  run;

  data &libout..&dsout. (drop=var);
    length TabID $3 Variable $15;
    set &libout..&dsout. (rename=(variable=var));
    TabID=upcase("&module.");
    variable=lowcase(var);
    format N best12. min--max yymmdd10.;
  run;
%mend date_percentiles;
/*-------------------------------------------------------------------------------*/
/* END ==> %date_percentiles                                                     */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* PROC MEANS that sums numeric variable over a class of variables (NO OUTPUT)   */
/*********************************************************************************/
%macro l2_procmeans_sum (libin=,dsin=,libout=,dsout=,keepvars=,vars=,classvars=,names=,numobs=);
  proc means noprint nway data=&libin..&dsin.(obs=&numobs. keep=&keepvars.) missing;
    var &vars.;
    class &classvars.;
    output out = &libout..&dsout.(drop=_:) &names.;
  run;
  %remove_labels(&libout.,&dsout.);
%mend l2_procmeans_sum;

/*********************************************************************************/
/* START ==> %snapshot_run                                                       */
/*********************************************************************************/
/*  Defines parameters to bypass scdm_snapshot master file, runs                 */
/*    scdm_snapshot program, and copies output to msoc/scdm_snapshot folder      */
/*-------------------------------------------------------------------------------*/
%macro snapshot_run;

  data _null_;
    put 75*'-';
    put ' ';
    put "==> Begin Execution of scdm_snapshot program";
    put ' ';
    put 75*'-';
  run;

  /* Note all scdm table name vars are populated in QA master file, but miltable is set to 'NA' */
  /* Set miltable macro variable to blank for snapshot run */
  %let MILTABLE= ;

  /* Define indata folder */
  %let indata=&QADATA.;
  %soc_lib(INDATA, &INDATA, options=%str(access=readonly))

  /* Redirect libraries to inputfiles/scdm_snapshot folder */
  %redirect_libs(&SSroot.)

  /* Create clean output environment */;
  %kill_directory(kill_list=dplocal msoc)

  /* Initialize and execute Snapshot-specific modules */
  %inc "&infolder.soc_scdm_snapshot_signature_module.sas" / nosource2;
  %inc "&infolder.soc_scdm_formats_agecat.sas" / nosource2;
  %inc "&infolder.macros/ms_macros.sas" / nosource2;
  %inc "&infolder.macros/ms_delpatients.sas" / nosource2;
  %inc "&infolder.macros/ms_delencounterids.sas" / nosource2;
  %inc "&infolder.macros/_mil_linkage_rates.sas" / nosource2;
  %inc "&infolder.macros/ms_delencounterids.sas" / nosource2;

  %include "&infolder.macros/compile_macros.sas";
  %compile_macros;

  /* Execute the main Snapshot Program. Note: This program generates its own log */
  %inc "&infolder.soc_snapshot_driver.sas"  /nosource2;

  /* Redirect libraries back to QA package request id root folder */
  %redirect_libs(&_root.)

  /* Reset miltable macro variable back to 'NA' */
  %let MILTABLE=NA;

  /* Create msoc/scdm_snapshot directory */
  %let MSOC_SS=&_root.msoc/scdm_snapshot;

  options dlcreatedir;
  libname MSOC_SS "&MSOC_SS.";
  options nodlcreatedir;

  /* Copy over snapshot output to msoc/scdm_snapshot folder */
  %dircopy(indir=&INFOLDER.scdm_snapshot/msoc, outdir=&MSOC_SS)

%mend snapshot_run;

/*-------------------------------------------------------------------------------*/
/* END ==> %snapshot_run                                                         */
/*-------------------------------------------------------------------------------*/

/*********************************************************************************/
/* START ==> %cc_run                                                             */
/*********************************************************************************/
/*  1. Defines parameters to bypass qa_common_components master file, runs       */
/*     qa_common_components, and copies output to msoc/qa_common_components      */
/*     folder                                                                    */
/*  2. Creates CC request id folder under QA package root directory (based on    */
/*     QA request id), copies entire CC package to CC request id folder          */
/*-------------------------------------------------------------------------------*/
%macro cc_run;

  proc printto log="&msoc.qa_package.log";
  run;

  data _null_;
    put 75*'-';
    put ' ';
    put "==> Begin Execution of qa_common_components program";
    put ' ';
    put 75*'-';
  run;

  /* Create library for CC dp_metadata folder */
  %soc_lib(QARESULT, &INFOLDER.qa_common_components/inputfiles/dp_metadata)

  /* Copy over necessary QA Package metadata datasets to CC dp_metadata folder */
  proc copy in=msoc out=qaresult memtype=data;
    select all_l1_cont etl_version minmax_dates qa_cc_metadata;
  run;

  /* Redirect libraries to inputfiles/qa_common_components folder */
  %redirect_libs(&CCroot.)

  /* Redefine infolder library to include cc_test folder */
  %soc_lib(INFOLDER, &INFOLDER. &INFOLDER.cc_test, options=%str(access=readonly))

  /* Execute the main Common Components file */
  %inc "&infolder.01.0_run_cc.sas" /nosource2;

  data _null_;
    put 75*'-';
    put ' ';
    put "==> Begin Execution of qa_common_components post processing";
    put ' ';
    put 75*'-';
  run;

  /* _ROOT macro variable is assigned in CC program, reassign back to QA package request id root folder */
  %let _ROOT=%soc_clean_paths(&QApackage_root);
  %let _ROOT=&_root.&reqid./;

  /* Redirect libraries back to QA package request id root folder */
  %redirect_libs(&_root.)

  proc printto log="&msoc.qa_package.log";
  run;

  /* Create msoc/qa_common_components directory */
  %let MSOC_CC=&_root.msoc/qa_common_components;

  options dlcreatedir;
  libname MSOC_CC "&MSOC_CC.";
  options nodlcreatedir;

  /* Copy over CC output to msoc/qa_common_components folder */
  %dircopy(indir=&INFOLDER.qa_common_components/msoc, outdir=&MSOC_CC)

  /* Create CC request id */
  %if %lowcase(&wptype.) = qmr %then %do;
    %let cc_wptype = ccb;
  %end;
  %else %do;
    %let cc_wptype = cca;
  %end;

  %let CCreqid=&ProjID._&cc_wptype._&wpid._&dpid._&verid.;

  /* Create CC request id folder */
  %let CCdir=&_ROOT.&CCreqid.;

  options dlcreatedir;
  libname CCdir "&CCdir.";
  options nodlcreatedir;

  /* Copy over entire completed CC package from inputfiles folder to CC request id folder */
  %dircopy(indir=&INFOLDER.qa_common_components, outdir=&CCdir)

%mend cc_run;

/*-------------------------------------------------------------------------------*/
/* END ==> %cc_run                                                     */
/*-------------------------------------------------------------------------------*/

*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-;
* End       scdm_standard_macros.sas                                              ;
*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-;