diff --git a/NzbDrone.Api/AutomapperBootstraper.cs b/NzbDrone.Api/AutomapperBootstraper.cs index ba6d4c4e6..7af24e420 100644 --- a/NzbDrone.Api/AutomapperBootstraper.cs +++ b/NzbDrone.Api/AutomapperBootstraper.cs @@ -2,6 +2,7 @@ using AutoMapper; using NzbDrone.Api.Calendar; using NzbDrone.Api.Episodes; +using NzbDrone.Api.History; using NzbDrone.Api.Missing; using NzbDrone.Api.QualityProfiles; using NzbDrone.Api.QualityType; @@ -42,6 +43,10 @@ namespace NzbDrone.Api //Episode Paging Mapper.CreateMap, PagingResource>(); + + //History + Mapper.CreateMap(); + Mapper.CreateMap, PagingResource>(); } } } \ No newline at end of file diff --git a/NzbDrone.Api/History/HistoryModule.cs b/NzbDrone.Api/History/HistoryModule.cs new file mode 100644 index 000000000..0e6a34af2 --- /dev/null +++ b/NzbDrone.Api/History/HistoryModule.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using AutoMapper; +using Nancy; +using NzbDrone.Api.Episodes; +using NzbDrone.Api.Extensions; +using NzbDrone.Core.Datastore; +using NzbDrone.Core.History; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Api.History +{ + public class HistoryModule : NzbDroneApiModule + { + private readonly IHistoryService _historyService; + + public HistoryModule(IHistoryService historyService) + : base("/history") + { + _historyService = historyService; + Get["/"] = x => GetHistory(); + } + + private Response GetHistory() + { + //TODO: common page parsing logic should be done somewhere else + + int pageSize; + Int32.TryParse(PrimitiveExtensions.ToNullSafeString(Request.Query.PageSize), out pageSize); + if (pageSize == 0) pageSize = 20; + + int page; + Int32.TryParse(PrimitiveExtensions.ToNullSafeString(Request.Query.Page), out page); + if (page == 0) page = 1; + + var sortKey = PrimitiveExtensions.ToNullSafeString(Request.Query.SortKey) + .Equals("SeriesTitle", StringComparison.InvariantCultureIgnoreCase) + ? "SeriesTitle" + : "AirDate"; + + var sortDirection = PrimitiveExtensions.ToNullSafeString(Request.Query.SortDir) + .Equals("Asc", StringComparison.InvariantCultureIgnoreCase) + ? ListSortDirection.Ascending + : ListSortDirection.Descending; + + var pagingSpec = new PagingSpec + { + Page = page, + PageSize = pageSize, + SortKey = sortKey, + SortDirection = sortDirection + }; + + var series = new Core.Tv.Series { Title = "30 Rock", TitleSlug = "30-rock" }; + var episode = new Episode { Title = "Test", SeasonNumber = 1, EpisodeNumber = 5 }; + + var result = new PagingSpec + { + Records = new List + { + new Core.History.History + { + Id = 1, + Date = DateTime.UtcNow.AddHours(-5), + Episode = episode, + Series = series, + Indexer = "nzbs.org", + Quality = new QualityModel(Quality.HDTV720p) + } + } + }; + + result.TotalRecords = result.Records.Count; + + return Mapper.Map, PagingResource>(result).AsResponse(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/History/HistoryResource.cs b/NzbDrone.Api/History/HistoryResource.cs new file mode 100644 index 000000000..c26028266 --- /dev/null +++ b/NzbDrone.Api/History/HistoryResource.cs @@ -0,0 +1,21 @@ +using System; +using NzbDrone.Api.REST; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Api.History +{ + public class HistoryResource : RestResource + { + public int EpisodeId { get; set; } + public int SeriesId { get; set; } + public string NzbTitle { get; set; } + public QualityModel Quality { get; set; } + public DateTime Date { get; set; } + public string Indexer { get; set; } + public string NzbInfoUrl { get; set; } + public string ReleaseGroup { get; set; } + + public Episode Episode { get; set; } + public Core.Tv.Series Series { get; set; } + } +} diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 631f29021..e3110a089 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -96,6 +96,8 @@ + + diff --git a/NzbDrone.Core/Datastore/TableMapping.cs b/NzbDrone.Core/Datastore/TableMapping.cs index 8762074ea..d9bbeed66 100644 --- a/NzbDrone.Core/Datastore/TableMapping.cs +++ b/NzbDrone.Core/Datastore/TableMapping.cs @@ -38,9 +38,10 @@ namespace NzbDrone.Core.Datastore Mapper.Entity().RegisterModel("SceneMappings"); - Mapper.Entity().RegisterModel("History") - .Relationship() - .HasOne(h => h.Episode, h => h.EpisodeId); + Mapper.Entity().RegisterModel("History"); +// .Relationship() +// .HasOne(h => h.Episode, h => h.EpisodeId) +// .HasOne(h => h.Series, h => h.SeriesId); Mapper.Entity().RegisterModel("Series") .Ignore(s => s.Path) diff --git a/NzbDrone.Core/History/History.cs b/NzbDrone.Core/History/History.cs index b05b6569e..e248fdd7b 100644 --- a/NzbDrone.Core/History/History.cs +++ b/NzbDrone.Core/History/History.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Marr.Data; using NzbDrone.Core.Datastore; using NzbDrone.Core.Tv; @@ -16,6 +17,7 @@ namespace NzbDrone.Core.History public string NzbInfoUrl { get; set; } public string ReleaseGroup { get; set; } - public LazyLoaded Episode { get; set; } + public Episode Episode { get; set; } + public Series Series { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Tv/SeriesRepository.cs b/NzbDrone.Core/Tv/SeriesRepository.cs index 9d17552d2..182722563 100644 --- a/NzbDrone.Core/Tv/SeriesRepository.cs +++ b/NzbDrone.Core/Tv/SeriesRepository.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Core.Tv { bool SeriesPathExists(string path); List Search(string title); + Series FindByTitle(string cleanTitle); Series FindByTvdbId(int tvdbId); void SetSeriesType(int seriesId, SeriesTypes seriesTypes); diff --git a/UI/Controller.js b/UI/Controller.js index 155326f51..f5dbaed87 100644 --- a/UI/Controller.js +++ b/UI/Controller.js @@ -4,7 +4,8 @@ define(['app', 'Shared/ModalRegion', 'AddSeries/AddSeriesLayout', 'Calendar/CalendarCollectionView', 'Shared/NotificationView', 'Shared/NotFoundView', 'MainMenuView', 'Series/Details/SeriesDetailsView', 'Series/EpisodeCollection', - 'Settings/SettingsLayout', 'Missing/MissingLayout'], + 'Settings/SettingsLayout', 'Missing/MissingLayout', + 'History/HistoryLayout'], function (app, modalRegion) { var controller = Backbone.Marionette.Controller.extend({ @@ -61,6 +62,12 @@ define(['app', 'Shared/ModalRegion', 'AddSeries/AddSeriesLayout', NzbDrone.mainRegion.show(new NzbDrone.Missing.MissingLayout()); }, + history: function () { + this._setTitle('History'); + + NzbDrone.mainRegion.show(new NzbDrone.History.HistoryLayout()); + }, + notFound: function () { this._setTitle('Not Found'); NzbDrone.mainRegion.show(new NzbDrone.Shared.NotFoundView(this)); diff --git a/UI/History/Collection.js b/UI/History/Collection.js new file mode 100644 index 000000000..ad2588824 --- /dev/null +++ b/UI/History/Collection.js @@ -0,0 +1,37 @@ +"use strict"; +define(['app', 'History/Model'], function () { + NzbDrone.Missing.Collection = Backbone.PageableCollection.extend({ + url : NzbDrone.Constants.ApiRoot + '/history', + model : NzbDrone.History.Model, + + state: { + pageSize: 10, + sortKey: "date", + 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; + } + }); +}); \ No newline at end of file diff --git a/UI/History/ControlsColumnTemplate.html b/UI/History/ControlsColumnTemplate.html new file mode 100644 index 000000000..2783a2600 --- /dev/null +++ b/UI/History/ControlsColumnTemplate.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/UI/History/EpisodeColumnTemplate.html b/UI/History/EpisodeColumnTemplate.html new file mode 100644 index 000000000..30ab0a188 --- /dev/null +++ b/UI/History/EpisodeColumnTemplate.html @@ -0,0 +1,2 @@ + +{{seasonNumber}}x{{paddedEpisodeNumber}} \ No newline at end of file diff --git a/UI/History/HistoryLayout.js b/UI/History/HistoryLayout.js new file mode 100644 index 000000000..4ed0c2972 --- /dev/null +++ b/UI/History/HistoryLayout.js @@ -0,0 +1,89 @@ +"use strict"; +define([ + 'app', + 'History/Collection', + 'Series/Index/Table/AirDateCell', + 'Shared/Toolbar/ToolbarView', + 'Shared/Toolbar/ToolbarLayout' +], + function () { + NzbDrone.History.HistoryLayout = Backbone.Marionette.Layout.extend({ + template: 'History/HistoryLayoutTemplate', + + regions: { + history: '#x-history', + toolbar: '#x-toolbar', + pager : '#x-pager' + }, + + showTable: function () { + + var columns = [ + { + name : 'seriesTitle', + label : 'Series Title', + editable : false, + cell : Backgrid.TemplateBackedCell.extend({ template: 'History/SeriesTitleTemplate' }), + headerCell: 'nzbDrone' + }, + { + name : 'episode', + label : 'Episode', + editable : false, + sortable : false, + cell : Backgrid.TemplateBackedCell.extend({ template: 'History/EpisodeColumnTemplate' }), + headerCell: 'nzbDrone' + }, + { + name : 'title', + label : 'Episode Title', + editable : false, + sortable : false, + cell : 'string', + headerCell: 'nzbDrone' + }, + { + name : 'airDate', + label : 'Air Date', + editable : false, + cell : 'airDate', + headerCell: 'nzbDrone' + }, + { + name : 'edit', + label : '', + editable : false, + sortable : false, + cell : Backgrid.TemplateBackedCell.extend({ template: 'History/ControlsColumnTemplate' }), + headerCell: 'nzbDrone' + } + ]; + + this.history.show(new Backgrid.Grid( + { + row : NzbDrone.History.Row, + columns : columns, + collection: this.historyCollection, + className : 'table table-hover' + })); + + this.pager.show(new Backgrid.NzbDronePaginator({ + columns: columns, + collection: this.historyCollection + })); + }, + + initialize: function () { + this.historyCollection = new NzbDrone.History.Collection(); + this.historyCollection.fetch(); + }, + + onShow: function () { + this.showTable(); + //this.toolbar.show(new NzbDrone.Shared.Toolbar.ToolbarLayout({right: [ viewButtons], context: this})); + } + + }) + ; + }) +; diff --git a/UI/History/HistoryLayoutTemplate.html b/UI/History/HistoryLayoutTemplate.html new file mode 100644 index 000000000..edf286c9a --- /dev/null +++ b/UI/History/HistoryLayoutTemplate.html @@ -0,0 +1,11 @@ +
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/UI/History/Model.js b/UI/History/Model.js new file mode 100644 index 000000000..55352f398 --- /dev/null +++ b/UI/History/Model.js @@ -0,0 +1,6 @@ +"use strict"; +define(['app'], function (app) { + NzbDrone.History.Model = Backbone.Model.extend({ + + }); +}); diff --git a/UI/History/Row.js b/UI/History/Row.js new file mode 100644 index 000000000..76003b0f2 --- /dev/null +++ b/UI/History/Row.js @@ -0,0 +1,9 @@ +NzbDrone.Missing.Row = Backgrid.Row.extend({ + events: { + 'click .x-search' : 'search' + }, + + search: function () { + window.alert('Episode Search'); + } +}); \ No newline at end of file diff --git a/UI/History/SeriesTitleTemplate.html b/UI/History/SeriesTitleTemplate.html new file mode 100644 index 000000000..0a8c3ae09 --- /dev/null +++ b/UI/History/SeriesTitleTemplate.html @@ -0,0 +1 @@ +{{series.title}} \ No newline at end of file diff --git a/UI/Missing/MissingLayout.js b/UI/Missing/MissingLayout.js index dd5ff3d2c..61eabeb12 100644 --- a/UI/Missing/MissingLayout.js +++ b/UI/Missing/MissingLayout.js @@ -71,12 +71,11 @@ define([ columns: columns, collection: this.missingCollection })); - - this.missingCollection.getFirstPage(); }, initialize: function () { this.missingCollection = new NzbDrone.Missing.Collection(); + this.missingCollection.fetch(); }, onShow: function () { diff --git a/UI/Routing.js b/UI/Routing.js index 0184fe7b2..9e2294b17 100644 --- a/UI/Routing.js +++ b/UI/Routing.js @@ -17,6 +17,7 @@ require(['app', 'Controller'], function (app, controller) { 'settings' : 'settings', 'settings/:action(/:query)' : 'settings', 'missing' : 'missing', + 'history' : 'history', ':whatever' : 'notFound' } }); diff --git a/UI/Series/SeriesCollection.js b/UI/Series/SeriesCollection.js index 2d9c58723..53ecbdff2 100644 --- a/UI/Series/SeriesCollection.js +++ b/UI/Series/SeriesCollection.js @@ -10,6 +10,11 @@ define(['app', 'Series/SeriesModel'], function () { sortKey: "title", order: -1, pageSize: 1000000 + }, + + queryParams: { + sortKey: null, + order: null } }); }); diff --git a/UI/app.js b/UI/app.js index 4659f15c0..7d70bbe4e 100644 --- a/UI/app.js +++ b/UI/app.js @@ -58,6 +58,7 @@ define('app', function () { window.NzbDrone.Settings.System = {}; window.NzbDrone.Settings.Misc = {}; window.NzbDrone.Missing = {}; + window.NzbDrone.History = {}; window.NzbDrone.Events = { //TODO: Move to commands