Pagination for missing is alive!

pull/4/head
Mark McDowall 11 years ago
parent c9b9d7b956
commit b4242f9fb2

@ -7,6 +7,7 @@ using NzbDrone.Api.QualityProfiles;
using NzbDrone.Api.QualityType;
using NzbDrone.Api.Resolvers;
using NzbDrone.Api.Series;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
@ -38,6 +39,9 @@ namespace NzbDrone.Api
//Episode
Mapper.CreateMap<Episode, EpisodeResource>();
//Episode Paging
Mapper.CreateMap<PagingSpec<Episode>, PagingResource<EpisodeResource>>();
}
}
}

@ -29,5 +29,6 @@ namespace NzbDrone.Api.Episodes
public DateTime? GrabDate { get; set; }
public PostDownloadStatusType PostDownloadStatus { get; set; }
public Core.Tv.Series Series { get; set; }
public String SeriesTitle { get; set; }
}
}

@ -1,10 +1,12 @@
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.Tv;
namespace NzbDrone.Api.Missing
@ -25,11 +27,35 @@ namespace NzbDrone.Api.Missing
bool includeSpecials;
Boolean.TryParse(PrimitiveExtensions.ToNullSafeString(Request.Query.IncludeSpecials), out includeSpecials);
var episodes = _episodeService.EpisodesWithoutFiles(includeSpecials);
int pageSize;
Int32.TryParse(PrimitiveExtensions.ToNullSafeString(Request.Query.PageSize), out pageSize);
if (pageSize == 0) pageSize = 20;
//TODO: Include the Series Title
//TODO: Remove Take(20)
return Mapper.Map<List<Episode>, List<EpisodeResource>>(episodes).Take(20).AsResponse();
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<Episode>
{
Page = page,
PageSize = pageSize,
SortKey = sortKey,
SortDirection = sortDirection
};
var result = _episodeService.EpisodesWithoutFiles(pagingSpec, includeSpecials);
return Mapper.Map<PagingSpec<Episode>, PagingResource<EpisodeResource>>(result).AsResponse();
}
}
}

@ -102,6 +102,7 @@
<Compile Include="Mapping\ValueInjectorExtensions.cs" />
<Compile Include="Missing\MissingModule.cs" />
<Compile Include="NzbDroneRestModule.cs" />
<Compile Include="PagingResource.cs" />
<Compile Include="Resolvers\EndTimeResolver.cs" />
<Compile Include="Resolvers\NextAiringResolver.cs" />
<Compile Include="Resolvers\NullableDatetimeToString.cs" />

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Api
{
public class PagingResource<TModel>
{
public int Page { get; set; }
public string SortKey { get; set; }
public int TotalRecords { get; set; }
public List<TModel> Records { get; set; }
}
}

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
@ -38,15 +40,30 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
[Test]
public void should_get_episodes()
{
var episodes = Subject.EpisodesWithoutFiles(false);
episodes.Should().HaveCount(1);
var episodes =
Subject.EpisodesWithoutFiles(new PagingSpec<Episode>
{
Page = 1,
PageSize = 10,
SortKey = "AirDate",
SortDirection = ListSortDirection.Ascending
}, false);
episodes.Records.Should().HaveCount(1);
}
[Test]
[Ignore("Specials not implemented")]
public void should_get_episode_including_specials()
{
var episodes = Subject.EpisodesWithoutFiles(true);
episodes.Should().HaveCount(2);
var episodes =
Subject.EpisodesWithoutFiles(new PagingSpec<Episode>
{
Page = 1,
PageSize = 10,
SortKey = "AirDate",
SortDirection = ListSortDirection.Ascending
}, true);
episodes.Records.Should().HaveCount(2);
}
}
}

@ -114,7 +114,6 @@ namespace NzbDrone.Core.Datastore
return model;
}
public void Delete(TModel model)
{
_dataMapper.Delete<TModel>(c => c.Id == model.Id);
@ -185,6 +184,5 @@ namespace NzbDrone.Core.Datastore
.Entity(model)
.Execute();
}
}
}

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Datastore
{
public class PagingSpec<TModel>
{
public int Page { get; set; }
public int PageSize { get; set; }
public int TotalRecords { get; set; }
public string SortKey { get; set; }
public ListSortDirection SortDirection { get; set; }
public List<TModel> Records { get; set; }
}
}

@ -208,6 +208,7 @@
<Compile Include="Datastore\MigrationType.cs" />
<Compile Include="Datastore\ModelBase.cs" />
<Compile Include="Datastore\BasicRepository.cs" />
<Compile Include="Datastore\PagingSpec.cs" />
<Compile Include="Datastore\RelationshipExtensions.cs" />
<Compile Include="Datastore\TableMapping.cs" />
<Compile Include="DecisionEngine\DownloadDecision.cs" />

@ -71,6 +71,8 @@ namespace NzbDrone.Core.Tv
}
}
public String SeriesTitle { get; private set; }
public Series Series { get; set; }
public EpisodeFile EpisodeFile { get; set; }

@ -4,7 +4,9 @@ using System.Linq;
using NLog;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download;
using NzbDrone.Core.Helpers;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Model;
@ -19,7 +21,7 @@ namespace NzbDrone.Core.Tv
Episode GetEpisode(int seriesId, DateTime date);
List<Episode> GetEpisodeBySeries(int seriesId);
List<Episode> GetEpisodesBySeason(int seriesId, int seasonNumber);
List<Episode> EpisodesWithoutFiles(bool includeSpecials);
PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, bool includeSpecials);
List<Episode> GetEpisodesByFileId(int episodeFileId);
List<Episode> EpisodesWithFiles();
void RefreshEpisodeInfo(Series series);
@ -93,11 +95,13 @@ namespace NzbDrone.Core.Tv
return _episodeRepository.GetEpisodes(seriesId, seasonNumber);
}
public List<Episode> EpisodesWithoutFiles(bool includeSpecials)
public PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, bool includeSpecials)
{
var episodes = _episodeRepository.EpisodesWithoutFiles(includeSpecials);
var episodeResult = _episodeRepository.EpisodesWithoutFiles(pagingSpec, includeSpecials);
return LinkSeriesToEpisodes(episodes);
episodeResult.Records = LinkSeriesToEpisodes(episodeResult.Records);
return episodeResult;
}
public List<Episode> GetEpisodesByFileId(int episodeFileId)

@ -58,7 +58,7 @@
@param {boolean} [options.fastForwardHandleLabels] Whether to render fast forward buttons.
*/
initialize: function (options) {
Backgrid.requireOptions(options, ["columns", "collection"]);
//Backgrid.requireOptions(options, ["columns", "collection"]);
this.columns = options.columns;
if (!(this.columns instanceof Backbone.Collection)) {

@ -4,10 +4,6 @@ define(['app', 'Series/EpisodeModel'], function () {
url : NzbDrone.Constants.ApiRoot + '/missing',
model : NzbDrone.Series.EpisodeModel,
comparator: function (model) {
return model.get('airDate');
},
state: {
pageSize: 10,
sortKey: "airDate",
@ -18,12 +14,24 @@ define(['app', 'Series/EpisodeModel'], function () {
totalPages: null,
totalRecords: null,
pageSize: 'pageSize',
sortKey: "sortBy",
order: "direction",
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;
}
});
});

@ -13,7 +13,8 @@ define([
regions: {
missing: '#x-missing',
toolbar: '#x-toolbar'
toolbar: '#x-toolbar',
pager : '#x-pager'
},
showTable: function () {
@ -48,6 +49,12 @@ define([
editable : false,
cell : 'airDate',
headerCell: 'nzbDrone'
// headerCell: Backgrid.NzbDroneHeaderCell.extend({
// initialize: function(options) {
// this.constructor.__super__.initialize.apply(this, [options]);
// this.direction('descending');
// }
// })
},
{
name : 'edit',
@ -66,6 +73,13 @@ define([
collection: this.missingCollection,
className : 'table table-hover'
}));
this.pager.show(new Backgrid.NzbDronePaginator({
columns: columns,
collection: this.missingCollection
}));
this.missingCollection.getFirstPage();
},
initialize: function () {

@ -4,3 +4,8 @@
<div id="x-missing"></div>
</div>
</div>
<div class="row">
<div class="span12">
<div id="x-pager"></div>
</div>
</div>

@ -40,6 +40,40 @@ Backgrid.NzbDroneHeaderCell = Backgrid.HeaderCell.extend({
return this._direction;
},
onClick: function (e) {
e.preventDefault();
var columnName = this.column.get("name");
if (this.column.get("sortable")) {
if (this.direction() === "ascending") {
this.sort(columnName, "descending", function (left, right) {
var leftVal = left.get(columnName);
var rightVal = right.get(columnName);
if (leftVal === rightVal) {
return 0;
}
else if (leftVal > rightVal) { return -1; }
return 1;
});
}
else if (this.direction() === "descending") {
this.sort(columnName, "ascending");
}
else {
this.sort(columnName, "ascending", function (left, right) {
var leftVal = left.get(columnName);
var rightVal = right.get(columnName);
if (leftVal === rightVal) {
return 0;
}
else if (leftVal < rightVal) { return -1; }
return 1;
});
}
}
},
_convertDirectionToIcon: function (dir) {
if (dir === 'ascending') {
return 'icon-sort-up';
@ -47,4 +81,69 @@ Backgrid.NzbDroneHeaderCell = Backgrid.HeaderCell.extend({
return 'icon-sort-down';
}
});
Backgrid.NzbDronePaginator = Backgrid.Extension.Paginator.extend({
events: {
"click a": "changePage",
"click i": "preventLinkClick"
},
windowSize: 1,
fastForwardHandleLabels: {
first: '<i class="icon-fast-backward"></i>',
prev: '<i class="icon-backward"></i>',
next: '<i class="icon-forward"></i>',
last: '<i class="icon-fast-forward"></i>'
},
changePage: function (e) {
e.preventDefault();
var target = $(e.target);
if (target.closest('li').hasClass('disabled')) {
return;
}
if (!$(target).is('a')){
target = target.parent('a');
}
var label = target.html();
var ffLabels = this.fastForwardHandleLabels;
var collection = this.collection;
if (ffLabels) {
switch (label) {
case ffLabels.first:
collection.getFirstPage();
return;
case ffLabels.prev:
if (collection.hasPrevious()) {
collection.getPreviousPage();
}
return;
case ffLabels.next:
if (collection.hasNext()) {
collection.getNextPage();
}
return;
case ffLabels.last:
collection.getLastPage();
return;
}
}
var state = collection.state;
var pageIndex = $(e.target).text() * 1;
collection.getPage(state.firstPage === 0 ? pageIndex - 1 : pageIndex);
},
preventLinkClick: function (e) {
e.preventDefault();
}
});
Loading…
Cancel
Save