Essbase Global Variables and DQM

One of my clients was running into a problem with migrating their reports to the DQM. The reports took 15 seconds to run in Compatible Mode, but in DQM failed with the XQE-PLN-0001 error:

Even by Cognos standards this is a bit obscure.

After a bit of troubleshooting I narrowed the problem to how they were dealing with Essbase Global Variables. When global variables were placed in the slicers, they functioned correctly and the mdx looked clean. Cognos put them into tuples exactly as I would expect. When nested in crosstabs they also worked. They can’t be put into tuple expressions. tuple([cube].[Measure],[cube].[Global Variable]) will return an error.

In the compatible mode the client found a work-around with item([cube].[Global Variable],0). This essentially turns the variable into a member. This worked and the report took 15 seconds.

Dynamic Query Mode has a much stricter parser. Since the global variables are neither sets or members, it is very difficult to use them. We can, however, trick Cognos into using them as members by converting them to calculated members with a specified hierarchy.

Changing

tuple(
    [Fm4].[Scenario].[Scenario]->:[DO].[Scenario].[Actual]
  , item([Fm4].[CED_ActualVer_Reports],0)
  , item([Fm4].[CED_ActCurrentYear],0)
  , item([Fm4].[CED_ActualRecentlyClosedQtr],0)
)

to

tuple(
    [Fm4].[Scenario].[Scenario]->:[DO].[Scenario].[Actual]
  , member(total(currentMeasure within set [Fm4].[CED_ActualVer_Reports]), 'a','a',[Fm4].[Version].[Version])
  , member(total(currentMeasure within set [Fm4].[CED_ActCurrentYear]), 'b','b',[Fm4].[Year].[Year])
  , member(total(currentMeasure within set [Fm4].[CED_ActualRecentlyClosedQtr]), 'c','c',[Fm4].[Period].[Period])
)

completely fixed the problem. As a benchmark test, the client took the changes I made and applied them to the Compatible Query Mode. In the CQM the report took 10 seconds (from 15). The DQM ran the report in under a second. It took a few trials to convince some people that the report was actually running and not being retrieved from cache.

The member function takes the parameters
1. value expression (a number, a measure, a total within a set.
2. ID (must be unique in the entire query)
3. Caption (what is displayed if the calculated member is used in an edge)
4. Hierarchy (generally optional, this explicitly sets the hierarchy of the calculated member. If no hierarchy is selected it wil default to the measures.

It’s worth noting the summary function I used was total(). When working with non-additive measures, it is recommended to use the aggregate function or the explicit summary function for the particular measure.

When using this method to create tuples, it is very important to correctly plan the query. A tuple cannot have two parameters from the same hierarchy, this includes measures. Conceptually it is easy to understand that, as you cannot find the intersection between two points on the same line. As mentioned before if no hierarchy is selected in the member function, it will default to the measures. In the case of a tuple, measures are considered part of a hierarchy and the same limitation applies. You cannot find the intersection between Quantity of Sales and Profit Margin, for instance. As such, some analysis of the query and Global Variables must be done before you can create the calculated member.

I created a test crosstab with the various Global Variables, and looked at the MDX (Tools–>Show Generated SQL/MDX). The crosstab has Actual Results (a member), CED_ActualVer_Reports (Global Variable), CED_ActCurrentYear (Global Variable), and CED_ActualRecentlyClosedQtr (Global Variable) nested in the columns and the measure Net Revenue in the rows.

SELECT
{([Scenario].[Actual], [Version].[OutsidePresentations], [Year].[FY11], [Period].[YearTotal].[Q4])} DIMENSION PROPERTIES [Original], LEVEL_NUMBER, GEN_NUMBER, MEMBER_ALIAS, ANCESTOR_NAMES ON AXIS(0),
{[Account].[Chart of Accounts].[ac_NET_INCOME].[ac_INC_AFTER_TAX].[ac_INC_BEFORE_TAX].[ac_INC_OPERATING].[ac_GROSS_PROFIT].[ac_SALES]} DIMENSION PROPERTIES [Original], LEVEL_NUMBER, GEN_NUMBER, MEMBER_ALIAS, ANCESTOR_NAMES ON AXIS(1)
FROM [CED.Fm]
WHERE ([ActivityType].[at_TotalActivityTypeDim], [Geography].[mk_TotalMarketDim], [Entity].[Global BUs], [Company].[co_GP_CFO])

Using this I can see that CED_ActualVer_Reports comes from the Version hierarchy, CED_ActCurrentYear from the Year and CED_ActualRecentlyClosedQtr from the Period. The calculated members are then created accordingly and Cognos treats them as proper members from the hierarchy.

The same crosstab, when using the member function, looks like this:

WITH
MEMBER [Version].[XQE_V5M_a_CM2] AS '([Version].[OutsidePresentations])', SOLVE_ORDER = 4
MEMBER [Year].[XQE_V5M_b_CM1] AS '([Year].[FY11])', SOLVE_ORDER = 4
MEMBER [Period].[XQE_V5M_c_CM0] AS '([Period].[YearTotal].[Q4])', SOLVE_ORDER = 4
MEMBER [Scenario].[XQE_V5M_Act Current Year_CM4] AS '([Scenario].[Actual], [Version].[XQE_V5M_a_CM2], [Year].[XQE_V5M_b_CM1], [Period].[XQE_V5M_c_CM0])', SOLVE_ORDER = 4
SELECT
{[Scenario].[XQE_V5M_Act Current Year_CM4]} DIMENSION PROPERTIES [Original], LEVEL_NUMBER, GEN_NUMBER, MEMBER_ALIAS, ANCESTOR_NAMES ON AXIS(0),
{[Account].[Chart of Accounts].[ac_NET_INCOME].[ac_INC_AFTER_TAX].[ac_INC_BEFORE_TAX].[ac_INC_OPERATING].[ac_GROSS_PROFIT].[ac_SALES]} DIMENSION PROPERTIES [Original], LEVEL_NUMBER, GEN_NUMBER, MEMBER_ALIAS, ANCESTOR_NAMES ON AXIS(1)
FROM [CED.Fm]
WHERE ([Company].[co_GP_CFO], [ActivityType].[at_TotalActivityTypeDim], [Geography].[mk_TotalMarketDim], [Entity].[Global BUs])

It is important to note that family functions will not work correctly with calculated members. If they work at all, without returning an error, you may get unpredictable results.

Hiding Cognos Connection Elements

Hiding Cognos Connection Elements

By default Cognos offers a ride range of UI options. Based on the group or role you can programmatically decide who can see which UI Elements. A simple modification of the system.xml file is all that is needed to take care of this.

The Admin and Security guide, which can be found in the \webcontents\documentation\en\ug_cra.pdf, has an excellent article on how to accomplish this. You can also find the article online here.

The full list of elements that you can hide can also be found in the guide, or online here.

While those links are for 8.4, they are essentially identical for 8.4.1 and 10.1.

But what happens when you have a requirement to remove elements that don’t appear in the list? This is possible by modifying one of the template files that controls the structure of the portal. These files are saved as XSL files, and are easily modified using any text editor. I strongly recommend using a text editor with XML support, such as Notepad++.

Before I continue, it’s worth mentioning the following. Changing these files may be risky. A misplaced semicolon will prevent Cognos from loading, and IBM has a tendancy not to support installations that have modified the Cognos internals. These changes will also be overridden by any patches or upgrades.

Always make a backup of any files you modify. When trouble does occur and you need IBM’s help, simply switch the active files with the backup, and IBM will be none the wiser (and if the problem is fixed, you know where to look).
You should also maintain a change log of everything you do. Upgrades will replace the template files, and there may be differences between the versions. Instead of simply overwriting the upgraded version with your modified version, you should make all the changes again manually. Fortunately (unfortunately?) patches and version upgrades tend to be rare occurrences.

A brief explanation of the files to modify:

There are two primary XSL files which control the portal.
1. \templates\ps\logicsheets\presentation\controls\presentation.xsl
2. \templates\ps\logicsheets\presentation\main\presentation.xsl

The \controls\presentation.xsl renders, among other things, the HTML behind tabs, and the rows and columns in the Public Folders/My Folders table.

The \main\presentation.xsl renders the links to the correct style sheets, the page headers, and the individual links in the Public Folders/My Folders table.

Between the two, they control the HTML between most of the objects you see on the page.

The system.xml file that contains the UI black list can be found in \templates\ps\portal\system.xml

And now the problem. The client has decided that no user, except administrators and report authors, should be able to see the Properties or More… links for any reports or folders.

First, hiding the properties.

In the \main\presentation.xsl do a search for action_properties.gif. There should be four instances of that string. Each of these instances controls the properties for a different type of object. Series 7 object, CRN object, Job and… well, I’m not entirely sure what the fourth one is for, but I’m sure it’s very important. 5 points to whoever enlightens me.

In each of these four cases the action_properties.gif is part of a xsl:call-template reference.

This statement is calling the renderActions template with values for the onclick, icon and tooltip parameters.

In order to prevent this segment from being rendered we need to add it to the “Elements you can hide” list. The code that controls items on the list is as follows:

<out:if test="not(contains($ui_black_list, 'NAME'))">			
</out:if>

This will check the system.xml for an appearance of NAME in the ui_hide list. Should it appear, it will not render anything inside the code block except for users or groups listed in the show parameter. Knowing this, all that is needed to hide the properties is to add the black list code to all four appearances of the renderAction call.

		<out:if test="not(contains($ui_black_list, ' PROPERTIES '))">			
			<xsl:call-template name="renderAction">
				<xsl:with-param name="onclick">actions('{xtsext:javascriptencode(string($obj-name))}', '{$obj-class}', '{xtsext:javascriptencode(string($obj-path))}','properties_general.xts');</xsl:with-param>
				<xsl:with-param name="icon" select="'action_properties.gif'"/>
				<xsl:with-param name="tooltip" select="'IDS_PROPERTIES'"/>
			</xsl:call-template>
		</out:if>

In this case, I set the black list name to PROPERTIES. Now, in the system.xml simply add the following to the ui_hide param:

	<PROPERTIES show ="Administrators RSUsers"/>

When you restart, only administrators and authors will be able to see the properties for any object.

Now for the More…

The technique to hide the “More…” link is almost identical.

Simply find all occurences of IDS_ACT_MORE in the same file. In my modified file all occurences appear between lines 4640 and 4722. Thanks to Notepad++’s XML support, I can see that all of these are held inside a single group.

Instead of black listing each individual appearance of the More… link, let’s try black listing the entire group.

Now to add MORE to the system.xml black list

As before, only administrators and authors will be able to see the More… link.

The following shows how folders look to normal users:

And this is what reports look like:

It’s very important to do extensive tests to ensure this meets the client needs. You could probably also reverse the black list by getting rid of the “not()” from the code. Remember that the idea behind this article is not to simply show how to hide specific elements from the Cognos Connection. It’s to show how easily it is to shape Cognos to meet your needs. The XSL sheets are there to be modified.

Layout Component References

Zephyr, author of the blog Cognos and Me, wrote an excellent (but brief) article on what Layout Components are, and why you’d want to use them. I’m going to expand briefly on what he wrote, and give an example of my own.

In his example, he created a generic header that authors would use in their reports. This allows the author of the components report to control the header in all of the reports referencing it. Very useful for maintaining a corporate look and feel.

There is another use. While the Cognos authors that I work with excel in SQL and MDX, many of them are lacking knowledge in JavaScript. One of the most common requests for help that I receive is to code a simple container that allows users to switch between viewing a table or a graph.

In order to accomplish this, you would need to use JavaScript. So, instead of having to help each author individually, I find it easier to create a single component library report. To create the component library, simply create a new report. Since this is post is about components, and not about JavaScript I’ll give you the XML of an example components library.

<report xmlns="http://developer.cognos.com/schemas/report/3.0/" expressionLocale="en-us"><!--RS:8.2-->
	<modelPath>/content/package[@name='GO Sales']/model[@name='model']</modelPath>
	<layouts>
		<layout>
			<reportPages>
				<page class="pg" name="Page1">
					<pageBody class="pb">
						<contents><table class="tb" name="Switch Container"><tableRows><tableRow><tableCells><tableCell><contents><textItem name="SW - Object Name"><dataSource><staticValue>Object Name</staticValue></dataSource></textItem></contents><style><CSS value="padding-left:3px;padding-top:2px;padding-bottom:2px;font-weight:bold;background-color:#FAFAFA;border-top:1pt solid silver;border-bottom:1pt solid silver;border-left:1pt solid silver"/></style></tableCell><tableCell><contents><HTMLItem description="span">
														<dataSource>
															<staticValue>&lt;span onclick="
table = this.parentNode.parentNode.parentNode;
divArr = table.getElementsByTagName('Div');
for (var i = 0; i &lt; divArr.length; i++) {
  if (divArr[i].getAttribute('id') != null &amp;&amp; divArr[i].getAttribute('id') == 'First' ) {divArr[i].style.display = 'block';}
  if (divArr[i].getAttribute('id') != null &amp;&amp; divArr[i].getAttribute('id') == 'Second' ) {divArr[i].style.display = 'none';}
}
this.style.fontWeight='bold';
this.nextSibling.nextSibling.style.fontWeight='normal';"
style="font-weight:bold"
&gt;</staticValue>
														</dataSource>
													</HTMLItem>
													<textItem name="SW - First Label"><dataSource><staticValue>Graph</staticValue></dataSource></textItem><HTMLItem description="span">
														<dataSource>
															<staticValue>&lt;/span&gt; | &lt;span onClick="
table = this.parentNode.parentNode.parentNode;
divArr = table.getElementsByTagName('Div');
for (var i = 0; i &lt; divArr.length; i++) {
  if (divArr[i].getAttribute('id') != null &amp;&amp; divArr[i].getAttribute('id') == 'First' ) {divArr[i].style.display = 'none';}
  if (divArr[i].getAttribute('id') != null &amp;&amp; divArr[i].getAttribute('id') == 'Second' ) {divArr[i].style.display = 'block';}
}
this.style.fontWeight='bold';
this.previousSibling.previousSibling.style.fontWeight='normal';"
style="font-weight:normal"
&gt;</staticValue>
														</dataSource>
													</HTMLItem>
													<textItem name="SW - Second Label"><dataSource><staticValue>Table</staticValue></dataSource></textItem><HTMLItem description="/span">
														<dataSource>
															<staticValue>&lt;/span&gt;</staticValue>
														</dataSource>
													</HTMLItem>
												</contents><style><CSS value="text-align:right;padding-right:3px;padding-top:2px;padding-bottom:2px;background-color:#FAFAFA;border-top:1pt solid silver;border-bottom:1pt solid silver;border-right:1pt solid silver"/></style></tableCell></tableCells></tableRow><tableRow><tableCells><tableCell colSpan="2"><contents><block>
														<contents><HTMLItem description="div First">
														<dataSource>
															<staticValue>&lt;div id="First" &gt;</staticValue>
														</dataSource>
													</HTMLItem><textItem name="SW - First"><dataSource><staticValue>First</staticValue></dataSource></textItem><HTMLItem description="div Second">
														<dataSource>
															<staticValue>&lt;/div&gt;&lt;div id="Second" style="display:none"&gt;</staticValue>
														</dataSource>
													</HTMLItem><textItem name="SW - Second"><dataSource><staticValue>Second</staticValue></dataSource></textItem><HTMLItem description="/div">
														<dataSource>
															<staticValue>&lt;/div&gt;
</staticValue>
														</dataSource>
													</HTMLItem></contents>
														<style><CSS value="width:350px;height:350px;overflow:auto;text-align:center"/></style></block>

												</contents><style><CSS value="text-align:center;background-color:#FAFAFA;vertical-align:middle;border-bottom-style:none;border-top:1pt solid silver;border-left:1pt solid silver;border-right:1pt solid silver"/></style></tableCell></tableCells></tableRow><tableRow><tableCells><tableCell><contents><textItem name="SW - More Data Left"><dataSource><staticValue>More Data Left</staticValue></dataSource></textItem></contents><style><CSS value="padding-left:3px;background-color:#FAFAFA;border-bottom:1pt solid silver;border-left:1pt solid silver"/></style></tableCell><tableCell><contents><textItem name="SW - More Data Right"><dataSource><staticValue>More Data Right</staticValue></dataSource></textItem></contents><style><CSS value="text-align:right;padding-right:3px;background-color:#FAFAFA;border-bottom:1pt solid silver;border-right:1pt solid silver"/></style></tableCell></tableCells></tableRow></tableRows><style><CSS value="border-collapse:collapse"/></style></table></contents>
					</pageBody>
				</page>
			</reportPages>
		</layout>
	</layouts>
</report>

It was written in 8.2, but it upgrades perfectly. As the report has no queries, you can simply point it to any package you have. When the report loads, you should see the following:

It’s important to note the names. The table is named Switch Container. This is what the report author will select when he uses it from the Layout Component. Each text item is also named. The author will be able to use the component override to replace those items as needed. If the author doesn’t need links for more data below the graph/table, he simply overrides the two bottom items without replacing them. The author can also use the same component multiple times in the same report. When writing JavaScript functions, you should take this possibility into account.

The library needs to be saved in a location that both the author and users can traverse and execute. Any report will fail if the user cannot access the original components library. The components library also needs to exist in the same location in the production environment.

There are many other possible uses for layout component. Any time you need complex functionality in multiple reports components should be considered.