{"id":1672,"date":"2013-07-23T00:00:00","date_gmt":"2013-07-23T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/creating-a-business-intelligence-dashboard-with-r-and-asp-net-mvc-part-2\/"},"modified":"2021-05-17T18:34:55","modified_gmt":"2021-05-17T18:34:55","slug":"creating-a-business-intelligence-dashboard-with-r-and-asp-net-mvc-part-2","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/creating-a-business-intelligence-dashboard-with-r-and-asp-net-mvc-part-2\/","title":{"rendered":"Creating a Business Intelligence Dashboard with R and ASP.NET MVC: Part 2"},"content":{"rendered":"<div id=\"pretty\">\n<p class=\"start\">In my <a href=\"https:\/\/www.simple-talk.com\/dotnet\/asp.net\/creating-a-business-intelligence-dashboard-with-r-and-asp.net-mvc-part-1\/\"> previous article<\/a>, I began a demonstration of how to build a Business  Intelligence (BI) application using R, Visual Studio 2012 and Twitter Bootstrap. The aim of this series is to leave you  at the end of each article with a working system, adding functionality in each article as we go. In that first article,  R was used only for data extraction and transformation supported by its two libraries, RODBC and R2HTML. With this  article, part 2, we will add two more R libraries into the mix. These are <a href=\"http:\/\/cran.r-project.org\/web\/packages\/ggplot2\/index.html\">GGPLOT2<\/a>  and <a href=\"http:\/\/cran.r-project.org\/web\/packages\/plyr\/index.html\">PLYR<\/a>. The  first one is &#8220;&#8230;<code>a plotting system for R, based on the grammar of graphics<\/code>&#8220;,  whereas the second one is &#8220;&#8230; a set of tools that solves a common set of problems&#8221;. Those &#8220;common set of problems&#8221;  include data transformation; and so PLYR significantly reduces not only the R coding effort, but also implicitly  simplifies the development of T-SQL views or stored procedures.<\/p>\n<p>Part 1 of this article showed the step-by-step process of &#160;developing the two reports in the top row of the dashboard, as you can see in the  image below.<\/p>\n<p class=\"illustration\">  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1840-clip_image001-630x441.png\" height=\"441\" width=\"630\" alt=\"1840-clip_image001-630x441.png\" \/><\/p>\n<p class=\"caption\">Figure  1.  The dashboard landing page.<\/p>\n<p>These reports contain a set of Key Performance Indicators (KPIs) evaluated for  parallel periods in 1998 and 1997 as well as the period-to-period percentage growth.<\/p>\n<p>Usually KPIs are calculated by management information systems and they help to  evaluate an organisation&#8217;s success by determining with some precision what is important to the organization, and then  measuring it over time. It is important for any business to select and manage these KPIs to ensure that they measure  performance. I came across a good example of the importance of KPI just a couple of months ago whilst watching a BBC  documentary called &#8216;The Concorde Story&#8217;. <\/p>\n<p>Among many fascinating episodes that were shown in the documentary, there was  one that is directly related to KPIs.&#160; This episode describes how John King  turned around the business of British Airways, that was losing &#194;&#163;140m a year, into a company making &#194;&#163;268 pre-tax profit.  One of his most successful management decisions was to select &#160;a proper KPI.  Astonishingly it was a single KPI: the timely departure and arrival of BA aeroplanes. Find more <a href=\"http:\/\/davidparmenter.com\/?s=British+Airways&amp;searchblogs=1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9\"> here<\/a>.<\/p>\n<p>From my point of view, a selection of KPIs and their values should, ideally,  indicate clearly which reports to then look at for explanations of the performance of the KPI. For example, the above  dashboard tells us that all the Common Retail KPIs are in a very good shape, but despite the 109.3% growth in the number  of transactions in 1998 compared to the same period in 1997, the <code>TransValue<\/code>  (average sales volume per transaction) growth dropped by 9.1%. This &#8220;strange&#8221; behaviour must be understood, its root  causes identified, and some actions taken in order to reduce the potential negative impact of the reduction of the <code>TransValue<\/code> &#160;in the future.<\/p>\n<p>The Transaction Retail KPIs report gives us a few hints as to what to do next.  The first one is to look at the <code>TransValue<\/code> changes over time and across the  available business dimensions, such as <code>Product<\/code> and <code>Customer<\/code><\/p>\n<h2>Implementing the TransValue Trend and Heat Map Reports<\/h2>\n<p>The first report is displayed in the left-bottom corner of Figure 1 above. The R  procedure below does almost all the job. All that is required on the SQL Server side is to create the <code>Ch1_QtyWk_Trend_vw<\/code> view (see Listing 2).<\/p>\n<pre class=\"lang:c# theme:vs2012\">library(RODBC)\r\nlibrary(ggplot2)\r\nlibrary(plyr)\r\ncn  &lt;- odbcConnect('NW',uid='******',pwd='******')\r\ntrend  &lt;- sqlFetch(cn,'Ch1_QtyWk_Trend_vw')\r\nodbcClose(cn)\r\nvol  &lt;-ddply(trend,c(\"Year\",\"Week\"),TransValue=mean(Qty))\r\nq  &lt;- ggplot(vol, aes(x=Week, y=TransValue, colour=Year,fill=Year,shape=Year))\r\nq  &lt;- q  + geom_point(size=3)+ geom_line(size=1)\r\nq  &lt;- q  + scale_colour_brewer(palette=\"Set1\")\r\nq  &lt;- q  + geom_smooth(colour=\"#0000c0\",stat=\"smooth\")\r\nq  &lt;- q  + xlab(\"Week No\")  + ylab(\"TransValue\")\r\nwd&lt;-getwd()\r\nimagedir  &lt;- paste(Sys.getenv(\"HOME\"), \"\\\\R Analytics\\\\R-2-ASP\\\\Reporting\\\\Content\\\\\", sep=\"\")\r\nsetwd(imagedir)\r\nfilename  &lt;- 'wktrend.png'\r\nif(file.exists(filename)) file.remove(filename)\r\nggsave(q,file=filename,width=9,height=4)\r\nsetwd(wd)\r\n<\/pre>\n<p class=\"caption\">Listing1. QtyTrend.r procedure.<\/p>\n<pre class=\"lang:c# theme:vs2012\">SELECT CASE WHEN YEAR(dbo.Orders.OrderDate) = 1997 THEN 'LY' ELSE 'TY' END AS Year, DATEPART(ww, dbo.Orders.OrderDate) AS Week, \r\n&#160;&#160;&#160;&#160;&#160;&#160; CAST(SUM(dbo.[Order Details].Quantity) AS numeric) \/ CAST(COUNT(DISTINCT dbo.Orders.OrderID) AS numeric) AS TransValue\r\nFROM dbo.Orders INNER JOIN dbo.[Order Details] ON dbo.Orders.OrderID = dbo.[Order Details].OrderID\r\nWHERE (dbo.Orders.OrderDate BETWEEN '19970101' AND '19970506') OR\r\n&#160;&#160;&#160;&#160;&#160; (dbo.Orders.OrderDate BETWEEN '19980101' AND '19980506')\r\nGROUP BY CASE WHEN YEAR(dbo.Orders.OrderDate) = 1997 THEN 'LY' ELSE 'TY' END, DATEPART(ww, dbo.Orders.OrderDate)\r\n\t<\/pre>\n<p class=\"caption\">Listing 2. <code> Ch1_QtyWk_Trend_vw T-SQL view.<\/code><\/p>\n<p>The <code>sqlFetch()<\/code> function reads the  data that is required for the report and saves it in the data frame variable <code> trend<\/code>. A few of the initial and final lines of the data frame are shown below.<\/p>\n<p class=\"illustration\">  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1840-clip_image003.png\" height=\"169\" width=\"189\" alt=\"1840-clip_image003.png\" \/><\/p>\n<p class=\"caption\">Figure  2.  The trend data frame as it is defines by the T-SQL view.<\/p>\n<p>After closing the ODBC connection, the <code> ddply()<\/code> function is used to apply the <code>mean ()<\/code> function to each row in the  subset which is defined by the second input argument, <code>c(\"Year\",\" Week\")<\/code>. The <code>mean()<\/code> function does not do anything in this case as the data frame has only one value per row and it is  provided just to demonstrate what can be done. Instead of the <code>mean(),<\/code> we  could use any other R or custom defined functions. I hope you are beginning to appreciate the power of R by now.<\/p>\n<p>This simple transformation is enough to make the new data frame <code>vol<\/code> &#160;compatible with the plotting  function <code>ggplot()<\/code>. The remaining code creates the weekly trend lines for two  parallel periods, saves it as the <code>wktrend.png<\/code> image in the Content directory  of the ASP.NET MVC project and resets the R environment to the initial state.<\/p>\n<p>Next we deal with the last report that shows <code>TransValue<\/code> per Category and Country as a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Heat_map\">heat map<\/a>. The following R procedure (see Listing 4) is very similar  to the trendQty.r although it uses a new function that we have not used so far. This function is <code>commandArgs()<\/code>.<\/p>\n<pre class=\"lang:c# theme:vs2012\">library(RODBC)\r\nlibrary(ggplot2)\r\nargs  &lt;- commandArgs(TRUE)\r\ncn&lt;-odbcConnect('NW',uid='******',pwd='******')\r\nview  &lt;- ifelse(args[1]  == 1998,'Ch1_TranValue_by_Country_Categoty98_vw',&#160;  'Ch1_TranValue_by_Country_Categoty97_vw')\r\nqtyNatCat&lt;-sqlFetch(cn,view)\r\nodbcClose(cn)\r\nq  &lt;-ggplot(qtyNatCat, aes(x=Category, y=Country, fill=TransValue))\r\nq  &lt;- q  + geom_tile()\r\nq  &lt;- q  + geom_raster()\r\nq  &lt;- q  + scale_fill_gradient(low=\"#c00000\",high=\"#00c000\",na.value=\"white\")\r\nq  &lt;- q  + xlab('')  + ylab('')\r\nq  &lt;- q  + theme(axis.text.x  = element_text(angle=10, hjust=1, vjust=1))\r\nq  &lt;- q  + labs(fill=\"Qty\\nper\\nTran\")\r\nwd  &lt;- getwd()\r\nimagedir  &lt;- paste(Sys.getenv(\"HOME\"), \"\\\\R Analytics\\\\R-2-ASP\\\\Reporting\\\\Content\\\\\", sep=\"\")\r\nsetwd(imagedir)\r\nfilename  &lt;- ifelse(args[1]  == 1998,'heatmap98.png', 'heatmap97.png')\r\nif(file.exists(filename)) file.remove(filename)\r\nggsave(q,file=filename,width=9,height=4)\r\nsetwd(wd)\r\n<\/pre>\n<p class=\"caption\"> Listing 4. heatMap.r procedure.<\/p>\n<p>The <code>commandArgs()<\/code> provides access to  a copy of the command line arguments when <code>heatMap.r<\/code> is called from within the  batch file shown below (it contains only one line)<\/p>\n<pre class=\"lang:c# theme:vs2012\">&gt;\"C:\\Program Files\\R\\R-3.0.1\\bin\\x64\\RScript.exe\" C:\\\\path-to-heatmap.r1998<\/pre>\n<p>The literal 1998 above is a parameter available to <code>heatMap.r<\/code> as a first element of the vector args (see 3rd line in Listing 4).<\/p>\n<p>Apart from &#160;allowing a &#160;parameter to be passed to it, <code>heatMap.r<\/code>  is similar to the <code>qtyTrend.r<\/code> in &#160;Listing  1 although the <code>ggplot()<\/code> called from the he<code>atMap.r <\/code>plots a heat map.<\/p>\n<p>The T-SQL view that supplies 1998 data to the <code>heatMap.r<\/code> is displayed below.<\/p>\n<pre class=\"lang:c# theme:vs2012\">SELECT&#160; dbo.Customers.Country, dbo.Categories.CategoryName  AS Category,  CAST(SUM(dbo.[Order Details].Quantity)  AS  numeric) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;  \/  CAST(COUNT(DISTINCT dbo.Orders.OrderID)  AS  numeric)  AS TransValue\r\nFROM&#160;&#160;&#160; dbo.[Order Details]  INNER  JOIN\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; dbo.Orders  ON dbo.[Order Details].OrderID  = dbo.Orders.OrderID  INNER  JOIN\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; dbo.Products  ON dbo.[Order Details].ProductID  = dbo.Products.ProductID  INNER  JOIN\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; dbo.Categories  ON dbo.Products.CategoryID  = dbo.Categories.CategoryID  INNER  JOIN\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160; dbo.Customers  ON dbo.Orders.CustomerID  = dbo.Customers.CustomerID\r\nWHERE&#160;  (dbo.Orders.OrderDate  BETWEEN '19980101'  AND '19980506')\r\nGROUP  BY dbo.Customers.Country, dbo.Categories.CategoryName\r\n<\/pre>\n<p class=\"caption\"> Listing 5. Ch1_TranValue_by_Country_Categoty98_vw T-SQL view.<\/p>\n<p>The <code>heatMap.r<\/code> creates a PNG image <code>heatmap98.png<\/code> that is written into the Content folder of the ASP.NET MVC  project. Having an option of creating two heat map images for 1997 and 1998 allows for developing &#8216;drill up \/ down&#8217; and  compare heat map reports for two parallel periods.<\/p>\n<h2>Updating the Presentation Layer<\/h2>\n<p>Open the Notepad and create a DAL.bat file of the following content<\/p>\n<pre class=\"lang:c# theme:vs2012\">\"C:\\Program Files\\R\\R-3.0.0\\bin\\x64\\RScript.exe\" C:\\\\DoBIwithR\\\\Code\\\\RCode\\\\coreKPI.r\r\n\"C:\\Program Files\\R\\R-3.0.0\\bin\\x64\\RScript.exe\" C:\\\\DoBIwithR\\\\Code\\\\RCode\\\\transKPI.r\r\n\"C:\\Program Files\\R\\R-3.0.0\\bin\\x64\\RScript.exe\" C:\\\\DoBIwithR\\\\Code\\\\RCode\\\\qtyTrend.r\r\n\"C:\\Program Files\\R\\R-3.0.0\\bin\\x64\\RScript.exe\" C:\\\\DoBIwithR\\\\Code\\\\RCode\\\\heatMap.r 1997\r\n\"C:\\Program Files\\R\\R-3.0.0\\bin\\x64\\RScript.exe\" C:\\\\DoBIwithR\\\\Code\\\\RCode\\\\heatMap.r 1998\r\n<\/pre>\n<p class=\"caption\">Listing 6. DAL.bat file.<\/p>\n<p>Note that the path <code> C:\\\\DoBIwithR\\\\Code\\\\RCode\\\\<\/code>  to the directory where you keep the R procedures as well as the location of the  <code>RScript.exe<\/code>  will be different on your computer. So please edit the <i>DAT.bat<\/i> according to your installation.<\/p>\n<p>The <code>DAL.bat<\/code> generates content of all  four reports on the dashboard page (Figure 1) and the <code>heatmap97.png<\/code> for 1997.<\/p>\n<p>Open the ASP.NET MVC project we created in the Part 1 and make changes to the  Index View shown highlighted in the Listing 7 below.<\/p>\n<pre class=\"lang:xhtml theme:github mark:5,6,7,8,9,10,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,49,50,51,52,53,54,55,56,57,58\">@using Reporting.Helpers\r\n@{\r\n&#160;&#160;&#160;&#160;&#160; ViewBag.Title = \"Index\";\r\n}\r\n&lt;link href=\"~\/Content\/jquery.ui.all.css\" rel=\"stylesheet\" \/&gt;\r\n&lt;script src=\"~\/scripts\/jquery-1.9.1.js\"&gt;&lt;\/script&gt;\r\n&lt;script src=\"~\/scripts\/jquery.ui.core.js\"&gt;&lt;\/script&gt;\r\n&lt;script src=\"~\/scripts\/jquery.ui.widget.js\"&gt;&lt;\/script&gt;\r\n&lt;script src=\"~\/scripts\/jquery.ui.position.js\"&gt;&lt;\/script&gt;\r\n&lt;script src=\"~\/scripts\/jquery.ui.tooltip.js\"&gt;&lt;\/script&gt;\r\n&#160;\r\n&lt;script&gt;\r\n&#160;&#160;&#160;&#160;&#160; $(function ()\r\n&#160;&#160;&#160;&#160;&#160; {\r\n&#160;&#160;&#160;&#160;&#160; &#160; $(document).tooltip();\r\n&#160;&#160;&#160;&#160;&#160; });\r\n&#160;&#160;&#160;&#160;&#160;  function drillOnHeatMapClick()\r\n&#160;&#160;&#160;&#160;&#160; {\r\n&#160;&#160;&#160;&#160;&#160; &#160;  var img = document.getElementById(\"heatMap\");\r\n&#160;&#160;&#160;&#160;&#160; &#160;  var title = document.getElementById(\"hmTitle\");\r\n&#160;&#160;&#160;&#160;&#160; &#160;  var imgUrl = img.src;\r\n&#160;&#160;&#160;&#160;&#160; &#160;  if (imgUrl.indexOf(\"97\") === -1)\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160; imgUrl = imgUrl.replace('98', '97');\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160; img.title = img.title.replace('97', '98');\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160; title.innerText = title.innerText.replace(\"This\", \"Last\");\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;  else\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160; imgUrl = imgUrl.replace('97', '98');\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160; img.title = img.title.replace('98', '97');\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160; title.innerText = title.innerText.replace(\"Last\", \"This\");\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; img.src = imgUrl;\r\n&#160;&#160;&#160;&#160;&#160; }\r\n&lt;\/script&gt;\r\n&#160;\r\n&lt;div class=\"container-fluid\"&gt;\r\n&#160;&#160;&#160;&#160;&#160; &lt;div class=\"row-fluid\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div class=\"span6\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;p class=\"lead\"&gt;Common Retail&lt;abbr title=\"key performance indicators\"&gt;KPIs&lt;\/abbr&gt;&lt;\/p&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; @Html.LoadHtml(\"~\/Content\/html\/coreKPI.html\")\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div class=\"span6\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;p class=\"lead\"&gt;Transactional Retail&lt;abbr title=\"key performance indicators\"&gt;KPIs&lt;\/abbr&gt;&lt;\/p&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; @Html.LoadHtml(\"~\/Content\/html\/transKPI.html\")\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&#160;&#160;&#160;&#160;&#160; &lt;div class=\"row-fluid\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div class=\"span6\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;p class=\"lead\"&gt;Weekly&lt;abbr title=\"Quantity Sold per Transaction\"&gt;TransValue&lt;\/abbr&gt;Trend for This and Last Year&lt;\/p&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;img class=\"img-polaroid\" src=\"~\/Content\/wktrend.png\" \/&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div class=\"span6\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;p id=\"hmTitle\" class=\"lead\"&gt;This Year&lt;abbr title=\"Quantity Sold per Transaction\"&gt;TransValue&lt;\/abbr&gt;by Country and Category&lt;\/p&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;img id=\"heatMap\" onclick='drillOnHeatMapClick()' class=\"img-polaroid\" title=\"Click on the image to see the 1997 report\" src=\"~\/Content\/heatmap98.png\" \/&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&#160;&#160;&#160;&#160;&#160; &lt;div class=\"row-fluid\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div class=\"span12\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;br \/&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;blockquote class=\"pull-right\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;p&gt;Above all else show the data.&lt;\/p&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;small&gt;&lt;cite title=\"Edward Tufte\"&gt;The Visual Display of Quantitative Information&lt;\/cite&gt;&lt;\/small&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/blockquote&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<p class=\"caption\"> Listing 7. Index View code.<\/p>\n<p>If you compare this code with the code we used for the &#160;Index View in part 1 (Listing 12) , you would find that I&#8217;ve added three new  blocks of code highlighted in yellow. <\/p>\n<p>The first block contains references to <a href=\"http:\/\/jquery.com\/\">jQuery<\/a>  and <a href=\"http:\/\/jqueryui.com\/\">jQuery UI<\/a> libraries. If you like to follow along you have to download them and  copy the libraries into the same folders of the ASP.NET MVC project as in the references tags of the first highlighted  block.<\/p>\n<p>The second block includes two JavaScript functions. The first function is to  display tooltips when the user hovers mouse over the heat map image, and the second function is required &#160;to allow the user to drill down or drill up on the heat map click, so as to view  and compare reports for the parallel periods. The second function also updates the report title and tooltip text in  order to coordinate them with the displayed report. The next two pictures illustrate this functionality<\/p>\n<p>  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1840-clip_image005.png\" height=\"313\" width=\"577\" alt=\"1840-clip_image005.png\" \/><\/p>\n<p class=\"illustration\">  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1840-clip_image007.png\" height=\"306\" width=\"568\" alt=\"1840-clip_image007.png\" \/><\/p>\n<p class=\"Caption\">Figure 2. The heat map report before  (above image) and after the drillOnHeatMapClick() function was executed.<\/p>\n<p>The new (compare to Part 1) bootstrap CSS class <a href=\"http:\/\/twitter.github.io\/bootstrap\/base-css.html\">img-polaroid<\/a> is used to style  images in the last row of the grid (see Figure 1).<\/p>\n<p>Finally, we have to make some changes to the navigation bar. Open the _<code>BootstapLayout.basic.cshtml<\/code>  layout view, comment out the current navigation bar mark-up and add the new one as follows.<\/p>\n<p>The updated toolbar contains links to four other pages that are not implemented  yet, so stay tuned, as there is more to come.<\/p>\n<pre class=\"lang:xhtml theme:github\">@*&lt;div class=\"navbar navbar-inverse navbar-fixed-top\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div class=\"navbar-inner\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div class=\"container\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;a class=\"btn btn-navbar\" data-toggle=\"collapse\" data-target=\".nav-collapse\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;span class=\"icon-bar\"&gt;&lt;\/span&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;span class=\"icon-bar\"&gt;&lt;\/span&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;span class=\"icon-bar\"&gt;&lt;\/span&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/a&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;a class=\"brand\" href=\"#\" title=\"change in _bootstrapLayout.basic.cshtml\"&gt;Application Name&lt;\/a&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;div class=\"nav-collapse collapse\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;ul class=\"nav\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; @Html.Navigation()\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/ul&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&lt;\/div&gt;*@\r\n&lt;nav class=\"navbar navbar-inverse navbar-fixed-top\"&gt;\r\n&#160;&#160;&#160;&#160;&#160; &lt;div class=\"navbar-inner\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;a class=\"brand\" href=\"\/\"&gt;Northwind Retail Analytics&lt;\/a&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;form class=\"navbar-search pull-right input-append\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;input class=\"span2\" id=\"appendedInputButtons\" type=\"search\" \/&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;button class=\"btn\" type=\"button\"&gt;Search&lt;\/button&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/form&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;ul class=\"nav pull-right\"&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;li class=\"active\"&gt;&lt;a href=\"#\"&gt;KPIs&lt;\/a&gt;&lt;\/li&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;li&gt;&lt;a href=\"#\"&gt;Customer&lt;\/a&gt;&lt;\/li&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;li&gt;&lt;a href=\"#\"&gt;Products&lt;\/a&gt;&lt;\/li&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;li&gt;&lt;a href=\"#\"&gt;Time&lt;\/a&gt;&lt;\/li&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;li&gt;&lt;a href=\"#\"&gt;Raw Data&lt;\/a&gt;&lt;\/li&gt;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/ul&gt;\r\n&#160;&#160;&#160;&#160;&#160; &lt;\/div&gt;\r\n&lt;\/nav&gt;\r\n<\/pre>\n<p class=\"caption\"> &#160;Listing 8. Changes to the application&#8217;s navigation bar.<\/p>\n<p>This makes the navigation bar look like the one that is shown below.<\/p>\n<p class=\"illustration\">  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1840-clip_image009-630x24.png\" height=\"24\" width=\"630\" alt=\"1840-clip_image009-630x24.png\" \/><\/p>\n<p class=\"caption\">Figure  3.  Updated navigation bar.<\/p>\n<h2>Summary<\/h2>\n<p>We made a significant step towards making our application more practical.<\/p>\n<ol>\n<li>As opposed to developing each reports separately (see the Part 1 of the article), all of  \t\tthem can now be generated simultaneously by executing the batch file DAL.bat that accepts parameters. Its  \t\texecution can either be scheduled to run immediately after an \t\t<a href=\"http:\/\/en.wikipedia.org\/wiki\/Extract,_transform,_load\">ETL<\/a> updates the data source or it can be \t\t&#160;integrated into an SSIS package that manages updates using the SQL Server Engine change-tracking  \t\tfeature.<\/li>\n<li>We introduced two new R libraries, PLYR and GGPLOT2. The PLYR streamlines data  \t\tpreparation for visualisation that was accomplished by the versatile GGPLOT2 library functions.<\/li>\n<li>Lastly, jQuery and jQuery UI libraries were employed for developing the application&#8217;s  \t\tinteractivity (drill down, drill up) and keeping the user interface coordinated when the user drills down or  \t\tdrills up.<\/li>\n<\/ol>\n<p>There is more to be done: passing parameters from R procedures to SQL server  procedures, multiple drill-down paths, etc. We&#8217;ll tackle all this in subsequent articles.<\/p>\n<p class=\"note\">The R code for this article, for which you&#8217;ll have to provide  ODBC uid and password, and change the batch file extension from txt to bat, is provided in the ZIP file at the bottom of the article. The T-SQL code for parts 1 and 2 is already in the database image supplied with part 1. Please remember that this is sample code provided only to illustrate the points being made about the design of the application<\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Continuing the project to build a Business Intelligence (BI) application using R, Visual Studio 2012 and Twitter Bootstrap, Sergei introduces drill-down via jQuery and jQuery UI, simplifies the R coding with the PLYR and GGPLOT2 libraries, and shows how to generate reports simultaneously with a batch f&hellip;<\/p>\n","protected":false},"author":174820,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[4143,4156,4157,4178,5166],"coauthors":[6788],"class_list":["post-1672","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-net","tag-asp","tag-asp-net","tag-bi","tag-mvc"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1672","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/174820"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1672"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1672\/revisions"}],"predecessor-version":[{"id":91059,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1672\/revisions\/91059"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1672"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1672"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1672"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1672"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}