At the most basic level the WWW provides the means to share SAS datasets, programs, macros, etc. using a more convenient interface than was provided for these functions previously by other internet protocols (FTP, email, listserves, discussion groups). And, starting with SAS 6.11, SAS itself can access remote datasets and files using the WWW-based URL method provided by the filename statement. But what possibilities exist for connecting SAS more directly to the web, and how can these possibilities be explored?
This paper describes some of my experiments and experiences which explore possible forms of interaction between SAS and the World Wide Web, including
- writing custom CGI scripts for statistical and graphic applications
- a general SAS--web CGI gateway
- serving SAS programs intelligently to remote users
Although this had many beneficial effects, it created two disconnected environments for the students' work: they could read course materials and the linked SAS files with Netscape, but had to use cut/paste or find the files on the local filesystem to run them with SAS. So, it made some sense to look for ways to connect SAS programs more dynamically to web documents.
In the simplest case, the CGI script is a Unix shell script (or other executable program) which passes the request to the SAS program via standard input,
!/bin/sh sas -stdio < myWebApp.sas > myWebApp.lst cat myWebApp.lst
This method, first illustrated by Larry Hoyle ([Hoyle:mwsug94],[Hoyle:mwsug95]) requires very little in the way of Shell programming, but places the burden of parsing the peculiar format in which the request is sent by the browser on the SAS program.
The WebPower form (http://www.datavis.ca/online/power/) runs a SAS program that calculates power or sample size needed to attain a given power for one effect in a factorial ANOVA design. The program is based on specifying the range of treatment means, and calculating the minimum power, or maximum required sample size.
The form invokes the perl CGI script, power.pl, which parses the request parameters, writes a temporary SAS program, runs that program, and returns the listing file to the web browser.
The SAS program itself simply calls a SAS macro, fpower.sas, with the user's input values substituted for '$' variables $a, $b, $delta, $alpha, and $out:
options nodate nocenter nonumber; title 'Power analysis for ANOVA designs'; %include "$power"; %fpower(a=$a, b=$b, delta=%str($delta), alpha=$alpha, $out);
power.pl uses one of the standard Perl CGI utility packages to parse the request parameters passed to it from the form. It does some rudimentary error checking to make sure that the request parameters are valid, and checks that the SAS program ran correctly, producing a listing file with the results. The output is returned as pre-formatted text (wrapped in PRE tags), but it would be easy enough to use a SAS macro to return the output in the form of an HTML table.
Although the SAS program is quite simple and power.pl uses the Perl CGI package, error trapping and formatting the query parameters for insertion in the program template did take some additional effort.
%let gsasfile= %sysget(GSASFILE); %let title = %sysget(TITLE); %let data = %sysget(DATA); filename gsasfile "&gsasfile"; goptions device=pscolor gsfname=gsasfile gsfmode=replace; title "&title"; proc iml; %include "$sieve"; f = { $data }; vnames = { $var }; lnames = { $lab }; title = '$title' ; font = '$font'; run sieve(f, vnames, lnames, title ); quit;Note again that all the '$' names are Perl variables whose values are substituted when the SAS file is written. However, some parameters in the program (GSASFILE, TITLE, and DATA) are passed in the environment, then retrieved to macro variables using the %sysget() function Writing---and debugging---sieve.pl, however, convinced me that there ought to be a better way, so I wrote a general-purpose program to connect any SAS program to a web form.
It works like this. You have a SAS application you want to make accessible to users on the web. That application requires some input from the user, to select records or variables to be processed, or to set parameters for some computation. You write an HTML form in which the user can enter the required information. When the user presses the SUBMIT button, the browser calls sascgi, passing the parameters defined in the form; sascgi retrieves the parameters, runs SAS, and returns the results to the user.
The script passes input parameters to the SAS program via the environment, which is much easier than trying to parse stdin in SAS. The SAS program is assumed to retrieve these parameters via via %sysget(PARAM), or sysget('PARAM') in a data step. The SAS program can return results to the browser by one of three methods:
The SAS program can communicate success or failure (with an error message) by writing a message to a .err file or by returning a message starting with 'ERROR:' to STDOUT.
The script is used in an HTML document by embedding a <FORM> block in the document, referencing this script as the ACTION attribute, as follows:
The <INPUT> tags define names of parameters which are passed to the SAS program. Use type="hidden" when you don't want the user to select or change the value; otherwise, you can use any form element which generates a value.<form method="POST" action="http://your.server/cgi-bin/sascgi"> <input type="hidden" name="SASFILE" value="getlist.sas"> <input type="hidden" name="TITLE" value="Page title"> <input type="hidden" name="OUTPUT_METHOD" value="STDOUT"> <input type="hidden" name="REQUIRE" value="ITEMS LISTS"> ... other form elements ... </form>
DEBUG If non-zero, turns on verbose output for debugging.
data _null_; file STDOUT; put "<pre>"; ... (output results) ... put "</pre>";
My %htmltab macro can also be used to produce output to STDOUT containing either a preformatted printout or an HTML table from a SAS dataset. You can also use the SAS-supplied Output formatter Data Set formatter tools with SAS 6.11 or later.
*-- Retrieve output file name; filename gsasfile "&gsasfile"; *-- Device options; goptions device=pscolor gsfname=gsasfile gsfmode=replace;
filename macros (' web/sasuser/macros', ' web/sasuser/webmacros');an application could use
%include macros(htmltab);
Alternatively, specify the full pathname to the file on the include statement.
>setenv LISTS 1 >setenv ITEMS 20 >sas getlist
sascgi is also designed so that it can be run from the command line for debugging, e.g.,
>setenv DEBUG 1 >./sascgi 'SASFILE=getlist.sas&LISTS=1&ITEMS=20'The parameters OUTPUT_METHOD, SASFILE, TITLE, and DEBUG may all be passed to this script via the form or the environment.
Several working examples, along with source code and further explanation are available online at http://www.datavis.ca/online/sascgi.
This is more convenient (the script is part of the document), more efficient (no need to fork an additional CGI process for each use), and also more secure (security is controlled by the same access restrictions used by the server itself).
For example, here is a form which allows a user to enter bivariate data for a regression model:
<FORM METHOD="POST" ACTION="glm-sas.phtml"> <H2>Input or Paste Y and X</H2> Each line should contain a value of the response, Y, followed by a value of X. <TEXTAREA NAME="yx" ROWS=10 COLS=20> </TEXTAREA> <BR> What Type of Regression ? <SELECT NAME="type"> <OPTION VALUE="1" SELECTED>Linear <OPTION VALUE="2">Quadratic <OPTION VALUE="3">Cubic </SELECT> <INPUT TYPE=SUBMIT VALUE="Submit"><BR> </FORM>
The form ACTION file, glm-sas.phtml is like an ordinary html file, except that it contains embedded PHP code, bracketed by <? ... > delimiters. The lines below process the regression type (created in PHP as the variable $type) value.
<HTML><HEAD> <TITLE>SAS Regression Calculator</TITLE> </HEAD><BODY> <H1>SAS Regression Calculator</H1> <? switch (intval($type)); case 1; $model = "X"; Echo "<H2>Linear Model</H2>"; break; case 2; $model = "X X*X"; Echo "<H2>Quadratic Model</H2>"; break; case 2; $model = "X X*X X*X*X"; Echo "<H2>Cubic Model</H2>"; break; default; $model = "X"; break; endswitch; >You can define functions in PHP to process or transform inputs. Here is one which creates a SAS datastep from the input entered into the YX field of the form:
<? Function Make_DataStep $dataset, $yx ( $text = "data " + $dataset + ";"; $text = $text + " input y x;" ; $text = $text + " cards;" ; $text = $text + $yx + ";" ; Return ($text); ); >Finally, these lines build the SAS program as a string, add lines to call PROC GLM, and echo the program as the standard input to SAS. The output listing file is then sent back to the browser.
<? $sas-program = Make_Datastep ("web", $yx) + "proc glm data=web;" + " model y = " + model + ";"; $sas = "/usr/local/bin/sas"; $execarg = "echo " + $sas-program + " | " sas + "-stdio > glm.lst" Exec ($execarg); $result = File("glm.lst"); $n = Count ($result); $i = 2; Echo "<pre>"; while ($i < $n) { Echo "%s<<br>" $result[$i]; $i++; } Echo "</pre>"; > </BODY> </HTML>This tight integration of CGI capabilities with HTML should make it much easier to develop powerful web-based applications. All of the standard capabilities for which people write separate CGI scripts (form mail, page counters, browser-sensitive responses, etc) are built right in, and the script language is rich enough to support interesting forms of client-server interaction. PHP also provides file upload capability and integrates a variety of database and SQL packages (mSQL, Postgress95, Sybase), and the GD library for GIF creation. It would not be difficult to add support for SAS datasets or for direct communication with the SAS System.
However, when one or more of these conditions is not true, it may be more useful for web-based interaction to run SAS on the client machine.
<EMBED SRC="http://www.server/path/file.sas" ACTION=OPEN>When a page containing an <EMBED> tag is loaded, the browser downloads a temporary copy of the file and draws an action button. When you click the action button, the plugin sends the action to the SAS System. For a SAS program, the Open action simply displays the program in the Program Editor window. Other actions (Browse, Print, Query, Submit) are defined for different types of SAS files.
However, note that the Submit action is inherently dangerous: it runs the program without the opportunity for the user to intervene.
At the server-side, .sas files are normally sent as the MIME type text/plain, which means they are displayed as-is in the browser window. To allow those files to be run locally by SAS, the server needs to send them as the MIME type application, with a subtype such as x-sas ("x" designates an experimental MIME type). With the NCSA and Apache httpd servers, this is accomplished by adding the following line to the server's srm.conf configuration file:
AddType application/x-sas .sas
For the browser to be able to launch SAS as a viewer, you have to set up your web browser to recognise SAS files which are served as application/x-sas. The details vary considerably between Netscape, Mosaic, MSIE and other browsers; instructions for setting up some browsers are detailed in http://www.datavis.ca/online/sasweb/.
Once this has been set up, when Netscape receives a file which is served as application/x-sas, it will
This is not very useful for web-based SAS applications, because the results are not displayed in the browser window, and insecure, because there is nothing to stop the .sas program from performing unwanted housecleaning on your hard-disk.
As an example of this kind of interaction, I've written a Perl CGI script, sasfile. In addition to providing a more secure environment for running SAS on the client, the script allows more intelligent and literate ways to serve SAS programs to remote users.
The script is normally invoked from a web document by including a reference of the form
<a href="http://server.name/cgi-bin/sasfile/path/to/file.sas">file.sas</a>
When invoked in this way, the script sends an HTML file which includes the text of file.sas and a link like this:
<a href="http://server.name/cgi-bin/sasfile/path/to/file.sas?run">file.sas <IMG SRC="launch.gif"> </a>
The addition of the keyword parameter ?run to the URL is the signal the script uses to decide to send the file as application/x-sas. To plug a security hole, the script checks that it was indeed called from the previous instance, rather than having been entered directly with the ?run parameter appended.
%include data(datafile);where data is defined as a collection of directories to be searched by a filename statement in the system autoexec.sas file, e.g.,
filename data ('n:\data', 'n:\psy3030\data', 'n:\psy6140\data' );
These programs also use a collection of directories for macro programs, set up as an autocall library. Since a remote user is not likely to have these data and macro files, the sasfile script scans the lines of the original files for lines starting with %include, and presents a list of them, in the form:
file.sas includes references to the following server data files and macros. You may wish to download them first. (With most browsers, shift-Click on the link and select an appropriate directory.)where each of these is an active link to the included file.
- %include data(nations);
- %include macros(resline);
Here are a few ideas:
/*: :*/which can signal a cgi script to treat lines contained within as ordinary text, rather than pre-formatted text. The sasfile script treats such comments in this way.
/*< =head1 Description: The omelet macro prepares an omelet from the contents of a SAS data set. =head1 Usage: ... >*/The lines included within such documentation blocks can then be automatically extracted and transformed into web documentation or other formats, similar to the way in which Perl ``pod'' documentation is handled, or the way Unix man pages can be transformed.
Further information, source code, and online demonstrations of these ideas are available on the Web at http://www.datavis.ca/online/. Pointers to other web-based statistical computing applications can be found in http://www.math.yorku.ca/SCS/StatResource.html.