Quantcast
Channel: SAP BusinessObjects Design Studio
Viewing all articles
Browse latest Browse all 662

Design Studio 1.6 SDK - Data Iterator - Read your data row by row finally!

$
0
0

Change Log

 

  • 02/08/2015 - Blog posted

 

Planned Enhancements

 

  • Up for discussion!
  • Iterate via columns?

 

Description

Use Case:

 

Design Studio, being a multi-dimensional client, likes to decompose data into 'tuples' which basically defines each measure as a cell value that is represented by indexed tuples that map back to a dimension member.  There's not really anything "wrong" with this, except that at least in my opinion, this is not the easiest way for me to work with the data in a 2D tabular form.  There have been many SCN threads where people have struggled with basically looping through (aka iterating over) the rows in order to apply some script logic.  Even in my other extensions, the first thing I do is "flatten" the dataset into a 2-dimensional (or table-like) form to work with.  I start thinking about why couldn't I just open this convenience up to BIAL scripting?  This is what I have written this component called 'Data Iterator'.

 

Welcome to the Data Iterator

The Data Iterator is a Technical Component available to add to your Design Studio application.  It is very simple to configure, in that you simply assign it a Data Source, and you are ready to use it in your script.

 

Step 1: Add a Data Iterator Component

di1.png


Step 2: Assign it a Data Source

 

di2.png

Optional Event to note is 'On Data Change' - This essentially fires an event any time the Data Source changes (via filtering, navigation, etc.)

 

That's it.  You're done configuring it.  But what does it do?  It's time to see what we can do with this at script-time!

 

Script-Time Methods

Before explaining the methods, I need to explain conceptually what it is doing with the data.  Let's start with a typical Data Source's result set:

 

di3.png

As mentioned in the use case, the first thing I do is 'flatten' the data internally into an easier to use form.  This means I have some of my own conventions going forward:

 

 

  • getDimensions - Returns a list of dimensions currently in Rows.  This is a simple call that you can do today with DS_1.getDimensions(Axis.ROWS); but there are some subtle differences in some cases.  See example below:

    di4.png
  • getMeasures - Returns a list of items in columns.  Most of the time this is usually Key Figures/Measure selection members, however it will go ahead and flatten in cases where you add a dimension in your Columns as well.  This is where you would begin to struggle using standard BIAL calls to pull this off:

    di5.png
  • getRows(optional offset, optional maxRows) - Returns rows optionally from a given row offset, and optionally a maximum amount of rows.  This is where things can get interesting!  Example:

    di6.png
    So, let's consider the rows here... we have a rows.forEach loop, which returns an element (named row in my example and an index that just comes with forEach, starting at 0...)  The row element itself has some BIAL methods we must explain:

    di7.png

    • getDimensionValueKey(dimensionKey) - Returns the member key for a given dimension key.
    • getDimensionValueText(dimensionKey) - Returns the member text for a given dimension key.

      Example:
      (In this case, key and text was the same.)
      di8.png
    • getMeasureValue(measureKey) - Returns value in float format for the current row's column for the passed measureKey.
    • getMeasureFormattedValue(measureKey) - Returns formatted value in String format for the current row's column for the passed measureKey.

      Example:

      di9.png
      A few things to note above.  On the 3rd line, you see that we can dynamically determine in this case, the measure "key" in the first column position by saying getMeasures().atIndex(index) where 'index' indicates the column you want (beginning at 0).  This is also available for getDimensions().atIndex(index).  This means that you don't HAVE to hard-code a key (like we are doing with 0D_MATERIAL) and have it adapt to whatever the contents are in the result set.  Also note that I switched the output from 'Formatted Text' component to my 'Rapid Prototyping' component.  This is because 'Formatted Text' was stripping out certain HTML markup and I wanted to show some conditional formatting quickly.

 

 

Putting it all together:

After seeing these basic, fundamental methods, what else can you do?  You can go as wild as you want!  If you are accustomed to writing in languages such as JSP, BSP, ASP, etc, the use cases are endless when using Data Iterator along with Rapid Prototyping, as an example.  Below are two proof-of concept examples.  With some additional CSS-clean up and more time, you could come up with some super-easy to create visuals!

 

Simple Table showing only first 10 rows (Could enhance to paginate with buttons etc):

// Get flattened rows from Data Iterator
var rows = DATAITERATOR_1.getRows(0,10);
// Get dimensions (Rows) and Measures (Cols)
var dimensions = DATAITERATOR_1.getDimensions();
var measures = DATAITERATOR_1.getMeasures();
// Start a simple HTML table
var html = "<div style='height:400px;overflow:scroll'><table class='example'><tr>";
// Draw headers for dimensions and measures
dimensions.forEach(function(element, index) {  html = html + "<th>" + element.text + "</th>";
});
measures.forEach(function(element, index) {  html = html + "<th>" + element.text + "</th>";
});
html = html + "</tr>";
// Loop through the rows...
rows.forEach(function(row, index) {  // Draw a new row  html = html + "<tr>";  // Write out the dimension texts  dimensions.forEach(function(member, index) {  var dimText = row.getDimensionValueText(member.key);  html = html + "<td class='dimension'>" + dimText + "</td>";  });  // Row striping example  var stripe = "even";  if(index/2 == Math.floor(index/2)) {  stripe = "odd";  }  // Write out the measure formatted values  measures.forEach(function(measure, index) {  var measureVal = row.getMeasureValue(measure.key);  var measureText = row.getMeasureFormattedValue(measure.key);  html = html + "<td class='measure " + stripe +" '>" + measureText + "</td>";  });  html = html + "</tr>";
});
html = html + "</table></div>";
RAPIDPROTOTYPE_1.setHTML(html);

 

Simple Table/Micro Chart:

// Get flattened rows from Data Iterator
var rows = DATAITERATOR_1.getRows(0,250);
// Get dimensions (Rows) and Measures (Cols)
var dimensions = DATAITERATOR_1.getDimensions();
var measures = DATAITERATOR_1.getMeasures();
var firstMeasureKey = measures.atIndex(0).key;
// Figure out Max in BIAL for rendering chart bars
var max = 0.0;
rows.forEach(function(row, index) {  var v = row.getMeasureValue(firstMeasureKey);  if(v>max){ max = v; }
});
// Start a simple HTML table
var html = "<div style='height:400px;overflow:scroll'><table class='chart'><tr>";
// Draw headers for dimensions and measures
dimensions.forEach(function(element, index) {  html = html + "<th>" + element.text + "</th>";
});
// Draw first measure header
html = html + "<th>" + measures.atIndex(0).text+"</th>";
var w = 300;
html = html + "</tr>";
// Loop through the rows...
rows.forEach(function(row, index) {  html = html + "<tr>";  // Draw a new row  dimensions.forEach(function(member, index) {  var dimText = row.getDimensionValueText(member.key);  html = html + "<td class='dimension'>" + dimText + "</td>";  });  var measureVal = row.getMeasureValue(firstMeasureKey);  var measureFVal = row.getMeasureFormattedValue(firstMeasureKey);  var barWidth = w * (measureVal / max);  html = html + "<td style = 'width:" + (w+200) +";'>";  html = html + "<div style = 'display:inline-block;width:" + barWidth + "px;background-color:#006699;'> </div>" + measureFVal;  html = html + "</td></tr>";
});
html = html + "</table></div>";
RAPIDPROTOTYPE_2.setHTML(html);

 

Runtime Example of both:

 

di10.png

Super Goofy Scorecard Example:

 

// Get flattened rows from Data Iterator
var rows = DATAITERATOR_1.getRows(0,10);
// Get dimensions (Rows) and Measures (Cols)
var dimensions = DATAITERATOR_1.getDimensions();
var measures = DATAITERATOR_1.getMeasures();
// Start a simple HTML table
var html = "<table class='example scorecard'><tr>";
// Draw headers for dimensions and measures
dimensions.forEach(function(element, index) {  if(element.text != "Key Figures"){  html = html + "<th>" + element.text + "</th>";  }
});
measures.forEach(function(element, index) {  html = html + "<th>" + element.text + "</th>";
});
html = html + "</tr>";
// Loop through the rows...
rows.forEach(function(row, index) {  // Draw a new row  html = html + "<tr>";  var priorValue = DATAITERATOR_1.makeNull();  // Write out the dimension texts  dimensions.forEach(function(member, index) {  var dimText = row.getDimensionValueText(member.key);  if(member.text!="Key Figures"){  html = html + "<td class='dimension'>" + dimText + "</td>";  }  });  // Row striping example  var stripe = "even";  if(index/2 == Math.floor(index/2)) {  stripe = "odd";  }  // Write out the measure formatted values  measures.forEach(function(measure, index) {  var trend = "";  var measureVal = row.getMeasureValue(measure.key);  var measureText = row.getMeasureFormattedValue(measure.key);  if(index>0 && !DATAITERATOR_1.isNull(priorValue) && !DATAITERATOR_1.isNull(measureVal)){  var delta = Math.round(measureVal / priorValue * 100) + "%";  var icon = "";  if(priorValue > measureVal){ // Down  trend = "downward";  }else{ // Up  trend = "upward";  }  measureText = "<div class='icon'></div><br />(" + delta + ")</span><br />" + measureText;  }  if(!DATAITERATOR_1.isNull(measureVal)){  priorValue = measureVal;  }else{  priorValue = DATAITERATOR_1.makeNull();  measureText = " - ";  }  html = html + "<td class='measure " + stripe + " " + trend + "'>" + measureText + "</td>";  });  html = html + "</tr>";
});
html = html + "</table>";
RAPIDPROTOTYPE_3.setHTML(html);

 

di11.png

 

Oh, and PS here's the CSS for my examples:

.example {  border-collapse : collapse;
}
.example .dimension {  background-color : #006699;  color : #FFFFFF;
}
.example th {  background-color : #006699;  color : #FFFFFF;  font-weight : bold;
}
.example.scorecard th {  /*white-space: nowrap;*/  padding : 20px;  background-color : #0099CC;  color : #FFFFFF;  font-weight : bold;  font-size : 20pt;
}
.example .measure {  text-align : center;
}
.example .icon {  display : inline-block;  width : 48px;  height : 48px;
}
.example .downward .icon{  background-image : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH2QcVCAsthDpI/AAABQ9JREFUaIHtV0tsG0UY/ma9XnudJq1Tp2ppRRMeF6jopVKFEIWq9NACogipQE9wQEgc4AYmAqkXWpUb0CvigopED1CqJgdSFBCoIFBJk5YWEhKnjR/JOonf+5wZDrEbs971ay0liHzSL8+u5//n++af+WcW2MAGNrCBDfyfQbw49x89zTtBQjDIlumRaLYdX9Hr4CdeeBIoy+AAOOcrjxxgnCOTLa6+A8D5Sp9K+/ufxnQv43sWAA6M/Z12/otzKIs5V9eH+rd6Hl7wGoDx9lcR9+BbgWcBXjh4EV/BGmfA6+gdEOAF62IJMdY+CQ+ud7G2e6ADCtZ0D3QCHciAlyW0LjLgwXc9LCEvWB9VyMs54HVwrPUe6MAScr3M9R89tR8gPwMACEqOnbi3Mlq+mQYgEaX/6dOmQ/wQAAggj0wPRSecYtT9Hhh45tRrASlw9sBjeyVBEEAZW70Ws5Vfvyji9kLehWD92+i2cAiU0ruTwCsZIYBlUsRisxQMJ2aGol+6xfDVE5D56/LV7vsOkXxe3b9je8SfzurIFk0UVAtFbcVyJaNuFkqq+3Vf1S1oBr1ruslAGYcoEMzdiReYyd6eGY5+Vo9jXQEAkJkc+SGw6/E9Jd144N6dEX9JM0EZL6e/8RKqJ4DbjAgEoaCIZDxZ0E3zXOxS9P1G/JraxHJIfSWRUMZmYwlja48MQjx9iToTIQRdQT8WkoquqfqVWHDgjab8mul04/xJg2j8yOTMXFxRFlm4O+CNrQ2EAHJARHYpYxaKxSldUI/h/HHajG/TZXR6JJoVKTlw42ZsuVgs8c1dUvuMbQhKIkrFAltaWlJESg8mLp50rnoOaOkcmBp+Z44wdnB84s8Csyx0Bb1/Ugf8PliGgfS8UoLADk0NDyqt+Ld8kE0PRScsgx67Nnar6PetEGgXkiiAMIpUMqEyxp+LXRy81WqMtkbPTl2e6bn/YGpxKXd4184+P+Xup6pbFfIJBKIgIBlPqJzS12PDgxfa4dL29C1PXv5908ATvYWCunfH9ohkUuZYUp0ECIQg4BegJJMFy6JnZobe/aRdHu3nH0BmcuTb0O7v9pkmHeiLhEXTYjV97AIIAQKSiMWFhaKh69/MXIq+5YWDx8sc4bKsvTQ/nx5PJhaMUEBEoyNCEn3IpNO6pqoTsqy96m381jNAyj4VE5U/Rokk9X1VIvKLXaFQd1dIFiy6monqDEiigFI+T3PZbLw0O/5UbPSsXo4jlK3lE7IZAZXgYtl8VW0RgL+4cJ0GN22/mDcDL/ds6ZYDkp/Q8qauCBB9Akxd40uLi8ta4vrh+fFzy1XkfbZ2RQxBg8+GegKqA4u2do2QQmpCDfbcM5ozfMfDvWG/KPrAGEdJ1eETCLhlQZlPqVrq5vOpq5/P2khXW4V4dRtuQtwE2INWi3GyFRHJa8ty5MHJvIbDka29fggEqmqAcIp0KqHpmdtvJn/59FfUzrKTOaFGhJOAivp65jRzAgBf/s5vsdC2PYWiRh/t7QuLqqohnUpqRkb5MP7jx1/XIedIsBHcMlBvZhoSyM1euSbv3BfWVOvhfC5n6MXsF3OjZz4CwMrGq9rM9r6R1RB1g9PadHu2r1sAsjBw5L0LDFyYHf7gWUCtlKYKeY5/C7GLchJZg2bKlhNZt81WnSESiuyWeGCzT42Pq1idPfuM2sU4iXJFq3XXjXANeRd/NxFOgpom1Ak0Q74Cu4gN/KfxDxmvmYQNdjkRAAAAAElFTkSuQmCC)
}
.example .upward .icon{  background-image : url(data:image/png;base64,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)
}
.example .downward {  color : #FF0000;
}
.example .upward {  color : #009966;
}
.example .measure.even {  background-color : #FFFFFF;
}
.example .measure.odd {  background-color : #DFDFDF;
}

 

 

 

What you have seen is available for download in the usual spot (details here: SCN Design Studio 1.6 SDK Components (ver 3.0)). 



Questions/Comments/Feedback always welcomed!


Viewing all articles
Browse latest Browse all 662

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>