- 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. - Check out Rick Langston's Super Demo on PROC STREAM. Presented at SAS Global Forum 2012.
Tips/techniques and advice on integrating traditional SAS Tools with the SAS Business Intelligence Suite
Monday, April 23, 2012
PROC STREAM News
Just a short posting for a couple of useful announcements about PROC STREAM.
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:
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>
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:
- Using the Stored Process Server:
http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/sasServerPage&page=PieChartUI - Using the SAS/IntrNet Application Dispatcher:
http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.sasServerPage.sas&page=PieChartUI
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>
Labels:
HTML5,
Next Generation Web,
Reporting,
SAS Server Pages,
User Interface
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 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.
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.
- Using the Stored Process Server: http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/sasServerPage&page=PieChart
- Using the SAS/IntrNet Application Dispatcher:
http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.sasServerPage.sas&page=PieChart.html
- Using the Stored Process Server:
http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/sasServerPage&page=PieChart&class=Region&var=Returns&statistic=Mean - Using the SAS/IntrNet Application Dispatcher:
http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.sasServerPage.sas&page=PieChart.html&class=Region&var=Returns&statistic=Mean
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.
Labels:
HTML5,
Next Generation Web,
Reporting,
SAS Server Pages
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:
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:
%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;
- Using the Stored Process Server: http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/sasServerPage&page=class_w_DoSub_UI
- Using the SAS/IntrNet Application Dispatcher: http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.sasServerPage.sas&page=class_w_DoSub_UI
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.
- This macro is hard-coded for the subject data set and select tag. In my SAS Press book, Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher, where I introduced the concept of SAS Server Pages, I included A Macro to Generate Data-Driven SELECT Tags (generateOptionTag). And update to that macro will be provided with my eBook SAS® Server Pages: Generating Dynamic Content. Using macro tools like generateOptionTag is a much preferred approach, but for purposes of this blog posting, I wanted to provide simple code that could be easily followed.
- This code also can't handle select tags where the generated text is more than 32K. That is something that is no longer an issue with the new generateOptionTag macro and PROC STREAM.
%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:
%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.
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:
- Using the Stored Process Server: http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/sasServerPage&page=class_wo_DoSub
- Using the SAS/IntrNet Application Dispatcher: http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.sasServerPage.sas&page=class_wo_DoSub
- 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).
- 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.
- Using the Stored Process Server: http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/sasServerPage&page=class_w_DoSub
- Using the SAS/IntrNet Application Dispatcher: http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.sasServerPage.sas&page=class_w_DoSub
%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:
- Using the Stored Process Server: http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/runMacro¯oToRun=singleLetter&_odsDest=none
- Using the SAS/IntrNet Application Dispatcher: http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.runMacro.sas¯oToRun=singleLetter&_odsDest=none
- Using the Stored Process Server: http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/runMacro¯oToRun=singleLetter&_odsDest=none&letterObs=8
- Using the SAS/IntrNet Application Dispatcher: http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.runMacro.sas¯oToRun=singleLetter&_odsDest=none&letterObs=8
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.
- A 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.
<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.
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:
- 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.
- 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:
%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:
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:
- 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.
- Have a default value used if no value is specified.
- 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).
- Which then led to a simple utility macro that does that validation and assigns a default value.
%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:
- Note the use of the NOTDIGIT function to validate that the value contains only integers.
- 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.
- 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.
- Using the Stored Process Server: http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/sasServerPage&page=blogCalendar
- Using the SAS/IntrNet Application Dispatcher: http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.sasServerPage.sas&page=blogCalendar
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:
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:
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:
- Using the Stored Process Server: http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/sasServerPage&page=blogCalendar
- Using the SAS/IntrNet Application Dispatcher: http://demos.hcsbi.com/scripts/broker.exe?_service=ssp&_program=sspebook.sasServerPage.sas&page=blogCalendar
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:
- Downloaded the code from the FullCalendar site (note that it can be used without restricion under the MIT License).
- 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).
- And thanks to the %let technique to handle HTML entities (© 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.
Labels:
JQuery,
Next Generation Web,
Reporting,
SAS Server Pages
Wednesday, March 28, 2012
A SAS Server Page macro
In recent posts I've talked about a few things that can be a bit easier by packaging them in a macro. In Chapter 5 of SAS® Server Pages: Generating Dynamic Content, I describe a macro that I call sasServerPage that package functionality that I've found that I used repeatedly.
For example, in my blog post Processing External Files with PROC STREAM, I talked about using %let statements to deal with HTML Entities that might be used in your input SAS Server Pages. My sasServerPage macro includes the following %let statements for the most commonly used entities:
%local quot amp apos lt gt nbsp copy reg;
%let quot = "
%let amp = &
%let apol = '
%let lt = <
%let gt = >
%let nbsp =  
%let copy = ©
%let reg = ®
Doing this for a complete list of HTML (and XML) Entities is propably overkill. But one of the advantages of sample macros is they can be edited/updated as you need to.
Likewise the same blog post also talks about using %include to define the input SAS Server Page. That is something else that can be packaged and simplified using macro parameters. For example, three of the parameters for my macro are used to specify:
%if %index(&sysvlong,9.03.01M0)
or %index(&sysvlong,9.03.01M1) %then
%do; /* streamDelim not supported before M2 */
%let streamDelim = %sysfunc(datetime(),z18.);
%let streamDelim = _&streamDelim;
%end; /* streamDelim not supported before M2 */
and then the following can be added to your PROC STREAM statement (again using macro logic):
proc stream . . . . resetDelim="&streamDelim";
so the macro will work for 9.3 releases before TS1M2.
Chapter 5 also describe a SAS program that can be invoked as either a Stored Process or a SAS/IntrNet Application Dispatcher program to make it easier to invoke SAS Server Pages in Web environment. That SAS program calls the sasServerPage macro with the parameter values specified in the HTML form, URL (with default values assigned). This allows you to not have to have a distinct program for each and every SAS Server Page you want to produce. So, for example, the follow two URLs illustrate this for the HelloWorld SAS Server Page:
And now I suspect everyone wants to see the complete source. Unfortunately it is not quite ready for that. I am blogging about the sasServerPage macro and Stored Process/IntrNet program so I can start to use them in some real-world SAS Server Page examples that I will start blogging about shortly.
For example, in my blog post Processing External Files with PROC STREAM, I talked about using %let statements to deal with HTML Entities that might be used in your input SAS Server Pages. My sasServerPage macro includes the following %let statements for the most commonly used entities:
%local quot amp apos lt gt nbsp copy reg;
%let quot = "
%let amp = &
%let apol = '
%let lt = <
%let gt = >
%let nbsp =  
%let copy = ©
%let reg = ®
Doing this for a complete list of HTML (and XML) Entities is propably overkill. But one of the advantages of sample macros is they can be edited/updated as you need to.
Likewise the same blog post also talks about using %include to define the input SAS Server Page. That is something else that can be packaged and simplified using macro parameters. For example, three of the parameters for my macro are used to specify:
- srvrpgs: The fileref for the aggregate location where my input SAS Server Pages live
- page: The filename in the aggregate location for the input SAS Server Page
- defaultExtension: An optional parameter that specifies the default extension on the file name to use if no extension is specified. The default is html. This allows you, for example, to specify HelloWorld, instead of HelloWorld.html as the value of page.
%if %index(&sysvlong,9.03.01M0)
or %index(&sysvlong,9.03.01M1) %then
%do; /* streamDelim not supported before M2 */
%let streamDelim = %sysfunc(datetime(),z18.);
%let streamDelim = _&streamDelim;
%end; /* streamDelim not supported before M2 */
and then the following can be added to your PROC STREAM statement (again using macro logic):
proc stream . . . . resetDelim="&streamDelim";
so the macro will work for 9.3 releases before TS1M2.
Chapter 5 also describe a SAS program that can be invoked as either a Stored Process or a SAS/IntrNet Application Dispatcher program to make it easier to invoke SAS Server Pages in Web environment. That SAS program calls the sasServerPage macro with the parameter values specified in the HTML form, URL (with default values assigned). This allows you to not have to have a distinct program for each and every SAS Server Page you want to produce. So, for example, the follow two URLs illustrate this for the HelloWorld SAS Server Page:
- http://demos.hcsbi.com/scripts/broker.exe/ssp?_service=ssp&_program=sspebook.sasServerPage.sas&page=HelloWorld
- http://demos.hcsbi.com:8080/SASStoredProcess/guest?_program=/GuestDemos/sasServerPage&page=HelloWorld
And now I suspect everyone wants to see the complete source. Unfortunately it is not quite ready for that. I am blogging about the sasServerPage macro and Stored Process/IntrNet program so I can start to use them in some real-world SAS Server Page examples that I will start blogging about shortly.
Friday, March 23, 2012
More on the streamDelim Macro Variable
Based on my last post (Processing External Files with PROC STREAM) you may now be curious about the rationale for the streamDelim macro variable as well as what else you can do with it.
But first, an announcement: The sasCommunity page SAS® Server Pages: Generating Dynamic Content has been updated to include:
Now back to streamDelim - why is it needed and what else can it be used for?
In working with Rick Langston of SAS R&D on this, one of the features we really wanted to support was the ability to use %include to include an external file into a SAS Server Page. In order to allow %include to be recognized and handled correctly it needs to be on a statement boundary - i.e., immediately after a semi-colon. So a way was needed to force a statement boundary without having the semi-colon appear in the output. The led to the resetDelim option (in the TS1M0 and TS1M1 releases of SAS 9.3):
proc stream . . . . resetDelim="a_SAS_name_token";
where you specify some text as a delimiter that does not occur in your input SAS Server Pages. This means that the following text could be used to allow %include to be recognized:
a_SAS_name_token; %include fileref-or-path-to-file;
but doing it that way allowed for the possibility of inconsistent values between the input SAS Server Page and the PROC STREAM statement (i.e., a SAS Server Page could be created or edited and not have the same value, or vice-versa). To address this, the TS1M2 release uses the value of a macro variable, streamDelim, as the delimiter. And if this macro variable does not exist, PROC STREAM creates it.
The resetDelim option still exists and that is why the following technique (mentioned in my last blog posting) works:
%let streamDelim = __&sysfunc(datetime(),z18.);
proc stream . . . . resetDelim = "&streamDelim";
So what else can you do with streamDelim?
You can use the following text in your SAS Server Page to force a new line or line break:
&streamDelim newline;
The tokenization process that PROC STREAM uses to resolve macro references ignores/loses line breaks (as many macro programmers know). There are any number of reasons you might want to force going to a new line, e.g.,:
&streamDelim readfile fileref-or-path-to-file;
And again, there are multiple reasons for not wanted resolution to occur:
But first, an announcement: The sasCommunity page SAS® Server Pages: Generating Dynamic Content has been updated to include:
- The hopefully final title of the eBook
- The list of chapters and topics covered
- A link to the preview copy of Chapter 1 at the SAS press site (which provides more details on the book's content)
- A list of my blog entries about SAS Server Pages (which will be regularly updated).
Now back to streamDelim - why is it needed and what else can it be used for?
In working with Rick Langston of SAS R&D on this, one of the features we really wanted to support was the ability to use %include to include an external file into a SAS Server Page. In order to allow %include to be recognized and handled correctly it needs to be on a statement boundary - i.e., immediately after a semi-colon. So a way was needed to force a statement boundary without having the semi-colon appear in the output. The led to the resetDelim option (in the TS1M0 and TS1M1 releases of SAS 9.3):
proc stream . . . . resetDelim="a_SAS_name_token";
where you specify some text as a delimiter that does not occur in your input SAS Server Pages. This means that the following text could be used to allow %include to be recognized:
a_SAS_name_token; %include fileref-or-path-to-file;
but doing it that way allowed for the possibility of inconsistent values between the input SAS Server Page and the PROC STREAM statement (i.e., a SAS Server Page could be created or edited and not have the same value, or vice-versa). To address this, the TS1M2 release uses the value of a macro variable, streamDelim, as the delimiter. And if this macro variable does not exist, PROC STREAM creates it.
The resetDelim option still exists and that is why the following technique (mentioned in my last blog posting) works:
%let streamDelim = __&sysfunc(datetime(),z18.);
proc stream . . . . resetDelim = "&streamDelim";
So what else can you do with streamDelim?
You can use the following text in your SAS Server Page to force a new line or line break:
&streamDelim newline;
The tokenization process that PROC STREAM uses to resolve macro references ignores/loses line breaks (as many macro programmers know). There are any number of reasons you might want to force going to a new line, e.g.,:
- readability of the generated text
- to prevent line breaks forced by the output LRECL in places that might introduce errors (e.g., in a long select tag)
- to deal with // style JavaScript comments (which says that all the rest of the text on the current line is a comment)
&streamDelim readfile fileref-or-path-to-file;
And again, there are multiple reasons for not wanted resolution to occur:
- no macro resolution is needed or desired
- proper handling of JavaScript - && is the JavaScript AND operator. Macro resolution will convert && to & causing the JavaScript to not function correctly
Monday, March 19, 2012
Processing External Files with PROC STREAM
In the examples posted in previous blog entries (A Gentle Introduction to SAS Server Pages and PROC STREAM: Extending the Macro Language to create more than just SAS code), the input text being processed was included in the SAS job stream and was delimited by:
• the token BEGIN (case insensitive)
• four semicolons with no intervening spaces starting in column 1 (;;;;)
PROC STREAM utilizes the SAS word-scanner and tokenization facilities to resolve and execute macro variable references, macro functions and macro calls for all the text delimited by BEGIN .... ;;;; and directs the output to a specified external file:
proc stream outfile= ... ;
BEGIN
/* Input text to be processed */
;;;;
run;
Quite often the text that you will want PROC STREAM to process is contained in an external file (what I would refer to as a SAS Server Page - text, e.g., HTML, along with commands interpreted by SAS to generate additional data-driven content). So the question becomes how to do that since there in no infile option. And the answer is to use a %INCLUDE statement. PROC STREAM recognizes the %INCLUDE statement and will use the contents of that file as its input. So our PROC STREAM statement looks like this:
proc stream outfile= ... ;
BEGIN
/* Include text file to be processed */
&streamDelim; %include fileref-or-path-to-file;
;;;;
Note that fileref-or-path-to-file can reference a fileref, a physical path or use SAS aggregate syntax. I prefer to use aggregate syntax as typically my SAS Server Pages are organized in one or more directories (and you can define your fileref so it points to concatenated directories).
The content of the file being included need not be SAS code, it can be any text (e.g., HTML, XML, CSV, SAS code, and more) - whatever text it contains will be processed by the SAS tokenizer.
But now you ask, what it &streamDelim; and why is it there? It is a delimiter that is needed because certain SAS statements must appear on statement boundaries (i.e., they must be the very first statement in your program or they must immediately follow a semi-colon). So in order for PROC STREAM to recognize the %INCLUDE, it needs to follow a semicolon. But since you typically don't want the ; in the output file, we need to have a way to tell PROC STREAM to ignore it - thus &streamDelim. There are a number of other things you can do with &streamDelim - and I'll have examples and blog postings between now and SAS Global Forum.
NOTE: In the SAS TS1M2 release, PROC STREAM will create the &streamDelim macro variable if it does not already exist. Until then, you can slightly modify the syntax above as follows to create a value for streamDelim that is a valid SAS name token but whose text value is not in your input SAS Server Page, for example:
%let streamDelim = __&sysfunc(datetime(),z18.)
proc stream . . . . resetDelim = "&streamDelim";
/* Include text file to be processed */
BEGIN
&streamDelim; %include srvrpgs(HelloWorld.html);
;;;;
Input HTML (and other) files may have an additional wrinkle - named HTML Entities. For example, the HelloWorld.html file contains ® for the registered trademark symbol in the text:
. . . input SAS® Server Page . . .
When this text is processed we will likely get a warning, or depending on the context, an error message for ® since the SAS tokenizer will interpret it as a macro variable reference. However, the comparable numeric HTML Entity (®) for the registered trademark symbol does not have this problem since the SAS tokenizer does not see #174 as a macro variable reference. In order to avoid editing the input files to convert named to numeric HTML Entities, we can let the SAS tokenizer do the work for us. Since the content of the input file is being tokenized and macro variable references are replaced with their values, including a series of %let statements like the following before invoking PROC STREAM for the standard HTML entities
%let reg = ®
will allow the tokenizer to do the substitutions for us: ® will be replaced by ® and so ® will be resolved to ®.
You can see both %include and this substitution in action on my server using:
• the SAS/IntrNet Application Dispatcher
• the Stored Process Server
both of which use the same code and the same input SAS Server Page (our Hello World example).
I will have more examples in future blog entries (and, of course, in the book) that take advantage of %INCLUDE, including input SAS Server Pages that have %INCLUDE statements.
• the token BEGIN (case insensitive)
• four semicolons with no intervening spaces starting in column 1 (;;;;)
PROC STREAM utilizes the SAS word-scanner and tokenization facilities to resolve and execute macro variable references, macro functions and macro calls for all the text delimited by BEGIN .... ;;;; and directs the output to a specified external file:
proc stream outfile= ... ;
BEGIN
/* Input text to be processed */
;;;;
run;
Quite often the text that you will want PROC STREAM to process is contained in an external file (what I would refer to as a SAS Server Page - text, e.g., HTML, along with commands interpreted by SAS to generate additional data-driven content). So the question becomes how to do that since there in no infile option. And the answer is to use a %INCLUDE statement. PROC STREAM recognizes the %INCLUDE statement and will use the contents of that file as its input. So our PROC STREAM statement looks like this:
proc stream outfile= ... ;
BEGIN
/* Include text file to be processed */
&streamDelim; %include fileref-or-path-to-file;
;;;;
Note that fileref-or-path-to-file can reference a fileref, a physical path or use SAS aggregate syntax. I prefer to use aggregate syntax as typically my SAS Server Pages are organized in one or more directories (and you can define your fileref so it points to concatenated directories).
The content of the file being included need not be SAS code, it can be any text (e.g., HTML, XML, CSV, SAS code, and more) - whatever text it contains will be processed by the SAS tokenizer.
But now you ask, what it &streamDelim; and why is it there? It is a delimiter that is needed because certain SAS statements must appear on statement boundaries (i.e., they must be the very first statement in your program or they must immediately follow a semi-colon). So in order for PROC STREAM to recognize the %INCLUDE, it needs to follow a semicolon. But since you typically don't want the ; in the output file, we need to have a way to tell PROC STREAM to ignore it - thus &streamDelim. There are a number of other things you can do with &streamDelim - and I'll have examples and blog postings between now and SAS Global Forum.
NOTE: In the SAS TS1M2 release, PROC STREAM will create the &streamDelim macro variable if it does not already exist. Until then, you can slightly modify the syntax above as follows to create a value for streamDelim that is a valid SAS name token but whose text value is not in your input SAS Server Page, for example:
%let streamDelim = __&sysfunc(datetime(),z18.)
proc stream . . . . resetDelim = "&streamDelim";
/* Include text file to be processed */
BEGIN
&streamDelim; %include srvrpgs(HelloWorld.html);
;;;;
Input HTML (and other) files may have an additional wrinkle - named HTML Entities. For example, the HelloWorld.html file contains ® for the registered trademark symbol in the text:
. . . input SAS® Server Page . . .
When this text is processed we will likely get a warning, or depending on the context, an error message for ® since the SAS tokenizer will interpret it as a macro variable reference. However, the comparable numeric HTML Entity (®) for the registered trademark symbol does not have this problem since the SAS tokenizer does not see #174 as a macro variable reference. In order to avoid editing the input files to convert named to numeric HTML Entities, we can let the SAS tokenizer do the work for us. Since the content of the input file is being tokenized and macro variable references are replaced with their values, including a series of %let statements like the following before invoking PROC STREAM for the standard HTML entities
%let reg = ®
will allow the tokenizer to do the substitutions for us: ® will be replaced by ® and so ® will be resolved to ®.
You can see both %include and this substitution in action on my server using:
• the SAS/IntrNet Application Dispatcher
• the Stored Process Server
both of which use the same code and the same input SAS Server Page (our Hello World example).
I will have more examples in future blog entries (and, of course, in the book) that take advantage of %INCLUDE, including input SAS Server Pages that have %INCLUDE statements.
Thursday, March 15, 2012
A Gentle Introduction to SAS Server Pages
My last post PROC STREAM: Extending the Macro Language to create more than just SAS code introduced the 9.3 experimental procedure, PROC STREAM, which provides direct support for SAS Server Pages. It also provided a very preliminary preview of a SAS Press eBook on PROC STREAM and SAS Server Pages. A free preview copy of selected chapters is targeted for the SAS Global Forum 2012 timeframe - look for a blog posting soon with more details on the topics covered in the eBook and the preview version.
data _null_;
file _webout;
infile datalines;
input;
_infile_ = resolve(_infile_);
put _infile_;
datalines4;
<html>
<head><title>The Obligatory Hello World Example</title></head>
<body>
<h1>Hello &_rmtuser..</h1>
<h2> This welcome note generated
at %sysfunc(time(),timeampm8.)<sup>1</sup>
on %sysfunc(date(),worddate.).</h2>
This HTML file was produced from an input
SAS Server Page and customized courtesy
of a DATA Step and the RESOLVE function
using SAS Release &sysver on &sysscp..
<p><sup>1</sup>The time listed is the server
time - the US Rocky Mountain time zone.
</body>
</html>
;;;;
The RESOLVE function is used to resolve macro variable references and execute macros (none included in this example) as well as macro functions (e.g., the %sysfunc macro fucntion). Try this out on my server:
Now lets look at the PROC STREAM version:
<html>
<head><title>The Obigatory Hello World Example</title></head>
<body>
<h1>Hello &_rmtuser..</h1>
<h2> This welcome note generated
at %sysfunc(time(),timeampm8.)<sup>1</sup>
on %sysfunc(date(),worddate.).</h2>
This HTML file was produced from an input
SAS Server Page and customized courtesy
of PROC STREAM
using SAS Release &sysver on &sysscp..
<p><sup>1</sup>The time listed is the server
time - the US Rocky Mountain time zone.
</body>
</html>
;;;;
You can run these out on my server as well:
The DATA Step and PROC STREAM both processed the same input SAS Server Page - and both produced the same results. PROC STREAM includes a number of features and capabilities that can't be done with the DATA Step approach. I'll be highlighting a number of those features in blog posts between now and SAS Global Forum.
Between now and SAS Global Forum I plan to write a number of blog entries on this topic - starting with an overview and a brief overview of what SAS Server Pages are. I will also be providing online demos so that even if you don't have access to SAS 9.3, you can see the generated output. I will be providing links the run the examples using both the SAS/IntrNet Application Dispatcher as well as the Stored Process Server. And note that the exact same programs are used by both the SAS/IntrNet Application Dispatcher and the Stored Process Server: one program for the DATA Step example; and one for the PROC STREAM example.
As discussed on sasCommunity.org, SAS Server Pages can be generated using the RESOLVE function in a DATA Step. So let's look at how to do that.
Here is a variation of the simple example in my last blog post:
data _null_;
file _webout;
infile datalines;
input;
_infile_ = resolve(_infile_);
put _infile_;
datalines4;
<html>
<head><title>The Obligatory Hello World Example</title></head>
<body>
<h1>Hello &_rmtuser..</h1>
<h2> This welcome note generated
at %sysfunc(time(),timeampm8.)<sup>1</sup>
on %sysfunc(date(),worddate.).</h2>
This HTML file was produced from an input
SAS Server Page and customized courtesy
of a DATA Step and the RESOLVE function
using SAS Release &sysver on &sysscp..
<p><sup>1</sup>The time listed is the server
time - the US Rocky Mountain time zone.
</body>
</html>
;;;;
The RESOLVE function is used to resolve macro variable references and execute macros (none included in this example) as well as macro functions (e.g., the %sysfunc macro fucntion). Try this out on my server:
Now lets look at the PROC STREAM version:
proc stream outfile=_webout quoting=both;
BEGIN<html>
<head><title>The Obigatory Hello World Example</title></head>
<body>
<h1>Hello &_rmtuser..</h1>
<h2> This welcome note generated
at %sysfunc(time(),timeampm8.)<sup>1</sup>
on %sysfunc(date(),worddate.).</h2>
This HTML file was produced from an input
SAS Server Page and customized courtesy
of PROC STREAM
using SAS Release &sysver on &sysscp..
<p><sup>1</sup>The time listed is the server
time - the US Rocky Mountain time zone.
</body>
</html>
;;;;
You can run these out on my server as well:
The DATA Step and PROC STREAM both processed the same input SAS Server Page - and both produced the same results. PROC STREAM includes a number of features and capabilities that can't be done with the DATA Step approach. I'll be highlighting a number of those features in blog posts between now and SAS Global Forum.
Subscribe to:
Posts (Atom)