From 969f8ae5e2e729310454e7c473c05f7b038e657b Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 20 Feb 2012 19:25:19 -0800 Subject: [PATCH 1/3] SeasonProvider Added to handle ignoring of Seasons. --- .../JobTests/SeriesSearchJobTest.cs | 2 +- NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 1 + .../ProviderTests/EpisodeProviderTest.cs | 187 ++--------------- .../ProviderTests/SeasonProviderTest.cs | 193 ++++++++++++++++++ .../Datastore/Migrations/Migration20120220.cs | 22 ++ NzbDrone.Core/Jobs/SeriesSearchJob.cs | 11 +- NzbDrone.Core/NzbDrone.Core.csproj | 3 + NzbDrone.Core/Providers/EpisodeProvider.cs | 16 +- NzbDrone.Core/Providers/SeasonProvider.cs | Bin 0 -> 7279 bytes NzbDrone.Core/Repository/Season.cs | 16 ++ 10 files changed, 265 insertions(+), 186 deletions(-) create mode 100644 NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs create mode 100644 NzbDrone.Core/Datastore/Migrations/Migration20120220.cs create mode 100644 NzbDrone.Core/Providers/SeasonProvider.cs create mode 100644 NzbDrone.Core/Repository/Season.cs diff --git a/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs b/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs index 6c74b4faf..8b527ee20 100644 --- a/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs +++ b/NzbDrone.Core.Test/JobTests/SeriesSearchJobTest.cs @@ -26,7 +26,7 @@ namespace NzbDrone.Core.Test.JobTests Mocker.GetMock() .Setup(c => c.GetSeasons(1)).Returns(seasons); - Mocker.GetMock() + Mocker.GetMock() .Setup(c => c.IsIgnored(It.IsAny(), It.IsAny())).Returns(false); Mocker.GetMock() diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 2126da70d..c69605810 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -115,6 +115,7 @@ + diff --git a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs index 47ff0f5f7..3ee535711 100644 --- a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs @@ -439,7 +439,6 @@ namespace NzbDrone.Core.Test.ProviderTests result.Should().HaveSameCount(fakeEpisodes.Episodes); } - [Test] public void RefreshEpisodeInfo_ignore_season_zero() { @@ -466,6 +465,10 @@ namespace NzbDrone.Core.Test.ProviderTests .Setup(c => c.GetSeries(seriesId, true)) .Returns(fakeEpisodes); + Mocker.GetMock() + .Setup(s => s.IsIgnored(seriesId, 0)) + .Returns(true); + //Act Mocker.Resolve().RefreshEpisodeInfo(fakeSeries); @@ -694,6 +697,10 @@ namespace NzbDrone.Core.Test.ProviderTests .Setup(c => c.GetSeries(seriesId, true)) .Returns(tvdbSeries); + Mocker.GetMock() + .Setup(s => s.IsIgnored(seriesId, It.IsAny())) + .Returns(true); + //Act Mocker.Resolve().RefreshEpisodeInfo(fakeSeries); @@ -704,180 +711,6 @@ namespace NzbDrone.Core.Test.ProviderTests result.Where(e => e.Ignored).Should().HaveCount(episodeCount); } - [Test] - public void IsSeasonIgnored_should_return_true_if_all_episodes_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.Ignored = true) - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 2) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 2); - - //Assert - result.Should().BeTrue(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_none_of_episodes_are_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.Ignored = false) - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 2) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 2); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_some_of_episodes_are_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 2) - .With(c => c.Ignored = true) - .Build(); - - episodes[2].Ignored = false; - - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 2); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_zero_episodes_in_db_for_season() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 3) - .With(c => c.Ignored = true) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 2); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_true_if_zero_episodes_in_db_for_season_and_previous_is_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 3) - .With(c => c.Ignored = true) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 4); - - //Assert - result.Should().BeTrue(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_zero_episodes_in_db_for_season_and_previous_is_not_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 3) - .With(c => c.Ignored = false) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 4); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_zero_episodes_in_db_for_season_one() - { - WithRealDb(); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 1); - - //Assert - result.Should().BeFalse(); - } - - [Test] - public void IsSeasonIgnored_should_return_true_if_zero_episodes_in_db_for_season_zero() - { - WithRealDb(); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 0); - - //Assert - result.Should().BeTrue(); - } - - [Test] - public void IsSeasonIgnored_should_return_false_if_season_zero_is_not_ignored() - { - WithRealDb(); - - var episodes = Builder.CreateListOfSize(4) - .All() - .With(c => c.SeriesId = 10) - .With(c => c.SeasonNumber = 0) - .With(c => c.Ignored = false) - .Build(); - - episodes.ToList().ForEach(c => Db.Insert(c)); - - //Act - var result = Mocker.Resolve().IsIgnored(10, 0); - - //Assert - result.Should().BeFalse(); - } - [Test] [Explicit] public void Add_daily_show_episodes() @@ -1049,6 +882,10 @@ namespace NzbDrone.Core.Test.ProviderTests .With(e => e.Ignored = false) .Build(); + Mocker.GetMock() + .Setup(s => s.IsIgnored(newEpisode.SeriesId, newEpisode.SeasonNumber)) + .Returns(true); + //Act Mocker.Resolve().AddEpisode(newEpisode); diff --git a/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs new file mode 100644 index 000000000..2a4401c22 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs @@ -0,0 +1,193 @@ +// ReSharper disable RedundantUsingDirective + +using System; +using System.Collections.Generic; +using System.Linq; + +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.AutoMoq; +using PetaPoco; +using TvdbLib.Data; + +namespace NzbDrone.Core.Test.ProviderTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class SeasonProviderTest : CoreTest + { + [Test] + public void AddSeason_should_insert_season_to_database_with_ignored_false() + { + WithRealDb(); + + var seriesId = 10; + var seasonNumber = 50; + + //Act + Mocker.Resolve().Add(seriesId, seasonNumber); + + //Assert + var result = Db.Fetch(); + result.Should().HaveCount(1); + result.First().SeriesId.Should().Be(seriesId); + result.First().SeasonNumber.Should().Be(seasonNumber); + result.First().Ignored.Should().BeFalse(); + } + + [TestCase(true)] + [TestCase(false)] + public void AddSeason_should_insert_season_to_database_with_preset_ignored_status(bool isIgnored) + { + WithRealDb(); + + var seriesId = 10; + var seasonNumber = 50; + + //Act + Mocker.Resolve().Add(seriesId, seasonNumber, isIgnored); + + //Assert + var result = Db.Fetch(); + result.Should().HaveCount(1); + result.First().SeriesId.Should().Be(seriesId); + result.First().SeasonNumber.Should().Be(seasonNumber); + result.First().Ignored.Should().Be(isIgnored); + } + + [Test] + public void DeleteSeason_should_remove_season_from_database() + { + WithRealDb(); + + var fakeSeason = Builder.CreateNew().Build(); + + Db.Insert(fakeSeason); + + //Act + Mocker.Resolve().Delete(fakeSeason.SeriesId, fakeSeason.SeasonNumber); + + //Assert + var result = Db.Fetch(); + result.Should().BeEmpty(); + } + + [Test] + public void SetIgnore_should_update_ignored_status() + { + WithRealDb(); + + var fakeSeason = Builder.CreateNew() + .With(s => s.Ignored = false) + .Build(); + + var id = Db.Insert(fakeSeason); + + //Act + Mocker.Resolve().SetIgnore(fakeSeason.SeriesId, fakeSeason.SeasonNumber, true); + + //Assert + var result = Db.SingleOrDefault(id); + result.Ignored.Should().BeTrue(); + } + + [Test] + public void IsIgnored_should_return_ignored_status_of_season() + { + //Setup + var fakeSeason = Builder.CreateNew() + .With(s => s.Ignored = false) + .Build(); + + Db.Insert(fakeSeason); + + //Act + var result = Mocker.Resolve().IsIgnored(fakeSeason.SeriesId, fakeSeason.SeasonNumber); + + //Assert + result.Should().Be(fakeSeason.Ignored); + Db.Fetch().Count.Should().Be(1); + } + + [Test] + public void IsIgnored_should_return_true_if_not_in_db_and_is_season_zero() + { + //Setup + WithRealDb(); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 0); + + //Assert + result.Should().BeTrue(); + Db.Fetch().Should().HaveCount(1); + } + + [Test] + public void IsIgnored_should_return_false_if_not_in_db_and_is_season_one() + { + //Setup + WithRealDb(); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 1); + + //Assert + result.Should().BeFalse(); + Db.Fetch().Should().HaveCount(1); + } + + [Test] + public void IsIgnored_should_return_false_if_not_in_db_and_previous_season_is_not_ignored() + { + //Setup + WithRealDb(); + + var lastSeason = Builder.CreateNew() + .With(s => s.SeriesId = 10) + .With(s => s.SeasonNumber = 4) + .With(s => s.Ignored = true) + .Build(); + + Db.Insert(lastSeason); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 5); + + //Assert + result.Should().BeFalse(); + Db.Fetch().Should().HaveCount(2); + } + + [Test] + public void IsIgnored_should_return_true_if_not_in_db_and_previous_season_is_ignored() + { + //Setup + WithRealDb(); + + var lastSeason = Builder.CreateNew() + .With(s => s.SeriesId = 10) + .With(s => s.SeasonNumber = 4) + .With(s => s.Ignored = true) + .Build(); + + Db.Insert(lastSeason); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 5); + + //Assert + result.Should().BeTrue(); + Db.Fetch().Should().HaveCount(2); + } + } +} diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs new file mode 100644 index 000000000..0d4d40de7 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs @@ -0,0 +1,22 @@ +using System; +using System.Data; +using Migrator.Framework; + +namespace NzbDrone.Core.Datastore.Migrations +{ + + [Migration(20120220)] + public class Migration20120220 : NzbDroneMigration + { + protected override void MainDbUpgrade() + { + Database.AddTable("Seasons", new[] + { + new Column("SeasonId", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity), + new Column("SeriesId", DbType.Int32, ColumnProperty.NotNull), + new Column("SeasonNumber", DbType.Int32, ColumnProperty.NotNull), + new Column("Ignored", DbType.Boolean, ColumnProperty.NotNull) + }); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/SeriesSearchJob.cs b/NzbDrone.Core/Jobs/SeriesSearchJob.cs index 5e225f1dd..6886b4cb4 100644 --- a/NzbDrone.Core/Jobs/SeriesSearchJob.cs +++ b/NzbDrone.Core/Jobs/SeriesSearchJob.cs @@ -8,15 +8,16 @@ namespace NzbDrone.Core.Jobs { public class SeriesSearchJob : IJob { - private readonly EpisodeProvider _episodeProvider; private readonly SeasonSearchJob _seasonSearchJob; + private readonly SeasonProvider _seasonProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - public SeriesSearchJob(EpisodeProvider episodeProvider, SeasonSearchJob seasonSearchJob) + public SeriesSearchJob(SeasonSearchJob seasonSearchJob, + SeasonProvider seasonProvider) { - _episodeProvider = episodeProvider; _seasonSearchJob = seasonSearchJob; + _seasonProvider = seasonProvider; } public string Name @@ -35,12 +36,12 @@ namespace NzbDrone.Core.Jobs throw new ArgumentOutOfRangeException("targetId"); Logger.Debug("Getting seasons from database for series: {0}", targetId); - var seasons = _episodeProvider.GetSeasons(targetId).Where(s => s > 0); + var seasons = _seasonProvider.GetSeasons(targetId).Where(s => s > 0); foreach (var season in seasons) { //Skip ignored seasons - if (_episodeProvider.IsIgnored(targetId, season)) + if (_seasonProvider.IsIgnored(targetId, season)) continue; _seasonSearchJob.Start(notification, targetId, season); diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 3b73de5e5..e9d984107 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -222,6 +222,7 @@ + @@ -269,6 +270,7 @@ + @@ -337,6 +339,7 @@ + diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index 0aceb7799..dbc157735 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -20,13 +20,16 @@ namespace NzbDrone.Core.Providers private static readonly Regex multiPartCleanupRegex = new Regex(@"\(\d+\)$", RegexOptions.Compiled); private readonly TvDbProvider _tvDbProvider; + private readonly SeasonProvider _seasonProvider; private readonly IDatabase _database; private readonly SeriesProvider _seriesProvider; [Inject] - public EpisodeProvider(IDatabase database, SeriesProvider seriesProvider, TvDbProvider tvDbProviderProvider) + public EpisodeProvider(IDatabase database, SeriesProvider seriesProvider, + TvDbProvider tvDbProviderProvider, SeasonProvider seasonProvider) { _tvDbProvider = tvDbProviderProvider; + _seasonProvider = seasonProvider; _database = database; _seriesProvider = seriesProvider; } @@ -38,8 +41,7 @@ namespace NzbDrone.Core.Providers public virtual void AddEpisode(Episode episode) { //If Season is ignored ignore this episode - if (IsIgnored(episode.SeriesId, episode.SeasonNumber)) - episode.Ignored = true; + episode.Ignored = _seasonProvider.IsIgnored(episode.SeriesId, episode.SeasonNumber); _database.Insert(episode); } @@ -312,7 +314,7 @@ namespace NzbDrone.Core.Providers //Else we need to check if this episode should be ignored based on IsIgnored rules else { - episodeToUpdate.Ignored = IsIgnored(series.SeriesId, episode.SeasonNumber); + episodeToUpdate.Ignored = _seasonProvider.IsIgnored(series.SeriesId, episode.SeasonNumber); } } else @@ -396,10 +398,14 @@ namespace NzbDrone.Core.Providers return _database.Fetch("SELECT EpisodeNumber FROM Episodes WHERE SeriesId=@0 AND SeasonNumber=@1", seriesId, seasonNumber).OrderBy(c => c).ToList(); } - public virtual void SetSeasonIgnore(long seriesId, int seasonNumber, bool isIgnored) + public virtual void SetSeasonIgnore(int seriesId, int seasonNumber, bool isIgnored) { logger.Info("Setting ignore flag on Series:{0} Season:{1} to {2}", seriesId, seasonNumber, isIgnored); + //Set the SeasonIgnore + _seasonProvider.SetIgnore(seriesId, seasonNumber, isIgnored); + + //Ignore all the episodes in the season _database.Execute(@"UPDATE Episodes SET Ignored = @0 WHERE SeriesId = @1 AND SeasonNumber = @2 AND Ignored = @3", isIgnored, seriesId, seasonNumber, !isIgnored); diff --git a/NzbDrone.Core/Providers/SeasonProvider.cs b/NzbDrone.Core/Providers/SeasonProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..f80ba99f54af110c2da8bc65c98120fef39d48b4 GIT binary patch literal 7279 zcmeHJU2iHk5bblM{D)J3J_lD^%BC@FB;l?XwBst-+7ncac7UF@yx5 z!II^YN^PY|>j&8Nj>mJ(oEbaOT&!UBq7AOUJwHqKUb_PsMHm<^h3JqO~zdO zaX2@}XVZPaRT42ZdX5!pZCw;*6E1!ux8?g-uF97`7egfl(&h@gcQVB2$hQZKrRGMe zm%?C*hD~K4i;4LrT#WgmJ7k8@z5VRF4h}>V_zTYvyXpR^)gG4D5W?=Vl zmR>sz)4H_d;=(*}4Ig9|V4c^x3;3_UIkXet@he%Jx`W<@kf)(cb}SV$QG*ZxY7&_jxF$s&#Q8`_8q z+Pc0Vzs9r4apaVctlRE>645!SCACn+0Z zga%Kr_7--WqbtYEwb%OThtb1mpMjg40=S(FGX`B!2sC{xQMYPfJ2(fwHWl04ZHG86 zeY}|kf6TSHBAr|Vbp{u{uJCajy+V^Hu_uuaXo9CJUzl$$iGtZ^JQ~bl=+EZ2f(vh3m9bBTGrZ(HMKY@wCWb8L<^ek%{5`_T4T6{(jrrfswGi+!53qbv2O`R9>vr zR%gMM#W{M$AR!^&w0@ipZ|7EdRt$86jOI|*%uV5}tdX3x!~behoJd_?IjP{(U~gAn zZ0L-`Fq^6qxJz^q-f=n(d{JDV7pq7gRQy$FmCU#RTt=2s9Z88!f`D!h%Os+sRHONu zIm(Irlnm20?B|(1A;aUc+c1@tJ@HVgVMgkU@LmmZ$>@CkZ@W?szxrP-lUiiXF9dnbyz=3`;KZ5+z@&^tkJNy`-YHT3G#Z8!Ur+)7Tm zSHhGz>HhGz>HhGz>HhGz>Hh MGz>Hh{BIce3tV&B3jhEB literal 0 HcmV?d00001 diff --git a/NzbDrone.Core/Repository/Season.cs b/NzbDrone.Core/Repository/Season.cs new file mode 100644 index 000000000..8f9ce8f74 --- /dev/null +++ b/NzbDrone.Core/Repository/Season.cs @@ -0,0 +1,16 @@ +using System; +using NzbDrone.Core.Model; +using PetaPoco; + +namespace NzbDrone.Core.Repository +{ + [TableName("Seasons")] + [PrimaryKey("SeasonId", autoIncrement = true)] + public class Season + { + public int SeasonId { get; set; } + public int SeriesId { get; set; } + public int SeasonNumber { get; set; } + public Boolean Ignored { get; set; } + } +} \ No newline at end of file From aac42d4882c9f9f5593f29fcbf0197e356a2fd4d Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 20 Feb 2012 22:50:38 -0800 Subject: [PATCH 2/3] More Season ignore work. Already ignored seasons will be ignored. Fix: Season Ignore is handled separately from Episode Ignore. --- .../ProviderTests/SeasonProviderTest.cs | 90 +++++++++++++++++- .../Datastore/Migrations/Migration20120220.cs | 7 ++ .../PetaPoco/EpisodeSeasonRelator.cs | 46 +++++++++ NzbDrone.Core/NzbDrone.Core.csproj | 1 + NzbDrone.Core/Providers/EpisodeProvider.cs | 2 +- NzbDrone.Core/Providers/SeasonProvider.cs | Bin 7279 -> 7972 bytes NzbDrone.Core/Repository/Season.cs | 4 + NzbDrone.Web/Controllers/SeriesController.cs | 39 ++++---- NzbDrone.Web/Models/SeasonModel.cs | 1 + .../Scripts/NzbDrone/seriesDetails.js | 22 ----- NzbDrone.Web/Views/Series/Details.cshtml | 4 +- NzbDrone.Web/Views/Series/Season.cshtml | 2 +- 12 files changed, 171 insertions(+), 47 deletions(-) create mode 100644 NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs diff --git a/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs index 2a4401c22..4a2ca931f 100644 --- a/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/SeasonProviderTest.cs @@ -103,6 +103,8 @@ namespace NzbDrone.Core.Test.ProviderTests [Test] public void IsIgnored_should_return_ignored_status_of_season() { + WithRealDb(); + //Setup var fakeSeason = Builder.CreateNew() .With(s => s.Ignored = false) @@ -155,7 +157,7 @@ namespace NzbDrone.Core.Test.ProviderTests var lastSeason = Builder.CreateNew() .With(s => s.SeriesId = 10) .With(s => s.SeasonNumber = 4) - .With(s => s.Ignored = true) + .With(s => s.Ignored = false) .Build(); Db.Insert(lastSeason); @@ -189,5 +191,91 @@ namespace NzbDrone.Core.Test.ProviderTests result.Should().BeTrue(); Db.Fetch().Should().HaveCount(2); } + + [Test] + public void IsIgnored_should_return_false_if_not_in_db_and_previous_season_does_not_exist() + { + //Setup + WithRealDb(); + + //Act + var result = Mocker.Resolve().IsIgnored(10, 5); + + //Assert + result.Should().BeFalse(); + Db.Fetch().Should().HaveCount(1); + } + + [Test] + public void All_should_return_seasons_with_episodes() + { + const int seriesId = 10; + + //Setup + WithRealDb(); + + var season = Builder.CreateNew() + .With(s => s.SeriesId = seriesId) + .With(s => s.SeasonNumber = 4) + .With(s => s.Ignored = true) + .Build(); + + var episodes = Builder.CreateListOfSize(10) + .All() + .With(e => e.SeriesId = seriesId) + .With(e => e.SeasonNumber = season.SeasonNumber) + .Build(); + + Db.Insert(season); + Db.InsertMany(episodes); + + //Act + var result = Mocker.Resolve().All(seriesId); + + //Assert + result.Should().HaveCount(1); + result.First().Episodes.Should().HaveCount(episodes.Count); + } + + [Test] + public void All_should_return_all_seasons_with_episodes() + { + const int seriesId = 10; + + //Setup + WithRealDb(); + + var seasons = Builder.CreateListOfSize(5) + .All() + .With(s => s.SeriesId = seriesId) + .Build(); + + var episodes = new List(); + + for (int i = 0; i < seasons.Count; i++) + { + var newEps = Builder.CreateListOfSize(2) + .All() + .With(e => e.SeriesId = seriesId) + .With(e => e.SeasonNumber = i + 1) + .Build(); + + episodes.AddRange(newEps); + } + + Db.InsertMany(seasons); + Db.InsertMany(episodes); + + //Act + var result = Mocker.Resolve().All(seriesId); + + //Assert + result.Should().HaveCount(5); + + foreach(var season in result) + { + season.Episodes.Count.Should().Be(2); + } + } } } diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs index 0d4d40de7..b9e4d291d 100644 --- a/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120220.cs @@ -17,6 +17,13 @@ namespace NzbDrone.Core.Datastore.Migrations new Column("SeasonNumber", DbType.Int32, ColumnProperty.NotNull), new Column("Ignored", DbType.Boolean, ColumnProperty.NotNull) }); + + Database.ExecuteNonQuery(@"INSERT INTO Seasons (SeriesId, SeasonNumber, Ignored) + SELECT SeriesId, SeasonNumber, + CASE WHEN Count(*) = + SUM(CASE WHEN Ignored = 1 THEN 1 ELSE 0 END) THEN 1 ELSE 0 END AS Ignored + FROM Episodes + GROUP BY SeriesId, SeasonNumber"); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs b/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs new file mode 100644 index 000000000..168e2920b --- /dev/null +++ b/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NzbDrone.Core.Repository; + +namespace NzbDrone.Core.Datastore.PetaPoco +{ + public class EpisodeSeasonRelator + { + public Season _current; + public Season MapIt(Season season, Episode episode) + { + // Terminating call. Since we can return null from this function + // we need to be ready for PetaPoco to callback later with null + // parameters + if (season == null) + return _current; + + // Is this the same season as the current one we're processing + if (_current != null && _current.SeasonId == season.SeasonId) + { + // Yes, just add this post to the current author's collection of posts + _current.Episodes.Add(episode); + + // Return null to indicate we're not done with this author yet + return null; + } + + // This is season different author to the current one, or this is the + // first time through and we don't have an season yet + + // Save the current author + var prev = _current; + + // Setup the new current season + _current = season; + _current.Episodes = new List(); + _current.Episodes.Add(episode); + + // Return the now populated previous season (or null if first time through) + return prev; + } + + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index e9d984107..260218917 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -234,6 +234,7 @@ + diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index dbc157735..bef791248 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -280,7 +280,7 @@ namespace NzbDrone.Core.Providers var updateList = new List(); var newList = new List(); - foreach (var episode in tvDbSeriesInfo.Episodes) + foreach (var episode in tvDbSeriesInfo.Episodes.OrderBy(e => e.SeasonNumber).ThenBy(e => e.EpisodeNumber)) { try { diff --git a/NzbDrone.Core/Providers/SeasonProvider.cs b/NzbDrone.Core/Providers/SeasonProvider.cs index f80ba99f54af110c2da8bc65c98120fef39d48b4..bab22a116353e5978834b5d939e8ac8ffd3d87ab 100644 GIT binary patch delta 463 zcmaEFvBYk|JRz6FlEmVY{GwF7fYg%2fc)hAjqNo&Vwq_Q8aY6T;MBz8{5%C)TZO#R zoSey7TyB%+aR*MG!X-0Vp0{c8KPEwmvdp5A(!?AEpUmPC8;E*41xKLbd93R7Wr;-! z#b7gv6>JsaQ-HQ5B^IaZxuupQXFyf!D7Y467U!p=>L@_9Yw&U@Ac4Hpa+nf`f}qr# zM4+2BH1&KF3p`776j0SWC&A^T9 delta 23 fcmZ2t_ugW{yp6|Gc_#1Wl9~LKw`p^jz&SnuiFOL3 diff --git a/NzbDrone.Core/Repository/Season.cs b/NzbDrone.Core/Repository/Season.cs index 8f9ce8f74..ac76d67df 100644 --- a/NzbDrone.Core/Repository/Season.cs +++ b/NzbDrone.Core/Repository/Season.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using NzbDrone.Core.Model; using PetaPoco; @@ -12,5 +13,8 @@ namespace NzbDrone.Core.Repository public int SeriesId { get; set; } public int SeasonNumber { get; set; } public Boolean Ignored { get; set; } + + [ResultColumn] + public List Episodes { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/SeriesController.cs b/NzbDrone.Web/Controllers/SeriesController.cs index aedc5d80e..26ca21316 100644 --- a/NzbDrone.Web/Controllers/SeriesController.cs +++ b/NzbDrone.Web/Controllers/SeriesController.cs @@ -24,16 +24,19 @@ namespace NzbDrone.Web.Controllers private readonly QualityProvider _qualityProvider; private readonly SeriesProvider _seriesProvider; private readonly JobProvider _jobProvider; + private readonly SeasonProvider _seasonProvider; // // GET: /Series/ public SeriesController(SeriesProvider seriesProvider, EpisodeProvider episodeProvider, - QualityProvider qualityProvider, JobProvider jobProvider) + QualityProvider qualityProvider, JobProvider jobProvider, + SeasonProvider seasonProvider) { _seriesProvider = seriesProvider; _episodeProvider = episodeProvider; _qualityProvider = qualityProvider; _jobProvider = jobProvider; + _seasonProvider = seasonProvider; } public ActionResult Index() @@ -117,25 +120,14 @@ namespace NzbDrone.Web.Controllers model.SeriesId = series.SeriesId; model.HasBanner = !String.IsNullOrEmpty(series.BannerUrl); - var seasons = new List(); - var episodes = _episodeProvider.GetEpisodeBySeries(seriesId); - - foreach (var season in episodes.Select(s => s.SeasonNumber).Distinct()) - { - var episodesInSeason = episodes.Where(e => e.SeasonNumber == season).ToList(); - var commonStatusList = episodesInSeason.Select(s => s.Status).Distinct().ToList(); - var commonStatus = commonStatusList.Count > 1 ? "Missing" : commonStatusList.First().ToString(); - - seasons.Add(new SeasonModel - { - SeriesId = seriesId, - SeasonNumber = season, - Episodes = GetEpisodeModels(episodesInSeason).OrderByDescending(e=> e.EpisodeNumber).ToList(), - AnyWanted = episodesInSeason.Any(e => !e.Ignored), - CommonStatus = commonStatus - }); - } - + var seasons = _seasonProvider.All(seriesId).Select(s => new SeasonModel + { + SeriesId = seriesId, + SeasonNumber = s.SeasonNumber, + Ignored = s.Ignored, + Episodes = GetEpisodeModels(s.Episodes).OrderByDescending(e => e.EpisodeNumber).ToList(), + CommonStatus = GetCommonStatus(s.Episodes) + }).ToList(); model.Seasons = seasons; return View(model); @@ -254,5 +246,12 @@ namespace NzbDrone.Web.Controllers return episodes; } + + private string GetCommonStatus(IList episodes) + { + var commonStatusList = episodes.Select(s => s.Status).Distinct().ToList(); + var commonStatus = commonStatusList.Count > 1 ? "Missing" : commonStatusList.First().ToString(); + return commonStatus; + } } } \ No newline at end of file diff --git a/NzbDrone.Web/Models/SeasonModel.cs b/NzbDrone.Web/Models/SeasonModel.cs index 33816b4d9..904fccb62 100644 --- a/NzbDrone.Web/Models/SeasonModel.cs +++ b/NzbDrone.Web/Models/SeasonModel.cs @@ -9,5 +9,6 @@ namespace NzbDrone.Web.Models public List Episodes { get; set; } public bool AnyWanted { get; set; } public string CommonStatus { get; set; } + public bool Ignored { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/Scripts/NzbDrone/seriesDetails.js b/NzbDrone.Web/Scripts/NzbDrone/seriesDetails.js index bdc1b5683..276494086 100644 --- a/NzbDrone.Web/Scripts/NzbDrone/seriesDetails.js +++ b/NzbDrone.Web/Scripts/NzbDrone/seriesDetails.js @@ -40,9 +40,7 @@ $(".ignoreEpisode").live("click", function () { else { //Check to see if this is the last one ignored or the first not ignored - seasonNumber = toggle.attr('class').split(/\s+/)[1].replace('ignoreEpisode_', ''); var episodeId = toggle.attr('id'); - toggleMaster(seasonNumber, ignored); saveEpisodeIgnore(episodeId, ignored); } }); @@ -68,26 +66,6 @@ function toggleChildren(seasonNumber, ignored) { } } -function toggleMaster(seasonNumber) { - //Toggles all master toggles when the childen changes - - var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber); - var ignoredCount = ignoreEpisodes.filter('.ignored').length; - var masters = $('.ignoreSeason_' + seasonNumber); - - masters.each(function (index) { - if (ignoreEpisodes.length == ignoredCount) { - $(this).attr('src', ignoredImage); - $(this).addClass('ignored'); - } - - else { - $(this).attr('src', notIgnoredImage); - $(this).removeClass('ignored'); - } - }); -} - function toggleMasters(seasonNumber, ignored) { //Toggles the other master(s) to match the one that was just changed var masters = $('.ignoreSeason_' + seasonNumber); diff --git a/NzbDrone.Web/Views/Series/Details.cshtml b/NzbDrone.Web/Views/Series/Details.cshtml index c95ca5bcb..0be7f1ed9 100644 --- a/NzbDrone.Web/Views/Series/Details.cshtml +++ b/NzbDrone.Web/Views/Series/Details.cshtml @@ -79,8 +79,8 @@ { var ignoreSeason = "ignoreSeason_" + season.SeasonNumber; diff --git a/NzbDrone.Web/Views/Series/Season.cshtml b/NzbDrone.Web/Views/Series/Season.cshtml index 81c30af32..1e7ff1444 100644 --- a/NzbDrone.Web/Views/Series/Season.cshtml +++ b/NzbDrone.Web/Views/Series/Season.cshtml @@ -22,7 +22,7 @@ @*Commands Column*@ - + Status @Ajax.ImageActionLink("../../Content/Images/Search.png", new { Alt = "Search", Title = "Search for all episodes in this season", @class = "gridImage" }, "SearchSeason", "Episode", new { SeriesId = Model.SeriesId, SeasonNumber = Model.SeasonNumber }, null, null) @Ajax.ImageActionLink("../../Content/Images/Rename.png", new { Alt = "Rename", Title = "Rename all episodes in this season", @class = "gridImage" }, "RenameSeason", "Episode", new { SeriesId = Model.SeriesId, SeasonNumber = Model.SeasonNumber }, null, null) From 6798cb342a12a87f067eb02b9cff3593a0edd351 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 21 Feb 2012 21:04:48 -0800 Subject: [PATCH 3/3] Added another test. --- .../ProviderTests/EpisodeProviderTest.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs index 3ee535711..cd8db4ffb 100644 --- a/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/EpisodeProviderTest.cs @@ -1100,6 +1100,42 @@ namespace NzbDrone.Core.Test.ProviderTests Mocker.VerifyAllMocks(); } + [Test] + public void IgnoreSeason_should_call_SetIgnore_in_season_provider_one_time_only() + { + WithRealDb(); + + var episodes = Builder.CreateListOfSize(4) + .All() + .With(c => c.SeriesId = 10) + .With(c => c.SeasonNumber = 1) + .With(c => c.Ignored = false) + .Build().ToList(); + + var season = new Season + { + SeriesId = 10, + SeasonNumber = 1, + Ignored = false + }; + + Db.Insert(season); + Db.InsertMany(episodes); + + Mocker.GetMock().Setup(s => s.SetIgnore(10, 1, true)).Verifiable(); + + //Act + Mocker.Resolve().SetSeasonIgnore(10, 1, true); + + //Assert + var episodesInDb = Db.Fetch(@"SELECT * FROM Episodes"); + + episodesInDb.Should().HaveCount(4); + episodesInDb.Where(e => e.Ignored).Should().HaveCount(4); + + Mocker.GetMock().Verify(s => s.SetIgnore(10, 1, true), Times.Once()); + } + [Test] public void EpisodesWithoutFiles_no_specials() {