Wednesday, April 11, 2012

Running SAS Code within a SAS Server Page

As promised in my last post, A Sample Mail-Merge Application, embedding SAS code to be executed in a SAS Server Page is the topic of this post. That is, the format that creates the $gender format and the code to create the macro variables will be part of the input SAS Server Page instead of as a program that then invokes PROC STREAM. By doing that, we can use the macro and stored process discussed in A SAS Server Page macro. Our url uses the sasServerPage stored process which has as a parameter, page, the name of the input SAS Server Page to use.

So what happens if we just put the very same code, as is, into the macro?

%global letterObs;
%let letterObs = %verifyInteger
                    (value=&letterObs
                    ,min=1
                    ,max=19
                    ,default=1
                    );
proc format;
 /* create a format that maps the value
    of sex to daughter/son
 */
 value $gender 'F' = 'daughter'
               'M' = 'son'
;
run;
data _null_;
 /* read one observation and create macro variables.
    note the use of the vvalue function to cause
    the formatted values to be used.
 */
 set sashelp.class(firstobs=&letterObs obs=&letterObs);
 /* associate the desired formats with sex and age */
 format sex $gender. age words12.;
 call symputx('Name',vvalue(Name));
 call symputx('Sex',vvalue(Sex));
 call symputx('Age',vvalue(Age));
 call symputx('Height',vvalue(Height));
 call symputx('Weight',vvalue(Weight));
 stop;
run;

You can see the results using either of the following links:
It is pretty obvious that just including the code did not work as most of it (though not all of it), was just included in the output stream that PROC STREAM wrote (in this case) back to your browser. Note however that:
  • The /*  . . . */ comments were not included in the output. That is because those comments were eaten by the SAS Wordscanner. Note there is an option to cause those to be passed along. But that is a topic for later.
  • The %let statement and the verifyInteger macro call were also not included in the output. And that is because they were actually processed and executed by the macro facility.
  • None of the macro variables resolved correctly (because the code to create them was never executed).
So the question then becomes how do we cause text in a SAS Server Page to be seen/interpreted as SAS code to be executed instead of as text to be passed along to the output stream . . . and the answer is the DOSUB functions:
  • DOSUB takes a single argument which is the fileref that points to the desired code.
  • DOSUBL also has a single argument which is the line (or lines) of code to be run, including macro calls.
  • And both of these can be run using the %SYSFUNC macro function.
The following two links illustrate this. Note that the only difference in the URLs from the ones listed above is the name of the input SAS Server Page:
The only change I had to make was to wrap the PROC FORMAT and DATA Step code inside of a DOSUBL call (using the %SYSFUNC Macro function). Note that the %let statement and the verifyInteger function macros were already properly handled in the SAS Server Page. So here's the complete SAS Server Page (with the DOSUBL function invocation highlighted):

%global letterObs;
%let letterObs = %verifyInteger
                    (value=&letterObs
                    ,min=1
                    ,max=19
                    ,default=1
                    );
%let rc = %sysfunc(dosubl(
proc format;
/* create a format that maps the value
   of sex to daughter/son
*/
 value $gender 'F' = 'daughter'
               'M' = 'son'
 ;
run;
data _null_;
 /* read one observation and create macro variables.
    note the use of the vvalue function to cause
    the formatted values to be used.
 */
 set sashelp.class(firstobs=&letterObs obs=&letterObs);
 /* associate the desired formats with sex and age */
 format sex $gender. age words12.;
 call symputx('Name',vvalue(Name));
 call symputx('Sex',vvalue(Sex));
 call symputx('Age',vvalue(Age));
 call symputx('Height',vvalue(Height));
 call symputx('Weight',vvalue(Weight));
 stop;
run;
));
<html>
<head>
<title>&Name
</head>
<body>
%sysfunc(date(),worddate.)
<br><br>
<b>Dear Parents:
<p>As &name's teacher I wanted to make you
 aware of a project we are doing this year.
<p>Every month we will be collecting your &age
 year old &sex's height and weight and recording
 it. At year end, &name will be asked to produce
 a chart of their growth over the school year.
<p>For your information, here are the
 measurements that we just collected:
<ul><li><b>Height:</b> &height inches.
<li><b>Weight:</b> &weight pounds.
</ul>
<p>Please don't hesitate to contact me if
 you have any questions,
<p>Regards,
<br><br>
&name's teacher
</body>
</html>

The body of the actual HTML file is unchanged. All that we had to do was add the SAS code using DOSUB/DOSUBL.

The facility to run SAS code inside of a SAS Server Page is one of the features about PROC STREAM that I like a LOT. And future blog posts will show even more things that you can do with.

And next up: creating a simple UI to allow the user to select which observation to generate the letter for.