A very common goal in dynamic reporting is to give the user the ability to specify some particular criteria to filter a report on, before the report data is queried for. These criteria are called parameters, and the Secure Filter Component in the Pentaho platform allows solution developers to achieve this goal. Taking the feature one step further, users many times would like to filter or choose a second set of criteria, based on the first parameter they chose. For instance, Jody manages the Western division of her company. The Western division has different products than the Eastern division. So the first parameter Jody would like to choose is the Western division. Based on that selection, Jody would like to see only Western division products. This is often called a master\slave relationship, where one set of slave data is dependent on a master set of related data.
This article explains a sample solution (I'll call it the dep-param solution) that Mike D'Amour created for providing the master\slave feature while still utilizing all of the power of the platform's Secure Filter Component. Mike's solution is very clean, using AJAX to dynamically populate a combobox with the slave filter selections on the parameter page, once the master selection is chosen. I'll get into more details in the article as we go about what this sample can do out of the box, and where it can be extended.
Resources Before You Get Started
The following software is what I used to build out this article. I highly recommend you set up these bits in your environment before you get started. While it may seem like a bit of work, each project has a very simple install and setup.
- Pentaho Design Studio (PDS) version 1.2 or later.
- Pentaho Pre-configured Installation (PCI) running locally, and the hypersonic database running locally with the SampleData database, version 1.2 or later.
- The dep-param Solution Files, courtesy of Mike D'Amour. (Pentaho team developer)
Sample Use Case
The Pentaho sample data is quite simplistic. The table that we will use in this exercise is QUADRANT_ACTUALS. The QUADRANT_ACTUALS table contains Region, Department, PositionTitle (job title), Actual, Budget and Variance data. Each department has a unique list of position titles.
We want to be able to create a report that will show actual and budget data for a selected position title within a department. We will set up the solution so that the user must first select a department, which will then filter the list of position titles that they choose from.
For the sake of keeping this solution simple and focusing on what needs to be done to enable the master\slave parameter feature, the output for the data will result in a plain table of data in the browser, rather than a new report. Some of the input data is also hard coded, although we easily could define additional SQL Lookup Rules for database retrieval instead. Also, the dep-param solution we are demonstrating here only accounts for one master\slave set of dependent fields, and the control used on the page must be a combo box. Once we have explained how this specific problem is solved, we will discuss how you can extend this solution to handle multiple dependencies (for example, select a region, that then filters departments, that then in turn filters position title). We will also talk briefly about how to tweak the solution to accommodate other control types, such as radio buttons, check boxes, etc.
Let's get started!
As I mentioned in the "Resources.." section, it's important at this point that you have the PCI and the Pentaho Design Studio installed and ready to run somewhere on your computer. If you have any questions on how to set these things up, they all have installation and user guides located at http://www.pentaho.org/download/latest.php. From here forward I will assume you have the necessary tools for the exercise.
Install the dep-param Solution
First thing you will want to do is install the dep-param solution for this article in the solutions folder that came with the PCI demo. Download the zipped file from this page, and extract the content to the following directory:
with <HOME> being the top level drive and/or directory that you installed the PCI to. The structure should look similar to the following path once the files are extracted:
Once you have installed the solution files installed, start your Pentaho server if you haven't already. If your server was running when you installed the dep-param solution, just Publish To Repository (from the demo home page, choose Admin | Repository), and you will be all set.
Navigate to the dep-param solution, so we can test that the solution indeed works:
- Using your favorite browser, navigate to the PCI demo homepage. If you installed the PCI locally and chose the default set up, that URL would be http://localhost:8080. If you changed any of the defaults, or installed to a different domain or port that URL will be slightly different. If you have any trouble finding the PCI home page, consult the Getting Started with the BI Platform documentation.
- From the PCI home page upper right corner, select Go | Solutions | Reporting Samples | Dependent Parameter Examples .
- From this page, select Dynamic Dependent Parameter Page. You should see a prompt for parameters, similar to the following screenshot:
Open the dep-param Solution in the Design Studio
We are going to examine the solution files that Mike used to demonstrate the master\slave parameters. The first thing we need to do is set up the Design Studio so that we can work and debug directly from the sample solutions that come with the PCI. (If you want to use your own solution directories, that's fine too, just modify the following steps to accommodate your structure).
- Open the Pentaho Design Studio. From the main menu, choose File | New Project | Simple Project.
- The wizard walks you through setting up your project. When prompted for the location of your project files, uncheck the "use default" checkbox, and enter the path to the PCI sample solutions directory, usually located at <pentaho-pci-root>/pentaho-solutions. Name your project something you like; I named mine param_solutions.
- Click the Finish button, and you are all set. You can now work with the solution files that we installed in the previous step.
- Using the Java Perspective, and the Navigator view, navigate to the dep-param folder, under <solution-root>\samples\reporting\dep-param. You should see the following files:
Explaining the dep-param Files
Before we delve into the technical detail of the action sequences, let me first explain the purpose of each solution file, to help give you a better perspective of how this solution works as a whole.
There are several files in our dep-param directory, but the only ones that contribute to the workings of this feature (and that we will concern ourselves with here) are DependentParameterExample.xaction, PositionTitlesForDept.xaction, and DependentParameterTemplate.html. The rest of the files in the directory (if you aren't already familiar with Pentaho solutions) support externalizing your solution's strings for localization, and support the appearance of the solution within the PCI demo. To learn more about these support files in detail, you can browse some of our early tech tips, as well as the Internationalization documentation.
The DependentParameterExample.xaction is the main action sequence. In this action sequence, we set up the Secure Filter Component that will request a region and department from the user. Once the user has chosen the department, we will submit a request to the server to get the list of position titles for that department. Retrieving the department specific position titles happens via the PositionTitlesForDept.xaction. I will get in to details on how the second action sequence gets executed in a minute, but for right now its useful to understand that there is no reloading of the page or the Secure Filter Component when we retrieve the position titles,as that function is an AJAX call.
The way we enable AJAX and the dynamic loading of the dependent parameter, position title, is by using a custom DHTML file as the template for the page that the Secure Filter Component (in our first action sequence) generates. That file is DependentParameterTemplate.html.
That's alot to digest in a very compressed fashion, so let's walk through the action sequences and the DHTML template in the Design Studio. Step by step, we will put the bits together and help you understand how Mike came up with this solution.
The DependentParameterExample Action Sequence
The DependentParameterExample action sequence controls our solution. Let's dig in and see what's in there.
- Open the DependentParameterExample.xaction in the Pentaho Design Studio. You can do this by navigating to the dep-param directory in our solution's project, and double-clicking on the DependentParameterExample.xaction file.
- In the Process Inputs box at the top of the page, you should see an inputs folder. Expand that folder using the plus sign next to the folder. In the inputs folder are the six inputs needed for our simple solution. We need three String inputs to hold the three selections that the user will specify: REGION, DEPARTMENT and POSITIONTITLE. Then we also need a String-list input to hold the available values for each of the three selections: that would be the REGION_FILTER, DEPARTMENT_FILTER and POSITIONTITLE_FILTER.
- Click on the REGION_FILTER String-list input. Notice that the Default Value checkbox is checked, and there are 4 hard coded values in the defaults list. We have hard coded the region values for the REGION selection, ONLY for the sake of simplicity. We could have used a SQL Lookup Rule to populate the REGION_FILTER values, but instead wanted to keep the example as simple as possible.
- The DEPARTMENT_FILTER values are also hard coded, in order to keep things simple. Click the DEPARTMENT_FILTER String-list input, and you will see it too has several default values.
- We start the action sequence by running a query to populate the POSITIONTITLE_FILTER String-list. Click on the first action in the Process Actions box, labeled "Perform SQL Query". You can examine the details of this action, and see that the query is retrieving all position titles from the database, and the resultset is being stored in the POSITIONTITLE_FILTER input.
At this point , you might ask, why are we populating the position title list now? We don't know what department the user will choose, so we will throw this list away anyway when we issue the command to retrieve only those position titles related to the chosen department. This is a bit of awkwardness that is necessary becuase of the nature of the Secure Filter Component. You see, the Secure Filter Component requires that, at the time of execution, it has a valid list of selections to validate the user's choice against. This prevents hackers from introducing values into the action sequence that could be potentially harmful or could expose confidential data. When we execute the Secure Filter Component at the beginning of the action sequence, we don't know what department will be selected, so we can't populate the filtered list of position titles. But we can start out with the COMPLETE list of position titles from the database, thus any department that is chosen later on will always send a list of valid position titles for the user to choose from, satisfying the Secure Filter Component's requirements.
- Our next step is to define our Secure Filter Component, that will generate the parameter page, prompting our user for the 3 selections we set up inputs for: REGION, DEPARTMENT, and POSITIONTITLE. Select the second action in the Process Actions box, titled "Prompt For Region, Dept, and Output Type".
- Click on each of the parameters in the "Prompt For" box in the right hand pane. Notice that these parameters are set up pretty standard: the String input is reserved to hold the user's selection and the list of choices (Source of Choices in the right pane) is coming from the String-list inputs we defined .
- The magic of populating the position title combo box based on the department selection comes from changing the template that the Secure Filter Component uses to render the parameter page. Notice in the Stylesheet field on the right pane, it lists our DependentParameterTemplate.html. That is a means for bringing the DependentParameterTemplate.html file into our action sequence.
The DependentParameterTemplate.html file is the template we are telling our Secure Filter Component to use. We will take a look at DependentParameterTemplate.html in greater detail in a moment, but first let's finish examining this action sequence.
- The last step in our action sequence executes after the Secure Filter Component has completed its work. The last action takes the values selected by the user and queries the database using those values as parameters in the query. If you are not familiar with parameterized queries or the PREPARE statement, you can learn more in this technical article.
So the action sequence is really not that special, intentionally so for demonstration purposes. The real key to this solution is in the template that we assigned to the Secure Filter Component for generating the parameter page. Let's take a look at DependentParameterTemplate.html next.
The DependentParameterTemplate.html Template File
- Open the DependentParameterTemplate.html file in the Design Studio. If you have any trouble opening the file, right click on it in the Navigator window, and select Open With => Text Editor.
- The first section of code we will look at in this template is the HTML inside the <form></form> tags. This HTML represents the boilerplate template code you would use to create a parameter page for the platform. The tags highlighted in red are the code needed to automatically build controls for each of the parameters we are expecting from the user: REGION, DEPARTMENT, POSITION TITLE. By specifying each input inside curly braces, you are telling the platform to replace these place holders with the values specified in the Secure Filter Component definition. The rest of the form should be left as is.
The action sequence we are directing it to execute is PositionTitlesForDept.xaction, which will retrieve the list of position titles for the selected department. We specify the selected department value, the solution, the path to the solution and the action sequence as name\value pairs in the array that is the first parameter to our paramService() function. The second parameter to the function is the first half of yet another function name, a function that will be executed when a return result from this function call is recieved. When we study the paramService() function in detail, you will see that this is where the request is sent to the server in an asynchronous manner. That is why we need to specify what should happen next, when the result is returned.
We will take a look at the returnObjById() function and the paramService() function next.