Update: Cached Job Index Data
Take advantage of repository monitor for change notification; in-memory cache rather than retrieving data on each request.
This commit is contained in:
@@ -7,6 +7,9 @@ using Disco.Models.BI.Job.Statistics;
|
|||||||
using Disco.Data.Repository;
|
using Disco.Data.Repository;
|
||||||
using Quartz.Impl;
|
using Quartz.Impl;
|
||||||
using Disco.Services.Tasks;
|
using Disco.Services.Tasks;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using Disco.Models.Repository;
|
||||||
|
using Disco.Data.Repository.Monitor;
|
||||||
|
|
||||||
namespace Disco.BI.JobBI.Statistics
|
namespace Disco.BI.JobBI.Statistics
|
||||||
{
|
{
|
||||||
@@ -15,6 +18,7 @@ namespace Disco.BI.JobBI.Statistics
|
|||||||
|
|
||||||
private static List<DailyOpenedClosedItem> _data;
|
private static List<DailyOpenedClosedItem> _data;
|
||||||
private static object _dataLock = new object();
|
private static object _dataLock = new object();
|
||||||
|
private static IDisposable _streamSubscription;
|
||||||
|
|
||||||
|
|
||||||
public override string TaskName { get { return "Job Statistics - Daily Opened/Closed Task"; } }
|
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)
|
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)
|
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));
|
resultData.Add(Data(dbContext, processDate));
|
||||||
processDate = processDate.AddDays(1);
|
processDate = processDate.AddDays(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_data = resultData;
|
_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<DateTime?>("ClosedDate");
|
||||||
|
DateTime? currentValue = e.GetCurrentPropertyValue<DateTime?>("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
|
else
|
||||||
resultData = _data.ToList();
|
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;
|
return resultData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.2.0411.1833")]
|
[assembly: AssemblyVersion("1.2.0430.1219")]
|
||||||
[assembly: AssemblyFileVersion("1.2.0411.1833")]
|
[assembly: AssemblyFileVersion("1.2.0430.1219")]
|
||||||
|
|||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.2.0411.1833")]
|
[assembly: AssemblyVersion("1.2.0430.1219")]
|
||||||
[assembly: AssemblyFileVersion("1.2.0411.1833")]
|
[assembly: AssemblyFileVersion("1.2.0430.1219")]
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ namespace Disco.Web.Controllers
|
|||||||
|
|
||||||
m.OpenJobs = jobList_OpenJobs;
|
m.OpenJobs = jobList_OpenJobs;
|
||||||
m.LongRunningJobs = jobList_LongRunning;
|
m.LongRunningJobs = jobList_LongRunning;
|
||||||
|
m.DailyOpenedClosedStatistics = Disco.BI.JobBI.Statistics.DailyOpenedClosed.Data(dbContext, true);
|
||||||
|
|
||||||
// UI Extensions
|
// UI Extensions
|
||||||
UIExtensions.ExecuteExtensions<JobIndexModel>(this.ControllerContext, m);
|
UIExtensions.ExecuteExtensions<JobIndexModel>(this.ControllerContext, m);
|
||||||
|
|||||||
@@ -12,9 +12,11 @@
|
|||||||
<h2>Daily Opened & Closed Jobs</h2>
|
<h2>Daily Opened & Closed Jobs</h2>
|
||||||
<div id="chartHostJobDailyOpenedClosed" style="height: 175px;">
|
<div id="chartHostJobDailyOpenedClosed" style="height: 175px;">
|
||||||
</div>
|
</div>
|
||||||
|
@{
|
||||||
|
var jsonData = new HtmlString(Json.Encode(Model.DailyOpenedClosedStatistics));
|
||||||
|
}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function () {
|
(function () {
|
||||||
|
|
||||||
var chartData;
|
var chartData;
|
||||||
|
|
||||||
function buildChart() {
|
function buildChart() {
|
||||||
@@ -105,12 +107,8 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
chartData = $.parseJSON('@(jsonData)');
|
||||||
|
buildChart();
|
||||||
$.getJSON('@(Url.Action(MVC.API.Job.StatisticsDailyOpenedClosed()))', function (data) {
|
|
||||||
chartData = data;
|
|
||||||
buildChart();
|
|
||||||
});
|
|
||||||
}());
|
}());
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <auto-generated>
|
||||||
// This code was generated by a tool.
|
// 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
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// the code is regenerated.
|
// the code is regenerated.
|
||||||
@@ -31,9 +31,9 @@ namespace Disco.Web.Views.Job
|
|||||||
using Disco.Web;
|
using Disco.Web;
|
||||||
using Disco.Web.Extensions;
|
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")]
|
[System.Web.WebPages.PageVirtualPathAttribute("~/Views/Job/Index.cshtml")]
|
||||||
public class Index : System.Web.Mvc.WebViewPage<Disco.Web.Models.Job.IndexModel>
|
public partial class Index : System.Web.Mvc.WebViewPage<Disco.Web.Models.Job.IndexModel>
|
||||||
{
|
{
|
||||||
public Index()
|
public Index()
|
||||||
{
|
{
|
||||||
@@ -78,77 +78,92 @@ WriteLiteral(" id=\"chartHostJobDailyOpenedClosed\"");
|
|||||||
|
|
||||||
WriteLiteral(" style=\"height: 175px;\"");
|
WriteLiteral(" style=\"height: 175px;\"");
|
||||||
|
|
||||||
WriteLiteral(">\r\n </div>\r\n <script");
|
WriteLiteral(">\r\n </div>\r\n");
|
||||||
|
|
||||||
WriteLiteral(" type=\"text/javascript\"");
|
|
||||||
|
|
||||||
WriteLiteral(">\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(\'");
|
|
||||||
|
|
||||||
|
|
||||||
#line 110 "..\..\Views\Job\Index.cshtml"
|
#line 15 "..\..\Views\Job\Index.cshtml"
|
||||||
Write(Url.Action(MVC.API.Job.StatisticsDailyOpenedClosed()));
|
|
||||||
|
|
||||||
|
|
||||||
#line default
|
#line default
|
||||||
#line hidden
|
#line hidden
|
||||||
WriteLiteral("\', function (data) {\r\n chartData = data;\r\n " +
|
|
||||||
"buildChart();\r\n });\r\n }());\r\n\r\n </script>\r\n " +
|
#line 15 "..\..\Views\Job\Index.cshtml"
|
||||||
"</div>\r\n</div>\r\n<h2>Open Jobs Awaiting Technician Action (");
|
|
||||||
|
var jsonData = new HtmlString(Json.Encode(Model.DailyOpenedClosedStatistics));
|
||||||
|
|
||||||
|
|
||||||
#line 119 "..\..\Views\Job\Index.cshtml"
|
#line default
|
||||||
|
#line hidden
|
||||||
|
WriteLiteral("\r\n <script");
|
||||||
|
|
||||||
|
WriteLiteral(" type=\"text/javascript\"");
|
||||||
|
|
||||||
|
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(jsonData);
|
||||||
|
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
WriteLiteral("\');\r\n buildChart();\r\n }());\r\n\r\n </script>\r\n <" +
|
||||||
|
"/div>\r\n</div>\r\n<h2>Open Jobs Awaiting Technician Action (");
|
||||||
|
|
||||||
|
|
||||||
|
#line 117 "..\..\Views\Job\Index.cshtml"
|
||||||
Write(Model.OpenJobs.Items.Count);
|
Write(Model.OpenJobs.Items.Count);
|
||||||
|
|
||||||
|
|
||||||
@@ -157,7 +172,7 @@ WriteLiteral("\', function (data) {\r\n chartData = data;\r\n
|
|||||||
WriteLiteral(")</h2>\r\n");
|
WriteLiteral(")</h2>\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()));
|
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<h2>Long Running Jobs (");
|
WriteLiteral("\r\n<h2>Long Running Jobs (");
|
||||||
|
|
||||||
|
|
||||||
#line 121 "..\..\Views\Job\Index.cshtml"
|
#line 119 "..\..\Views\Job\Index.cshtml"
|
||||||
Write(Model.LongRunningJobs.Items.Count);
|
Write(Model.LongRunningJobs.Items.Count);
|
||||||
|
|
||||||
|
|
||||||
@@ -175,7 +190,7 @@ WriteLiteral("\r\n<h2>Long Running Jobs (");
|
|||||||
WriteLiteral(")</h2>\r\n");
|
WriteLiteral(")</h2>\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()));
|
Write(Html.Partial(MVC.Shared.Views._JobTable, Model.LongRunningJobs, new ViewDataDictionary()));
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user