From 32336af075f3bc25f74fbe3a368411d659ad54cf Mon Sep 17 00:00:00 2001 From: Gary Sharp Date: Tue, 30 Apr 2013 12:42:59 +1000 Subject: [PATCH] Update: Cached Job Index Data Take advantage of repository monitor for change notification; in-memory cache rather than retrieving data on each request. --- .../BI/JobBI/Statistics/DailyOpenedClosed.cs | 65 +++++++- Disco.BI/Properties/AssemblyInfo.cs | 4 +- .../Properties/AssemblyInfo.cs | 4 +- Disco.Web/Controllers/JobController.cs | 1 + Disco.Web/Views/Job/Index.cshtml | 12 +- Disco.Web/Views/Job/Index.generated.cs | 145 ++++++++++-------- 6 files changed, 152 insertions(+), 79 deletions(-) diff --git a/Disco.BI/BI/JobBI/Statistics/DailyOpenedClosed.cs b/Disco.BI/BI/JobBI/Statistics/DailyOpenedClosed.cs index 554e5856..7c37a845 100644 --- a/Disco.BI/BI/JobBI/Statistics/DailyOpenedClosed.cs +++ b/Disco.BI/BI/JobBI/Statistics/DailyOpenedClosed.cs @@ -7,6 +7,9 @@ using Disco.Models.BI.Job.Statistics; using Disco.Data.Repository; using Quartz.Impl; using Disco.Services.Tasks; +using System.Reactive.Linq; +using Disco.Models.Repository; +using Disco.Data.Repository.Monitor; namespace Disco.BI.JobBI.Statistics { @@ -15,6 +18,7 @@ namespace Disco.BI.JobBI.Statistics private static List _data; private static object _dataLock = new object(); + private static IDisposable _streamSubscription; public override string TaskName { get { return "Job Statistics - Daily Opened/Closed Task"; } } @@ -65,7 +69,7 @@ namespace Disco.BI.JobBI.Statistics private static void UpdateDataHistory(DiscoDataContext dbContext, bool Refresh = false) { - DateTime historyEnd = DateTime.Now.AddDays(-1).Date; + DateTime historyEnd = DateTime.Now.Date; if (Refresh || _data == null || _data.Count == 0 || _data.Last().Timestamp < historyEnd) { @@ -97,8 +101,62 @@ namespace Disco.BI.JobBI.Statistics resultData.Add(Data(dbContext, processDate)); processDate = processDate.AddDays(1); } - _data = resultData; + + // Subscribe to Live Repository Events + if (_streamSubscription != null) + _streamSubscription.Dispose(); + _streamSubscription = Disco.Data.Repository.Monitor.RepositoryMonitor.StreamBeforeCommit.Where( + e => e.EntityType == typeof(Job) && + (e.EventType == RepositoryMonitorEventType.Added || (e.EventType == RepositoryMonitorEventType.Modified && e.ModifiedProperties.Contains("ClosedDate")))).Subscribe(RepositoryEvent_JobChange); + } + } + } + } + + private static void RepositoryEvent_JobChange(RepositoryMonitorEvent e) + { + + if (e.EventType == RepositoryMonitorEventType.Added) + { + // New Job + var todaysStats = _data.Last(); + todaysStats.OpenedJobs += 1; + todaysStats.TotalJobs += 1; + } + else + { + DateTime? previousValue = e.GetPreviousPropertyValue("ClosedDate"); + DateTime? currentValue = e.GetCurrentPropertyValue("ClosedDate"); + + if (previousValue.HasValue) + { + // Remove Statistics + var statItem = _data.FirstOrDefault(i => i.Timestamp == previousValue.Value.Date); + if (statItem != null) + { + statItem.ClosedJobs -= 1; + statItem.TotalJobs += 1; + } + foreach (var affectedStat in _data.Where(i => i.Timestamp > previousValue)) + { + affectedStat.TotalJobs += 1; + } + } + + if (currentValue.HasValue) + { + // Add Statistics + // Remove Statistics + var statItem = _data.FirstOrDefault(i => i.Timestamp == currentValue.Value.Date); + if (statItem != null) + { + statItem.ClosedJobs += 1; + statItem.TotalJobs -= 1; + } + foreach (var affectedStat in _data.Where(i => i.Timestamp > currentValue)) + { + affectedStat.TotalJobs -= 1; } } } @@ -134,7 +192,8 @@ namespace Disco.BI.JobBI.Statistics else resultData = _data.ToList(); - resultData.Add(Data(dbContext, DateTime.Today)); + // Removed - Live Updated via Repository Monitor; See: RepositoryEvent_JobChange + //resultData.Add(Data(dbContext, DateTime.Today)); return resultData; } diff --git a/Disco.BI/Properties/AssemblyInfo.cs b/Disco.BI/Properties/AssemblyInfo.cs index 274ee1a8..d6e78dc0 100644 --- a/Disco.BI/Properties/AssemblyInfo.cs +++ b/Disco.BI/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.0411.1833")] -[assembly: AssemblyFileVersion("1.2.0411.1833")] +[assembly: AssemblyVersion("1.2.0430.1219")] +[assembly: AssemblyFileVersion("1.2.0430.1219")] diff --git a/Disco.Web.Extensions/Properties/AssemblyInfo.cs b/Disco.Web.Extensions/Properties/AssemblyInfo.cs index 173e1cd2..ba5cf0f2 100644 --- a/Disco.Web.Extensions/Properties/AssemblyInfo.cs +++ b/Disco.Web.Extensions/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.0411.1833")] -[assembly: AssemblyFileVersion("1.2.0411.1833")] +[assembly: AssemblyVersion("1.2.0430.1219")] +[assembly: AssemblyFileVersion("1.2.0430.1219")] diff --git a/Disco.Web/Controllers/JobController.cs b/Disco.Web/Controllers/JobController.cs index 1c7671ba..86b2a4d4 100644 --- a/Disco.Web/Controllers/JobController.cs +++ b/Disco.Web/Controllers/JobController.cs @@ -71,6 +71,7 @@ namespace Disco.Web.Controllers m.OpenJobs = jobList_OpenJobs; m.LongRunningJobs = jobList_LongRunning; + m.DailyOpenedClosedStatistics = Disco.BI.JobBI.Statistics.DailyOpenedClosed.Data(dbContext, true); // UI Extensions UIExtensions.ExecuteExtensions(this.ControllerContext, m); diff --git a/Disco.Web/Views/Job/Index.cshtml b/Disco.Web/Views/Job/Index.cshtml index b9d937de..63eefab8 100644 --- a/Disco.Web/Views/Job/Index.cshtml +++ b/Disco.Web/Views/Job/Index.cshtml @@ -12,9 +12,11 @@

Daily Opened & Closed Jobs

+ @{ + var jsonData = new HtmlString(Json.Encode(Model.DailyOpenedClosedStatistics)); + } diff --git a/Disco.Web/Views/Job/Index.generated.cs b/Disco.Web/Views/Job/Index.generated.cs index 14d7e76d..0bffa311 100644 --- a/Disco.Web/Views/Job/Index.generated.cs +++ b/Disco.Web/Views/Job/Index.generated.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.17929 +// Runtime Version:4.0.30319.18033 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -31,9 +31,9 @@ namespace Disco.Web.Views.Job using Disco.Web; using Disco.Web.Extensions; - [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "1.5.0.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "1.5.4.0")] [System.Web.WebPages.PageVirtualPathAttribute("~/Views/Job/Index.cshtml")] - public class Index : System.Web.Mvc.WebViewPage + public partial class Index : System.Web.Mvc.WebViewPage { public Index() { @@ -78,77 +78,92 @@ WriteLiteral(" id=\"chartHostJobDailyOpenedClosed\""); WriteLiteral(" style=\"height: 175px;\""); -WriteLiteral(">\r\n \r\n \r\n \r\n"); + + + #line 15 "..\..\Views\Job\Index.cshtml" + + + #line default + #line hidden + + #line 15 "..\..\Views\Job\Index.cshtml" + + var jsonData = new HtmlString(Json.Encode(Model.DailyOpenedClosedStatistics)); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n (function () {\r\n\r\n var chartData;\r\n\r\n " + -" function buildChart() {\r\n $(function () {\r\n\r\n " + -" var data = chartData;\r\n\r\n var dataTotalOpenJobs" + -" = [];\r\n var dataOpenedJobs = [];\r\n " + -" var dataClosedJobs = [];\r\n for (var i = 0; i < data.len" + -"gth; i++) {\r\n var dataItem = data[i];\r\n " + -" var dataItemDate = new Date(parseInt(dataItem.Timestamp.substr(6, " + -"dataItem.Timestamp.length - 8))).getTime(); // $.datepicker.parseDate(\'yy-mm-dd\'" + -", dataItem.Timestamp.substr(0, 10)).getTime();\r\n data" + -"TotalOpenJobs.push([dataItemDate, dataItem.TotalJobs]);\r\n " + -" dataOpenedJobs.push([dataItemDate, dataItem.OpenedJobs]);\r\n " + -" dataClosedJobs.push([dataItemDate, dataItem.ClosedJobs]);\r\n " + -" }\r\n Highcharts.setOptions({\r\n " + -" global: {\r\n useUTC: false\r\n " + -" }\r\n });\r\n new" + -" Highcharts.Chart({\r\n chart: {\r\n " + -" renderTo: \'chartHostJobDailyOpenedClosed\',\r\n " + -" height: 175,\r\n animation: false\r\n " + -" },\r\n colors: [\'#BBBBBB\', \'#005fab\'" + -", \'#DB761D\'],\r\n title: {\r\n " + -" text: null\r\n },\r\n plo" + -"tOptions: {\r\n series: {\r\n " + -" marker: {\r\n radius: 3\r\n " + -" },\r\n animation:" + -" false\r\n }\r\n },\r\n " + -" legend: {\r\n align: \'left\'," + -"\r\n verticalAlign: \'top\',\r\n " + -" y: 0,\r\n floating: true,\r\n " + -" borderWidth: 0\r\n },\r\n " + -" xAxis: {\r\n type: \'datetime\',\r\n " + -" tickInterval: 7 * 24 * 3600 * 1000, // week\r\n " + -" tickWidth: 1,\r\n gridLineWi" + -"dth: 1,\r\n dateTimeLabelFormats: {\r\n " + -" week: \'%e %b\'\r\n }\r\n " + -" },\r\n yAxis: [{\r\n " + -" title: {\r\n text: null\r\n " + -" },\r\n labels: {\r\n " + -" enabled: false\r\n },\r\n" + -" min: 0\r\n }, {\r\n " + -" title: {\r\n text: nu" + -"ll\r\n },\r\n labels: " + -"{\r\n enabled: false\r\n " + -" },\r\n min: 0\r\n }" + -"],\r\n series: [{\r\n name" + -": \'Total Open Jobs\',\r\n data: dataTotalOpenJobs,\r\n" + -" yAxis: 1\r\n }, {\r\n " + -" name: \'Closed Jobs\',\r\n " + -" data: dataClosedJobs\r\n }, {\r\n " + -" name: \'Opened Jobs\',\r\n data: dataOpened" + -"Jobs\r\n }],\r\n credits: {\r\n " + -" enabled: false\r\n }\r\n " + -" });\r\n });\r\n }\r\n\r\n\r\n " + -" $.getJSON(\'"); +WriteLiteral(">\r\n (function () {\r\n var chartData;\r\n\r\n " + +"function buildChart() {\r\n $(function () {\r\n\r\n " + +" var data = chartData;\r\n\r\n var dataTotalOpenJobs =" + +" [];\r\n var dataOpenedJobs = [];\r\n " + +"var dataClosedJobs = [];\r\n for (var i = 0; i < data.lengt" + +"h; i++) {\r\n var dataItem = data[i];\r\n " + +" var dataItemDate = new Date(parseInt(dataItem.Timestamp.substr(6, da" + +"taItem.Timestamp.length - 8))).getTime(); // $.datepicker.parseDate(\'yy-mm-dd\', " + +"dataItem.Timestamp.substr(0, 10)).getTime();\r\n dataTo" + +"talOpenJobs.push([dataItemDate, dataItem.TotalJobs]);\r\n " + +" dataOpenedJobs.push([dataItemDate, dataItem.OpenedJobs]);\r\n " + +" dataClosedJobs.push([dataItemDate, dataItem.ClosedJobs]);\r\n " + +" }\r\n Highcharts.setOptions({\r\n " + +" global: {\r\n useUTC: false\r\n " + +" }\r\n });\r\n new H" + +"ighcharts.Chart({\r\n chart: {\r\n " + +" renderTo: \'chartHostJobDailyOpenedClosed\',\r\n " + +" height: 175,\r\n animation: false\r\n " + +" },\r\n colors: [\'#BBBBBB\', \'#005fab\', " + +"\'#DB761D\'],\r\n title: {\r\n " + +" text: null\r\n },\r\n plotO" + +"ptions: {\r\n series: {\r\n " + +" marker: {\r\n radius: 3\r\n " + +" },\r\n animation: f" + +"alse\r\n }\r\n },\r\n " + +" legend: {\r\n align: \'left\',\r\n" + +" verticalAlign: \'top\',\r\n " + +" y: 0,\r\n floating: true,\r\n " + +" borderWidth: 0\r\n },\r\n " + +" xAxis: {\r\n type: \'datetime\',\r\n " + +" tickInterval: 7 * 24 * 3600 * 1000, // week\r\n " + +" tickWidth: 1,\r\n gridLineWidt" + +"h: 1,\r\n dateTimeLabelFormats: {\r\n " + +" week: \'%e %b\'\r\n }\r\n " + +" },\r\n yAxis: [{\r\n " + +" title: {\r\n text: null\r\n " + +" },\r\n labels: {\r\n " + +" enabled: false\r\n },\r\n " + +" min: 0\r\n }, {\r\n " + +" title: {\r\n text: null" + +"\r\n },\r\n labels: {\r" + +"\n enabled: false\r\n " + +" },\r\n min: 0\r\n }]," + +"\r\n series: [{\r\n name: " + +"\'Total Open Jobs\',\r\n data: dataTotalOpenJobs,\r\n " + +" yAxis: 1\r\n }, {\r\n " + +" name: \'Closed Jobs\',\r\n " + +"data: dataClosedJobs\r\n }, {\r\n " + +" name: \'Opened Jobs\',\r\n data: dataOpenedJo" + +"bs\r\n }],\r\n credits: {\r\n " + +" enabled: false\r\n }\r\n " + +" });\r\n });\r\n }\r\n " + +" chartData = $.parseJSON(\'"); #line 110 "..\..\Views\Job\Index.cshtml" - Write(Url.Action(MVC.API.Job.StatisticsDailyOpenedClosed())); + Write(jsonData); #line default #line hidden -WriteLiteral("\', function (data) {\r\n chartData = data;\r\n " + -"buildChart();\r\n });\r\n }());\r\n\r\n \r\n " + -"\r\n\r\n

Open Jobs Awaiting Technician Action ("); +WriteLiteral("\');\r\n buildChart();\r\n }());\r\n\r\n \r\n <" + +"/div>\r\n\r\n

Open Jobs Awaiting Technician Action ("); - #line 119 "..\..\Views\Job\Index.cshtml" + #line 117 "..\..\Views\Job\Index.cshtml" Write(Model.OpenJobs.Items.Count); @@ -157,7 +172,7 @@ WriteLiteral("\', function (data) {\r\n chartData = data;\r\n WriteLiteral(")

\r\n"); - #line 120 "..\..\Views\Job\Index.cshtml" + #line 118 "..\..\Views\Job\Index.cshtml" Write(Html.Partial(MVC.Shared.Views._JobTable, Model.OpenJobs, new ViewDataDictionary())); @@ -166,7 +181,7 @@ Write(Html.Partial(MVC.Shared.Views._JobTable, Model.OpenJobs, new ViewDataDicti WriteLiteral("\r\n

Long Running Jobs ("); - #line 121 "..\..\Views\Job\Index.cshtml" + #line 119 "..\..\Views\Job\Index.cshtml" Write(Model.LongRunningJobs.Items.Count); @@ -175,7 +190,7 @@ WriteLiteral("\r\n

Long Running Jobs ("); WriteLiteral(")

\r\n"); - #line 122 "..\..\Views\Job\Index.cshtml" + #line 120 "..\..\Views\Job\Index.cshtml" Write(Html.Partial(MVC.Shared.Views._JobTable, Model.LongRunningJobs, new ViewDataDictionary()));