Previous section   Next section

Defining Plugins

This chapter describes how to define plugins, which are a specialized form of KPI. It discusses the following:
Note:
Before reading this chapter, be sure to read the previous chapters on KPIs.
For a comparison of plugins with other kinds of model elements, see the chapter “Summary of Model Options” in Defining DeepSee Models.

Introduction

A plugin is a class that defines one or more computations to use in the Analyzer and in queries. A plugin has the following features:
  • In any given context, the plugin instance has access to the lowest-level data.
  • It can accept parameters.
  • It executes asynchronously. When the plugin is used in a pivot table, DeepSee can display the plugin current status (as the string n
    % complete
    ) in any pending cells.
    The pivot table automatically refreshes when the results are available.
  • Values returned by the plugin are cached.
Plugins are especially appropriate for complex or time-consuming computations. For example, you might have a computation that uses several different parts of the source record, as well as external information; a plugin would be suitable in this case.

How Plugins Can Be Used

Depending on the plugin class, you can use it in some or all of the following ways:
  • With the MDX %KPI function (which also enables you to specify values for any parameters). This is possible in all cases.
    This means that in all cases, you can define a calculated member that uses the plugin. (For information on defining calculated members, see Defining DeepSee Models.)
  • Directly in the Analyzer and in widgets. This is possible if the PLUGINTYPE class parameter is
    "Pivot"
    and the PUBLIC class parameter is 1 (the default).
    To create a plugin that cannot be directly used in the Analyzer or in widgets, specify PLUGINTYPE as
    "Aggregate"
    . Or specify PUBLIC as 0.

Available Plugin Classes

The %DeepSee.PlugIn package provides several plugin classes for you to use in calculated measures. These classes are as follows:
  • %DeepSee.PlugIn.Distinct — Gets the count of distinct values for a given level in a given cell.
    %DeepSee.PlugIn.Median — Gets the median value for a given measure, across all the lowest-level records used in a cell.
  • %DeepSee.PlugIn.Percentile — Gets a percentile value for a given measure, across all the lowest-level records used in a cell.
    These plugin classes are defined with PLUGINTYPE as
    "Aggregate"
    and so cannot be directly used in the Analyzer or in widgets. For more details on them, see “%KPI” in the reference “MDX Functions” in DeepSee MDX Reference.
  • Other classes — More advanced plugins for use with the iKnow-DeepSee integration. These are used by the Pivot Analysis screens in the Analyzer.
Another sample plugin class is DeepSee.Model.KPIs.PluginDemo, in the SAMPLES namespace. This plugin class is defined with PLUGINTYPE as
"Pivot"
and thus can be used directly.

Samples That Demonstrate Plugins

In the SAMPLES namespace, see the dashboards in the
KPIs & Plugins
folder:
  • The dashboard
    HoleFoods Plugins
    uses the calculated measures
    Median Revenue
    and
    90th Percentile Revenue
    , which are defined in the HoleFoods cube. These measures use the %KPI function to retrieve values from the sample plugin classes %DeepSee.PlugIn.Median and %DeepSee.PlugIn.Percentile.
  • The dashboard
    Patients Plugins
    has a pivot table that uses the calculated measures
    Median Test Score
    and
    90th Percentile Test Score
    . These calculated members are defined in the Patients cube, in a similar manner to the ones in the previous bullet.
    This dashboard contains another pivot table, which directly uses the plugin defined by the class DeepSee.Model.KPIs.PluginDemo.

Requirements for a Simple Plugin

To define a simple plugin, create a class as follows:
  • Use %DeepSee.KPIPlugIn as a superclass.
  • Define an XData block named KPI that specifies at least one property. For example:
    XData KPI [ XMLNamespace = "http://www.intersystems.com/deepsee/kpi" ]
    {
    <kpi name="PluginDemo" displayName="PluginDemo" caption="PluginDemo" >
    
    <property name="PatientCount" displayName="PatientCount" />
    <property name="HighScoreCount" displayName="HighScoreCount" />
    
    </kpi>
    }
    You can also include filters, as with other KPIs.
    For details, see the appendix “Reference Information for KPI and Plugin Classes.”
  • Specify the BASECUBE class parameter. For a simple plugin, specify the logical name of a single cube or subject area. (But also see “Creating a Plugin for Multiple Cubes,” later in this chapter.)
  • Specify the base MDX query to use. Either specify the
    mdx
    attribute of
    <kpi>
    or implement the %OnGetMDX() method in the following generic way:
    Method %OnGetMDX(ByRef pMDX As %String) As %Status
    {
        Set pMDX = "SELECT FROM "_..#BASECUBE
        Quit $$$OK
    }
    
    DeepSee automatically applies context information (row, column, and filter) to this base query.
  • Specify the fields that need to be available to the %OnCompute method. These can be fields in the source table of the cube or can be fields in the fact table. You can specify a hardcoded list, or you can use a callback to define the list of fields.
    To specify these fields:
    • If the fields you want to use are in the fact table, specify the LISTINGSOURCE class parameter as
      "FactTable"
      . (For details on the fact table, see “Details for the Fact and Dimension Tables” in Defining DeepSee Models.)
      If you omit this parameter or specify it as
      "SourceTable"
      , the plugin queries the source table of the given cube.
    • If you want to specify a hardcoded list of field names, specify the LISTINGFIELDS class parameter. Specify a comma-separated list of fields to use.
      For example:
      Parameter LISTINGFIELDS = "Field1, Field2, Field3";
      You can specify an alias for any field. For example:
      Parameter LISTINGFIELDS = "Field1, Field2 as FieldAlias, Field3";
      You can also use Caché arrow syntax and SQL functions, as with other listings.
      If you use Caché arrow syntax, be sure to specify an alias for the field.
    • Or if you want to define the list of fields programmatically, implement the %OnGetListingFields() method. For example, the following method causes the plugin to retrieve a single field:
      Method %OnGetListingFields() As %String
      {
          //could use an API to get the field name, but in this case factName is set 
          //so the field name is known
          Set tListingFields = "MxTestScore"
          Quit tListingFields
      }
      For information, see “Defining a Listing for a KPI,” earlier in this book.
    Note:
    For a plugin, the LISTINGFIELDS parameter and the %OnGetListingFields() do not define a detail listing or any listing fields. These options only define the fields that are available to the %OnCompute() method.
  • Implement the %OnCompute() method. The following section provides details on this task.
  • Optionally specify the PLUGINTYPE and PUBLIC class parameters. See “How Plugins Can Be Used,” earlier in this chapter.

Implementing %OnCompute()

For each pivot table cell where the plugin is used, the plugin performs either a DRILLTHROUGH or DRILLFACTS query (depending on the value of LISTINGSOURCE) and returns the fields specified by LISTINGFIELDS or %OnGetListingFields() (as applicable). It then passes the field values to the %OnCompute() method. This method has the following signature:
Method %OnCompute(pSQLRS As %SQL.StatementResult, pFactCount As %Integer) As %Status
Where:
  • pSQLRS is an instance of %SQL.StatementResult that contains the fields specified by LISTINGFIELDS or %OnGetListingFields().
    For information on using this class, see “Using Dynamic SQL” in Using Caché SQL.
  • pFactCount is total number of facts in the given context.
In your implementation of this method, do the following:
  1. Iterate through the statement result. To do so, use the %Next() method of this instance.
  2. As needed, retrieve values for each row. The statement result instance (pSQLRS) provides one property for each field in the listing query; the name of the property is the same as the field name.
    For example, in the previous section, %OnGetListingFields() retrieves a single field,
    MxTextScore
    . In this case, pSQLRS has a property named
    MxTextScore
    .
  3. Perform the desired computations.
  4. Set the properties of the plugin instance, as described in the previous chapter. At a minimum, set the following properties:
    • %seriesCount — Specifies the number of series (rows) in this plugin.
      InterSystems recommends that plugins have only one series. (For plugins with PLUGINTYPE equal to
      "Pivot"
      , when a user drags and drops a plugin property, the Analyzer uses only the first series.)
    • %seriesNames(n) — Specifies the name of the series n, where n is an integer.
    • %data(n,propname) — Specifies the value of the given property (propname), for the series n.
      The property name must exactly match the name of a
      <property>
      in the XData block.
For example:
 // place answer in KPI output
 Set ..%seriesCount = 1
 Set ..%seriesNames(1) = "PluginDemo"
 //set Count property of KPI -- just use received pFactCount
 Set ..%data(1,"PatientCount") = pFactCount
 
 // iterate through result set to get HighScoreCount 
 set n = 0
 Set highcount = 0
 While (pSQLRS.%Next(.tSC)) {
   If $$$ISERR(tSC) Quit
   set n = n + 1

   Set testscore = pSQLRS.MxTestScore
   if (testscore>95) {
     Set highcount = highcount + 1
  }

 }
 Set ..%data(1,"HighScoreCount") = highcount
This is an extract from DeepSee.Model.KPIs.PluginDemo in SAMPLES, which is available in the Analyzer for use with the Patients cube.

Indicating State of Completion

Plugins are executed asynchronously. When a query containing plugins is executed, the query can be complete before the plugins have completed execution. In this case, there are cells whose results are pending. Within these cells, you can display the plugin current status (as the string n
% complete
). To do so, within %OnCompute(), periodically invoke the %SetPercentComplete() instance method; the argument is an integer between 0 and 100. For example, you could do the following while iterating through the statement result:
    // update pct complete
    If (n#100 = 0) {
        Do ..%SetPercentComplete(100*(n/pFactCount))
    }
The appropriate approach depends on the logic in %OnCompute(). In some cases, the majority of the computation time might occur outside of this iteration.
The pivot table automatically refreshes when the results are available.

Creating a Plugin for Multiple Cubes

The previous sections describe how to create a plugin that can be used with a single cube or subject area. You can also create a plugin that can be used in multiple cubes. In practice, this is difficult to do because it is usually necessary to programmatically determine the fields to query.
To create a plugin that you can use with multiple cubes, use the following additional instructions:
  • Specify the BASECUBE class parameter as one of the following:
    • A comma-separated list of logical cube or subject area names
    • "*"
      — refers to all cubes and subject areas in this namespace
    This option determines which cubes and subject areas can use the plugin.
  • Include the following filter definition within the XData block:
    <filter name="%cube" displayName="Subject Area" />
    
    The name must be
    %cube
    but you can use any value for the display name.
    When you use this plugin within the Analyzer (if applicable), DeepSee passes the name of the current cube or subject area to this filter. Similarly, when you use this plugin within an MDX query, the FROM clause of the query determines the value of this filter.
  • Implement the %OnGetMDX() method so that it uses the value of the
    %cube
    filter. For example:
    Method %OnGetMDX(ByRef pMDX As %String) As %Status
    {
        Set tBaseCube = ""
    
        // Use %cube filter to find the base cube
        If $IsObject(..%filterValues) {
            If (..%filterValues.%cube'="") {
                Set tBaseCube = ..%filterValues.%cube
            }
        }
    
        If (tBaseCube'="") {
            Set pMDX = "SELECT FROM "_tBaseCube
        }
        Quit $$$OK
    }
  • Ensure that the listing query can work with all the desired cubes and subject areas. Either:
    • For hardcoded listings, use only fields that are suitable in all cases.
    • Programmatically determine the fields to use.

Determining the Listing Fields Programmatically

If the query for the plugin specifies LISTINGSOURCE as
"FactTable"
, there are additional tools that enable you to programmatically determine the fields to use in %OnGetListingSQL(). You can do the following:
  • Include the following filter definition within the XData block:
    <filter name="%measure" displayName="Measure" />
    
    The name must be
    %measure
    but you can use any value for the display name. This filter provides a list of all measures defined in the applicable cube or subject area.
  • Implement the %OnGetListingSQL() method as follows:
    1. Examine the value of the
      %measure
      filter.
    2. Use the %GetDimensionInfo() method of the %DeepSee.Utils class to retrieve, by reference, information about the selected measure.
      Use this information as input for the next step.
    3. Use the %GetDimensionFact() method of the %DeepSee.Utils class to retrieve the name of the field that stores the selected measure.
  • Optionally implement the %OnGetListingOrderBy() and %OnGetListingMaxRows() callbacks. For details, see the class reference for %DeepSee.KPIPlugIn.
For examples, see %DeepSee.PlugIn.Median and %DeepSee.PlugIn.Percentile. Also see the class reference for the %DeepSee.Utils class.

Filtering the Listing

Plugins provide a feature that is not available in other scenarios: namely, the ability to specify which records to use when a detail listing is displayed. By default, when a user requests a detail listing for a given cell or set of cells in the results, DeepSee displays a listing that shows all the records associated with those cells. In some cases, however, it is preferable to show a subset of them. For example, the sample DeepSee.Model.KPIs.PluginDemo has a plugin property called
HighScoreCount
. The following shows an example MDX query that uses this plugin property as a measure:
SELECT NON EMPTY {[Measures].[%COUNT],%KPI("PluginDemo","HighScoreCount",,"%CONTEXT")} ON 0,NON EMPTY [AllerSevD].[H1].[Allergy Severities].Members ON 1 FROM [PATIENTS]
 
                       Patient Count       HighScoreCount
1 Nil known allergi               158                   12
2 Minor                           113                    7
3 Moderate                        103                    5
4 Life-threatening                133                    9
5 Inactive                        122                    8
6 Unable to determi               119                    6
7 No Data Available               385                   29
Consider the row for
Nil known allergies
. If you display a listing for either cell, by default, DeepSee displays a listing that consists of 158 records, because there are 158 patients with no known allergies. But the purpose of the
HighScoreCount
measure is to count the patients with scores above a given threshold, so when we display the detail listing for the cell
HighScoreCount
in this row, we might prefer to see only the patients with scores above that threshold.
To apply this sort of filtering to a plugin, include the following logic in your implementation of %OnCompute(), for any source class ID that should be shown in the listing:
   set ..%data("IDLIST",pluginProperty,sourceClassID) = ""
Where pluginProperty is the name of the plugin property that should use this filtering, and sourceClassID is the ID in the source class. (The ID should be a source class ID even if plugin otherwise uses the fact class. To make the source class ID available to the plugin, add
%sourceId
to the field list.)
For a given plugin property, if
%data("IDLIST",
pluginProperty
)
is not defined, the listing shows all the records associated with the given cell or cells.

Example

To see an example, edit the sample DeepSee.Model.KPIs.PluginDemo as follows:
  1. Change LISTINGFIELDS to be the following:
    Parameter LISTINGFIELDS As STRING = "%sourceId,MxTestScore";
    
  2. Find the part of %OnCompute() that sets the highcount variable, and modify it as follows:
            if (testscore>95) {
                Set highcount = highcount + 1
                Set tHighScoreId = pSQLRS.sourceId
                Set ..%data("IDLIST","HighScoreCount",tHighScoreId)=""
            }
    
  3. Save and recompile the class.
Then, in the Analyzer, create a pivot table that uses both properties of this plugin (for purposes of comparison). Select a cell that displays the
HighScoreCount
property, display a listing, and notice that only patients with a high score are shown. For contrast, select a cell that displays the
PatientCount
property and display a listing for that. In this case, you will see patients with all scores.

Available Error Logging

If a plugin encounters an error, DeepSee writes to the error log file in the manager’s directory. The name of this file is DeepSeeTasks_namespace.log.

Defining a Calculated Member That Uses a Plugin

For any plugin (and any other KPI), you can create a calculated member that retrieves values from it. Then users can drag and drop this member within the Analyzer. To create such a calculated member:
  • Define a calculated measure as described in “Defining a Calculated Measure,” in Defining DeepSee Models.
  • For Expression, specify an MDX expression of the following form:
    %KPI(pluginname,propertyname,seriesname,"%CONTEXT")
    
    Where pluginname is the name of the plugin, propertyname is the name of the property, and seriesname is the name of the series. You can omit seriesname; if you do, this function accesses the first series in the plugin.
    "%CONTEXT"
    is a special parameter that provides row, column, and filter context to the plugin; this information is passed to the base MDX query used by the plugin.
    For example (for a plugin with only 1 series):
    %KPI("PluginDemo2","Count",,"%CONTEXT")
    
    For plugins with PLUGINTYPE equal to
    "Pivot"
    , when a user drags and drops a plugin property, the Analyzer automatically uses syntax like this in the underlying MDX query that it generates.
    For additional options, see the %KPI function in the DeepSee MDX Reference.
Previous section   Next section