From 969f8ae5e2e729310454e7c473c05f7b038e657b Mon Sep 17 00:00:00 2001
From: Mark McDowall <markus.mcd5@gmail.com>
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<EpisodeProvider>()
                 .Setup(c => c.GetSeasons(1)).Returns(seasons);
 
-            Mocker.GetMock<EpisodeProvider>()
+            Mocker.GetMock<SeasonProvider>()
                 .Setup(c => c.IsIgnored(It.IsAny<int>(), It.IsAny<int>())).Returns(false);
 
             Mocker.GetMock<SeasonSearchJob>()
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 @@
     <Compile Include="JobTests\RecentBacklogSearchJobTest.cs" />
     <Compile Include="ProviderTests\AnalyticsProviderTests\AnalyticsProviderFixture.cs" />
     <Compile Include="ProviderTests\ConfigProviderTests\ConfigCachingFixture.cs" />
+    <Compile Include="ProviderTests\SeasonProviderTest.cs" />
     <Compile Include="ProviderTests\DecisionEngineTests\RetentionSpecificationFixture.cs" />
     <Compile Include="ProviderTests\DecisionEngineTests\QualityAllowedByProfileSpecificationFixtrue.cs" />
     <Compile Include="ProviderTests\DecisionEngineTests\UpgradeHistorySpecificationFixtrue.cs" />
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<SeasonProvider>()
+                .Setup(s => s.IsIgnored(seriesId, 0))
+                .Returns(true);
+
             //Act
             Mocker.Resolve<EpisodeProvider>().RefreshEpisodeInfo(fakeSeries);
 
@@ -694,6 +697,10 @@ namespace NzbDrone.Core.Test.ProviderTests
                 .Setup(c => c.GetSeries(seriesId, true))
                 .Returns(tvdbSeries);
 
+            Mocker.GetMock<SeasonProvider>()
+                .Setup(s => s.IsIgnored(seriesId, It.IsAny<int>()))
+                .Returns(true);
+
             //Act
             Mocker.Resolve<EpisodeProvider>().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<Episode>.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<EpisodeProvider>().IsIgnored(10, 2);
-
-            //Assert
-            result.Should().BeTrue();
-        }
-
-        [Test]
-        public void IsSeasonIgnored_should_return_false_if_none_of_episodes_are_ignored()
-        {
-            WithRealDb();
-
-            var episodes = Builder<Episode>.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<EpisodeProvider>().IsIgnored(10, 2);
-
-            //Assert
-            result.Should().BeFalse();
-        }
-
-        [Test]
-        public void IsSeasonIgnored_should_return_false_if_some_of_episodes_are_ignored()
-        {
-            WithRealDb();
-
-            var episodes = Builder<Episode>.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<EpisodeProvider>().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<Episode>.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<EpisodeProvider>().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<Episode>.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<EpisodeProvider>().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<Episode>.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<EpisodeProvider>().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<EpisodeProvider>().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<EpisodeProvider>().IsIgnored(10, 0);
-
-            //Assert
-            result.Should().BeTrue();
-        }
-
-        [Test]
-        public void IsSeasonIgnored_should_return_false_if_season_zero_is_not_ignored()
-        {
-            WithRealDb();
-
-            var episodes = Builder<Episode>.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<EpisodeProvider>().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<SeasonProvider>()
+                .Setup(s => s.IsIgnored(newEpisode.SeriesId, newEpisode.SeasonNumber))
+                .Returns(true);
+
             //Act
             Mocker.Resolve<EpisodeProvider>().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<SeasonProvider>().Add(seriesId, seasonNumber);
+
+            //Assert
+            var result = Db.Fetch<Season>();
+            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<SeasonProvider>().Add(seriesId, seasonNumber, isIgnored);
+
+            //Assert
+            var result = Db.Fetch<Season>();
+            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<Season>.CreateNew().Build();
+
+            Db.Insert(fakeSeason);
+
+            //Act
+            Mocker.Resolve<SeasonProvider>().Delete(fakeSeason.SeriesId, fakeSeason.SeasonNumber);
+
+            //Assert
+            var result = Db.Fetch<Season>();
+            result.Should().BeEmpty();
+        }
+
+        [Test]
+        public void SetIgnore_should_update_ignored_status()
+        {
+            WithRealDb();
+
+            var fakeSeason = Builder<Season>.CreateNew()
+                .With(s => s.Ignored = false)
+                .Build();
+
+            var id = Db.Insert(fakeSeason);
+
+            //Act
+            Mocker.Resolve<SeasonProvider>().SetIgnore(fakeSeason.SeriesId, fakeSeason.SeasonNumber, true);
+
+            //Assert
+            var result = Db.SingleOrDefault<Season>(id);
+            result.Ignored.Should().BeTrue();
+        }
+
+        [Test]
+        public void IsIgnored_should_return_ignored_status_of_season()
+        {
+            //Setup
+            var fakeSeason = Builder<Season>.CreateNew()
+                .With(s => s.Ignored = false)
+                .Build();
+
+            Db.Insert(fakeSeason);
+
+            //Act
+            var result = Mocker.Resolve<SeasonProvider>().IsIgnored(fakeSeason.SeriesId, fakeSeason.SeasonNumber);
+
+            //Assert
+            result.Should().Be(fakeSeason.Ignored);
+            Db.Fetch<Season>().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<SeasonProvider>().IsIgnored(10, 0);
+
+            //Assert
+            result.Should().BeTrue();
+            Db.Fetch<Season>().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<SeasonProvider>().IsIgnored(10, 1);
+
+            //Assert
+            result.Should().BeFalse();
+            Db.Fetch<Season>().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<Season>.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<SeasonProvider>().IsIgnored(10, 5);
+
+            //Assert
+            result.Should().BeFalse();
+            Db.Fetch<Season>().Should().HaveCount(2);
+        }
+
+        [Test]
+        public void IsIgnored_should_return_true_if_not_in_db_and_previous_season_is_ignored()
+        {
+            //Setup
+            WithRealDb();
+
+            var lastSeason = Builder<Season>.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<SeasonProvider>().IsIgnored(10, 5);
+
+            //Assert
+            result.Should().BeTrue();
+            Db.Fetch<Season>().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 @@
     <Compile Include="Datastore\MigrationLogger.cs" />
     <Compile Include="Datastore\MigrationsHelper.cs" />
     <Compile Include="Datastore\CustomeMapper.cs" />
+    <Compile Include="Datastore\Migrations\Migration20120220.cs" />
     <Compile Include="Datastore\Migrations\Migration20120123.cs" />
     <Compile Include="Datastore\Migrations\Migration20120118.cs" />
     <Compile Include="Datastore\Migrations\Migration20111125.cs" />
@@ -269,6 +270,7 @@
     <Compile Include="Model\Xbmc\ErrorResult.cs" />
     <Compile Include="Model\Xbmc\IconType.cs" />
     <Compile Include="Providers\BackupProvider.cs" />
+    <Compile Include="Providers\SeasonProvider.cs" />
     <Compile Include="Providers\DecisionEngine\AlreadyInQueueSpecification.cs" />
     <Compile Include="Providers\DecisionEngine\RetentionSpecification.cs" />
     <Compile Include="Providers\DownloadClients\BlackholeProvider.cs" />
@@ -337,6 +339,7 @@
     <Compile Include="Jobs\UpdateInfoJob.cs" />
     <Compile Include="Providers\SceneMappingProvider.cs" />
     <Compile Include="Providers\Xbmc\EventClientProvider.cs" />
+    <Compile Include="Repository\Season.cs" />
     <Compile Include="Repository\NewznabDefinition.cs" />
     <Compile Include="Repository\ExternalNotificationDefinition.cs" />
     <Compile Include="Repository\JobDefinition.cs" />
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<int>("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&#3)J3J_lD^%BC@FB;l?XwBst-+7ncac7UF@yx<t9b^_m0>5
z!II^YN^PY|>j&8Nj>mJ(oEbaOT&!UBq7AOUJwHqKUb_PsMHm<^h3<Zj0u>JqO~zdO
zaX2@}XVZPaRT42ZdX5!pZCw;*6E1!ux8?g-uF97`7egfl(&h@gcQVB2$hQZKrRGMe
zm%?C*hD~K4i;4LrT#WgmJ7k8@<oTIkYt%6d5GIw4rb=#jh)SQIZO_kuMw~1n9zYN=
ztzm{tOOYEn1F~}uskP!8W)QSthE^$Lp%l>z5VRF4h}>V_zTYvyXpR^)gG4D5W?=Vl
zmR>sz)4H_d;=(*}4Ig<Fk<Yc<Ab7oY$GdF9Qpq)#Cj=BB$R(_k$Z&|6GJrAHM4axP
zj*rrW&u;CMmJT9hB8lB>9|V4c^x3;<J$jGfSC8#4S{*laA3I?T<T~aNhSk91jE{l{
z-=<)}G{Uc8_EbUov!9CMm%Wf(0;<$|rJvkQyIhi`&T$9jPSA(knl{Zp?<xtb-L2kt
z-NFr5CSeh5BoE;>3_UIkXet@he%Jx`W<@kf)(cb}SV$QG*ZxY7&_jxF$s&#Q8`_8q
z+Pc0Vzs9r4apaVctl<F(c50TzLAw+oRTnv{GQQNZeC@opE(0$>RE>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=+EZ<gdUvp
z4D*MF`@3vLLzS$44vA_$g;=NucJ1!HqJH_^ix)uu8iICrE-iwzf#vj^%=!$C(@_QJ
z>2f(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-<f_K&(#DtI
zxrPRPx)D$+_E@m4YtEZ;P2O1h@a|yQk)?Lp9-qgG(5M9W4;E{mkE{(^lf9!S|5{N&
zK6!C5l;TSR!4n3*gH<8}y0LD!Omv=5LX#0dSFv7I)YeZO+TK5d*4+tez}S5=xT#Hz
z$q~Frw~W^qtbXYhnw>lUiiXF9dnbyz=3`;KZ5+z@&^tkJNy`-YHT3G#Z8!Ur+)7Tm
zS<kE!UzUm2#s_J6m6B|si!|S1ZxJo95z^o8vt~35Gz>HhGz>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 <markus.mcd5@gmail.com>
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<Season>.CreateNew()
                 .With(s => s.Ignored = false)
@@ -155,7 +157,7 @@ namespace NzbDrone.Core.Test.ProviderTests
             var lastSeason = Builder<Season>.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<Season>().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<SeasonProvider>().IsIgnored(10, 5);
+
+            //Assert
+            result.Should().BeFalse();
+            Db.Fetch<Season>().Should().HaveCount(1);
+        }
+
+        [Test]
+        public void All_should_return_seasons_with_episodes()
+        {
+            const int seriesId = 10;
+
+            //Setup
+            WithRealDb();
+
+            var season = Builder<Season>.CreateNew()
+                .With(s => s.SeriesId = seriesId)
+                .With(s => s.SeasonNumber = 4)
+                .With(s => s.Ignored = true)
+                .Build();
+
+            var episodes = Builder<Episode>.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<SeasonProvider>().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<Season>.CreateListOfSize(5)
+                .All()
+                .With(s => s.SeriesId = seriesId)
+                .Build();
+
+            var episodes = new List<Episode>();
+
+            for (int i = 0; i < seasons.Count; i++)
+            {
+                var newEps = Builder<Episode>.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<SeasonProvider>().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<Episode>();
+            _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 @@
     <Compile Include="Datastore\DbProviderFactory.cs" />
     <Compile Include="Datastore\Migrations\NzbDroneMigration.cs" />
     <Compile Include="Datastore\Migrations\SchemaInfo.cs" />
+    <Compile Include="Datastore\PetaPoco\EpisodeSeasonRelator.cs" />
     <Compile Include="Fluent.cs" />
     <Compile Include="Helpers\EpisodeSortingHelper.cs" />
     <Compile Include="Helpers\FileSizeFormatHelper.cs" />
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<Episode>();
             var newList = new List<Episode>();
 
-            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<VLvxH^X@XhkWw1^N3zjV(r11+u`?&(AeT!OP#%4`xBJ
zf<J<-7o1v@nOf|b0(3jX=3=<e<c~bU^?ojJeLx9NQ2CYSCZ!gks7DB6Iw0J`HOLji
jY@l`r10@|`$U&?})u(CA%LNgcT*fQPSiE^M?;Sn>&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<Episode> 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<SeasonModel>();
-            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<Episode> 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<EpisodeModel> 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;
         <div class="seasonToggleTop">
-            <img src='../../Content/Images/@(season.AnyWanted ? "notIgnored" : "ignored").png'
-                 class='ignoredEpisodesMaster ignoreEpisode @ignoreSeason@(season.AnyWanted ? " " : " ignored") gridImage'
+            <img src='../../Content/Images/@(season.Ignored ? "ignored" : "notIgnored").png'
+                 class='ignoredEpisodesMaster ignoreEpisode @ignoreSeason@(season.Ignored ? " ignored" : " ") gridImage'
                  title='Click to toggle season ignore status' />
             <a href="@string.Format("#SeasonSection_{0}", season.SeasonNumber)" class="seasonToggleLabel">@(season.SeasonNumber == 0 ? "Specials" : "Season " + season.SeasonNumber)</a>
         </div>
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*@
             <th>
-                <img src='../../Content/Images/@(Model.AnyWanted ? "notIgnored" : "ignored").png' class='ignoredEpisodesMaster ignoreEpisode ignoreSeason_@(Model.SeasonNumber) @(Model.AnyWanted ? "" : "ignored") gridImage' title='Click to toggle season ignore status' />
+                <img src='../../Content/Images/@(Model.Ignored ? "ignored" : "notIgnored").png' class='ignoredEpisodesMaster ignoreEpisode ignoreSeason_@(Model.SeasonNumber)@(Model.Ignored ? " ignored" : " ") gridImage' title='Click to toggle season ignore status' />
                 <img src='../../Content/Images/@(Model.CommonStatus).png' alt='Status' title='Season Status' class='gridImage' />
                 @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 <markus.mcd5@gmail.com>
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<Episode>.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<SeasonProvider>().Setup(s => s.SetIgnore(10, 1, true)).Verifiable();
+
+            //Act
+            Mocker.Resolve<EpisodeProvider>().SetSeasonIgnore(10, 1, true);
+
+            //Assert
+            var episodesInDb = Db.Fetch<Episode>(@"SELECT * FROM Episodes");
+
+            episodesInDb.Should().HaveCount(4);
+            episodesInDb.Where(e => e.Ignored).Should().HaveCount(4);
+
+            Mocker.GetMock<SeasonProvider>().Verify(s => s.SetIgnore(10, 1, true), Times.Once());
+        }
+
         [Test]
         public void EpisodesWithoutFiles_no_specials()
         {