Quantcast
Channel: Paul's Cognos Blog » Uncategorized
Viewing all 37 articles
Browse latest View live

#IBMIOD Monday

$
0
0

The last few days have been completely exhausting, and there are still three more days of the conference. As part of my deal with PerformanceG2, I’ll be posting more details of the conference on their blog. They should be up soon here.

Like last year I’ve had a lot of fun going to the various booths at the expo and hearing what people have to offer. Pens and other toys are just bonus.

An extremely interesting technology is coming from Servergy. They make extremely cool running, low power servers. Their big selling point is the savings you’d get on electricity bills from powering the servers, to the air conditioners you wouldn’t need. Their demo unit, running at 100% CPU, showed 0.56 amps. I am by no means a hardware guy, but this all seemed very very impressive, and I suspect a few of my clients may be interested in learning more.

An Ernst and Young reps chatted with me about the importance of Forensice Data Analytics – fraud identification and prevention. http://www.ey.com/

Fusion-io is offering a flash adapter for System X. From what I understand, it will cache a database in memory, providing significant performance improvements. http://www.fusionio.com/

Esri was giving out their latest map book, and I was lucky enough to snag one of the last ones they had. GIS has always fascinated me, and the book shows their maps are as much art as informative.

I visited a few IBM booths. IBM consistently and regularly blows me away with the new tools they’re developing. End users now can build complex statistical models with only a few clicks, there is software that will send alerts when it detects imminent infrastructure failure, natural language recognition that will automatically search through data sets to try to answer your questions.

There were a few other technology vendors I spoke with, but unfortunately those guys didn’t have any documentation handy! Guys, with all the flashing lights of the Expo, make sure I have some papers to remind me what you’re all doing! (I did manage to score a hat from PerformanceG2′s arch nemesis, but I won’t mention their name since I don’t want to upset my wonderful hosts).

Did I miss anyone? Are there any groups I should make a special effort to visit? The expo is open until Thursday, so drop a comment and I’ll make a special visit.

The post #IBMIOD Monday appeared first on CognosPaul.


Advanced Select and Search Prompts

$
0
0

This solution was built on 10.2.1.1, but should work on all versions after 10.2. Versions prior to 10.2 will need some rewriting as this is based on the Prompt API.

One of the biggest complaints I get about search prompts is the amount of real estate they take from reports. The multiselect prompts are huge, and users hate them for it. So I was asked to find a way to shrink it down.
SnS hogging the page

Obviously the solution was to everything but the search input and button. The keyword and instruction text can be controlled by the properties, simply setting it to “Specified Text” is sufficient to hide them:
Hiding Keywords

That leaves the Options link and the search results. Personally I don’t believe the results list need to appear unless there are actually results. The options should be at the mercy of the report author, so my solution includes a way to choose a default value and to hide the options.

First a look at the JS

/* function paulScripts.advancedSnS - Paul Mendelson - 2013-09-16
 * Will convert a standard search and select. It will add a button to expand/collapse the 
 * are selection area. The button will display how many matches have been found, and how 
 * many selected. 
 * 
 * The Option param will control the default search method. 
 * 1 = Starts with any of these keywords **DEFAULT**
 * 2 = Starts with the first keyword and contains all of the remaining keywords
 * 3 = Contains any of these keywords
 * 4 = Contains all of these keywords
 *
 * Hide the options link by setting 1 in the hideOption param.
 */ 
paulScripts.advancedSnS = function(promptName,option,hideOption){
  var prompt = paulScripts.getControl(promptName)
    , elm = document.getElementsByName(prompt.getParameterName())[0].parentNode
    , selectTable = elm.getElementsByTagName('table')[4]
    , selectDiv = document.createElement('div')
    , selectOptions = cognos.Report.getReport("_THIS_").prompt.getControlByName(promptName).getSelectOptions()
    , selectChoices = cognos.Report.getReport("_THIS_").prompt.getControlByName(promptName).getSelectChoices()
    , tr = elm.getElementsByTagName('button')[0].parentNode.parentNode.parentNode.appendChild(document.createElement('tr'))
    , td= tr.insertCell(0)
    , slideDiv = document.createElement('div')
    , btn = document.createElement('input');


    if(option) elm.getElementsByTagName('input')[option+2].checked=true;
    if(hideOption) tr.parentNode.parentNode.parentNode.parentNode.nextSibling.style.display='none';
    td.setAttribute('colSpan',2);

//Hacky – Building a new dispatcher request to save the state of the SnS. Do any IBMers have any comments? 
  if(typeof oReq ==='undefined') oReq = new ViewerDispatcherEntry(window['oCV'+'_THIS_'].getRV().getCV());
  if(!oReq.getFormFields().m_aValues[promptName]) oReq.addFormField(promptName,'hidden')

  btn.type='button';
  btn.value='';

  if(!selectOptions&&!selectChoices) btn.value='click to open';
  if(selectOptions)  btn.value = selectOptions.length + ' found. ';
  if(selectChoices)  btn.value += selectChoices.c.length + ' selected.';

  selectTable.parentNode.insertBefore(selectDiv,selectTable);
  selectDiv.appendChild(selectTable);
  
  //the right side of the table is a bit ugly; not enough padding.
  selectDiv.style.paddingRight='5px';

  // Sets the table of the select options to position absolute. This will prevent the page from expanding vertically
  // when the table is visible;
  selectDiv.style.position='absolute';
  selectDiv.style.overflow='hidden';
  selectDiv.style.backgroundColor='white';

  btn.onclick = function() {
    if(selectDiv.style.visibility=='hidden')
      {slidedown(selectDiv);
oReq.addFormField(promptName,'visible');}
    else {slideup(selectDiv);oReq.addFormField(promptName,'hidden');}
      return true;

  }

  selectDiv.style.visibility=oReq.getFormFields().m_aValues[promptName];
  td.appendChild(btn);
  return true;
}

The JS itself is fairly straight forward. The variables define the DOM element, a JSON array of the found options, another JSON array of the selected objects, and a couple of generated elements.

If a default search option is selected, it will set that.

if(option) elm.getElementsByTagName('input')[option+2].checked=true;

If hideOption is set to true, it will hide the options link.

 if(hideOption) tr.parentNode.parentNode.parentNode.parentNode.nextSibling.style.display='none';

Then it will insert a button, and finally the “results” lists are moved into a generated div which can then be slid open or shut.

  td.appendChild(btn);

Whenever I create a solution that changes the appearance of the page, showing or hiding an element, the users demand that the change persist through a page reset. The easiest way would be to create a new parameter to store the display state. Possibly a hidden text box with box type set to none. But if we always did things the easy way, we’d never learn anything.

 //Hacky – Building a new dispatcher request to save the state of the SnS. Do any IBMers have any comments? 
  if(typeof oReq ==='undefined') oReq = new ViewerDispatcherEntry(window['oCV'+'_THIS_'].getRV().getCV());
  if(!oReq.getFormFields().m_aValues[promptName]) oReq.addFormField(promptName,'hidden')

I haven’t tested this on a wide scale, so I don’t know of any performance impact. To be honest, while I love figuring out little hacks like these, without a more in-depth understanding of what it’s doing, it may not be wise to push this to all reports. The attached report contains two functions, the first using the hacky dispatcher request, and the second using a secondary text box to retain the state. The disadvantage of using the text box is that you’ll need to remember to add that in.

Once the JS is in place, we can call the functions with a simple:

paulScripts.advancedSnS ('Retailers', 3,1);

It will look for the prompt named Retailers, set the option to “Contains any of these keywords”, and hide the options link.

And the final product:
Searchng and Selecting

Report XML:
Advanced SnS (20 downloads)

The post Advanced Select and Search Prompts appeared first on CognosPaul.

Creating a data entry page in Cognos

$
0
0

Cognos is a wonderful BI platform, but it does not handle write-backs to the database very well. By no means should Cognos be considered a data entry platform, but there are times when you may want to store comments associated with a specific report. Other times you may want to give users the ability to enter and save targets for specific data. It is even possible to set up a system to store the prompt selections in a report in a table.

The solution presented here was created in 10.2.1, but will work in every version since 8.4.

At the most basic level, writing back data is very simple. You run a stored procedure which takes specific parameters and passes them to the database. A system for entering comments is a good example:
DE Adding a comment

Multiple comments:
DE Multiple Comments

To begin with, let’s take a look at the stored procedure:

ALTER PROCEDURE [dbo].[insertDateComment]
	@dateKey numeric(18,0)
	, @comment varchar(1000)
AS
BEGIN
	SET NOCOUNT ON;

	  INSERT dbo.datecomments (DateKey,comment)
	  VALUES (@dateKey, @comment) ;
	SELECT @@ROWCOUNT as "rowCount"
END

You can’t get much simpler than this. It accepts two values, dateKey as a numeric and comment as a string, and inserts them into a table. In real-world usage more fields would be necessary, such as the username running the procedure, or the report name, or any other metadata you would want tracked. Since we’re using this in a Cognos report, a value must be returned. Even a simple “select 1 as whatever” would be sufficient.

In Framework, import the SP and populate the input parameters with prompt macros:
DE importDateCommentSP

There are two reports shown, the first report is a simple list showing date and comment (if exists). That’s accomplished by joining the comments table and the time dimension.

The second report contains a conditional block, a singleton, and two input boxes. When the report runs, the conditional block checks if the comment parameter is populated. If not, it renders the first page containing a hidden input for the dateKey, and a visible textbox for the comment. The user enters a comment and presses the refresh button. Again, the report runs, checking if the comments parameter is populated. Since it is this time, it renders the singleton block. The singleton contains the reference to the procedure. Since the parameter names in the textboxes match the parameter names from the procedure, it runs those. As the return value from the procedure is useless to the end user I hide it by checking “rowCount” in the properties of the singleton, and dropping a text item with a descriptive “data entered” message.

So far this has all been simple. But what happens when you want to enter multiple values? One at a time simply won’t do. The trick here is to dynamically change the name of the parameter in the framework. This method is a little more complex, as it uses HTML items and a little bit of JavaScript.

In this method the stored procedure is upserting the value into the table. The SQL for the procedure is using the merge function, which is not the most efficient way of doing an upsert.

ALTER PROCEDURE [dbo].[upsertValue]
	@DateKey [numeric](18, 0)
	, @Value [numeric](18, 0)
AS
BEGIN
	SET NOCOUNT ON;

	MERGE dbo.DataEntry as t
	USING (select @DateKey as DateKey, @Value as Value) as n 
	  on t.DateKey = n.DateKey 
	WHEN MATCHED then update set t.Value = @Value 
	WHEN NOT MATCHED THEN 
	  INSERT (DateKey,Value)
	  VALUES (@DateKey, @Value) ;
	SELECT @@ROWCOUNT as "rowCount"
END

The reference to the procedure uses prompt macros, but with a slight twist.
DE framework upsert

The prompt for the macro is set to #prompt(prompt(‘DateKey’,'integer’,’0′)+’Value’,'integer’,’0′)#

The DateKey prompt will actually change the parameter name for the Value prompt. So for date 1100, the parameter for the value would be 1100Value.

In the report, a simple list of dates is entered, with an HTML item
DE datelist

The HTML item is a report expression with the expression:

'<input 
  type="text" 
  name="p_'+number2string([DataEntry].[Day Key])+'Value" 
  value="'
+case when [Value] is null then '' else number2string([DataEntry].[Value]) end
+'"
  onchange="updateDateArr('+number2string([DataEntry].[Day Key])+')"
/>'1

Each row will generate an input box, the name will be the dateKey + 'Value' (just like the parameter in Framework). Parameters are populated by inputs with a specific name. So an input with the name p_123Hello will populate the parameter 123Hello. You would be able to use that parameter in a query, or on the page with paramDisplayValue('123Hello'). 

When a change is made to the input, it will be added to a list of dateKeys, that list is to ensure the procedure is run on updated values only:

1
<script>
var fW = (typeof getFormWarpRequest == "function" ? getFormWarpRequest() : document.forms["formWarpRequest"]);
if ( !fW || fW == undefined) {fW = ( formWarpRequest_THIS_ ? formWarpRequest_THIS_ : formWarpRequest_NS_ );}
var preFix = "";
if (fW.elements["cv.id"]){preFix = fW.elements["cv.id"].value;}    
var nameSpace = "oCV" + preFix;


var dateArr = [];

Array.prototype.unique = function() {
    var o = {}, i, l = this.length, r = [];
    for(i=0; i<l;i+=1) o[this[i]] = this[i];
    for(i in o) r.push(o[i]);
    return r;
};

function updateDateArr(dateKey){
  dateArr.push(dateKey);
  dateArr=dateArr.unique();
  document.getElementById('p_UpdatedDates').value=dateArr.join(',');
}

</script>
<input type="hidden" name="p_UpdatedDates" id="p_UpdatedDates">
 

Whenever the updateDateArr is called, it will add the new dateKey into the array, make the array unique, and then push it into the p_UpdatedDates input.

In the previous method, the stored procedure was only called when the Comments parameter was populated. The same thing is possible here. Create a conditional block with the Boolean variable paramValue(‘UpdatedDates’) is not null.

Inside the yes block, drag in a new list, stick in the dateKey, and filter that query with

dateKey in (#prompt('UpdatedDates','token')#)

Make sure to set the pagination options of the list. Check “Repeat every page” and uncheck “Allow contents to break across pages” and “Allow horizontal pagination”. This will ensure that the entire list appears in one page. In the same thread, go to File -> Report Properties, and set “Page break by data container for interactive HTML” to Yes. Without setting this option, the next list would be pushed to the next page.

When the report is run with updated values, that list will generate only those rows with updated dates.
DE Only Updated

Put a list in that list, drag in the rowCount from the upsertValue procedure, and define a master detail connection from Day Key to the parameter DateKey:
DE Master detail

Now when you run it, it will update the values.
DE It works

Hiding the list is a simple as wrapping it with a hidden div:
<div style=”display:none”> to the left of it, </div> to the right.
Here it is:
DE Entering multiple values

This post is a bit unusual in that it has several files attached. Three reports, the framework model, and an SQL Server 2012 backup.

Reports:
Data-Entry-Reports.zip (13 downloads)

Model:
Data Entry - Model.zip (11 downloads)

Database backup (SQL Server 2012):
Data Entry Database (6 downloads)

The post Creating a data entry page in Cognos appeared first on CognosPaul.

Quick and painless way of accessing users’ folders

$
0
0

As part of my administrative duties for my various clients, I periodically trawl through the logs. Many times I’ll want to see exactly what the users are doing in the reports on my server. It becomes a headache when these reports are in the users’ “My Folders”. I need to go into the administration page, find the user, go into their “My Folders”, copy out the report, paste it into mine then run it. It’s a mess and I’m far too lazy for that.

Fortunately Cognos allows us to access objects through URL parameters. On my laptop, I’m using OpenDJ as my authentication provider, so the CAMID is slightly different than Active Directory. My CAMID here is CAMID(“OpenDJ:u:cn=administrator”). In AD, it might be CAMID(“AD:u:r7a1n7d8o8m3l1e4t9t2e2r4s01234567)

My user

I can just copy that CAMID and paste it into the URL as:
http://server/cognos/cgi-bin/cognosisapi.dll?b_action=xts.run&m=portal/cc.xts&m_path=CAMID(“OpenDJ:u:cn=administrator”)

2. Accessing my user

Obviously this is still predicated on the security in place. A user who doesn’t have permission to access another user’s folder will get an “Access Denied”

3. Thwarted again

The post Quick and painless way of accessing users’ folders appeared first on CognosPaul.

Review: IBM Cognos BI v10.2 Administration Essentials

$
0
0

Part of my job as an Admin is to understand all aspects of the Cognos environment, from configuring and tuning distributed systems to being able to decipher the cryptic error messages that occasionally plague the users. Obviously this book is not for me. This book is more geared for beginners, as it says in the beginning:

Who this book is for
This book is for beginners planning to learn IBM Cognos BI Administration 10.

So, with that in mind it’s important to remember that experienced admins can skip the book. It is the people with little or no administrative experience who will benefit most from the book.

My concern is the length, at 128 pages there is simply not enough room to go into detail on everything. While it gives descriptions on the various tabs and settings of the administration page, I would have preferred more explanations on the settings and their effects.

Despite the brevity it does cover, albeit briefly, the various elements that are involved in the administrative process. The chapters are split by area of interest, configuration, components, security, etc. It describes the what happens when Cognos gets a request and the path the request takes from the gateway through the dispatcher. It explains the authentication and security system fairly well. Most importantly it also provides advice for new admins on how to run the system.

Ultimately I believe the book succeeds in its goal, namely taking people to the point where they can keep a Cognos server up and running.

You can find the book at the PacktPub website here.

The post Review: IBM Cognos BI v10.2 Administration Essentials appeared first on CognosPaul.

Freezing headers in Cognos 10.2

$
0
0

Starting in Cognos 10.2, IBM released a way to freeze crosstab headers and rows. This method works in all major browsers, and doesn’t rely on dirty CSS hacks. To get it working, simply right-click on the list or crosstab, and select Freeze Headers. The report will even remember the state of the crosstab after the page refreshes (from a prompt, possibly).

right click action

While this method works well, there are many times where we’d want to have the lists or crosstab start off frozen; or maybe we want to give the user a button to freeze one or all of the crosstabs in one go. Somewhat surprisingly, the JavaScript is very easy to use.

First, let’s define the initial variables:

var paulScripts = {}
  , win=window['oCV'+'_THIS_'];

paulScripts.oCV = win.getRV().getCV();

Reports run from Report Studio need to use window['oCVRS'] while reports from the connection need window['oCV_NS_']. Cognos will automatically replace _THIS_ to the correct fragment. Most of the Cognos functions we need are found inside win.getRV().getCV(), so we’re aliasing that into “paulScripts.oCV”.

The first function I’ll define is:

paulScripts.freezeContainer = function(objectName){
  var pFMngr = paulScripts.oCV.getPinFreezeManager();
  pFMngr.freezeContainer(objectName,true,true);
  window.onResizeViewerEvent()
};

All of the freezing functions are found inside getPinFreezeManager. The function getPinFreezeManager().freezeContainer() takes the object name (“Crosstab1″), and a Boolean to freeze/unfreeze the headers, and another Boolean to freeze/unfreeze the rows.

The freezing mechanism seems to have a small bug in the way it handles resizing. In my tests, it always resizes to a much smaller window than I need. The window.onResizeViewerEvent() function tricks the browser into thinking the window has been resized, and Cognos will then correctly recalculate the size of the frozen crosstab.

The unfreezing function is simple:

paulScripts.unfreezeContainer = function(objectName){
  var pFMngr = paulScripts.oCV.getPinFreezeManager();
  pFMngr.freezeContainer(objectName,false,false);
};

These two functions will let us automatically freeze a crosstab as soon as the page loads. But say we want to toggle it on and off.

/* paulScripts.toggleContainer 
 * Paul Mendelson - 2014-03-25
 * This wil check if a container has frozen headers. If so, it unfreezes it, if not, it freezes the container. 
 */
paulScripts.toggleAll = function(objectName) {
  var pFMngr = paulScripts.oCV.getPinFreezeManager();
  if(pFMngr.hasFrozenColumnHeadings(objectName)) {
    paulScripts.unfreezeContainer (objectName);
  }
  else {
    paulScripts.freezeContainer (objectName);
    window.onResizeViewerEvent()
  }
}

The hasFrozenColumnHeadings function returns a Boolean, true or false, on the state of the column headers.

And finally let’s say that we want to freeze all the crosstabs on the page in one go.

/* paulScripts.toggleAll 
 * Paul Mendelson - 2014-03-25
 * in: type {string - crosstab or list}
 * This wil loop through every "type" on the page, and freezing or unfreezing, depending if the column header is frozen. 
 */

paulScripts.toggleAll = function(type) {
  setTimeout(function(){
    var xts = win._getContainers(type)
      , xtLen = xts.length
      , pFMngr = paulScripts.oCV.getPinFreezeManager();
      for(var i =0;i<xtLen;++i){
        var lid = pFMngr.removeNamespace(xts[i].getAttribute('lid'));
        if(i<xtLen-1 && lid==pFMngr.removeNamespace(xts[i+1].getAttribute('lid'))) continue; //when the panes are frozen the crosstab is split into four elements, all with the same lid. Without this hack, the xtab would toggle four times!
        if(pFMngr.hasFrozenColumnHeadings(lid)) {
          pFMngr.freezeContainer(lid,false,false);
        }
        else {
          pFMngr.freezeContainer(lid,true,true);
          window.onResizeViewerEvent()
        }
      }
    }
  ,200);
}

That was a little bit more complex than before. We can use the _getContainers functions to get an array of all the lists or crosstabs on the page. The lid of the object is the name, plus the namespace. We can use the removeNamespace function to get it back to the name the freezeContainer functions expect.

The toggleAll function should only be used in a button. As Cognos stores the state of the object; if the headers are locked, they will remain locked after refreshing the page. Toggling them will cause them to unlock. Instead, it’s best to use a freezeAll function when loading the page:

paulScripts.freezeAll = function(type){
  var xts = win._getContainers(type)
  , xtLen = xts.length
  , pFMngr = paulScripts.oCV.getPinFreezeManager();
  for(var i =0;i<xtLen;++i){
    var lid = pFMngr.removeNamespace(xts[i].getAttribute('lid'));
    if(pFMngr.hasFrozenColumnHeadings(lid)) continue; 
    paulScripts.freezeContainer (lid);
  }
};

Once everything is working, we can see how it works.
Freezing panes

The example report in using 10.2.1, against the sales and marketing cube.
Freezing Panes report XML (30 downloads)

The post Freezing headers in Cognos 10.2 appeared first on CognosPaul.

Quickie: Resizing a value prompt list

$
0
0

Another problem from a reader. Unlike checkbox or radio prompts, list prompts don’t expand and collapse to fit the number of options available to them.
This prompt is taking far too much room.

In the above image, we have a prompt that has too much space. Let’s shrink that down.
resizing lists

Now it shrinks to the number of rows displayed, or expands to a maximum of 15 items.

It’s actually a very easy script. The first step is to identify the prompt. We’ll use the old fW script (so this should work in every since 8.4).

var fW = (typeof getFormWarpRequest == "function" ? getFormWarpRequest() : document.forms["formWarpRequest"]);
if ( !fW || fW == undefined)
{
  fW = ( formWarpRequest_THIS_ ? formWarpRequest_THIS_ : formWarpRequest_NS_ );
}
var preFix = "";
if (fW.elements["cv.id"])
{
  preFix = fW.elements["cv.id"].value;
}
var nameSpace = "oCV" + preFix;

Next, we need to identify the prompt object:

var pr = fW._oLstChoicesPromptName;

The prompt itself is a standard SELECT element. We can set the size attribute to easily set the height.

This gives us:

if  (pr.options.length>15)
{
  pr.size=15
}
else 
{
  pr.size=pr.options.length
}

The following report XML is based on 10.2.1, but you can downgrade it easily by changing the schema number on the first line.
Resizing-Value-Prompt-List.txt (10 downloads)

The post Quickie: Resizing a value prompt list appeared first on CognosPaul.

#ibmiod Wednesday

$
0
0

Dynamic Cubes was the word of the day for me. It’s an exciting new feature of Cognos that lets you build cubes on top of relational systems. By using these cubes, you can get tremendous improvements in your report runtimes. The method of building them seems fast and easy, and while I have a few minor misgivings about some of the design decisions, I am really looking forward to getting an opportunity to play with them.

At the most basic level, Dynamic Cubes are an extension of the DQM engine. As I’m sure everyone in the world is aware, DQM is the 64bit querying engine released with Cognos 10.1. They are based on relational data sources, and work by caching the contents of your data warehouse.

First the cube needs to be modeled. I didn’t get a chance to see the cube modeler, but the developers are saying it looks and feels very similar to a cleaned up Transformer. Attributes can be defined (sadly lacking in Transformer), and dynamic dates are easy to build. It is important to note that the new modeling tool is far less forgiving for nonsense than Transformer. MUNs must be unique, or it will throw an error. The data warehouse must be set up in a star schema or a snowflake for the cube to work.

Once the cube has been built and published, it needs to be started. After it starts up it will start building the various caches. The member cache consists of all of the members in the dimension tables. Aggregation caches are populated as the cube runs. These can be contextually aware aggregates, so the cube will benefit if you have aggregated tables. The data cache is populated as reports are run. If a report has been run before, it will be in the cache and will be rendered instantly. An expression cache exists – any matching expression will be populated instantly.

As reports are run, it will be possible to determine if the aggregations used are optimal. The dynamic query analyzer has a new option for the cubes. You can have it check the run history for specific reports, or users, and it will optimize the caches aggregations to ensure the best performance.

The developers responsible for this innovation referenced a case study in which a report that took roughly a day to run went down to 3 seconds. From what I understand, the data warehouse was the same, but they needed to add some more ram.

The dynamic cubes take a fair amount of ram. IBM will release a white paper discussing the various sizing and ram requirements.

It seems that real life is creeping it’s way back in. Today was the final day of the Expo, and unfortunately I didn’t have a chance to meet with all of the people manning the booths. Over the next week I’ll try to go over the remaining documentation that I pulled from various booths.



Blatant self promotion

$
0
0

As I wrap up some of my existing contracts, I realize I have some spare time coming up. Far be it from me to goof off in my spare time (the alternative being cleaning and other house work, and I can’t let the wife get used to that idea), I’d much rather be solving problems people are having with their Cognos set up.

So, does your company need help building that new dashboard for the CFO? Are the reports running too slowly over your new OLAP server? Does JavaScript make your brains leak out? Drop me a line at cognospaul@gmail.com and we can come to an arrangement.


Quickie: Changing default option in a Select and Search Prompt

$
0
0

I’ve been somewhat negligent in responding to the comments lately. One of the most common questions appears to be how to change the default option in a select and search prompt.

The following should work for all versions of Cognos.

Put an HTML item before the prompt, with the expression:

<div id="mySnS">

Now put another HTML item after the prompt with the expression:

</div>
<script>
/* Select and search options
 * 2 - Starts with any of these keywords * DEFAULT
 * 3 - Starts with the first keyword and contains all of the remaining keywords 
 * 4 - Contains any of these keywords 
 * 5 - Contains all of these keywords 
 */
document.getElementById('mySnS').getElementsByTagName('input')[5].click();
</script>

The script will locate the mySnS element that surrounds the prompt, generate an array of the input tags, and click() on the one specified. In the example, 5 is selected so it will click on the sixth element (0 based array).

Going through the Cognos JavaScript files, it looks like there is supposed to be a function in the new Prompt API that would let you do it in an easier manner, unfortunately it appears to be an incomplete function, maybe it will be released in the new fixpack.

In theory, you should be able to do something like the following:

var acme = {};
acme.getControl = function(promptName) 
{
  var ocr = cognos.Report.getReport("_THIS_");
  return ocr.prompt.getControlByName(promptName);
};

acme.getControl('mySnS2').setProperty( "caseInsensitive", false);
acme.getControl('mySnS2').setProperty( "searchType", "containsAny");

But, as I said, the function doesn’t appear to be complete in this version. Use the other method for now.


Review: IBM Cognos Insight

$
0
0

While I’ve got a bit of experience working with the various Cognos studios, I haven’t had much chance to play with Insight. Fortunately I have IBM Cognos Insight, by Sanjeev Datta.

As a in depth study, it is a bit lacking. The book doesn’t get into serious detail about the inner workings, effects on the server, nor does it go into detail about every single available function. That is fine however, as the book is not targeted at administrators. The only load balancing the readers of this book need to deal with are in the laundry.

This book is very well suited for analysts – people who need to actually play with the data, and who need to learn about the tools available to them. As an example, I have a friend who is an internal auditor at a very large port. His days are spent pulling data from different sources into Access and running queries on them. He is not very technically inclined, and has only a rudimentary understanding of SQL. This would be a perfect guide for him.

As a guide, it is split logically in sections. What is BI and how does it help, installing, configuring, importing the data, and the various ways of manipulating the data. It walks the through each step clearing and succinctly, with screenshots to guide the way. (One small complaint though, I read the book on my black and white Kindle, and there was at least one instance referring to text highlighted in color that I couldn’t see.)

The meat of the book is where it describes how to design and use your cube. From building the hierarchy to writing custom members, it touches on each area. It shows how you can build TurboIntegrator scripts, and why you would, but unfortunately doesn’t go into detail. Ultimately this book shows a user how to go from raw data to a complex dashboard that meets the user’s needs.

To the seasoned veteran of Insight, this book won’t be so useful. To everyone else, this book is invaluable and will get them to the seasoned part. When working with Insight, keep this book open and you won’t go wrong.

IBM Cognos Insight was written by Sanjeev Datta, and published by Packt Publishing.


Recent Java exploits and Cognos

$
0
0

Several new Java exploits have recently been uncovered. So far the all of the exploits that IBM has tested against their JDK and JREs have been unsuccessful. As my source in the upper echelons of IBM has said, “Thus, IBM products shipping the IBM JDK are not vulnerable to this exploit.”

Products that ship the Oracle JRE may be vulnerable to the exploits, and care should be taken until all of the fixes are released. Remember, nothing is sure until the fix is in.

Please read: Java Vulnerability Blog, from Tivoli AVP

In general it would be best to stay subscribed to the IBM Product Security Incident Response Blog to make sure you have the latest information on any security concerns with IBM products.


Cognos Mashup Services – a brief example

$
0
0

In a previous post I showed how to embed Cognos reports in other applications or reports by using iframes. Unfortunately there are many problems with using iframes, difficulty interacting with objects with JavaScript, security issues, even positioning objects well into a page.

Instead it might be better to use CMS. You can pull a specific object from the report, and it becomes an actual element of the page you’re working on. CMS also allows you to pull elements in a number of different formats. HTMLFragment will return the report object exactly as it appears in the report, while JSON will allow you to easily use the data from the report in a JS function. This will post will two examples, HTMLFragment and JSON.

We’ll start with a JSON example.

In this report, we’ll create a text box prompt and a list prompt. Typing anything into the text prompt will populate the list prompt, like a search and select, but without refreshing the page.

This example is using Cognos 10.2 and the sales and marketing cube. The JavaScript will not downgrade to previous versions as I’m using the new Prompt API. People using previous versions can get around it by attaching events to their prompts.

To begin, create a list report with a value and key. In my example I’m using Product and Product – Category Code from the Sales and Marketing cube.
1. Source list

The Product field is actually a filter expression:

filter(
  [sales_and_marketing].[Products].[Products].[Product]
  , upper([sales_and_marketing].[Products].[Products].[Product].[Product - Long Name]) contains upper(#prompt('SearchString','string')#)
    or [sales_and_marketing].[Products].[Products].[Product].[Product - Category Code]  contains (#prompt('SearchString','string')#)
)

As CMS allows us to reference objects directly, it’s important to remember to give each object a name that describes what it is, what it contains, while being short enough to be easily referenceable . In this case, I’m calling the list object “List”.

When you run the report, you’re prompted to enter a string, and any product that contains the caption or code will be returned.

Save it under /CMS/Source and create a new report.

This report should have a table with 3 rows, 1 column. In the first row, put a text box prompt. In the second, a multi select list prompt. Leave the third blank for now. Remember to name the text box and value prompts. Let’s call them Text and Select.

Drag in an HTML item in the bottom row of the table, and paste in the following code.

<script>
/*
 * Fake Namespace and prompt getters.
 */ 	
var paulScripts = {}
var oCR = cognos.Report.getReport("_THIS_");

paulScripts.getSource = function()
{
    var targ;
    if (!e) var e = window.event;
    if(!e) return false;
    if (e.target) targ = e.target;
    else if (e.srcElement) targ = e.srcElement;
    if (targ.nodeType == 3) // defeat Safari bug
      targ = targ.parentNode;
    return targ;
}

paulScripts.getControl = function(promptName) 
{
  return oCR.prompt.getControlByName(promptName);
}


paulScripts.setPromptValue = function ( promptName, value ) {
var 
  newOption = ''
  , selElm = document.getElementById('PRMT_SV_'+paulScripts.getControl ( promptName )._id_);
//  selElm.options.length=0;
  for(i=0;i<selElm.options.length;i++)
  {
    if(selElm.options[i].selected==true){
      for(x in value) {if(value[x].use == selElm.options[i].value) {value.splice(x,1); break;}}
    } else {selElm.remove(i);i--}
  }
  
  for(i=0;i<value.length;i++)
  {
    newOption = document.createElement( 'option');
    newOption.value=value [i].use ;
    newOption.innerHTML = value[i].display ;
    newOption.dv = value [i].display ;

    selElm.appendChild(newOption );

  }
}

/*
* This creates the XMLHttpRequest object used to communicate with CMS.
* The initialization of the object depends on what browser is being used. This
* code is compatible with IE 5.5, 6, 7, 8 and all versions of Firefox and Chrome
*
* For more information on the XMLHttpRequest object, see http://www.w3.org/TR/XMLHttpRequest/
*/
try  {
var objXHR = new XMLHttpRequest();
} catch (e) {
try {
var objXHR = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
var objXHR = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
alert('XMLHttpRequest not supported'); }
}
}


paulScripts.getValues = function (searchString)
{
  var 
    searchString = searchString?searchString:'',
    url= '../ibmcognos/cgi-bin/cognos.cgi/rds/reportData/searchPath/%2fcontent%2ffolder%5b%40name%3d%27CMS%27%5d%2freport%5b%40name%3d%27Source%27%5d?fmt=JSON&async=off&selection=List&p_SearchString=' + searchString;
   objXHR.open("POST", url, false);
   objXHR.send(null);
   if (objXHR.status == 200)
   {
     dataCache = (eval('(' + objXHR.responseText + ')'));
     return paulScripts.parseJSON(dataCache);
   }
}

/*
 * Loop through tableData, extract the use and display fields, and dump them into 
 * a JS object.
 */
paulScripts.parseJSON = function(tableData)
{
  if(!tableData.filterResultSet.filterResult) return false;
  var 
      rows = tableData.filterResultSet.filterResult[0].reportElement[0].lst.group.row
    , JSONData = [];

  for (var i=0; i < rows.length; i++)
  {
    JSONData.push ( {use :  rows[i].cell[1].item[0].txt.fmtVal, display : rows[i].cell[0].item[0].txt.fmtVal  });
  }
return JSONData;
}


/*
 * function loadOptions. Paul Mendelson - 2013-01-15
 * When text is entered into the text box, this will be triggered. It will wait for further input
 * before loading the select box with values.
 */
paulScripts.loadOptions= (function () {
  var timer;
  return function (){
    var name = this.getName()
    , search = this.getValue();
    clearTimeout(timer);
    timer = window.setTimeout(function() {  
      if(this.oldValue==search) {return true} else {this.oldValue=search}
      paulScripts.setPromptValue( 'Select', paulScripts.getValues(search));
    },1000);
    return true;
    };
})();

paulScripts.getControl('Text').setValidator(paulScripts.loadOptions);

</script>

When you run the report the select box will be empty. Start typing into the textbox. The JS will wait 1 second after the last keystroke, then pass the value to the Source report, retrieve data in JSON format, parse it and populate the select.

I’m not going to get into all of the JS here, just what is salient to CMS.

The paulScripts.getValues first coalesces the search string into nothing. You can make prompts optional by making your filters “this = ?searchString? or ‘-1′ = ?searchstring?”, and having the searchString set to ‘-1′. The URL in this example uses the search path of the report. While longer, I find it preferable over using the storeID. Just remember to URL Encode it. Notice the search string is appended to the URL. It then opens an XMLHttpRequest to Cognos. Cognos will interpret the request and send back a responseText.

The responseText will need to be handled differently depending on the format of the request. In this case, Cognos is returning JSON, and the results will need to be parsed as such.

The paulScripts.parseJSON will loop through the rows in the table. I know that the first cell is the label, and the second is the code, I also know there is only a single object in each table cell.

The monster tableData.filterResultSet.filterResult[0].reportElement[0].lst.group.row[1].cell[0].item[0].txt is how we reference text of the first item in the first cell of the second row (indexes are 0 based). If I managed to pull two lists, I could decide to use reportElement[1] to get the second list.

When parseJSON finishes creating the JS object, it will return it to getValues which in turn returns it to setPromptValue. setPromptValue will loop through the JS object and create the options in the select list.

Now that we have functional prompts. Let’s create a chart that shows revenue per month for each of the selected products.

Put the Month level in the categories, Revenue in the Measure and a new Query Calculation: filter(
[sales_and_marketing].[Products].[Products].[Product]
, [sales_and_marketing].[Products].[Products].[Product].[Product - Category Code] in (#promptmany('Products','string')#)
)

When run, it will prompt for codes.

Now let’s change the HTML item to:

<div id="chart"></div>
<script>
/*
 * Fake Namespace and prompt getters.
 */ 	
var paulScripts = {}
var oCR = cognos.Report.getReport("_THIS_");

paulScripts.getSource = function()
{
    var targ;
    if (!e) var e = window.event;
    if(!e) return false;
    if (e.target) targ = e.target;
    else if (e.srcElement) targ = e.srcElement;
    if (targ.nodeType == 3) // defeat Safari bug
      targ = targ.parentNode;
    return targ;
}

paulScripts.getControl = function(promptName) 
{
  return oCR.prompt.getControlByName(promptName);
}


paulScripts.setPromptValue = function ( promptName, value ) {
var 
  newOption = ''
  , selElm = document.getElementById('PRMT_SV_'+paulScripts.getControl ( promptName )._id_);
//  selElm.options.length=0;
  for(i=0;i<selElm.options.length;i++)
  {
    if(selElm.options[i].selected==true){
      for(x in value) {if(value[x].use == selElm.options[i].value) {value.splice(x,1); break;}}
    } else {selElm.remove(i);i--}
  }
  
  for(i=0;i<value.length;i++)
  {
    newOption = document.createElement( 'option');
    newOption.value=value [i].use ;
    newOption.innerHTML = value[i].display ;
    newOption.dv = value [i].display ;

    selElm.appendChild(newOption );

  }
}

/*
* This creates the XMLHttpRequest object used to communicate with CMS.
* The initialization of the object depends on what browser is being used. This
* code is compatible with IE 5.5, 6, 7, 8 and all versions of Firefox and Chrome
*
* For more information on the XMLHttpRequest object, see http://www.w3.org/TR/XMLHttpRequest/
*/
try  {
var objXHR = new XMLHttpRequest();
} catch (e) {
try {
var objXHR = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
var objXHR = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
alert('XMLHttpRequest not supported'); }
}
}


paulScripts.getValues = function (searchString)
{
  var 
    searchString = searchString?searchString:'',
    url= '../cgi-bin/cognos.cgi/rds/reportData/searchPath/%2fcontent%2ffolder%5b%40name%3d%27CMS%27%5d%2freport%5b%40name%3d%27Source%27%5d?fmt=JSON&async=off&selection=List&p_SearchString=' + searchString;
   objXHR.open("POST", url, false);
   objXHR.send(null);
   if (objXHR.status == 200)
   {
     dataCache = (eval('(' + objXHR.responseText + ')'));
     return paulScripts.parseJSON(dataCache);
   }
}

/*
 * Loop through tableData, extract the use and display fields, and dump them into 
 * a JS object.
 */
paulScripts.parseJSON = function(tableData)
{
  if(!tableData.filterResultSet.filterResult) return false;
  var 
      rows = tableData.filterResultSet.filterResult[0].reportElement[0].lst.group.row
    , JSONData = [];

  for (var i=0; i < rows.length; i++)
  {
    JSONData.push ( {use :  rows[i].cell[1].item[0].txt.fmtVal, display : rows[i].cell[0].item[0].txt.fmtVal  });
  }
return JSONData;
}


/*
 * function loadOptions. Paul Mendelson - 2013-01-15
 * When text is entered into the text box, this will be triggered. It will wait for further input
 * before loading the select box with values.
 */
paulScripts.loadOptions= (function () {
  var timer;
  return function (){
    var name = this.getName()
    , search = this.getValue();
    clearTimeout(timer);
    timer = window.setTimeout(function() {  
      if(this.oldValue==search) {return true} else {this.oldValue=search}
      paulScripts.setPromptValue( 'Select', paulScripts.getValues(search));
    },1000);
    return true;
    };
})();

paulScripts.getControl('Text').setValidator(paulScripts.loadOptions);

/*
 * function loadProducts. Paul Mendelson - 2013-01-15
 * When a product is selected in the select, this will be triggered. It will wait for further input
 * before attempting to retrieve the chart.
 */
paulScripts.loadProducts= (function () {
  var timer;
  return function (){
    var name = this.getName()
    , products = this.getValues()
    , productsLabel='';
    clearTimeout(timer);
    timer = window.setTimeout(function() {  
    if(products.length===0) return true;
    for (i=0;i<products.length;i++) {productsLabel+='&p_Products='+products[i].use}
    paulScripts.getChart(productsLabel);
    },1000);
    return true;
    };
})();

paulScripts.getChart = function (products)
{
  var 
    url= '../cgi-bin/cognos.cgi/rds/reportData/searchPath/%2fcontent%2ffolder%5b%40name%3d%27CMS%27%5d%2freport%5b%40name%3d%27Chart%27%5d?fmt=HTMLFragment&async=off&selection=Chart' + products;
   objXHR.open("POST", url, false);
   objXHR.send(null);
   if (objXHR.status == 200)
   {
     document.getElementById('chart').innerHTML = objXHR.responseText ;
   }
}

paulScripts.getControl('Select').setValidator(paulScripts.loadProducts);

</script>

A div has been added above the scripts node. A validator for the Select prompt has been added. When the user selects a value it will wait one second for further input, then pass the selected codes to the chart report. The chart report will return an HTMLFragment as a string, which is then passed to the div as it’s innerHTML.

Cognos Mashup Services is an incredibly versatile tool. The possibilities are limitless. I suspect, but haven’t tried, that it will allow you to embed objects in systems that do not allow iFrames. The only drawback is that it will only work in HTML. You can’t use this to merge objects from different models into a single PDF

IBM has a few guides on it. Start here.

Report XML:

<report xmlns="http://developer.cognos.com/schemas/report/9.0/" useStyleVersion="10" expressionLocale="en-us">
				<modelPath>/content/folder[@name='Samples']/folder[@name='Cubes']/package[@name='Sales and Marketing (cube)']/model[@name='2008-07-25T15:28:38.072Z']</modelPath>
				<drillBehavior modelBasedDrillThru="true"/>
				<layouts>
					<layout>
						<reportPages>
							<page name="Page1">
								<style>
									<defaultStyles>
										<defaultStyle refStyle="pg"/>
									</defaultStyles>
								</style>
								<pageBody>
									<style>
										<defaultStyles>
											<defaultStyle refStyle="pb"/>
										</defaultStyles>
									</style>
									<contents><table><style><defaultStyles><defaultStyle refStyle="tb"/></defaultStyles><CSS value="border-collapse:collapse"/></style><tableRows><tableRow><tableCells><tableCell><contents><textBox parameter="Parameter1" name="Text" required="false"/></contents></tableCell></tableCells></tableRow><tableRow><tableCells><tableCell><contents><selectValue parameter="Parameter2" multiSelect="true" selectValueUI="listBox" name="Select"/></contents></tableCell></tableCells></tableRow></tableRows></table><HTMLItem description="scripts">
			<dataSource>
				<staticValue>&lt;div id="chart"&gt;&lt;/div&gt;
&lt;script&gt;
/*
 * Fake Namespace and prompt getters.
 */ 	
var paulScripts = {}
var oCR = cognos.Report.getReport("_THIS_");

paulScripts.getSource = function()
{
    var targ;
    if (!e) var e = window.event;
    if(!e) return false;
    if (e.target) targ = e.target;
    else if (e.srcElement) targ = e.srcElement;
    if (targ.nodeType == 3) // defeat Safari bug
      targ = targ.parentNode;
    return targ;
}

paulScripts.getControl = function(promptName) 
{
  return oCR.prompt.getControlByName(promptName);
}


paulScripts.setPromptValue = function ( promptName, value ) {
var 
  newOption = ''
  , selElm = document.getElementById('PRMT_SV_'+paulScripts.getControl ( promptName )._id_);
//  selElm.options.length=0;
  for(i=0;i&lt;selElm.options.length;i++)
  {
    if(selElm.options[i].selected==true){
      for(x in value) {if(value[x].use == selElm.options[i].value) {value.splice(x,1); break;}}
    } else {selElm.remove(i);i--}
  }
  
  for(i=0;i&lt;value.length;i++)
  {
    newOption = document.createElement( 'option');
    newOption.value=value [i].use ;
    newOption.innerHTML = value[i].display ;
    newOption.dv = value [i].display ;

    selElm.appendChild(newOption );

  }
}

/*
* This creates the XMLHttpRequest object used to communicate with CMS.
* The initialization of the object depends on what browser is being used. This
* code is compatible with IE 5.5, 6, 7, 8 and all versions of Firefox and Chrome
*
* For more information on the XMLHttpRequest object, see http://www.w3.org/TR/XMLHttpRequest/
*/
try  {
var objXHR = new XMLHttpRequest();
} catch (e) {
try {
var objXHR = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
var objXHR = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
alert('XMLHttpRequest not supported'); }
}
}


paulScripts.getValues = function (searchString)
{
  var 
    searchString = searchString?searchString:'',
    url= '../cgi-bin/cognos.cgi/rds/reportData/searchPath/%2fcontent%2ffolder%5b%40name%3d%27CMS%27%5d%2freport%5b%40name%3d%27Source%27%5d?fmt=JSON&amp;async=off&amp;selection=List&amp;p_SearchString=' + searchString;
   objXHR.open("POST", url, false);
   objXHR.send(null);
   if (objXHR.status == 200)
   {
     dataCache = (eval('(' + objXHR.responseText + ')'));
     return paulScripts.parseJSON(dataCache);
   }
}

/*
 * Loop through tableData, extract the use and display fields, and dump them into 
 * a JS object.
 */
paulScripts.parseJSON = function(tableData)
{
  if(!tableData.filterResultSet.filterResult) return false;
  var 
      rows = tableData.filterResultSet.filterResult[0].reportElement[0].lst.group.row
    , JSONData = [];

  for (var i=0; i &lt; rows.length; i++)
  {
    JSONData.push ( {use :  rows[i].cell[1].item[0].txt.fmtVal, display : rows[i].cell[0].item[0].txt.fmtVal  });
  }
return JSONData;
}


/*
 * function loadOptions. Paul Mendelson - 2013-01-15
 * When text is entered into the text box, this will be triggered. It will wait for further input
 * before loading the select box with values.
 */
paulScripts.loadOptions= (function () {
  var timer;
  return function (){
    var name = this.getName()
    , search = this.getValue();
    clearTimeout(timer);
    timer = window.setTimeout(function() {  
      if(this.oldValue==search) {return true} else {this.oldValue=search}
      paulScripts.setPromptValue( 'Select', paulScripts.getValues(search));
    },1000);
    return true;
    };
})();

paulScripts.getControl('Text').setValidator(paulScripts.loadOptions);

/*
 * function loadProducts. Paul Mendelson - 2013-01-15
 * When a product is selected in the select, this will be triggered. It will wait for further input
 * before attempting to retrieve the chart.
 */
paulScripts.loadProducts= (function () {
  var timer;
  return function (){
    var name = this.getName()
    , products = this.getValues()
    , productsLabel='';
    clearTimeout(timer);
    timer = window.setTimeout(function() {  
    if(products.length===0) return true;
    for (i=0;i&lt;products.length;i++) {productsLabel+='&amp;p_Products='+products[i].use}
    paulScripts.getChart(productsLabel);
    },1000);
    return true;
    };
})();

paulScripts.getChart = function (products)
{
  var 
    url= '../cgi-bin/cognos.cgi/rds/reportData/searchPath/%2fcontent%2ffolder%5b%40name%3d%27CMS%27%5d%2freport%5b%40name%3d%27Chart%27%5d?fmt=HTMLFragment&amp;async=off&amp;selection=Chart' + products;
   objXHR.open("POST", url, false);
   objXHR.send(null);
   if (objXHR.status == 200)
   {
     document.getElementById('chart').innerHTML = objXHR.responseText ;
   }
}

paulScripts.getControl('Select').setValidator(paulScripts.loadProducts);

&lt;/script&gt;
</staticValue>
			</dataSource>
		</HTMLItem></contents>
								</pageBody>
							</page>
						</reportPages>
					</layout>
				</layouts>
			<XMLAttributes><XMLAttribute name="RS_CreateExtendedDataItems" value="true" output="no"/><XMLAttribute name="listSeparator" value="," output="no"/><XMLAttribute name="RS_modelModificationTime" value="2008-07-25T15:28:38.133Z" output="no"/></XMLAttributes><reportName>start2</reportName><reportVariables><reportVariable type="boolean" name="dontRender">
			<reportExpression>1=0</reportExpression>
			<variableValues>
				<variableValue value="1"/>
			</variableValues>
		</reportVariable></reportVariables></report>

Playing with MotioCI

$
0
0

A few posts back I gave a sneak peek of a new tool Motio was working on. Recently I was fortunate enough to get into the beta.

The installation, for the most part, is fairly straight forward. You download, decompress, modify a few config files to declare which version of Cognos you’re running, location of your JDK. The only major stumbling block I had was Motio requires some sort of authentication against Cognos. Since I was running Cognos anonymously on my laptop, it simply wouldn’t connect. To get around this, I set up OpenDJ and was able to progress.

Once MotioCI is installed and set up, it will create an instance. Each instance is a connection to a specific Cognos server. You can have as many instances as you have Cognos gateways. You can communicate between instances. So, for example, you can a report in Instance 1 to get specific results, and compare them to results in Instance 2. This is all part of the assertions, which I will get into later.

After you create an instance, you will be prompted to create a new project. Create the new project with a descriptive name and continue. The wizard will walk you through selecting which folders you want to test against which assertions. It comes with a suite of default assertions created by Motio and the Best Practices team. Once selected, it will generate test cases and run them.

Generate Test Cases

After the test cases run, you can see which reports have failures or warnings. You can see the results of the test cases, and the outputs of the reports themselves (if the test cases required the reports to run).
failing on assertions
The most interesting part of this tool is the Assertion Studio. This is where you can define the assertions.

Assertions allow you to test almost every aspect of any Cognos object. Do you have a corporate look and feel that every report must follow? Set up a template report and compare each and every report against that. Do you want to find every report that has a run time of more than 5 minutes? Do you want to automatically compare the output of a series of reports against specific SQL queries? Are you upgrading from 8.2 and need to find all instances of the old JS API? Do you want to test your dispatchers for certain settings and response times? The possibilities are endless.

When you design your own assertions you can specify whether the report needs to be executed or not. Executing the report allows you to check things like run time, or results match. Does the third row in the list match the first row of another report? Not executing allows you to use combinations of xpath and regex to parse the report (or model) xml. You might use that to find all reports that contain “formwarprequest” in HTML items.

There is a bit of a learning curve when it comes to MotioCI. It is definitely not something you’d give end users. It does seem to be an invaluable tool for administrators.

You can read more about it at the Motio Website.


Information On Demand 2013!

$
0
0

Well, it’s once again time to start planning for the IOD. It was worth every penny last year, and I am certain it will only be better this year. The IOD is a great place to connect with other IBM business partners and customers. Where else can you talk with people from Boeing and Costco while eating breakfast? One of my most memorable moments was waiting for the flight back, talking to a woman from the Miami-Dade police on the way they use Cognos and BI to catch criminals.

The sessions are broken out by tracks of interest. Each track can have multiple sessions at the same time. To my despair, it’s impossible to attend more than one simultaneously. In addition to the sessions, there are hands on workshops about what you can do with the latest technology. The people manning the workshops are knowledgeable and will very cheerfully talk about how their tools work.

The early bird special ends June 28 for customers (September 13 for business partners), so I recommend registering earlier rather than later. You may want to contact your Cognos business partner of choice to find out if they have a promo code this year. So far I am aware of only three companies that are offering early bird registration specials, The following list is informational, they are in alphabetical order and I am not endorsing any one company over another, more will be added as more companies come out with deals. Feel free to leave a comment if you know of any others that should be mentioned.

  1. BSP – G13BSPSOFT
  2. Ironside – G13IRONSDE
  3. Motio – G13MOTIO
  4. PerformanceG2 – G13PERFG2

I’m holding off registering until IBM let’s me know if my session proposal has been accepted (I hear that they received over 2500 sessions proposals this year, so chances are slim).

Obviously, I am planning on going this year, and I have an offer. If any company wants to sponsor my trip (partially or fully) I will very happily give their products a completely biased review and will randomly tell people how awesome my sponsors’ products are. (Or I could do work for them, or something. I’m open to suggestions.)

And remember – PerformanceG2 for all your training needs!



Book Review: IBM Cognos 10 Framework Manager, by Terry Curran

$
0
0

A well built metamodel is essential for any successful Cognos project. This book offers a solid guide to the best practices for building your model. The entire process of building a model, from joins to dimensional layers, is described in detail.

The chapters are ordered logically, first describing the tool and the various buttons and basic functionality, and then moving on to the advanced features. The book describes the entire process of building a model according to the most effective practices. Modellers will be well suited to keep this book open while building a model, even if it’s just to keep them on track.

In addition to describing the standard modeling features, the book also describes some advanced methods. Various multiuser modelling strategies are detailed; session parameters and parameter maps are explained with useful examples.

Terry does not go into every single trick, or explain how to fix every single bug in a given model (if he did, it would probably have to be several thousand pages long). However, he does lay down a set of guidelines which will provide for a portable and efficient model.

My only criticism is that it is sometimes too short. Some sections feel more like “how-to” guides that give step by step instructions without explaining the theory behind it.

Ultimately, both users who are unfamiliar with Framework, and veterans with years of experience will find it useful to read through and to keep it as a reference.

The book may be purchased from Amazon at http://www.amazon.com/IBM-Cognos-Framework-Manager-ebook/dp/B00CXPRD4G or directly from Packt http://www.packtpub.com/ibm-cognos-10-framework-manager/book


Automatically looping through charts in Active Reports

$
0
0

This is the first of a series of articles on extending the usability of Active Reports.

Active Reports makes a wonderful addition to the Cognos suite. Locally processed interactivity (my Brio alter ego is shouting, “so what!”; just ignore him) means complex, though predefined, data exploration scenarios can be offered to ever more demanding users. Just by clicking on a row in a list, the entire report can be refreshed instantly. Standard web-based reports are simply incapable of that behavior.

Periodically I get requests for things Active Reports can’t do. For some reason, saying that something is not possible completely renders all of the other features moot. One such request was: “I want an animated chart that will loop through all of the months in the database”. The user wanted a chart that would show 12 months at a time, incrementing one month per second, looping back to the start once it hits the last month.

As a mockup, I built an example chart in a data deck that shows that behavior.
Chart in Data Deck

I stuck a Data Iterator under it. By pressing the next button you can make it look like it’s animated. Sadly, this wasn’t enough. The needed it to be automatic.

Now we can start playing with JavaScript. This presents a few interesting problems, some of which I’m still trying to solve.

Problem 1. Cognos rewrites IDs.
To demonstrate, I create a text item and an HTML item – a div with text inside it:
HTML Item In RS

But when I run it, and examine the HTML:
HTML Item Div ID Changed

That’s interesting! The ID of the div changed from myDiv to v9! This means that any JavaScript written will have to not use getElementById(). There is no guarantee that the div will be v9 tomorrow. Especially if there are data changes in the report. That’s fine though. I could do a getElementsByTagName(‘div’) and loop through them until I find one that has the correct name, or some other attribute I set. It’ll be slow, but better than nothing.

Let’s see what happens if I use JavaScript to count the number of divs. It should be a simple:

<script>alert(document.getElementsByTagName('div').length)</script>

I just add it to the existing HTML item:
script tag

And yet when I run it, I’m not getting the alert. The JS is sound, and if I copy it to the script console in IE Dev Toolbar I get back the expected value. So what is going on?

Unfortunately this is still one of the things that I’m still trying to figure out. My guess is that every element (list, chart, HTML item) is stored in an object and loaded into the page after the page has been loaded as needed. When running a report with a data deck, we can see that the individual cards aren’t loaded until another control references them. This allows the page to be significantly smaller on the first load, and far more responsive than if everything was rendered. Of course, this is just a guess and I could be completely wrong (but that hardly ever happens).

Effectively this means that onload events are out. And predefined functions are out. Everything will have to be inline events on elements hand written with HTML items.

Getting back to the original problems, the user said that he would be okay with a start/stop button. He presses it to start the “animation”, and clicks it again to stop it whenever he wants. This means we can define the function in the onclick event. And once again I’ve turned to my good friend Dan Freundel for help. And once again he turned 20 lines of spaghetti code into 5 lines of pure JS genius.

Since we can’t loop through divs that don’t yet exist, we’ll have to find a way to recreate the action of clicking on the next/previous buttons on the iterator. One way is to set the parameter directly. Any control that uses that param will be effected. Unfortunately, that way requires referencing minified JS functions. If we do it this way, any upgrade or fix pack will kill the report. Instead, I opted to literally click on the next/prev buttons in the loop.

First, the JS:

<button onclick="
if(!document.runAnim)
{

runAnim = function(buttonElement)
{
var intervalTracker, doStuff = function() {
var iter = buttonElement.previousSibling
, ibuttons = iter.getElementsByTagName(‘button’);
if(ibuttons[2].getAttribute(‘disabled’)) {ibuttons[0].setAttribute(‘disabled’,false);ibuttons[0].click();} else {ibuttons[2].click()}
}
buttonElement.onclick = function()
{
if(intervalTracker)
{
clearInterval(intervalTracker);
intervalTracker = false;
}
else
{
intervalTracker = setInterval(doStuff,1000);
}
};
intervalTracker = setInterval(doStuff,1000);

};}
runAnim(this);
this.value=’stop’;
" value="Start animation">Start</button>

First this defines the function. Since we can’t use inline HTML functions will have to be defined like this. On the first run, if the runAnim function doesn’t exist, it will create it. Next it will call that function in the scope of the button. That means the setInterval timer will exist only for that button, and it will not interfere with any other timers on the page. As mentioned before, because the IDs get rewritten, it is impossible to easily reference an element on the page. For this, I simply placed the button directly after the iterator. Next, when an iterator is first loaded, it has no value set and you can’t click any of the buttons. But if, somehow, the disabled=true was removed from the First button, clicking it would select the first item in the list.

So we have the script. The timer function is created the first time the button is clicked, that function and all associated variables are invoked in the scope of the button and it immediately starts working.
Loopy Charts

The list on the right acts both as a highlighter for the current row, and as a control to select which month you want to see in the chart.

10.2 XML (expand and double-click to select everything)

<report xmlns="http://developer.cognos.com/schemas/report/9.0/" useStyleVersion="10" expressionLocale="en-us" application="true">
				<modelPath>/content/folder[@name='Samples']/folder[@name='Cubes']/package[@name='Sales and Marketing (cube)']/model[@name='2008-07-25T15:28:38.072Z']</modelPath>
				<drillBehavior modelBasedDrillThru="true"/>
				<layouts>
					<layout>
						<reportPages>
							<page name="Page1">
								<style>
									<defaultStyles>
										<defaultStyle refStyle="pg"/>
									</defaultStyles>
								</style>
								<pageBody>
									<style>
										<defaultStyles>
											<defaultStyle refStyle="pb"/>
										</defaultStyles>
									</style>
									<contents>
						<table><style><defaultStyles><defaultStyle refStyle="tb"/></defaultStyles><CSS value="border-collapse:collapse"/></style><tableRows><tableRow><tableCells><tableCell><contents><appDataDeck name="Data Deck1" refQuery="Query2" width="0px" height="0px">
			<appDataCard>
				<contents><v2_combinationChart maxHotspots="10000" refQuery="Query1" name="Scatter Chart1">
																							<v2_combinationTypeTooltips/>
																							<v2_commonAxis>
																								<v2_ordinalAxis>
																									<v2_axisTitle refQuery="Query1">
																										<v2_chartTextContents>
																											<v2_automaticText/>
																										</v2_chartTextContents>
																										<style>
																											<defaultStyles>
																												<defaultStyle refStyle="at"/>
																											</defaultStyles>
																										</style>
																									</v2_axisTitle>
																									<v2_axisLine lineWeight="0" majorTickMarkLocation="none" minorTickMarkLocation="none"/>
																									<v2_axisLabels>
																										<style>
																											<defaultStyles>
																												<defaultStyle refStyle="al"/>
																											</defaultStyles>
																										</style>
																									</v2_axisLabels>
																								</v2_ordinalAxis>
																								<chartNodes><chartNode><chartNodeMembers><chartNodeMember refDataItem="Month"><chartContents><chartTextItem><dataSource><memberCaption/></dataSource></chartTextItem></chartContents></chartNodeMember></chartNodeMembers></chartNode></chartNodes></v2_commonAxis>
																							<v2_topLeftAxis>
																								<v2_combinationChartTypes>
																									<v2_line>
																										<v2_linePalette markerSize="5pt">
																											<v2_linePaletteEntries><v2_linePaletteEntry>
																							<v2_lineFill>
																								<v2_linearGradient gradientAngle="0">
																									<v2_gradientColor colorPosition="0" gradientColor="#8599D3"/>
																									<v2_gradientColor colorPosition="100" gradientColor="#5876AE"/>
																								</v2_linearGradient>
																							</v2_lineFill>
																							<v2_pointPaletteEntry markerShape="circle">
																								<v2_fillEffect>
																									<v2_linearGradient gradientAngle="0">
																										<v2_gradientColor colorPosition="0" gradientColor="#8599D3"/>
																										<v2_gradientColor colorPosition="100" gradientColor="#5876AE"/>
																									</v2_linearGradient>
																								</v2_fillEffect>
																							</v2_pointPaletteEntry>
																						</v2_linePaletteEntry>
																					</v2_linePaletteEntries></v2_linePalette>
																										<!-- v2_linePaletteRef ref="gDefaultLine"/ -->
																									<chartNodes><chartNode><chartNodeMembers><chartNodeMember refDataItem="Revenue"><chartContents><chartTextItem><dataSource><memberCaption/></dataSource></chartTextItem></chartContents></chartNodeMember></chartNodeMembers></chartNode></chartNodes></v2_line>
																								</v2_combinationChartTypes>
																								<v2_axis>
																									<v2_axisTitle refQuery="Query1">
																										<v2_chartTextContents>
																											<v2_automaticText/>
																										</v2_chartTextContents>
																										<style>
																											<defaultStyles>
																												<defaultStyle refStyle="at"/>
																											</defaultStyles>
																										</style>
																									</v2_axisTitle>
																									<v2_axisLine lineWeight="0"/>
																									<v2_axisRange>
																										<v2_automaticRange/>
																									</v2_axisRange>
																									<v2_axisLabels>
																										<style>
																											<defaultStyles>
																												<defaultStyle refStyle="al"/>
																											</defaultStyles>
																										</style>
																									</v2_axisLabels>
																									<v2_majorGridlines lineWeight="0" lineColor="#CCCCCC"/>
																									<v2_majorBackgroundColors>
																										<v2_firstBackgroundColor color="#D2D2D2" transparency="50"/>
																										<v2_secondBackgroundColor color="#E2E2E2" transparency="50"/>
																									</v2_majorBackgroundColors>
																								</v2_axis>
																							</v2_topLeftAxis>
																							<style>
																								<CSS value="width:600px;height:400px"/><defaultStyles><defaultStyle refStyle="ch"/></defaultStyles></style>
																							
																							<v2_chartTitle refQuery="Query1"><style><defaultStyles><defaultStyle refStyle="ct"/></defaultStyles></style><v2_chartTextItems><v2_chartTextItem><dataSource><staticValue>Revenue</staticValue></dataSource></v2_chartTextItem></v2_chartTextItems></v2_chartTitle><masterDetailLinks><masterDetailLink><masterContext><dataItemContext refDataItem="Month"/></masterContext><detailContext><parameterContext parameter="Month"/></detailContext></masterDetailLink></masterDetailLinks><v2_bottomLeftAxis><v2_axis>
													<v2_axisTitle refQuery="Query1">
														<v2_chartTextContents>
															<v2_automaticText/>
														</v2_chartTextContents>
														<style>
															<defaultStyles>
																<defaultStyle refStyle="at"/>
															</defaultStyles>
														</style>
													</v2_axisTitle>
													<v2_axisLine lineWeight="0"/>
													<v2_axisRange>
														<v2_automaticRange/>
													</v2_axisRange>
													<v2_axisLabels>
														<style>
															<defaultStyles>
																<defaultStyle refStyle="al"/>
															</defaultStyles>
														</style>
													</v2_axisLabels>
													<v2_majorGridlines lineWeight="0" lineColor="#CCCCCC"/>
													<v2_majorBackgroundColors>
														<v2_firstBackgroundColor color="#D2D2D2" transparency="50"/>
														<v2_secondBackgroundColor color="#E2E2E2" transparency="50"/>
													</v2_majorBackgroundColors>
												</v2_axis>
												<v2_combinationChartTypes><v2_line seriesType="absolute"><chartNodes><chartNode><chartNodeMembers><chartNodeMember refDataItem="Quantity"><chartContents><chartTextItem><dataSource><memberCaption/></dataSource></chartTextItem></chartContents></chartNodeMember></chartNodeMembers></chartNode></chartNodes><v2_linePalette><v2_linePaletteEntries><v2_linePaletteEntry>
																							<v2_lineFill>
																								<v2_linearGradient gradientAngle="0">
																									<v2_gradientColor colorPosition="0" gradientColor="#8599D3"/>
																									<v2_gradientColor colorPosition="100" gradientColor="#5876AE"/>
																								</v2_linearGradient>
																							</v2_lineFill>
																							<v2_pointPaletteEntry markerShape="circle">
																								<v2_fillEffect>
																									<v2_linearGradient gradientAngle="0">
																										<v2_gradientColor colorPosition="0" gradientColor="#8599D3"/>
																										<v2_gradientColor colorPosition="100" gradientColor="#5876AE"/>
																									</v2_linearGradient>
																								</v2_fillEffect>
																							</v2_pointPaletteEntry>
																						</v2_linePaletteEntry>
																					</v2_linePaletteEntries></v2_linePalette></v2_line></v2_combinationChartTypes></v2_bottomLeftAxis></v2_combinationChart></contents>
			</appDataCard>
		<appCardDefinition><appCardValues><appCardValue refDataItem="Month"/></appCardValues></appCardDefinition><appContainerSelect><appCondition><appConditionDataItemComparison refDataItem="Month" refAppVariable="Month" operator="in"/></appCondition></appContainerSelect></appDataDeck><HTMLItem>
			<dataSource>
				<staticValue>&lt;div style="display:none"&gt;</staticValue>
			</dataSource>
		</HTMLItem><appDataIterator name="Data Iterator1" refQuery="Query2">
			<appIteratorUI>
				<appIteratorPrevious>
					<appIteratorButton/>
				</appIteratorPrevious>
				<appIteratorLabelArea>
					
				<appIteratorDropDownList width="200px"/></appIteratorLabelArea>
				<appIteratorNext>
					<appIteratorButton/>
				</appIteratorNext>
			<appIteratorFirst><appIteratorButton/></appIteratorFirst></appIteratorUI>
		<appIteratorDefinition><appIteratorValues><appIteratorValue refDataItem="Month"/></appIteratorValues><appIteratorLabel refDataItem="Month"/></appIteratorDefinition><appOnSelectSetVariableValues><appSetVariableValueToDataItemValue refDataItem="Month" refAppVariable="Month"/></appOnSelectSetVariableValues><appContainerSelect><appCondition><appConditionDataItemComparison refDataItem="Month" refAppVariable="Month" operator="in"/></appCondition></appContainerSelect></appDataIterator><HTMLItem>
			<dataSource>
				<staticValue>&lt;/div&gt;&lt;button onclick="
  if(!document.runAnim) 
  {

runAnim = function(buttonElement)
{
 var intervalTracker, doStuff = function() { 
  var iter = buttonElement.previousSibling
  , ibuttons = iter.getElementsByTagName('button');
  if(ibuttons[2].getAttribute('disabled')) {ibuttons[0].setAttribute('disabled',false);ibuttons[0].click();} else {ibuttons[2].click()}
}
 buttonElement.onclick = function()
 {
    if(intervalTracker)
   {
     clearInterval(intervalTracker);
     intervalTracker = false;
   }
   else
   {
     intervalTracker = setInterval(doStuff,1000);
   }
 };
 intervalTracker = setInterval(doStuff,1000);

};}
runAnim(this);
this.value='stop';
" value="Start animation"&gt;Start&lt;/button&gt;</staticValue>
			</dataSource>
		</HTMLItem></contents><style><CSS value="vertical-align:top"/></style></tableCell><tableCell><contents><block>
			<contents><list horizontalPagination="true" name="List1" refQuery="Query1">
			
			
			
			<noDataHandler>
				<contents>
					<block>
						<contents>
							<textItem>
								<dataSource>
									<staticValue>No Data Available</staticValue>
								</dataSource>
								<style>
									<CSS value="padding:10px 18px;"/>
								</style>
							</textItem>
						</contents>
					</block>
				</contents>
			</noDataHandler>
			<style>
				<CSS value="border-collapse:collapse"/>
				<defaultStyles>
					<defaultStyle refStyle="ls"/>
				</defaultStyles>
			</style>
		<listColumns><listColumn><listColumnTitle><style><defaultStyles><defaultStyle refStyle="lt"/></defaultStyles></style><contents><textItem><dataSource><dataItemLabel refDataItem="Month1"/></dataSource></textItem></contents></listColumnTitle><listColumnBody><style><defaultStyles><defaultStyle refStyle="lc"/></defaultStyles></style><contents><textItem><dataSource><dataItemValue refDataItem="Month1"/></dataSource></textItem></contents></listColumnBody></listColumn><listColumn><listColumnTitle><style><defaultStyles><defaultStyle refStyle="lt"/></defaultStyles></style><contents><textItem><dataSource><dataItemLabel refDataItem="Revenue"/></dataSource></textItem></contents></listColumnTitle><listColumnBody><style><defaultStyles><defaultStyle refStyle="lm"/></defaultStyles></style><contents><textItem><dataSource><dataItemValue refDataItem="Revenue"/></dataSource></textItem></contents></listColumnBody></listColumn><listColumn><listColumnTitle><style><defaultStyles><defaultStyle refStyle="lt"/></defaultStyles></style><contents><textItem><dataSource><dataItemLabel refDataItem="Quantity"/></dataSource></textItem></contents></listColumnTitle><listColumnBody><style><defaultStyles><defaultStyle refStyle="lm"/></defaultStyles></style><contents><textItem><dataSource><dataItemValue refDataItem="Quantity"/></dataSource></textItem></contents></listColumnBody></listColumn></listColumns><appContainerSelect><appCondition><appConditionDataItemComparison refAppVariable="Month" refDataItem="Month1" operator="in" dropIfValueIsEmpty="true"/></appCondition></appContainerSelect><appOnSelectSetVariableValues><appSetVariableValueToDataItemValue refAppVariable="Month" refDataItem="Month1"/></appOnSelectSetVariableValues></list></contents>
		<style><CSS value="height:550px;overflow:auto"/></style></block></contents></tableCell></tableCells></tableRow></tableRows></table></contents>
								</pageBody>
							</page>
						</reportPages>
					</layout>
				</layouts>
			<XMLAttributes><XMLAttribute name="RS_CreateExtendedDataItems" value="true" output="no"/><XMLAttribute name="listSeparator" value="," output="no"/><XMLAttribute name="RS_modelModificationTime" value="2008-07-25T15:28:38.133Z" output="no"/></XMLAttributes><queries><query name="Query1"><source><model/></source><selection><dataItem name="Revenue"><expression>[sales_and_marketing].[Measures].[Revenue]</expression></dataItem><dataItem name="Product line" aggregate="none" rollupAggregate="none"><expression>[sales_and_marketing].[Products].[Products].[Product line]</expression></dataItem><dataItem name="Month" aggregate="none" rollupAggregate="none"><expression>lastPeriods(12,#prompt('Month','mun','[sales_and_marketing].[Time].[Time].[Time]-&gt;:[PC].[@MEMBER].[Time]')#)</expression></dataItem><dataItem name="Quantity"><expression>[sales_and_marketing].[Measures].[Quantity]</expression></dataItem><dataItemLevelSet name="Month1"><dmLevel><LUN>[sales_and_marketing].[Time].[Time].[Month]</LUN><itemCaption>Month</itemCaption></dmLevel><dmDimension><DUN>[sales_and_marketing].[Time]</DUN><itemCaption>Time</itemCaption></dmDimension><dmHierarchy><HUN>[sales_and_marketing].[Time].[Time]</HUN><itemCaption>Time</itemCaption></dmHierarchy></dataItemLevelSet></selection></query><query name="Query2"><source><model/></source><selection><dataItem name="Month" aggregate="none" rollupAggregate="none"><expression>[sales_and_marketing].[Time].[Time].[Month]</expression></dataItem></selection><detailFilters><detailFilter><filterExpression>[sales_and_marketing].[Measures].[Revenue]&gt;0</filterExpression></detailFilter></detailFilters></query></queries><appProperties><appVariables><appVariable name="Month"/></appVariables></appProperties><reportName>AR Start Animation</reportName></report>

Viewing all 37 articles
Browse latest View live


Latest Images