Log Grid added, using server side filtering, sorting and paging. Using DynamicQueryable.

This commit is contained in:
Mark McDowall 2012-02-09 09:41:51 -08:00
parent ca5888160d
commit 9c24b5989b
16 changed files with 4886 additions and 64 deletions

View File

@ -21,7 +21,7 @@
</assemblyBinding>
</runtime>
<system.web>
<customErrors mode="Off"/>
<customErrors mode="Off" />
<compilation debug="true" targetFramework="4.0" />
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
@ -34,8 +34,8 @@
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="Cache1Hour" duration="3600" varyByParam="none"/>
<add name="Cache1HourVaryBySeriesId" duration="3600" varyByParam="seriesId"/>
<add name="Cache1Hour" duration="3600" varyByParam="none" />
<add name="Cache1HourVaryBySeriesId" duration="3600" varyByParam="seriesId" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>

View File

@ -0,0 +1,15 @@
using System.Web.Mvc;
using System.Web.WebPages;
using NzbDrone.Web.Helpers;
using NzbDrone.Web.Models;
[assembly: WebActivator.PreApplicationStartMethod(typeof(NzbDrone.Web.App_Start.RegisterDatatablesModelBinder), "Start")]
namespace NzbDrone.Web.App_Start {
public static class RegisterDatatablesModelBinder {
public static void Start() {
if (!ModelBinders.Binders.ContainsKey(typeof(DataTablesParams)))
ModelBinders.Binders.Add(typeof(DataTablesParams), new DataTablesModelBinder());
}
}
}

View File

@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic;
using System.Text;
using System.Web.Mvc;
using NzbDrone.Common;
@ -26,6 +29,11 @@ namespace NzbDrone.Web.Controllers
return View();
}
public ActionResult IndexOld()
{
return View();
}
public FileContentResult File()
{
string log = string.Empty;
@ -47,8 +55,56 @@ namespace NzbDrone.Web.Controllers
return JsonNotificationResult.Info("Logs Cleared");
}
public ActionResult AjaxBinding(DataTablesParams dataTablesParams)
{
var logs = _logProvider.GetAllLogs();
var totalCount = logs.Count();
IQueryable<Log> q = logs;
if (!string.IsNullOrEmpty(dataTablesParams.sSearch))
{
q = q.Where(b => b.Logger.Contains(dataTablesParams.sSearch)
|| b.Exception.Contains(dataTablesParams.sSearch)
|| b.Message.Contains(dataTablesParams.sSearch));
}
int filteredCount = q.Count();
int sortCol = dataTablesParams.iSortCol.First();
var sortColName = sortCol == 0 ? "Time" : sortCol == 1 ? "Level" : "Logger";
var sortExpression = String.Format("{0} {1}", sortColName, dataTablesParams.sSortDir.First());
var sorted = q.OrderBy(sortExpression);
IQueryable<Log> filteredAndSorted = sorted;
if (filteredCount > dataTablesParams.iDisplayLength)
{
filteredAndSorted = sorted.Skip(dataTablesParams.iDisplayStart).Take(dataTablesParams.iDisplayLength);
}
var logModels = filteredAndSorted.ToList().Select(s => new LogModel
{
Time = s.Time.ToString(),
Level = s.Level,
Source = s.Logger,
Message = s.Message,
Method = s.Method,
ExceptionType = s.ExceptionType,
Exception = s.Exception
});
return Json(new
{
sEcho = dataTablesParams.sEcho,
iTotalRecords = totalCount,
iTotalDisplayRecords = filteredCount,
aaData = logModels
},
JsonRequestBehavior.AllowGet);
}
[GridAction]
public ActionResult AjaxBinding()
public ActionResult AjaxBindingOld()
{
return View(new GridModel(_logProvider.GetAllLogs()));
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NzbDrone.Web.Models;
namespace NzbDrone.Web.Helpers
{
/// <summary>
/// Model binder for datatables.js parameters a la http://geeksprogramando.blogspot.com/2011/02/jquery-datatables-plug-in-with-asp-mvc.html
/// </summary>
public class DataTablesModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
DataTablesParams obj = new DataTablesParams();
var request = controllerContext.HttpContext.Request.Params;
obj.iDisplayStart = Convert.ToInt32(request["iDisplayStart"]);
obj.iDisplayLength = Convert.ToInt32(request["iDisplayLength"]);
obj.iColumns = Convert.ToInt32(request["iColumns"]);
obj.sSearch = request["sSearch"];
obj.bEscapeRegex = Convert.ToBoolean(request["bEscapeRegex"]);
obj.iSortingCols = Convert.ToInt32(request["iSortingCols"]);
obj.sEcho = int.Parse(request["sEcho"]);
for (int i = 0; i < obj.iColumns; i++)
{
obj.bSortable.Add(Convert.ToBoolean(request["bSortable_" + i]));
obj.bSearchable.Add(Convert.ToBoolean(request["bSearchable_" + i]));
obj.sSearchColumns.Add(request["sSearch_" + i]);
obj.bEscapeRegexColumns.Add(Convert.ToBoolean(request["bEscapeRegex_" + i]));
obj.iSortCol.Add(Convert.ToInt32(request["iSortCol_" + i]));
obj.sSortDir.Add(request["sSortDir_" + i]);
}
return obj;
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace NzbDrone.Web.Models
{
public class DataTablesParams
{
public int iDisplayStart { get; set; }
public int iDisplayLength { get; set; }
public int iColumns { get; set; }
public string sSearch { get; set; }
public bool bEscapeRegex { get; set; }
public int iSortingCols { get; set; }
public int sEcho { get; set; }
public List<bool> bSortable { get; set; }
public List<bool> bSearchable { get; set; }
public List<string> sSearchColumns { get; set; }
public List<int> iSortCol { get; set; }
public List<string> sSortDir { get; set; }
public List<bool> bEscapeRegexColumns { get; set; }
public DataTablesParams()
{
bSortable = new List<bool>();
bSearchable = new List<bool>();
sSearchColumns = new List<string>();
iSortCol = new List<int>();
sSortDir = new List<string>();
bEscapeRegexColumns = new List<bool>();
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace NzbDrone.Web.Models
{
public class LogModel
{
public string Message { get; set; }
public string Time { get; set; }
public string Source { get; set; }
public string Method { get; set; }
public string Exception { get; set; }
public string ExceptionType { get; set; }
public string Level { get; set; }
public string Details { get; set; }
}
}

View File

@ -45,6 +45,9 @@
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="Dynamic">
<HintPath>..\packages\DynamicQuery.1.0\lib\35\Dynamic.dll</HintPath>
</Reference>
<Reference Include="EntityFramework, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\EntityFramework.4.2.0.0\lib\net40\EntityFramework.dll</HintPath>
</Reference>
@ -270,6 +273,7 @@
<Compile Include="App_Start\EntityFramework.SqlServerCompact.cs" />
<Compile Include="App_Start\Logging.cs" />
<Compile Include="App_Start\MiniProfiler.cs" />
<Compile Include="App_Start\RegisterDataTablesModelBinder.cs" />
<Compile Include="Filters\JsonErrorFilter.cs" />
<Compile Include="Controllers\CommandController.cs" />
<Compile Include="Controllers\DirectoryController.cs" />
@ -290,6 +294,7 @@
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Helpers\DataTablesModelBinder.cs" />
<Compile Include="Helpers\HtmlIncludeExtentions.cs" />
<Compile Include="Helpers\LinkHelper.cs" />
<Compile Include="Helpers\ProfilerHelper.cs" />
@ -297,6 +302,8 @@
<Compile Include="Helpers\DescriptionExtension.cs" />
<Compile Include="Helpers\HtmlPrefixScopeExtensions.cs" />
<Compile Include="Helpers\IsCurrentActionHelper.cs" />
<Compile Include="Models\DataTablesParams.cs" />
<Compile Include="Models\LogModel.cs" />
<Compile Include="Models\UpcomingEpisodesModel.cs" />
<Compile Include="Models\SeasonModel.cs" />
<Compile Include="Models\SeriesDetailsModel.cs" />
@ -410,6 +417,7 @@
<Content Include="Content\themes\base\minified\jquery.ui.slider.min.css" />
<Content Include="Content\themes\base\minified\jquery.ui.tabs.min.css" />
<Content Include="Content\themes\base\minified\jquery.ui.theme.min.css" />
<Content Include="Dynamic Expressions.html" />
<Content Include="favicon.ico" />
<Content Include="Global.asax" />
<Content Include="Scripts\2011.3.1115\jquery-1.6.4.min.js" />
@ -680,6 +688,7 @@
<None Include="Scripts\DataTables-1.9.0\extras\FixedColumns\media\js\FixedColumns.min.js.gz" />
<None Include="Scripts\DataTables-1.9.0\extras\Scroller\media\js\Scroller.min.js.gz" />
<None Include="Scripts\DataTables-1.9.0\extras\TableTools\media\js\TableTools.min.js.gz" />
<None Include="Views\Log\IndexOld.cshtml" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />

View File

@ -15,7 +15,7 @@
}
<div class="grid-container">
<table id="historyGrid" class="hidden-grid">
<table id="historyGrid" class="dataTablesGrid hidden-grid">
<thead>
<tr>
<th></th>
@ -33,10 +33,6 @@
</tr>
</thead>
<tbody>
@*@foreach(var history in Model)
{
Html.RenderPartial("History", history);
}*@
</tbody>
</table>
</div>
@ -53,7 +49,7 @@
$(document).ready(function () {
$('#historyGrid').removeClass('hidden-grid');
oTable = $('#historyGrid').dataTable({
oTable = $('.dataTablesGrid').dataTable({
//"sAjaxSource": "History/AjaxBinding",
//"bProcessing": true,
"bShowAll": false,

View File

@ -2,26 +2,6 @@
@using NzbDrone.Core.Instrumentation
@using NzbDrone.Web.Helpers
@model IEnumerable<NzbDrone.Core.Instrumentation.Log>
@section Scripts{
<script type="text/javascript">
function onRowDataBound(e) {
e.row.style.boarder = "";
if (e.dataItem.Level == "Warn") {
e.row.style.backgroundColor = "#FFD700";
}
else if (e.dataItem.Level == "Error") {
e.row.style.backgroundColor = "#FF7500";
}
else if (e.dataItem.Level == "Fatal") {
e.row.style.backgroundColor = "black";
e.row.style.color = "red";
}
//e.row.style.color = 'blue';
}
</script>
}
@{ ViewBag.Title = "Logs";}
@section ActionMenu{
<ul class="sub-menu">
@ -29,37 +9,86 @@
<li>@Html.ActionLink("File", "File", "Log")</li>
</ul>
}
@section HeaderContent{
<style>
#logGrid td {
padding: 2px 8px 2px 8px;
}
</style>
}
<div class="infoBox">
Log entries older than 30 days are automatically deleted.</div>
@{Html.Telerik().Grid<Log>().Name("logsGrid")
.TableHtmlAttributes(new { @class = "Grid" })
.Columns(columns =>
{
columns.Bound(c => c.Time).Title("Time").Width(170);
columns.Bound(c => c.Level).Title("Level").Width(70);
columns.Bound(c => c.Logger).Title("Source");
columns.Bound(c => c.Message);
})
.DetailView(detailView => detailView.ClientTemplate(
"<div>Method: <#= Method #></div>" +
"<div><#= ExceptionType #></div>" +
"<div class='stackframe'><#= Exception #></div>"
))
.DataBinding(data => data.Ajax().Select("AjaxBinding", "Log").Enabled(true))
.Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.Time).Descending()).Enabled(true))
.Pageable(paging => paging.Style(GridPagerStyles.Status).PageOnScroll(true).PageSize(100))
.Filterable()
.ClientEvents(c => c.OnRowDataBound("onRowDataBound"))
.Scrollable(c => c.Height(500))
.ClientEvents(clientEvents =>
{
if (EnviromentProvider.IsProduction)
clientEvents.OnError("grid_onError");
})
.Render();}
<script type="text/javascript">
function reloadGrid() {
var grid = $('#logsGrid').data('tGrid');
grid.rebind();
}
</script>
<div class="grid-container">
<table id="logGrid" class="dataTablesGrid hidden-grid">
<thead>
<tr>
<th>Time</th>
<th>Level</th>
<th>Source</th>
<th>Message</th>
@*Details Column*@
<th style="display: none;">Details</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
@section Scripts{
<script type="text/javascript">
$(document).ready(function() {
$('.dataTablesGrid').removeClass('hidden-grid');
oTable = $('#logGrid').dataTable({
"sAjaxSource": "Log/AjaxBinding",
"bProcessing": true,
"bServerSide": true,
"bShowAll": false,
"bPaginate": true,
"bLengthChange": false,
"bFilter": true,
"bSort": true,
"bInfo": true,
"bAutoWidth": false,
"iDisplayLength": 50,
"sPaginationType": "four_button",
"aoColumns": [
{ sWidth: '150px', "mDataProp": "Time" }, //Time
{ sWidth: '80px', "mDataProp": "Level" }, //Level
{ sWidth: '240px', "mDataProp": "Source" }, //Source
{ sWidth: 'auto', "mDataProp": "Message", "bSortable": false }, //Message
{ sWidth: 'auto', "mDataProp": "Details", "bSortable": false, "bVisible": false, "fnRender": function (row) {
var result = "<div>Method: " + row.aData["Method"] + "</div>" +
"<div>Exception Type: " + row.aData["ExceptionType"] + "</div>" +
"<div class=\"stackFrame\">Exception: " + row.aData["Exception"] + "</div>";
return result;
}
} //Details
],
"aaSorting": [[0, 'desc']],
"fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull) {
fnRowCallback(nRow, aData);
}
});
});
function fnRowCallback(nRow, aData) {
//e.row.style.boarder = "";
if (aData["Level"] == "Warn") {
nRow.style.backgroundColor = "#FFD700";
}
else if (aData["Level"] == "Error") {
nRow.style.backgroundColor = "#FF7500";
}
else if (aData["Level"] == "Fatal") {
nRow.style.backgroundColor = "black";
nRow.style.color = "red";
}
}
</script>
}

View File

@ -0,0 +1,65 @@
@using NzbDrone.Common
@using NzbDrone.Core.Instrumentation
@using NzbDrone.Web.Helpers
@model IEnumerable<NzbDrone.Core.Instrumentation.Log>
@section Scripts{
<script type="text/javascript">
function onRowDataBound(e) {
e.row.style.boarder = "";
if (e.dataItem.Level == "Warn") {
e.row.style.backgroundColor = "#FFD700";
}
else if (e.dataItem.Level == "Error") {
e.row.style.backgroundColor = "#FF7500";
}
else if (e.dataItem.Level == "Fatal") {
e.row.style.backgroundColor = "black";
e.row.style.color = "red";
}
//e.row.style.color = 'blue';
}
</script>
}
@{ ViewBag.Title = "Logs";}
@section ActionMenu{
<ul class="sub-menu">
<li>@Ajax.ActionLink("Clear Logs", "Clear", "Log", new AjaxOptions { OnSuccess = "reloadGrid" })</li>
<li>@Html.ActionLink("File", "File", "Log")</li>
</ul>
}
<div class="infoBox">
Log entries older than 30 days are automatically deleted.</div>
@{Html.Telerik().Grid<Log>().Name("logsGrid")
.TableHtmlAttributes(new { @class = "Grid" })
.Columns(columns =>
{
columns.Bound(c => c.Time).Title("Time").Width(170);
columns.Bound(c => c.Level).Title("Level").Width(70);
columns.Bound(c => c.Logger).Title("Source");
columns.Bound(c => c.Message);
})
.DetailView(detailView => detailView.ClientTemplate(
"<div>Method: <#= Method #></div>" +
"<div><#= ExceptionType #></div>" +
"<div class='stackframe'><#= Exception #></div>"
))
.DataBinding(data => data.Ajax().Select("AjaxBindingOld", "Log").Enabled(true))
.Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.Time).Descending()).Enabled(true))
.Pageable(paging => paging.Style(GridPagerStyles.Status).PageOnScroll(true).PageSize(100))
.Filterable()
.ClientEvents(c => c.OnRowDataBound("onRowDataBound"))
.Scrollable(c => c.Height(500))
.ClientEvents(clientEvents =>
{
if (EnviromentProvider.IsProduction)
clientEvents.OnError("grid_onError");
})
.Render();}
<script type="text/javascript">
function reloadGrid() {
var grid = $('#logsGrid').data('tGrid');
grid.rebind();
}
</script>

View File

@ -16,7 +16,7 @@
}
<div class="grid-container">
<table id="historyGrid" class="hidden-grid">
<table id="missingGrid" class="dataTablesGrid hidden-grid">
<thead>
<tr>
<th>Series Title</th>
@ -44,7 +44,7 @@
$(document).ready(function () {
$('#historyGrid').removeClass('hidden-grid');
oTable = $('#historyGrid').dataTable({
oTable = $('.dataTablesGrid').dataTable({
//"sAjaxSource": "History/AjaxBinding",
//"bProcessing": true,
"bShowAll": false,

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DynamicQuery" version="1.0" />
<package id="EntityFramework" version="4.2.0.0" />
<package id="EntityFramework.SqlServerCompact" version="4.1.8482.2" />
<package id="jQuery" version="1.6.1" />

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.