Monday, April 23, 2012

PROC STREAM News

Just a short posting for a couple of useful announcements about PROC STREAM.

  1. Many of the examples presented in my blog and in SAS® Server Pages: Generating Dynamic Content require the version of PROC STREAM that will ship with SAS 9.3M2. See Generating SAS Server Pages in different Releases of SAS for details about getting the updated version of PROC STREAM.
    NOTE:  This update can also be used in SAS 9.2.
  2. Check out Rick Langston's Super Demo on PROC STREAM. Presented at SAS Global Forum 2012.

Friday, April 20, 2012

A SAS Server Page UI for the Pie Chart

In my last post, A JavaScript Based Pie Chart, I showed a JavaScript based SAS Server Page that produced a pie chart using SAS data. That post illustrated how different pie charts could be produced by including parameters (also known as name/value pairs) in the URL. Clearly manually editing URLs to change the data or characteristics of the output from a web report is not desirable. Web pages should be created to provide options to the user that they can select.

This post describes using a SAS Server Page to present the user with the available choices. Try out this sample SAS Server Page based UI by clicking on either of the following links:
And here is the source for the SAS Server Page, interspersed with comments.

Assign a default for the data set to use. The data set could be selected in a prior page. To keep this example simple, a different data set can be specified by adding data to the URL as a name/value pair. The %sysfunc macro function uses the COALESCEC function to assign a default value. The macro variables lib and mem are used later in the page.

%global data;
%let data = %sysfunc(coalescec(&data,sashelp.shoes));
%let lib = %upcase(%scan(&data,1));
%let mem = %upcase(%scan(&data,2));


The initial HTML tags.

<html>
<head>
<title>UI for PieChart SSP Example</title>
</head>

When the page is initially loaded, submit the form with the default values.

<body onLoad="document.forms[0].submit();">

The FORM tag specifies that the output is displayed in another part of the page. It also uses the macro variable, _URL. This macro variable is created by the server and using this parameter is a sugggested Best Practice. Using _URL also allows for the same SAS Server Page to be processed by the both the Stored Process Server as well as the SAS/IntrNet Application Dispatcher.

<form action="&_url" target="results">

A standard header is defined by using %INCLUDE to dynamically include another input SAS Server Page. Using %INCLUDE ensures consistency and allows for easy updates/maintenance.

&streamDelim;%include srvrpgs(standardHeader.html);

Use an HTML table to arrange the select tags along the top of the page.

<table border="0">

Use the server created macro variable _program - both this UI and the PieChart SAS Server Page use the sasServerPage program. Using &_program is another Best Practice.

<input type="hidden" name="_program" value="&_program">
<input type="hidden" name="page" value="PieChart">
<input type="hidden" name="data" value="&data">

Use the generateOptionTag macro (a topic for a future posting), to create a select tag from data in a SAS Data Set. In this case, the data set is one that was created by a program that queried the provided SAS Styles to extract the color schemes.

<tr>
<td>ODS Color Pattern:
<td>%generateOptionTag
        (data=parms.colorList
        ,var=colorlist
        ,name=colors
        ,label=Style
        )

Use the generateOptionTag macro using the DATA step views of the dictionary tables. In this case the slice variables are limited to character variables (not a good approach in general - but done here for the sake of simplicity).

<td>Slice Variable:
<td>%generateOptionTag
        (data=sashelp.vcolumn
        ,var=name
        ,name=class
        ,selected=Product
        ,where=libname="&lib"
               and memname="&mem"
               and type="char"
        )

Use the generateOptionTag macro again, this time including numeric variables.

lt;td>Analysis Variable:
<td>%generateOptionTag
        (data=sashelp.vcolumn
        ,var=name
        ,name=var
        ,selected=Sales
        ,where=libname="&lib"
               and memname="&mem"
               and type="num"
        )

A hard-coded select tag for the statistic to use.

<td>Statistic:
<td>@select name="statistic">
    @option>Sum
    @option>Mean
    @option>Min
    @option>Max
</select>

The submit button and close the form.

<td>@input type="submit" value="Run">
<form>
</tr>
</table>

An iframe tag is used the embed the output into the same page as the UI. Note that the value of the name parameter matches the target value from the form tag.

<iframe name="results"
        width="100%"
        height="100%"
        frameborder="0">
</iframe>

Close the HTML page.

</body>
</html>

Wednesday, April 18, 2012

A JavaScript Based Pie Chart

This example illustrates the integration of the numerous resources available on the Web, which provides a rich source of samples and starting points for readers to create their own SAS Server Pages.

Try out any or all of the links below to see a pie chart of the SASHELP.SHOES data set. Once the pie chart displays, click on a slice or a table row to explode the slice.
The following URLs illustrate customizing the pie chart by passing parameters in the URL.
Note that this example uses the sasServerPage stored process (which doubles as a SAS/IntrNet Application Dispatcher program) and all of the processing, including creating the aggreates, is defined in the PieChart.html page.

The starting point for this example was found via a simple internet search and was modified so the data set, slice variable and analysis variable are parameters. The details can be found in Section 9.3 of the free preview copy of my eBook, SAS® Server Pages: Generating Dynamic Content. The download is scheduled to be available by SAS Global Forum on my SAS Press Author Page.

One of the key points to keep in mind here is that the use of PROC STREAM and SAS Server Pages enables a SAS programmer to leverage the broad range of tools available on the Internet to produce various output reports. You only need find samples that meet your needs or interest you, and then parameterize them by converting the input files to SAS Server Pages in order to customize the output. And, of course, it is also important to make sure that any tools you download and modify allow such use.

My next posting will illustrate creating a prompts page to allow the user to customize the generated pie chart.

Friday, April 13, 2012

Creating a simple UI

The point of this post is to illustrate how PROC STREAM and SAS Server Pages can be used to not only create the desired content, but also to create a user interface for a stored process (or SAS/IntrNet Application Dispatcher program). Following up on my previous posts, we need to allow the user to select which student letter is to be generated. Before describing how to do that, lets look at the results first:
We invoke the sasServerPage stored process (or SAS/IntrNet Application Dispatcher program) and just need to define the name of our input SAS Server Page which allows us to select the student for whom we'd like to generate the letter.

Here is the source for our input SAS Server Page, followed by a few notes and key points about it.

%let rc = %sysfunc(dosubl('%selectTag'));
<html>
<head>
<title>Select Letter to Generate
</head>
&streamDelim;%include srvrpgs(standardHeader.html);
<table style="width:100%; height:100%;">
<tr>
<td align="left" valign="top">
<form action="&_url" target="letter">
<input type="hidden" name="_program" value="&_program">
<input type="hidden" name="page" value="class_w_DoSub">
&selectTag
<p><input type="submit" value="Display Letter">
</form>
</td>
<td>
<iframe name="letter"
        style="width:100%; height:100%;"
        frameborder="0">
</iframe>
</td>
</tr>
</table>
</body>
</html>

Now a few notes about what is going on in the above SAS Server Page:
  • We are using DOSUBL to run some SAS code. In this case a macro (included below) that reads the SASHELP.CLASS data set and creates a macro variable that contains the text for the select tag of students that you see when you click on either of the links above to see the results.
    • The technique used here does require the M2 release of PROC STREAM/DOSUBL.
    • Note that the macro call is quoted with single quotes. Typically when functions are invoked with the %SYSFUNC macro, text is not quoted. However (again for the M2 release), for the DOSUBL function the decision was made to allow for the argument to be quoted - the rationale being that it might be desirable to allow for a single quoted string to provide a method to prevent macros froms being resolved in-line. Thus, starting with the M2 release, DOSUBL will recognize that there are leading/trailing quotes and will ignore them, thereby allowing a macro specification to be given and not expanded by %SYSFUNC.
    • Note however that the argument to DOSUBL does not need to be quoted.
    • Also note that there was no need to use a macro here. As seen in previous posts, the argument to DOSUBL could be the DATA Step code that the macro generates. I chose to use a macro here to highlight the fact that the argument to DOSUB could be a macro and also to highlight the quoting issue mentioned above.
  • The select tag is included in the output simply by using the macro variable reference &selectTag.
  • A %INCLUDE is used in the SAS Server Page to define a standard set of header text. In this example, is the the text on the grey backgound with the links to my book page on sasCommunity.org and to my web-site. I could have just as easily included trailer text, or both header and trailer text - simply by including the approapriate file.
  • Since both this user interface SAS Server Page and the SAS Server Page for the letter are self-contained (i.e., any needed SAS code is included instead of in separate programs to be run), we can simply chain these together using the same stored process, sasServerPage (which we can reference using the server created macro variable _program), and just specify the name of the input SAS Server Page using the page parameter.
  • We have complete control over how and where the output is displayed since we have complete control over what the SAS Server Pages do. In this case, we use the <iframe> tag so the user interface and the output appear in the same browser window. 
Here is the macro that we are invoking in the DOSUBL call. But before showing the code, a few disclaimers about it:
Here is the code with no explanation since it should be easy to undertand by most SAS programmers. And furthermore, it is not an approach I would want readers to think is a good idea. :-).

%macro selectTag;
 data _null_;
  length selectTag $32767;
  retain selectTag '<select name="letterObs">';
  set sashelp.class(keep=name) end=lr;
  selectTag = cats(selectTag
                  ,'<option value="'
                  ,_n_
                  ,'">'
                  ,name
                  ,'</option>'
                  );
  if lr;
  selectTag = cats(selectTag,'</select>');
  call symputx('selectTag',selectTag,'G');
 run;
%mend selectTag;

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.

Monday, April 9, 2012

A Sample Mail-Merge Application

So far all of my postings about PROC STREAM and SAS Server Pages have been setting up some of the basics. This post is going to be about a real-world use case: a mail-merge example where the requirement is to generate custom content for each defined subset of data—usually each observation. This example is based on the example from Section 4.1 of SAS® Server Pages: Generating Dynamic Content (and Chapter 4 is included in the soon to be released free preview copy). It has been slightly modified so it can be run over the web. That way you can see the results using these links:
If no observation number is specified, the default is to generate a letter for observation 1. But you can supply an observation number as follows:
The value of the parameter letterObs is checked to make sure it is an integer as discussed in my last post, Simple Utility Macros and SAS Server Pages. Feel free to try different observation numbers by editing the URL.

Assuming you've clicked on the links to try these out, lets talk about how this works. The code is being run by my runMacro facility that runs the following macro.

%macro singleLetter;

%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;

proc stream outfile=_Webout sqac;
/* %include the input SAS Server Page */
BEGIN
&streamDelim;%include srvrpgs(class.html);
;;;;
run;

%mend singleLetter;

The code is pretty simple at a conceptual level:
  • A format is created to map F/M to daughter/son so that the generated letter looks a little nicer.
  • DATA Step is used to subset the desired observations and load all the variables into like-name macro variables as well as to assign formats to both the Age and Sex variables. Note that the SYMPUTX function is used as it handles numeric to character conversion. The VVALUE function is used so the values of the macro variables are the formatted values.
  • Then  PROC STREAM is called and the input SAS Server Page which is the desired HTML letter is defined using %INCLUDE.
And, here is the input SAS Server Page that PROC STREAM processes:

<html>
<head>
<title>&Name</title>
</head>
<body>
%sysfunc(date(),worddate.)
<br><br>
<b>Dear Parents</b>:
<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>

Just a few notes about this HTML file (which I refer to as a SAS Server Page due to the use of the Macro Language to customize it):
  • The %SYSFUNC Macro is used with the DATE function to insert the current date using the worddate format.
  • The desired text is intermixed with the needed HTML tags (e.g., P, BR, UL, LI, B, etc.).
  • The macro variables created in the DATA Step are simply referenced and thus resolved by PROC STREAM.
So, yes, it really can be that simple.

In Chapter 4 I build upon this example by next using the macro facility to generate a letter for each observation in the data set. I then show how to create an RTF document (that can be viewed and edited by word processing softtware like Microsoft Word). My next two blogs will take a different path by building on this example to illustrate:
  1. Embedding SAS code to be executed into a SAS Server Page. 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.
  2. Creating a SAS Server Page which is a user-interface to the letter generation process - allowing the user to select which letter to generate.

Thursday, April 5, 2012

Simple Utility Macros and SAS Server Pages

So much for plans . . .

Today's post was supposed to be a mail-merge example from Chapter 4 (included in the preview copy available at SAS Global Forum 2012) of SAS® Server Pages: Generating Dynamic Content.

In the book that example was not Web-based. I started with a simple example: generating a letter for a specified observation, and built on it. I had planned to something similar here but packaged for the Web - using it to illustrate some important features of PROC STREAM. My original plan was to just hard code the observation number - but I decided that was not a good idea and so I decided to:
  1. Allow the observation number from the SASHELP.CLASS data set for which the letter is to be generated to be passed in as a parameter.
  2. Have a default value used if no value is specified.
  3. That then led to needing to confirm that the value was an integer between 1 and the number of observations in the data set (19 in this case).
  4. Which then led to a simple utility macro that does that validation and assigns a default value.
Just as with any SAS application, I've discovered as I've used SAS Server Pages on numerous projects that its a good idea to create simple/short utility macros that perform a specific function. So here is the code for my macro that I can use in a SAS Server Page to validate the observation number.

%macro verifyInteger
  (value= /* the value to be verified as an integer */
  ,default=1 /* default if null */
  ,min= /* if specified, the minimum allowed value */
  ,max= /* if specified, the maximum allowed value */
  );

%let value =
     %sysfunc(coalescec(%superQ(value),&default));
%if %sysfunc(notdigit(%superQ(value)))
    %then %let value=1;
%if %length(&min) gt 0 and &value lt &min
    %then %let value = &min;
%if %length(&max) gt 0 and &value gt &max
    %then %let value = &max;

&value /* return the value to the input stack */

%mend verifyInteger;
 
In my code, I can just add the following statement:
 
%let letterObs = %verifyInteger(value=&letterObs
                               ,min=1
                               ,max=19
                               ,default=1
                               );
 
And before ending this post, just a few comments about this macro and how I am using it:
  1. Note the use of the NOTDIGIT function to validate that the value contains only integers.
  2. I've hard-coded the value of the max parameter on the call because I know the data set only has 19 observations. I could have used the SCL data access functions to get the value if it was unknown.
  3. While it is true that I could define a parameter for a stored process that forces the value to be an integer, since I want these to work for the SAS/IntrNet Application Dispatcher as well, that is not an option. In addition, since I will typically want to use my generic sasServerPage and runMacro stored processes (also runnable as SAS/IntrNet Application Dispatcher programs), having the constraints on the parameter value is not really an option.
And I'll get back on track with my next blog posting - the SAS Server Page mail-merge example. The revised schedule can be seen using the links from my last blog post:

Tuesday, April 3, 2012

Leveraging JQuery Widgets - An Events Calendar

This example is a bit of a teaser as I am going to illustrate a capability that I think is very cool - it illustrates the integration of a resource found on Web - but I am not going to include or discuss the code (which is actually a SAS Server Page). At least, not yet.

All of us programmers know about copying and modifying something that already exists instead of building something from scratch. And the Web, with the help of a Google search, provides a rich source of samples and starting points for readers to create their own SAS Server Pages.

So the idea for this example was born when a fellow consultant asked me (and a few others) a while back about whether I knew of a way to publish an events calendar on the SAS Portal. And before I had a chance to even think about it, Vince DelGobbo of SAS R&D suggested that maybe this could be done using SAS Server Pages (and PROC STREAM). So after a simple Google search, I found the FullCalendar plugin for JQuery.

And everyone reading this can probably guess that it was not too hard to create a calendar from data in a SAS Data Set. In fact, the hardest part was figuring out a set of events data I could use. Then it struck me - a calendar of blog postings about PROC STREAM which you can see here:
These both use the sasServerPage macro and the same source program (which can be run as both a Stored Process and a SAS/IntrNet Application Dispatcher program) as discussed in my last blog posting. The events data are my blog postings about PROC STREAM and SAS Server Pages. For blog postings I've already published, the events are hyperlinked; for future postings, the events are my best guess (at the time this blog was published) of what I plan to blog about between now and SAS Global Forum 2012.

The details of how I did this with PROC STREAM will be discussed in a future blog (sometime after SAS Global Forum 2012). But for now let me say that at the 10,000 foot level, it was pretty simple:
  1. Downloaded the code from the FullCalendar site (note that it can be used without restricion under the MIT License).
  2. Modified one of the demo programs replacing the hardcoded event data with a SAS macro that reads the data set of event data and generates JSON (JavaScript Object Notation).
  3. And thanks to the %let technique to handle HTML entities (&copy; in this case) discussed in my last blog posting I did not have to worry about SAS attempting to resolve Named HTML Entities as macro variable references.
That's all there was to it. I promise!