In my last post, I demonstrated how developers can use the new Visual Analytics API to easily create svg images of both entire Visual Analytics report tabs, as well as individual report graph objects. This got me to thinking, would it be possible to use the same method to create images of all the graphs in a Visual Analytics report, and archive them into a zip file? Well, the answer turns out to be YES! With just a bit more code, this can be done; and in this article, I'll show you how!
The first thing we will need is a list of the Visual Analytics report object's unique names. In my last post, we obtained this for a single graph object by opening the report and clicking the “Create link” option in the object's properties. This works well for a single object, but what if our report has LOTS of graphs? For this, it would be better if we could programmatically get a list of the graph objects and their respective names.
I've written a previous article on how to get this list by using SAS code which leverages PROC HTTP to make an API call to the Viya Reports API and get a report's content elements. A (slightly modified) version of this SAS code is below:
/*retrieve service endpoint*/ %let BASE_URI=%sysfunc(getoption(servicesbaseurl));
%let reportID = <- report uri ->; /* create filenames to hold responses*/ filename rcontent temp; /* Make request */ proc http oauth_bearer=sas_services method="GET" url="&BASE_URI/reports/reports/&reportID/content/elements?characteristics=visualElement" /* place response in filenames */ out=rcontent; headers "Accept"="application/vnd.sas.collection+json"; run; /* read in response */ libname rcontent json; /* return only "Graph" elements */ data reportElements; set rcontent.items; where Type eq "Graph"; run;
proc print data=reportElements;
var name label;
*Notice, the code above uses the same methodology as does the programs in my previous article, where there is a SAS macro variable "&reportID" that defines the unique uri of the Visual Analytics report we want to retrieve the list of graph elements from.
When we run this code using the URI for the example report "Retail Insights" (that comes installed with SAS Viya), the table reportElements is created and printed:
Great! Now that we have our list of the report's graph objects (and most importantly, their unique names), we can use this in conjunction with the code from my last post to create svg image files from each of them.
For this article’s use case we will want to use the same method from my previous article of using the new Visual Analytics API to generate svg images; but with just a few modifications. First, since we want to produce a single zip file that contains all of our generated svg files, we will need to add an ODS package statement to our code. The ODS package statement is used to first create a package of files (svg files in our case) and then publish them to a zip file. If you want to learn more about using the ODS package statement, here is a great paper on the topic. Also, we need to decide where to place our output files. For this article's example, I'm going to initially place both the svg files and zip file in the directory used by this SAS Session’s work library. To retrieve the physical location of the work library we will use the %sysfunc macro and save its output to the macro variable 'workDir'. We will also need to give the final output zip file a name. For our current example, I've chosen "myZipArchive.zip" and placed it in the macro variable "zipArchiveName". For the creation of the SVG images themselves we want this part of the process to be automated. Hence, we will wrap the code I demonstrated in my last post into a simple SAS Macro named "createVAReportObjectImage". Additionally, we have added two lines to our macro's source code that add the output svg image to the ods package.
Finally, we can call the macro by submitting the line (but substituting the <-reportObjectName-> value for a report object that exists in your Visual Analytics report:
After we have called the macro one (or more) time(s) we close our ods package.
/*retrieve service endpoint*/ %let BASE_URI=%sysfunc(getoption(servicesbaseurl)); %let reportID = <- report uri ->; %let height=800px; %let width=600px; %let workDir = %sysfunc(getoption(work)); %let zipArchiveName = myZipArchive.zip;
/* Create macro to retrieve reportObjects */ %macro createVAReportObjectImage(reportUri,reportObjectId); /* create the svg file */ filename imgfile "&workDir/myOutputImage_&reportObjectId..svg"; proc http method="GET" oauth_bearer=sas_services url="&BASE_URI/visualAnalytics/reports/&reportUri/svg?size=&height,&width&reportObject=&reportObjectId." out=imgfile; run; /* add the svg file to the ods zip archive */ ods package add file="&workDir/myOutputImage_&reportObjectId..svg" mimetype="application/x-compress"; ods package publish archive properties(archive_name="&zipArchiveName" archive_path="&workDir"); %mend;
/* create ods package */ ods package open nopf; /* call the macro */ %createVAReportObjectImage(&reportID.,<-reportObjectName->) /* close the ods package */ ods package close;
When I run the code above on the example report "Retail Insights" for the two report object names ve179 and ve166, the code to call the macro looks like this:
/* call the macro */ %createVAReportObjectImage(&reportID.,ve179) %createVAReportObjectImage(&reportID.,ve166)
And that's it! We have successfully created the svg images files, placed them in our work library's directory and ALSO created a zip file in our work library's directory. If you would like to get a listing of these files you can use the following code which gets a list of your files from the work directory and only prints the zip files and files that have the text "myOutputImage" in the filename:
/* test to ensure that the file exists */ data filenames; length fref $8 fname $200; label fname = "Filename"; did = filename(fref,"&workDir"); did = dopen(fref); do i = 1 to dnum(did); fname = dread(did,i); output; end; did = dclose(did); did = filename(fref); keep fname; run; /* print the expected files */ title "Generated svg and zip files in the work library's directory"; proc print data=filenames noobs label; where index(fname,'myOutputImage')>0 or index(fname,'.zip')>0; run; title;
And the printed output looks like this:
In step 2 we successfully created our zip archive. However, we had to manually add the names of the VA Graph objects that we want to add. This would be better if the program automatically added any graph objects it finds. This piece is actually quite straight forward using the dataset we created in Step 1 and the CALL EXECUTE Routine. In short, this routine allows us to execute SAS code that is located within the cell of a SAS Dataset.
So first, let's create the dataset with the needed SAS macro call to add each Visual Analytics graph object that is in the 'reportElements' dataset. The code to do this is below:
data reportElementsCode; set reportElements; length code $ 500; code = '%createVAReportObjectImage(' || "&reportID." || ',' || trim(name) || ')'; keep name type label code; run;
Great! As you can see, in the 'code' variable we have placed a call to the 'createVAReportObjectImage' macro and dynamically placed the uri of the report as well as the name of the graph object. From here, it's a simple process of using the call execute routine to call our macro instead of manually calling it as we did at the end of Step2. The code to do this (and have it wrapped in the proper ODS package statements) is below:
ods package open nopf; data _NULL_; set reportElementsCode; call execute(code); run; ods package close;
Great! We've successfully automated our macro calls!
You might have noticed in Step 2, I placed the generated zip archive in the work library's directory. We probably want to copy this file to a better place before we end our SAS Studio Session. You can choose any place you like. For example, you might want to place the file in a directory which points to an internal git repo for your organization. Or you might want to place it in an appropriately shared network directory so your team members can access it. For this article, I'm going to use the FILENAME Statement: FILESRVC Access Method to save the output zip file to a folder in the Viya folders Content area. Specifically, I'm going to place the zip file in the private "My Folder" area. This is achieved using the following lines of code:
/* create the source filename */ filename src "&workDir/&zipArchiveName" recfm=n; /* create the destination filename using FILESRVC to the destination a zip file and folder */ filename dest filesrvc folderuri='/folders/folders/@myFolder' filename="&zipArchiveName" debug=http recfm=n; /* copy the file: output return code and any message */ data _null_; rc=fcopy("src","dest"); msg=sysmsg(); put rc=; put msg=; run; filename src clear; filename dest clear;
The code above uses the filesrvc method and the process described in this post to move the zip file from the work library's directory to the "My Folder" space Viya Content folder.
And that's it! We have successfully written the needed SAS code to programmatically do the following:
After running the code, the zip file appears in the "My Folder" area within the Viya Content Folders:
When we double click this zip file, SAS Studio offers a message that it can't open the zip file, but also allows us to download the file:
After downloading the file and unzipping it, we can see its contents:
From here we can open any of the image files using a browser of our choice:
And that's it! In just a few lines of code we have programmatically created svg images of all the graph objects in a Visual Analytics report and archived them into a zip file!
*The code in this article is intended to be run against VA reports which have been developed using the Visual Analytics interface using CAS Data sources that are ACTIVELY loaded into memory.
On GitHub, you will find all the code discussed in this article. All code is intended to be executed within a SAS Studio session running in a SAS Viya 2021.2.4 (or later) environment which contains the SAS Viya services that are being called. Additional code is included which will check first to ensure that the selected Visual Analytics report contains graph objects. If no graph objects are found, the program gracefully exits. Alternatively, if graph objects are found the svg images are created an archived.
On Github, you will find the following support files for this article:
Registration is open! SAS is returning to Vegas for an AI and analytics experience like no other! Whether you're an executive, manager, end user or SAS partner, SAS Innovate is designed for everyone on your team. Register for just $495 by 12/31/2023.
If you are interested in speaking, there is still time to submit a session idea. More details are posted on the website.
Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning and boost your career prospects.