From 9160343a51632e4aa1356a26bb57faed8d84ba44 Mon Sep 17 00:00:00 2001 From: Keivan Beigi Date: Tue, 4 Jun 2013 17:49:53 -0700 Subject: [PATCH] added /logs --- .../MappingTests/ResourceMappingFixture.cs | 3 + NzbDrone.Api/Logs/LogModule.cs | 25 +++++++ NzbDrone.Api/Logs/LogResource.cs | 16 +++++ NzbDrone.Api/NzbDrone.Api.csproj | 2 + NzbDrone.Api/REST/RestModule.cs | 30 ++++---- NzbDrone.Core/Datastore/BasicRepository.cs | 16 +++++ NzbDrone.Core/History/HistoryRepository.cs | 3 +- NzbDrone.Core/History/HistoryService.cs | 2 +- NzbDrone.Core/Instrumentation/LogService.cs | 7 ++ UI/Controller.js | 7 ++ UI/Logs/Collection.js | 37 ++++++++++ UI/Logs/Layout.js | 72 +++++++++++++++++++ UI/Logs/LayoutTemplate.html | 11 +++ UI/Logs/Model.js | 14 ++++ UI/Routing.js | 1 + UI/app.js | 1 + 16 files changed, 231 insertions(+), 16 deletions(-) create mode 100644 NzbDrone.Api/Logs/LogModule.cs create mode 100644 NzbDrone.Api/Logs/LogResource.cs create mode 100644 UI/Logs/Collection.js create mode 100644 UI/Logs/Layout.js create mode 100644 UI/Logs/LayoutTemplate.html create mode 100644 UI/Logs/Model.js diff --git a/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs b/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs index 0dd0ef7c4..50831a45c 100644 --- a/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs +++ b/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs @@ -8,6 +8,7 @@ using NzbDrone.Api.Config; using NzbDrone.Api.Episodes; using NzbDrone.Api.History; using NzbDrone.Api.Indexers; +using NzbDrone.Api.Logs; using NzbDrone.Api.Mapping; using NzbDrone.Api.Qualities; using NzbDrone.Api.RootFolders; @@ -15,6 +16,7 @@ using NzbDrone.Api.Series; using NzbDrone.Api.Update; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -40,6 +42,7 @@ namespace NzbDrone.Api.Test.MappingTests [TestCase(typeof(UpdatePackage), typeof(UpdateResource))] [TestCase(typeof(QualityProfile), typeof(QualityProfileResource))] [TestCase(typeof(Quality), typeof(QualityResource))] + [TestCase(typeof(Log), typeof(LogResource))] public void matching_fields(Type modelType, Type resourceType) { MappingValidation.ValidateMapping(modelType, resourceType); diff --git a/NzbDrone.Api/Logs/LogModule.cs b/NzbDrone.Api/Logs/LogModule.cs new file mode 100644 index 000000000..f65e99602 --- /dev/null +++ b/NzbDrone.Api/Logs/LogModule.cs @@ -0,0 +1,25 @@ +using NzbDrone.Core.Datastore; +using NzbDrone.Core.History; +using NzbDrone.Core.Instrumentation; +using NzbDrone.Api.Mapping; + +namespace NzbDrone.Api.Logs +{ + public class LogModule : NzbDroneRestModule + { + private readonly ILogService _logService; + private readonly IHistoryService _historyService; + + public LogModule(ILogService logService) + { + _logService = logService; + GetResourcePaged = GetLogs; + } + + private PagingResource GetLogs(PagingResource pagingResource) + { + var pageSpec = pagingResource.InjectTo>(); + return ApplyToPage(_logService.Paged, pageSpec); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Logs/LogResource.cs b/NzbDrone.Api/Logs/LogResource.cs new file mode 100644 index 000000000..bb6370303 --- /dev/null +++ b/NzbDrone.Api/Logs/LogResource.cs @@ -0,0 +1,16 @@ +using System; +using NzbDrone.Api.REST; + +namespace NzbDrone.Api.Logs +{ + public class LogResource : RestResource + { + public DateTime Time { get; set; } + public String Exception { get; set; } + public String ExceptionType { get; set; } + public String Level { get; set; } + public String Logger { get; set; } + public String Message { get; set; } + public String Method { get; set; } + } +} diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index da25c790c..8fa07e4eb 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -115,6 +115,8 @@ + + diff --git a/NzbDrone.Api/REST/RestModule.cs b/NzbDrone.Api/REST/RestModule.cs index b3dbb3b4f..c5e18447e 100644 --- a/NzbDrone.Api/REST/RestModule.cs +++ b/NzbDrone.Api/REST/RestModule.cs @@ -195,21 +195,25 @@ namespace NzbDrone.Api.REST Int32.TryParse(Request.Query.Page.ToString(), out page); if (page == 0) page = 1; - var sortKey = Request.Query.SortKey.ToString(); - if (String.IsNullOrEmpty(sortKey)) sortKey = "AirDate"; - - var sortDirection = Request.Query.SortDir.ToString() - .Equals("Asc", StringComparison.InvariantCultureIgnoreCase) - ? SortDirection.Ascending - : SortDirection.Descending; var pagingResource = new PagingResource - { - PageSize = pageSize, - Page = page, - SortKey = sortKey, - SortDirection = sortDirection - }; + { + PageSize = pageSize, + Page = page, + }; + + if (Request.Query.SortKey != null) + { + pagingResource.SortKey = Request.Query.SortKey.ToString(); + + if (Request.Query.SortDir != null) + { + pagingResource.SortDirection = Request.Query.SortDir.ToString() + .Equals("Asc", StringComparison.InvariantCultureIgnoreCase) + ? SortDirection.Ascending + : SortDirection.Descending; + } + } return pagingResource; } diff --git a/NzbDrone.Core/Datastore/BasicRepository.cs b/NzbDrone.Core/Datastore/BasicRepository.cs index f4266fa95..1ca871339 100644 --- a/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/NzbDrone.Core/Datastore/BasicRepository.cs @@ -31,6 +31,7 @@ namespace NzbDrone.Core.Datastore void DeleteMany(IEnumerable ids); void SetFields(TModel model, params Expression>[] properties); TModel Single(); + PagingSpec GetPaged(PagingSpec pagingSpec); } @@ -198,6 +199,21 @@ namespace NzbDrone.Core.Datastore } + public virtual PagingSpec GetPaged(PagingSpec pagingSpec) + { + var pagingQuery = Query.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) + .Skip(pagingSpec.PagingOffset()) + .Take(pagingSpec.PageSize); + + pagingSpec.Records = pagingQuery.ToList(); + + //TODO: Use the same query for count and records + pagingSpec.TotalRecords = Count(); + + return pagingSpec; + } + + private void PublishModelEvent(TModel model, RepositoryAction action) { if (PublishModelEvents) diff --git a/NzbDrone.Core/History/HistoryRepository.cs b/NzbDrone.Core/History/HistoryRepository.cs index c3ad41da3..e391954f9 100644 --- a/NzbDrone.Core/History/HistoryRepository.cs +++ b/NzbDrone.Core/History/HistoryRepository.cs @@ -13,7 +13,6 @@ namespace NzbDrone.Core.History { void Trim(); List GetEpisodeHistory(int episodeId); - PagingSpec Paged(PagingSpec pagingSpec); } public class HistoryRepository : BasicRepository, IHistoryRepository @@ -36,7 +35,7 @@ namespace NzbDrone.Core.History return history.Select(h => h.Quality).ToList(); } - public PagingSpec Paged(PagingSpec pagingSpec) + public override PagingSpec GetPaged(PagingSpec pagingSpec) { var pagingQuery = Query.Join(JoinType.Inner, h => h.Series, (h, s) => h.SeriesId == s.Id) .Join(JoinType.Inner, h => h.Episode, (h, e) => h.EpisodeId == e.Id) diff --git a/NzbDrone.Core/History/HistoryService.cs b/NzbDrone.Core/History/HistoryService.cs index d1200780c..7bbda0e41 100644 --- a/NzbDrone.Core/History/HistoryService.cs +++ b/NzbDrone.Core/History/HistoryService.cs @@ -36,7 +36,7 @@ namespace NzbDrone.Core.History public PagingSpec Paged(PagingSpec pagingSpec) { - return _historyRepository.Paged(pagingSpec); + return _historyRepository.GetPaged(pagingSpec); } public void Purge() diff --git a/NzbDrone.Core/Instrumentation/LogService.cs b/NzbDrone.Core/Instrumentation/LogService.cs index bb4460d3c..d214166dd 100644 --- a/NzbDrone.Core/Instrumentation/LogService.cs +++ b/NzbDrone.Core/Instrumentation/LogService.cs @@ -1,4 +1,5 @@ using System.Linq; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Instrumentation { @@ -6,6 +7,7 @@ namespace NzbDrone.Core.Instrumentation { void DeleteAll(); void Trim(); + PagingSpec Paged(PagingSpec pagingSpec); } public class LogService : ILogService @@ -26,5 +28,10 @@ namespace NzbDrone.Core.Instrumentation { _logRepository.Trim(); } + + public PagingSpec Paged(PagingSpec pagingSpec) + { + return _logRepository.GetPaged(pagingSpec); + } } } \ No newline at end of file diff --git a/UI/Controller.js b/UI/Controller.js index bbca914cb..b1e1d71c2 100644 --- a/UI/Controller.js +++ b/UI/Controller.js @@ -10,6 +10,7 @@ define(['app', 'Series/Details/SeriesDetailsLayout', 'Series/EpisodeCollection', 'Settings/SettingsLayout', + 'Logs/Layout', 'Missing/MissingLayout', 'History/HistoryLayout'], function () { @@ -62,11 +63,17 @@ define(['app', NzbDrone.mainRegion.show(new NzbDrone.History.HistoryLayout()); }, + logs: function () { + this._setTitle('logs'); + NzbDrone.mainRegion.show(new NzbDrone.Logs.Layout()); + }, + notFound: function () { this._setTitle('Not Found'); NzbDrone.mainRegion.show(new NzbDrone.Shared.NotFoundView(this)); }, + _setTitle: function (title) { //$('#title-region').html(title); diff --git a/UI/Logs/Collection.js b/UI/Logs/Collection.js new file mode 100644 index 000000000..b7d949f2c --- /dev/null +++ b/UI/Logs/Collection.js @@ -0,0 +1,37 @@ +"use strict"; +define(['app', 'Logs/Model'], function () { + NzbDrone.Logs.Collection = Backbone.PageableCollection.extend({ + url : NzbDrone.Constants.ApiRoot + '/log', + model : NzbDrone.Logs.Model, + + state: { + pageSize: 50, + sortKey: "time", + order: 1 + }, + + queryParams: { + totalPages: null, + totalRecords: null, + pageSize: 'pageSize', + sortKey: "sortKey", + order: "sortDir", + directions: { + "-1": "asc", + "1": "desc" + } + }, + + parseState: function (resp, queryParams, state) { + return {totalRecords: resp.totalRecords}; + }, + + parseRecords: function (resp) { + if (resp) { + return resp.records; + } + + return resp; + } + }); +}); diff --git a/UI/Logs/Layout.js b/UI/Logs/Layout.js new file mode 100644 index 000000000..185ee225b --- /dev/null +++ b/UI/Logs/Layout.js @@ -0,0 +1,72 @@ +"use strict"; +define([ + 'app', + 'Logs/Collection', + 'Shared/Toolbar/ToolbarLayout' +], + function () { + NzbDrone.Logs.Layout = Backbone.Marionette.Layout.extend({ + template: 'Logs/LayoutTemplate', + + regions: { + grid : '#x-grid', + toolbar: '#x-toolbar', + pager : '#x-pager' + }, + + columns: [ + { + name : 'level', + label : 'Level', + sortable: true, + cell : Backgrid.StringCell + }, + { + name : 'logger', + label : 'Component', + sortable: true, + cell : Backgrid.StringCell + }, + { + name : 'message', + label : 'Message', + sortable: false, + cell : Backgrid.StringCell + }, + { + name : 'time', + label: 'Time', + cell : Backgrid.DatetimeCell + } + ], + + showTable: function () { + + this.grid.show(new Backgrid.Grid( + { + row : Backgrid.Row, + columns : this.columns, + collection: this.collection, + className : 'table table-hover' + })); + + this.pager.show(new Backgrid.NzbDronePaginator({ + columns : this.columns, + collection: this.collection + })); + }, + + initialize: function () { + this.collection = new NzbDrone.Logs.Collection(); + this.collection.fetch(); + }, + + onShow: function () { + this.showTable(); + //this.toolbar.show(new NzbDrone.Shared.Toolbar.ToolbarLayout({right: [ viewButtons], context: this})); + } + + }) + ; + }) +; diff --git a/UI/Logs/LayoutTemplate.html b/UI/Logs/LayoutTemplate.html new file mode 100644 index 000000000..d8a74911a --- /dev/null +++ b/UI/Logs/LayoutTemplate.html @@ -0,0 +1,11 @@ +
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/Logs/Model.js b/UI/Logs/Model.js new file mode 100644 index 000000000..7f0ff7edf --- /dev/null +++ b/UI/Logs/Model.js @@ -0,0 +1,14 @@ +"use strict"; +define(['app'], function (app) { + NzbDrone.Logs.Model = Backbone.Model.extend({ + /* mutators: { + seasonNumber: function () { + return this.get('episode').seasonNumber; + }, + + paddedEpisodeNumber: function () { + return this.get('episode').episodeNumber.pad(2); + } + }*/ + }); +}); diff --git a/UI/Routing.js b/UI/Routing.js index cc8fb9e58..53c7914a2 100644 --- a/UI/Routing.js +++ b/UI/Routing.js @@ -16,6 +16,7 @@ require(['app', 'Controller'], function (app, controller) { 'settings/:action(/:query)' : 'settings', 'missing' : 'missing', 'history' : 'history', + 'logs' : 'logs', ':whatever' : 'notFound' } }); diff --git a/UI/app.js b/UI/app.js index 4c20bf44f..020b361d1 100644 --- a/UI/app.js +++ b/UI/app.js @@ -82,6 +82,7 @@ define('app', ['shared/modal/region'], function (ModalRegion) { window.NzbDrone.Missing = {}; window.NzbDrone.History = {}; + window.NzbDrone.Logs = {}; window.NzbDrone.Mixins = {}; window.NzbDrone.Events = {