sugi 26: using sas/af(r) to create a sas(r) program file explorer · using sas/af® to create a...

Post on 19-Feb-2019

212 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Paper 212-26

Using SAS/AF® to Create a SAS® Program File Explorer Rob Nelson, Centers for Disease Control and Prevention, Atlanta, GA

Janet Royalty, Centers for Disease Control and Prevention, Atlanta, GA Ernie Lewis, Greenville, SC

ABSTRACT The SAS version 8 application presented in this poster combines the interactive features of SAS/AF with a methodology for documenting SAS programs to create a descriptive index of SAS program files within a given Windows directory. File name, creation date, and program header description are captured and displayed in an easy-to-browse data table. This application also includes a search tool function that can locate programs containing a specified string of SAS code. While the methods presented in this paper apply to intermediate SAS programmers, they can also be modified for use by more advanced programmers.

INTRODUCTION Most SAS programmers find it useful to adapt previously written code to new projects. Normally, efficient maintenance of a SAS program file directory depends on internal program code documentation, explaining the intricacies of specific code, and external file documentation, explaining the overall purpose of the program and summarizing any innovative code. The prototype SAS Program File Explorer application illustrated in this article was developed in SAS version 8 on the Windows NT platform. It modifies Mr. Gary Cunningham’s technique for creating an index of SAS programs to enable display of program file summary information in table format, searching directories for specific SAS program code, and submitting and viewing selected SAS programs. The application is presented in three parts. First, the base SAS code for creating a SAS program file indexing program is presented. Second, the SAS/AF techniques for developing the frame, frame objects, and SCL are explored. Third, additional base SAS code is given which can be used to create a search mechanism for the application. In reading this paper, please consider the following. Directory information read into SAS using the pipe varies across operating system and operating system version. Section 2 of programs.sas and section 1 of search.sas might need to be changed to reflect this. In addition, this program was creating using SAS files with short file extensions. As a result, this application will include SAS version 8.0 data sets and catalogs with long file extensions in the SAS program file listing unless the application is modified. Finally, some familiarity with SAS/AF is assumed.

CREATING A BASE SAS INDEXING PROGRAM Below, the file programs.sas, a modification of Mr. Cunningham’s program, is reviewed. This file, which obtains information on SAS program files within a specified directory, is divided into 4 sections that are explained separately.

SECTION 1 Descriptive headers are the backbone of this application, since this information is captured and displayed on the program directory frame. In order to fully take advantage of the application, all SAS program files should have a header. Otherwise, only file name and file modification date will be displayed. Additionally, the header structure should remain uniform across programs. Note that the programming techniques below can be used to add additional header categories

/********************************************

* Program Name: programs.sas* Description: SAS program dir, mod. of 66-25* Date: 06/13/2000* Modified: 08/02/2000; 1/9/2001* Author: Rob Nelson* Comments: All pgm hdrs should use format!********************************************/

SECTION 2 In this section, the macro %progindx is created. This macro is called from the SAS program file explorer application each time a new directory is opened. The directory is fed to the macro using the parameter &dir. An unnamed pipe is used to create a SAS data set, named pgmdat, containing file name and file creation date for all files in the directory with the extension .sas.

%macro progindx(dir=);options nodate nocenter number;/********************************************Index function - which searches source forstring - could be used to make sure that pathpassed in is correct:

if index(dir,'(default)') then endsas;Use a pipe to create list of all SAS programs********************************************//* Clear old data sets */proc datasets;

delete pgmdat allpgm pgmdes sasprogs;run;/* Unnamed pipe obtains file info. &dir givenin SCL */filename fileinfo pipe "dir &dir.\*.sas";

data pgmdat (KEEP=PGM DATE);infile fileinfo length=l;input @;input @1 line $varying200. l;pgm=substr(line,40,50);pgm = upcase(pgm);date=substr(line,1,8);if substr(date,3,1)='/' and pgm NE ' ';

run;

filename fileinfo clear;

SECTION 3 Next, the first 10 lines of each SAS program file identified in the previous step are parsed for a description. This information is stored in the data set pgmdes. If the header convention shown in step 1 is not used, description data will be not included. The dataset pgmdes is then merged with the data set from section 2, pgmdat to create sasprogs.

/* Determine num of SAS pgms*/data _null_;set pgmdat end=eof;if eof then call symput

('total',left(_n_));run;

/** Retrieve program SAS pgm file descrip **/data sasprogs;run;

%if &total NE 0 %then %do i=1 %to &total;%let pgm=;

Posters

2

data _null_;set pgmdat;if _n_=&i;call symput('pgm',trim(pgm));call symput('dat',trim(date));

run;

filename saspgm "&dir.\&pgm";

data pgmdes (keep=program descript date);length program $ 200;

infile saspgm missover lrecl=200 pad obs=10end=eof;input @1 line $200.;retain gotdesc 0;drop colon i;program="&pgm";date="&dat";if index(upcase(line),'DESCRIPTION:') > 0

THEN DO;colon=index(line,':');do i=1 to colon+1;

substr(line,i,1)=' ';gotdesc=1;

end;descript=left(line);output;

end;if eof and gotdesc=0 then do;descript=' ';output;

end;run;

data sasprogs;set sasprogs pgmdes;if _n_=1 and program=' ' then delete;

filename saspgm clear;%end;/* If no SAS pgm files, create blank ds */%else %do;data sasprogs(keep=program descript date);attrib program length=$25 descriptlength=$100;run;%end;

SECTION 4 In the final step, macro variables are created which will be used to supply column width to the AF application. The data set is also sorted for display, and the macro definition ended.

/* Create str len macro vars to *//* dynamically control col width in SCL *//* (labeled sections update & btnsearch) */data sasprogs;set sasprogs end=last;attrib pgmlenc deslenc length=$8;retain pgmlen 15 deslen 15;label program='Program'

descript='Description' date='Date';if length(trim(pgm)) > pgmlenthen pgmlen=length(trim(pgm));

if length(trim(descript)) > deslenthen deslen=length(trim(descript));

if last=1 then do;pgmlenc=pgmlen; deslenc=deslen;call symput('PGMLEN',pgmlenc);call symput('DESLEN',deslenc);

end;run;

proc sort data=sasprogs;by program;

run;%mend progindx;

CREATING THE SAS/AF INTERFACE The application’s frame layout is illustrated below in Figure 1. The frame is named programs. Table 1 lists object name and type. Object text and labels are shown in the figure. Note that the text entry object is used for all text, including labels. This allows more flexibility, particularly with background color. However, the following object attributes must be ste to use text entry objects in place of text labels: borderStyle=”None”, editable=”No’. Also, the commandOnClick property of btnExit should be set to ‘End’. Finally, the ‘use object name as id in scl’ box should be checked in the property box of the object tblprogs.

Figure 1 - Frame Layout

Table 1 – Object List

No. Name Object type 1 lblTitle Text entry control 2 tblProgs Data table 3 ntnDir Push button 4 lblDirlabel Text entry control 5 lblDirdisplay Text entry control 6 containerbox1 Container box 7 entSearch Text entry control 8 btnSearch Push button control 9 lblSearch Text entry control 10 btnOpen Push button control 11 btnExit Push button control 12 lblTotobs Text entry control 13 lblNumfiles Text entry control 14 lblNumfound Text entry control 15 lblFound Text entry control PROGRAMS.SCL After creating the frame and all objects, add the following frame SCL. This code allows the user to select a program file directory, search for a string of code within the SAS program files in that directory. Several features of this code deserve mention. Submit blocks are used to run the Base SAS programs programs.sas and search.sas. These programs should be tested before starting frame development. The filedialog function is used to call a directory dialog box. This is used both at application start-up, and when the user changes directory. Column width in the data table is controlled using the _setcolumnattributes method.

init:_frame_=_frame_;length dat1 8 direct progg program $50;declare object expid datable,num pgmvar desvar codvar X Y,char string1 pgmbox findcnt;

/* Position form */_frame_=_frame_;dcl num col row scol srow;col=round((winfo('maxcol')),1);row=round((winfo('maxrow')),1);

Posters

3

_frame_._setWindowsize(1,1,row,col);

_frame_._get_widget_('tblprogs',datable);call symput('pgmlen',' ');call symput('deslen',' ');call symput('dirtex',' ');

* select directory dialog box;

rc=filedialog('aggregate',director,'','','*.sas','','','','','','','','','','','Choose SASProgram File Directory',

'','','','','','','','','','');if rc NE 0 then goto errDir;else do;lbldirdisplay.text=director;goto update;

end;return;

update:direct=trim(lbldirdisplay.text);call symput('direct1',direct);

submit continue;%include

't:\serodata\sasapps\dev\SUGI\programs.sas';%progindx(dir=&direct1);

DATA _null_;CALL SYMPUT('totobs',numobs);STOP;

SET work.sasprogs NOBS=numobs;RUN;endsubmit;

rc=open('work.sasprogs','U');dat1=dsid('work.sasprogs','U');datable._setDataset('work.sasprogs');

datable._displayColumnLabel('program','date','descript');

datable._setDisplayedColumns('program','descript','date');pgmvar=((SYMGET('PGMLEN') + 1) * 5);*BECAUSE UPCASE;X=(SYMGET('DESLEN')); If X < 4 then X=4;Y=(SYMGET('CODLEN')); If Y < 4 then Y=4;desvar=(X - 1) * 4.5;codvar=(Y - 1) * 4.5;

datable._setColumnAttribute('program','COLUMN_WIDTH',pgmvar);

datable._setColumnAttribute('descript','COLUMN_WIDTH',desvar);

* HOLD COLUMN 1;heldcol=makelist();heldcol=insertn(heldcol,1,1);

tblprogs._set_held_columns_(heldcol,heldcol);heldcol=dellist(heldcol);

lblTotobs.text=trim(left(symget('totobs')));lblFound.visible='No';lblNumfound.visible='No';return;

btnopen:

datable._getcolumntext('program',progg);call execcmd('wedit "' ||

trim(direct)||'\'||trim(progg) || '" Use');return;

btndir:*start in previously selected directory;prevdir=director || "\*.sas ";

rc=filedialog('aggregate',director,'','',prevdir,

'','','','','','','','','','','ChooseSAS Program File Directory',

'','','','','','','','','','');lbldirdisplay.text=director;datable._setDataset(' ');rc=close(dat1);goto update;return;

btnsearch:if lblTotobs.text='0' then goto

errSearch1;string1=entSearch.text;if string1='' then goto errSearch2;datable._setDataset(' ');rc=close(dat1);call symput('search',string1);call symput('findcnt',' ');

submit continue;%include

't:\serodata\sasapps\dev\SUGI\search.sas';

%strfind(&search,dir=&direct1);endsubmit;

entSearch.text='';rc=open('work.sasprogs','U');dat1=dsid('work.sasprogs','U');datable._setDataset('work.sasprogs');

datable._displayColumnLabel('program','date','descript');

datable._setDisplayedColumns('program','descript','date','finds');

pgmvar=((SYMGET('PGMLEN') + 1) * 5);*BECAUSE UPCASE;X=(SYMGET('DESLEN')); If X < 4 then X=4;Y=(SYMGET('CODLEN')); If Y < 4 then Y=4;desvar=(X - 1) * 4.5;codvar=(Y - 1) * 4.5;

datable._setColumnAttribute('program','COLUMN_WIDTH',pgmvar);

datable._setColumnAttribute('descript','COLUMN_WIDTH',desvar);

datable._hideColumn('finds');

* HOLD COLUMN 1;heldcol1=makelist();heldcol1=insertn(heldcol1,1,1);

datable._set_held_columns_(heldcol1,heldcol1);

heldcol1=dellist(heldcol1);

* DISPLAY NUMBER FOUND;lblFound.visible='Yes';

lblNumfound.text=trim(left(symget('findcnt')));

lblNumfound.visible='Yes';return;

errSearch1:warnlist=makelist();warnlist=insertc(warnlist,'No SAS programs

in dir');rc=messagebox(warnlist);warnlist=dellist(warnlist);return;

errSearch2:warnlist=makelist();warnlist=insertc(warnlist,'Please enter

string');

Posters

4

rc=messagebox(warnlist);warnlist=dellist(warnlist);return;

errDir:***********************************

use dsid function to see if data setwork.programs exists (see

above);***********************************;

submit continue;data sasprogs(keep=program descript date);attrib program length=$25 descript

length=$100;run;endsubmit;warnlist=makelist();warnlist=insertc(warnlist,'You must select

a directory');rc=messagebox(warnlist);warnlist=dellist(warnlist);return;

Once the frame SCL has been added, include the following SCL for the data table object TBLPROGS. Remember to compile the data table SCL before compiling the frame SCL. This is done by selecting the data table object in the frame build environment, left mouse clicking, and selecting Table and Compile SCL from the resulting menus.

init: dcl object datable,char finds;_viewer_=_viewer_;

if finds='Y' then do;call

send(_viewer_,'_setViewerAttribute','program','bcolor','yellow');

callsend(_viewer_,'_setViewerAttribute','descript','bcolor','yellow');

callsend(_viewer_,'_setViewerAttribute','date','bcolor','yellow');

end;return;

CREATING A BASE SAS SEARCH PROGRAM The Base SAS program search.sas, is a modification of programs.sas. It searches SAS program files in the chosen directory for a specified string of code. The search feature of the application does not have to be included for the application to work. To exclude this feature, remove the objects btnSearch, entSearch, lblSearch, lblNumfound, lblFound and the SCL code blocks btnSearch, errSearch1, and errSearch2.

SECTION 1 This section begins the macro %strfind. A data set nalled flmns is created, containing file name for each of the files in the directory.

/*************************************** Program Name: search.sas* Description: Searches SAS program directoryprogram for string, mod. of SAS SUGI 25-66* Author: Rob Nelson***************************************/%macro strfind(str,dir=);options nodate nocenter number pageno=1 ps=50ls=100;

proc datasets;delete flnms str1 pgmstr search;run;

filename fls pipe "dir/b &dir.\*.sas";

data flnms;

infile fls length=l;length filename $200;input @;input @1 filename $varying200. l;filename = upcase(filename);

run;

filename fls clear;

*determine num of SAS pgms & get pgm descriptfor each;data _null_;set flnms end=eof;

if eof then call symput('total',left(_n_));run;

data search;run;

SECTION 2 In this section, the first 10 lines of each SAS program file in the directory are searched for header information.

%if &total NE 0 %then %do i=1 %to &total;%let pgm=;

data _null_;set flnms;

if _n_=&i;call symput('pgm',trim(filename));

run;

filename pgm "&dir.\&pgm";

data pgmstr (keep=program finds findstrstring);infile pgm missover lrecl=200 pad end=eof;

attrib find length=$3. programformat=$varying200.;

input @1 line $200.;retain findstr 0;program="&pgm";string=Upcase("&str");if index(upcase(line),string) > 0 THEN

findstr=1;if findstr=1 then finds='Y';if eof then output;

run;

data search;set search pgmstr;

if program='' then delete;run;

filename pgm clear;

%end;

SECTION 3 Finally, results from the search are combined with the existing data set containing information on SAS program files. PROC freq is used to provide the number of files encountered to the SAS/AF application.

data work.search;set work.search;

attrib indxvar length=$200;indxvar=program;run;

data work.sasprogs(keep=indxvar programdescript code date);set work.sasprogs;

attrib indxvar length=$200;indxvar=program;run;

proc sort data=work.search;

Posters

5

by indxvar;proc sort data=work.sasprogs;

by indxvar;data sasprogs(drop=indxvar);merge search

sasprogs;by indxvar;run;

proc freq data=work.sasprogs;tables finds / NOPRINT out=findcoun;run;

data work.findcoun;set work.findcoun;

call symput('findcnt','0');if finds='Y';call symput('findcnt',count);run;

proc sort data=sasprogs;by program;

run;

%mend strfind;

CONCLUSION This application illustrates the usefulness of combining the interactive features of SAS/AF with the flexibility of Base SAS. The authors hope that use of this application will encourage documentation of Base SAS programs and consistent selection of storage location. Additional features for this application might include a tree display of the directory structure, allowing users to select and submit SAS programs without opening, and pre-designation of user start directory.

REFERENCES Cunningham, Gary “An Automated Method to Create a Descriptive Index for a Directory of SAS Programs”, SAS Institute Proceedings of the Twenty-Fifth Annual SAS Users Group International Conference, Cary, NC: SAS institute Inc. SAS Institute Inc. (1999) , SAS OnlineDoc® Version 8., SAS Institute Inc., Cary, NC: SAS Institute Inc.

CONTACT INFORMATION Contact the author at:

Rob Nelson Centers for Disease Control & Prevention Mailstop E-48

1600 Clifton Road Atlanta, GA 30333 E-mail: RXN1@CDC.GOV

Posters

top related