Application Architecture: Air.htm Air1.sas Air2.sas Shared.sas Air3.sas Air1.htm (web form section): <!-- Here is the first hint that this is calling the SAS/IntrNet service to process the input from the web form: the action pointing to /cgi-bin/broker.exe. All the web form variables will be converted into SAS macro variables which will be used to direct the creation of the HTML output. The reason most of these are called save_... is that I want them to be variables that are saved and passed automatically during the users SAS "session", which I will start with the first program call (air1.sas). Only variables named with the save_ will be passed automatically during the drilldown operations of this Staff/Faculty counts application.--> <FORM name="myform" method="post" action=/cgi-bin/broker.exe> Choose Campus: <select name=save_campus size=1> <option value="6">Calumet <option value="7">Fort Wayne <option value="9">North Central <option value="2">State Wide Technology <option value="1" selected>West Lafayette </select> <br><br> Choose Staff Group Detail: <select name=save_staff_detail size=1> <option value="idn_summarized_group_name">Major Groups (5 levels) <option value="idn_datadigest_group_code">Staffing Groups (9 levels) <option value="idn_summarized_group_name idn_detailed_staff_group_code">Faculty Rank and Staff Detail (25 levels) </select> <br><br> Select Race/Ethnicity Type:<dl> <dd><input type="radio" name="save_ethnictype" value="race_code">Self Reported (Affirmative Action Reporting) <dd><input type="radio" name="save_ethnictype" value="race_name_reporting" checked>Internationals as separate category (IPEDS Reporting) </dl> Choose Gender and Race/Ethnicity Detail: <select name=save_gender_ethnic size=1> <option value="">None <option value="gender_name_reporting">By Gender <option value="reportethnic">By Race/Ethnicity <option value="bth">By Gender/Race/Ethnicity </select> <br><br> Choose Part-time/Full-time Detail: <select name=save_part_fulltime size=1> <option value="">None <option value="fulltime_parttime">By Part-time/Full-time </select> <br><br> Select Year(s):<dl> <dd><input type="checkbox" <dd><input type="checkbox" <dd><input type="checkbox" <dd><input type="checkbox" checked>2000-01 <dd><input type="checkbox" checked>2001-02 <dd><input type="checkbox" checked>2002-03 <dd><input type="checkbox" checked>2003-04 </dl> name="save_yr1" name="save_yr2" name="save_yr3" name="save_yr4" value="1997">1997-98 value="1998">1998-99 value="1999">1999-00 value="2000" name="save_yr5" value="2001" name="save_yr6" value="2002" name="save_yr7" value="2003" <!-- To close the form I need to specify the service I want to use (The admin can set up different services depending on what is requiredthis application uses the "default" service). I also must state the program I want to call (air1.sas) and the sas program library it is at (oirprgms). These library assignments can only be made by the admin. Passing the variable _debug as 131 will show the complete SAS log in HTML- an often used option during testing! --> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<INPUT type=submit value=Submit> <INPUT type=hidden value=default name=_SERVICE> <INPUT type=hidden value=oirprgms.air1.sas name=_PROGRAM><br><br> <INPUT type="checkbox" name="_debug" value="131">Show SAS Log </FORM> Air1.sas: /*********************************************************************/ /* This is the program called by the web form, for producing the campus wide results. First I will set the pagesize quite large so I can output large HTML tables. Next I start the users "session", so that variables and data are attached to the session and saved between program calls (macro variables and datasets must be noted with the "save" as the first part of the name). In the url bar you will notice a 10 character random code that identifies the session of the individual user- it will time out in 15 minutes of no user input. Sessions make for much easier coding! */ options pagesize=10000; %let rc=%sysfunc(appsrv_session(create)); /* I like the include statement, which simply puts the code in the noted file right here in the input stream. In the include subdirectory I have formats and macros that are common to all the programs in this application. Also, I can set up the include subdirectory myself or create new ones- since they are not SAS libraries, I do not need to bug the admin about these (SAS catalogs would provide similar functionality) */ %include 'D:\home\web\scripts\oir\include\shared.sas'; /* Begin building the web page- Here I start making the html, bit by bit. I can interrupt the html building at any point to do data input, analysis, run macros, etc, and come back to it when ready. */ data _null_; file _webout; put 'Content-type: text/html'; put ; put '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">'; put '<HTML>'; put '<HEAD><TITLE>Staff Headcounts</TITLE></HEAD>'; put '<body>'; run; /* A little macro to finalize gender/ethnicity reporting according to user input. */ %macro reset_gender_ethnic; %if &save_gender_ethnic eq reportethnic %then %let save_gender_ethnic = &save_ethnictype; %if &save_gender_ethnic eq bth %then %let save_gender_ethnic = gender_name_reporting &save_ethnictype; %mend; %reset_gender_ethnic; /* Here is the heart of this application. I am referring to the SAS data set air.sas7bdat in the SAS data library oirdata. By calling the new data set save.campusdata the campusdata dataset will be available to other programs called during the session. The core operation here is the creation of the hcampus variable (short for "hyperlink campus"), which when pointed to html output in the proc tabulate (using ODS statements), will appear as a hyperlink containing all the information required to fire the next program in the application, wrkcounts2.sas. This hyperlink will expand the campus to show all the Schools. The %superq function puts in all the misc variables required for a SAS/IntrNet application (port, session ID, service, etc.) */ DATA save.campusdata; SET oirdata.air; length hcampus $300; if campus_code eq "&save_campus"; where data_year in ("&save_yr1","&save_yr2","&save_yr3","&save_yr4", "&save_yr5","&save_yr6","&save_yr7"); hcampus = '<big><A HREF="' || "%superq(_thissession)" || '&_program=oirprgms.air2.sas' || '">' || campus_name || '</A></big>'; run; /* All this mess is for is to form my variable selections into a format that the tabulate proc likes (such as adding the * and the ' ' in the proper places). The goal here is to be able to use one basic bit of tabulate code usable at any drilldown level for any selection the user wants. Of course, you can use other methods- it is SAS! */ %let bkstr = ; %let detstr = ; %let partstr = ; %let save_tabcodestr = ; %macro tabulate_str; %if &save_staff_detail eq idn_summarized_group_name %then %let detstr = idn_summarized_group_name=' '; %if &save_staff_detail eq idn_datadigest_group_code %then %let detstr = idn_datadigest_group_code=' '; %else %if &save_staff_detail eq idn_summarized_group_name idn_detailed_staff_group_code %then %let detstr = idn_summarized_group_name= ' '*idn_detailed_staff_group_code=' '; %if &save_gender_ethnic eq &save_ethnictype %then %let bkstr = *&save_ethnictype=' '; %else %if &save_gender_ethnic eq gender_name_reporting %then %let bkstr = *gender_name_reporting=' '; %else %if &save_gender_ethnic eq gender_name_reporting &save_ethnictype %then %let bkstr = *gender_name_reporting=' '*&save_ethnictype=' '; %if &save_part_fulltime eq fulltime_parttime %then %let partstr = *fulltime_parttime=' '; %let save_tabcodestr = &detstr&bkstr&partstr; %mend; %tabulate_str; %let tablevar=hcampus; %let tabtext=Campus; /* Output the tabulate results to the web page, note the no top or bottom option- this is just a preference of mine, in that I want to control the creation of the web page carefully, so I do not want <html> </html> or <body> </body> put in automatically. This way, I only get the table html statements created */ ods html body=_webout (no_top_matter no_bottom_matter) style=sasweb path=&_tmpcat (url=&_replay) rs=none; proc tabulate data=save.campusdata missing; %tabulate_code; run; ods html close; /* Now the web page is finished, and with this put statement all the html has been sent to the user's browser. */ data _null_; file _webout; put '</BODY></HTML>'; run; /* Here at the bottom, commented out all the time, is the code used to produce the dataset this application uses (air.sas7bdat). It uses a proc sql to access the Oracle table and pull the needed data into a SAS dataset. When the oracle data is updated I just select this and run to update my SAS dataset. To go live against the Oracle table, basically move this code to the top and uncomment (also, only save the data extraction inside the session with a save.name. You can also read in a username and password from the web form if you want limited access/security). */ /* LIBNAME Intrnet "I:\oirdata"; proc sql; connect to oracle as getdata(user="abcdefghi" password="xxxxxx" path="ribbit" ); create table Intrnet.air as select * from connection to getdata (SELECT data_year, race_name_reporting, gender_name_reporting, idn_SUMMARIZED_group_name, IDN_DATADIGEST_group_name, DETAILED_staff_group_name, campus_code, CAMPUS_NAME, bud_GRP_code, bud_GRP_DESC, DEPT_TITLE, IDN_DATADIGEST_group_code, fulltime_parttime, dept, race_code, sum(headcount) AS COUNT FROM complete.IDN_WORKFORCE_RAW where campus_code ^= '3' and campus_code ^= '8' GROUP BY data_year, race_name_reporting, gender_name_reporting, idn_SUMMARIZED_group_name, IDN_DATADIGEST_group_name, DETAILED_staff_group_name, campus_code, CAMPUS_NAME, bud_GRP_code, bud_GRP_DESC, DEPT_TITLE, IDN_DATADIGEST_group_code, fulltime_parttime, dept, race_code); disconnect from getdata; quit; run; */ Air2.sas: /******************************************************************/ options pagesize=10000; /* This program provides school level results and provides the drill down into the department level. */ %include 'D:\home\web\scripts\oir\include\shared.sas'; data _null_; file _webout; put put put put put put run; 'Content-type: text/html'; ; '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">'; '<HTML>'; '<HEAD><TITLE>Staff Headcounts</TITLE></HEAD>'; '<body>'; /* Note that the hsch variable here ("hyperlink school") encodes a variable save_sch that enables drill down into all the departments in that school, to be performed by air3.sas. The urlencode function puts the required web formatting into this variable (the %20s, %26s, etc.) so that when it is pointed to html it looks and functions correctly. For using text and macro variables with blanks and SAS tokens (* & / ...) this and other SAS functions will be required. */ DATA save.campusdata2; SET save.campusdata; length hsch $300; hsch = '<A HREF="' || "%superq(_thissession)" || '&_program=oirprgms.air3.sas&save_sch=' || urlencode(trim(bud_grp_desc)) || '">' || bud_grp_desc || '</A>'; run; %let tablevar=hsch; %let tabtext=School; ods html body=_webout (no_top_matter no_bottom_matter) style=sasweb path=&_tmpcat (url=&_replay) rs=none; proc tabulate data=save.campusdata2 missing; %tabulate_code; %title2; run; ods html close; data _null_; file _webout; put '</BODY></HTML>'; run; Air3.sas: /********************************************************************/ options pagesize=10000; /* This program provides department level results. This is the end of the line and no further drill down is provided */ %include 'D:\home\web\scripts\oir\include\shared.sas'; data _null_; file _webout; put 'Content-type: text/html'; put ; put put put put run; '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">'; '<HTML>'; '<HEAD><TITLE>Staff Headcounts</TITLE></HEAD>'; '<body>'; /* Here is another one of those SAS functions that may be requiredappsrv_unsafe. By default SAS/IntrNet strips some SAS tokens from variables passed between programs. This function turns that off (some schools have & in the name). */ DATA school; SET save.campusdata2; if bud_grp_desc eq appsrv_unsafe('save_sch'); run; %let tablevar=dept_title; %let tabtext=Department; ods html body=_webout (no_top_matter no_bottom_matter) style=sasweb path=&_tmpcat (url=&_replay) rs=none; proc tabulate data=school missing; %tabulate_code; %title2; run; ods html close; data _null_; file _webout; put '</BODY></HTML>'; run; Appstart.sas: /*******************************************************************/ /* * * * ********************************************** This file starts an Application Server for the sasinttnet01 socket service. ********************************************** */ /* ********************************************** * The ifcexist macro is defined so that catalogs * can be conditionally included in a proglibs * statement. * ********************************************** */ %macro ifcexist(catname); %local catname; %if %sysfunc(cexist(&catname)) %then &catname; %mend; %let rc=%sysfunc(ntlog(INFORMATION,SAS/IntrNet Application Server started for the sasinttnet01 service.)); proc appsrv adminpw='fa11c013rs' unsafe='&";%''' &sysparm; allocate file sample '!SASROOT\intrnet\sample'; allocate library oirdata 'd:\usr\local\oirdata'; allocate library samplib '!SASROOT\intrnet\sample' access=readonly; allocate library sampdat '!SASROOT\intrnet\sample' access=readonly; allocate library tmplib 'd:\opt\sas institute\My SAS Files\V8\IntrNet\sasinttnet01\temp'; allocate file oirprgms 'd:\home\web\scripts\oir'; allocate file logfile 'd:\opt\sas institute\My SAS Files\V8\IntrNet\sasinttnet01\logs\%a_%p.log'; allocate library oirsasp 'd:\home\web\scripts\oir'; proglibs oirprgms oirsasp; proglibs sample samplib %ifcexist(sashelp.webeis) sashelp.webprog; proglibs sashelp.websdk1; adminlibs sashelp.webadmn; datalibs sampdat tmplib oirdata; log file=logfile; run; %let rc=%sysfunc(ntlog(INFORMATION,SAS/IntrNet Application Server stopped for the sasinttnet01 service.)); All Materials available at: http://bfpdev.admin.purdue.edu/irvba/