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 f80ba99f5..bab22a116 100644 Binary files a/NzbDrone.Core/Providers/SeasonProvider.cs and b/NzbDrone.Core/Providers/SeasonProvider.cs differ 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)