Wikipedia of SAS Programs

ubaid darwaish

One of the things that I’ve learned from Wikipedia is that the people can actually help. Imagine if you had a Wikipedia of SAS programs in your organization wherein you could go and search for any SAS Program and you would get the information like the Author’s name, when was the program created, who modified it, when was it modified, why was it modified, what are the sources, what are the outputs and much more.

Having access to such information can solve so many problems for us, it will ensure that there is a high level of clarity of code and also the efforts for maintenance of the code is minimal. It brings in a transparency with regards to what an application (SAS) is being used for. The task of creating this kind of information asset for your SAS programs may sound tedious and complicated, but in reality its quite easy and simple.

Its all in the header, ‘the program header’.

The program header at the top of your SAS program is not unnecessary, its vital. It tells you so much about the program, all in one glance.Moreover you will never think of the program header to be ‘boring’, When you realize what it can do for you. A small conscious effort by the programmers in the organization can actually go a long way and create a huge difference. …it can create the Wikipedia for you.

The following macro does the job for you, it extracts the header portion of all your programs and populates the required fields for your Wikipedia.

/*Macro for Wiki Starts*/

%macro Wiki;

/*Dummy macro to retrieve color coding*/
                        %macro dummy;%mend dummy;

/*Define Path*/

%LET DIR=/home/ubaid/code;
%LET EXPO_PATH=/home/ubaid;

/*Fileref*/

FILENAME IN_CODE PIPE "ls &DIR.";

/*List of all files*/

DATA LIST;
    LENGTH CODE_NAME $100.;
    INFILE IN_CODE TRUNCOVER;
    INPUT CODE_NAME $100.;
RUN;


/*List of sas files from all files*/

DATA CODE_LIST;
    SET LIST;
    IF FIND(CODE_NAME,".sas")>0;
    CALL SYMPUT('NO_OF_FILES',_n_);
run;

/*Run in loop for all sas programs*/

%do i=1 %to &NO_OF_FILES;

/*Retrieve Program names (one at a time) */

DATA _NULL_;
    SET CODE_LIST;
    IF _N_=&I;
    CALL SYMPUT('FILEIN',CODE_NAME);
RUN;

/*Read code from program n*/

DATA CODE;
    LENGTH
        F1               $ 336      ;
    FORMAT
        F1               $CHAR336.  ;
    INFORMAT
        F1               $CHAR336.  ;
    INFILE "/home/ubaid/code/&filein" MISSOVER  DSD ;
    INPUT
        F1               : $CHAR336.;
RUN;

/*No of lines in the Program*/

Data _NULL_;
Set Code;
call symput('Lines',_N_);
run;


/*Extract info from header and Set the Flag*/

Data X;
Set CODE(firstobs=7 obs=20);
    if FIND(F1,'Program') THEN DO; Program=SUBSTR(F1,FIND(F1,':')+1,50);Flag=1;Lines=&Lines.;END;
    if FIND(F1,'Version')  THEN  DO; Version=SUBSTR(F1,FIND(F1,':')+1,10);Flag=1;END;
    if FIND(F1,'Author')  THEN  DO; Author=SUBSTR(F1,FIND(F1,':')+1,50);Flag=1;END;
    if FIND(F1,'Date ')    THEN  DO; Date=SUBSTR(F1,FIND(F1,':')+1,15);Flag=1;END;
    if FIND(F1,'Purpose')  THEN  DO; Purpose=SUBSTR(F1,FIND(F1,':')+1,150);Flag=1;END;
    if FIND(F1,'Bussiness Unit')  THEN  DO; Business_Unit=SUBSTR(F1,FIND(F1,':')+1,20);Flag=1;END;
    if FIND(F1,'Input')  THEN  DO; Input=SUBSTR(F1,FIND(F1,':')+1,20);Flag=1;END;
    if FIND(F1,'Output')  THEN  DO; Output=SUBSTR(F1,FIND(F1,':')+1,20);Flag=1;END;
    if FIND(F1,'Date Modified')  THEN  DO; Date_Modified=SUBSTR(F1,FIND(F1,':')+1,15);Flag=1;END;
    if FIND(F1,'Modified by')  THEN  DO; Modified_by=SUBSTR(F1,FIND(F1,':')+1,50);Flag=1;END;
    ;
RUN;

/*Skim the actual data from rest(not of any use)*/

Proc Sql;
Create table Y as select * from X where Flag=1;
Quit;

/*For every information bucket create a seperate table and load it*/

PROC SQL;
    CREATE TABLE PROGRAM AS SELECT PROGRAM AS PROGRAM FROM Y WHERE PROGRAM NE '' ;
    CREATE TABLE VERSION AS SELECT VERSION AS VERSION FROM Y WHERE VERSION NE '' ;
    CREATE TABLE AUTHOR AS SELECT AUTHOR AS AUTHOR FROM Y WHERE AUTHOR NE '' ;
    CREATE TABLE DATE AS SELECT DATE AS DATE FROM Y WHERE DATE NE '' ;
    CREATE TABLE PURPOSE AS SELECT PURPOSE AS PURPOSE FROM Y WHERE PURPOSE NE '' ;
    CREATE TABLE BUSINESS_UNIT AS SELECT BUSINESS_UNIT AS BUSINESS_UNIT FROM Y WHERE BUSINESS_UNIT NE '' ;
    CREATE TABLE INPUT AS SELECT INPUT AS INPUT FROM Y WHERE INPUT NE '' ;
    CREATE TABLE OUTPUT AS SELECT OUTPUT AS OUTPUT FROM Y WHERE OUTPUT NE '' ;
    CREATE TABLE DATE_MODIFIED AS SELECT DATE_MODIFIED AS DATE_MODIFIED FROM Y WHERE DATE_MODIFIED NE '' ;
    CREATE TABLE MODIFIED_BY AS SELECT MODIFIED_BY AS MODIFIED_BY FROM Y WHERE MODIFIED_BY NE '' ;
    CREATE TABLE LINES AS SELECT LINES AS LINES FROM Y WHERE PROGRAM NE '';
QUIT;

/*First time create table-Put it all together*/

%If &i==1 %then %do;

DATA Z;
MERGE 
PROGRAM VERSION AUTHOR DATE PURPOSE BUSINESS_UNIT INPUT OUTPUT DATE_MODIFIED MODIFIED_BY LINES;
RUN;

                %end;

/*Next time append table-Put it all together*/

%else %do;

DATA ADD;
MERGE PROGRAM VERSION AUTHOR DATE PURPOSE BUSINESS_UNIT INPUT OUTPUT DATE_MODIFIED MODIFIED_BY LINES;
RUN;

PROC APPEND BASE=Z DATA=ADD;
RUN;

      %end;

/*Group by teams*/

PROC SORT DATA=Z;
BY BUSINESS_UNIT;
RUN;

%end;

/*Create the HTML Report*/

ODS HTML BODY ="&expo_path./Wiki.html" STYLE=GEARS;
FOOTNOTE1 "Source Code/Algorithm by Ubaid Darwaish.";
/*FOOTNOTE2 "Generated (&_SASSERVERNAME, &SYSSCPL) on %TRIM(%QSYSFUNC(DATE(), NLDATE20.)) at %TRIM(%SYSFUNC(TIME(), TIMEAMPM12.))";*/

PROC REPORT DATA=Z NOWD ;
COLUMN PROGRAM VERSION AUTHOR DATE PURPOSE BUSINESS_UNIT INPUT OUTPUT DATE_MODIFIED MODIFIED_BY LINES;

/*LOGO inclusion*/

                    COMPUTE BEFORE _PAGE_ / LEFT  

   STYLE=[PREIMAGE='/home/exud103/ud.png'  
          FONT_WEIGHT=BOLD 
          FONT_SIZE=5 
          FOREGROUND=cx993300]; 
   LINE " Wiki of SAS Programs."; 

                    ENDCOMP; 
RUN;


FOOTNOTE1;
FOOTNOTE2;

After collating the information from the program headers and creating the HTML report, next we will need to host that HTML report on SAS Web Server to make it available to the wider audience on intranet.

The sample output will look like:-

Wiki.JPG

You can also use Tableeditor to create the filters in HTML page, the tableeditor  is a custom tagset that is created by the TEMPLATE procedure. The tableeditor tagset uses HTML, CSS, JavaScript, and the Markup language. It enables you to create HTML output with the same kind of functionality that you would get from Visual Basic .Net (VB.NET). Currently, the tagset supports Internet Explorer only. Please read more at:-Using Tableeditor for HTML Reports

Sample output for tableeditor report with filters on Author and Business unit:-

wiki2.JPG

Assumptions:-

  • A standard header is used by all programs, the following was used for this example:-
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/Please read the instructions carefully.
/Do not change the formatting of this header
/Please add all the relevant fields, Program,Version,Author,Date,Purpose,Business Unit are mandatory fields.
/In Modification section + denotes addition ,- denotes removal and * is for change 
/Date/Date modified has to be in date9. format, e.g 01JAN2000
/+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/ Program       : Test10.sas
/ Version       : 2.1
/ Author        : Jimmy 
/ Date          : 09NOV2008
/ Purpose       : Regression model for FI 
/ Bussiness Unit: XO
/ Input         : CAR (Customer Analytics Record)
/ Output        : SAS Dataset
/===============================================================================
/ Modification      
/ Date Modified  : 
/ Modified by    : 
/            +   : 
/            -   : 
/            *   :
/ Notes          :
/
/
/
/==============================================================================
/==============================================================================*/
  • Programs are saved centrally by all users on server and not  on individual machines (/home/ubaid/code in this case).

Contributed by: Ubaid Darwaish