diff --git a/NzbDrone.Core.Test/EpisodeProviderTest.cs b/NzbDrone.Core.Test/EpisodeProviderTest.cs index 466612259..47b9a38a9 100644 --- a/NzbDrone.Core.Test/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/EpisodeProviderTest.cs @@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test var episode = mocker.Resolve().GetEpisode(1); //Assert - episode.ShouldHave().AllPropertiesBut(e => e.Series).EqualTo(fakeEpisodes.First()); + episode.ShouldHave().AllPropertiesBut(e => e.Series, e => e.EpisodeFile).EqualTo(fakeEpisodes.First()); episode.Series.ShouldHave().AllProperties().EqualTo(fakeSeries); } @@ -90,6 +90,35 @@ namespace NzbDrone.Core.Test episode.Should().BeNull(); } + [Test] + public void GetEpisode_with_EpisodeFile() + { + var mocker = new AutoMoqer(); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var fakeSeries = Builder.CreateNew().Build(); + var fakeFile = Builder.CreateNew().With(f => f.EpisodeFileId).Build(); + var fakeEpisodes = Builder.CreateListOfSize(5) + .WhereAll().Have(e => e.SeriesId = 1).WhereTheFirst(1).Have(e => e.EpisodeFileId = 1).Have(e => e.EpisodeFile = fakeFile).Build(); + + + db.InsertMany(fakeEpisodes); + db.Insert(fakeFile); + + mocker.GetMock() + .Setup(p => p.GetSeries(1)) + .Returns(fakeSeries); + + //Act + var episode = mocker.Resolve().GetEpisode(1); + + //Assert + episode.ShouldHave().AllPropertiesBut(e => e.Series, e => e.EpisodeFile).EqualTo(fakeEpisodes.First()); + episode.Series.ShouldHave().AllProperties().EqualTo(fakeSeries); + episode.EpisodeFile.Should().NotBeNull(); + } + [Test] [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Sequence contains no elements")] public void GetEpisodes_invalid_series() @@ -124,7 +153,6 @@ namespace NzbDrone.Core.Test result.Should().HaveCount(0); } - [Test] public void AttachSeries_list_success() { @@ -195,7 +223,6 @@ namespace NzbDrone.Core.Test var returnedEpisode = mocker.Resolve().AttachSeries(fakeEpisodes); } - [Test] public void GetEpisodesBySeason_success() { @@ -255,7 +282,6 @@ namespace NzbDrone.Core.Test mocker.VerifyAllMocks(); } - [Test] public void new_episodes_only_calls_Insert() { @@ -276,7 +302,7 @@ namespace NzbDrone.Core.Test .Returns(tvdbSeries); mocker.GetMock() - .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) + .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) .Returns(currentEpisodes); @@ -290,7 +316,6 @@ namespace NzbDrone.Core.Test mocker.VerifyAllMocks(); } - [Test] public void existing_episodes_only_calls_Update() { @@ -314,7 +339,7 @@ namespace NzbDrone.Core.Test .Returns(tvdbSeries); mocker.GetMock() - .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) + .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) .Returns(currentEpisodes); //Act @@ -326,7 +351,6 @@ namespace NzbDrone.Core.Test mocker.VerifyAllMocks(); } - [Test] public void should_try_to_get_existing_episode_using_tvdbid_first() { @@ -345,7 +369,7 @@ namespace NzbDrone.Core.Test var mocker = new AutoMoqer(); mocker.GetMock() - .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) + .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) .Returns(fakeEpisodeList); mocker.GetMock() @@ -393,7 +417,7 @@ namespace NzbDrone.Core.Test .Returns(tvdbSeries); mocker.GetMock() - .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) + .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) .Returns(new List { localEpisode }); //Act @@ -404,7 +428,6 @@ namespace NzbDrone.Core.Test mocker.GetMock().Verify(c => c.Update(localEpisode), Times.Once()); } - [Test] public void existing_episodes_keep_their_episodeId_file_id() { @@ -430,7 +453,7 @@ namespace NzbDrone.Core.Test var updatedEpisodes = new List(); mocker.GetMock() - .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) + .Setup(d => d.Fetch(It.IsAny(), It.IsAny())) .Returns(currentEpisodes); mocker.GetMock() @@ -448,7 +471,6 @@ namespace NzbDrone.Core.Test updatedEpisodes.Should().OnlyContain(c => c.Ignored == true); } - [Test] public void IsSeasonIgnored_should_return_true_if_all_episodes_ignored() { @@ -556,6 +578,112 @@ namespace NzbDrone.Core.Test episodes.Should().NotBeEmpty(); } + [Test] + public void GetEpisode_by_Season_Episode_with_EpisodeFile() + { + var mocker = new AutoMoqer(); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var fakeSeries = Builder.CreateNew().Build(); + var fakeFile = Builder.CreateNew().With(f => f.EpisodeFileId).Build(); + var fakeEpisodes = Builder.CreateListOfSize(5) + .WhereAll().Have(e => e.SeriesId = 1).WhereTheFirst(1).Have(e => e.EpisodeFileId = 1).Have(e => e.EpisodeFile = fakeFile).Build(); + + db.InsertMany(fakeEpisodes); + db.Insert(fakeFile); + + mocker.GetMock() + .Setup(p => p.GetSeries(1)) + .Returns(fakeSeries); + + //Act + var episode = mocker.Resolve().GetEpisode(1, 1, 1); + + //Assert + episode.ShouldHave().AllPropertiesBut(e => e.Series, e => e.EpisodeFile).EqualTo(fakeEpisodes.First()); + episode.Series.ShouldHave().AllProperties().EqualTo(fakeSeries); + episode.EpisodeFile.Should().NotBeNull(); + } + + [Test] + public void GetEpisode_by_Season_Episode_without_EpisodeFile() + { + var mocker = new AutoMoqer(); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var fakeSeries = Builder.CreateNew().Build(); + var fakeEpisodes = Builder.CreateListOfSize(5) + .WhereAll().Have(e => e.SeriesId = 1).WhereTheFirst(1).Have(e => e.EpisodeFileId = 0).Build(); + + db.InsertMany(fakeEpisodes); + + mocker.GetMock() + .Setup(p => p.GetSeries(1)) + .Returns(fakeSeries); + + //Act + var episode = mocker.Resolve().GetEpisode(1, 1, 1); + + //Assert + episode.ShouldHave().AllPropertiesBut(e => e.Series).EqualTo(fakeEpisodes.First()); + episode.Series.ShouldHave().AllProperties().EqualTo(fakeSeries); + episode.EpisodeFile.Should().BeNull(); + } + + [Test] + public void GetEpisode_by_AirDate_with_EpisodeFile() + { + var mocker = new AutoMoqer(); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var fakeSeries = Builder.CreateNew().Build(); + var fakeFile = Builder.CreateNew().With(f => f.EpisodeFileId).Build(); + var fakeEpisodes = Builder.CreateListOfSize(5) + .WhereAll().Have(e => e.SeriesId = 1).WhereTheFirst(1).Have(e => e.EpisodeFileId = 1).Have(e => e.EpisodeFile = fakeFile).Build(); + + db.InsertMany(fakeEpisodes); + db.Insert(fakeFile); + + mocker.GetMock() + .Setup(p => p.GetSeries(1)) + .Returns(fakeSeries); + + //Act + var episode = mocker.Resolve().GetEpisode(1, fakeEpisodes[0].AirDate); + + //Assert + episode.ShouldHave().AllPropertiesBut(e => e.Series, e => e.EpisodeFile).EqualTo(fakeEpisodes.First()); + episode.Series.ShouldHave().AllProperties().EqualTo(fakeSeries); + episode.EpisodeFile.Should().NotBeNull(); + } + + [Test] + public void GetEpisode_by_AirDate_without_EpisodeFile() + { + var mocker = new AutoMoqer(); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + var fakeSeries = Builder.CreateNew().Build(); + var fakeEpisodes = Builder.CreateListOfSize(5) + .WhereAll().Have(e => e.SeriesId = 1).WhereTheFirst(1).Have(e => e.EpisodeFileId = 0).Build(); + + db.InsertMany(fakeEpisodes); + + mocker.GetMock() + .Setup(p => p.GetSeries(1)) + .Returns(fakeSeries); + + //Act + var episode = mocker.Resolve().GetEpisode(1, fakeEpisodes[0].AirDate); + + //Assert + episode.ShouldHave().AllPropertiesBut(e => e.Series).EqualTo(fakeEpisodes.First()); + episode.Series.ShouldHave().AllProperties().EqualTo(fakeSeries); + episode.EpisodeFile.Should().BeNull(); + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/HistoryProviderTest.cs b/NzbDrone.Core.Test/HistoryProviderTest.cs index b7c405de4..f127b89e3 100644 --- a/NzbDrone.Core.Test/HistoryProviderTest.cs +++ b/NzbDrone.Core.Test/HistoryProviderTest.cs @@ -29,7 +29,6 @@ namespace NzbDrone.Core.Test db.InsertMany(historyItem); - //Act var result = mocker.Resolve().AllItems(); @@ -37,6 +36,39 @@ namespace NzbDrone.Core.Test result.Should().HaveSameCount(historyItem); } + [Test] + public void AllItemsWithRelationships() + { + //Setup + var seriesOne = Builder.CreateNew().With(s => s.SeriesId = 12345).Build(); + var seriesTwo = Builder.CreateNew().With(s => s.SeriesId = 54321).Build(); + + var episodes = Builder.CreateListOfSize(10).Build(); + + var historyItems = Builder.CreateListOfSize(10).WhereTheFirst(5).Have(h => h.SeriesId = seriesOne.SeriesId).WhereTheLast(5).Have(h => h.SeriesId = seriesTwo.SeriesId).Build(); + + var mocker = new AutoMoqer(); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + db.InsertMany(historyItems); + db.InsertMany(episodes); + db.Insert(seriesOne); + db.Insert(seriesTwo); + + //Act + var result = mocker.Resolve().AllItemsWithRelationships(); + + //Assert + result.Should().HaveSameCount(historyItems); + + foreach (var history in result) + { + Assert.NotNull(history.Episode); + Assert.That(!String.IsNullOrEmpty(history.SeriesTitle)); + } + } + [Test] public void PurgeItem() { @@ -157,7 +189,5 @@ namespace NzbDrone.Core.Test Assert.AreEqual(history.Quality, storedHistory.First().Quality); Assert.AreEqual(history.IsProper, storedHistory.First().IsProper); } - - } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index 16e965abc..8bdca33be 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -35,27 +35,73 @@ namespace NzbDrone.Core.Providers public virtual Episode GetEpisode(long id) { - return AttachSeries(_database.Single(id)); + var episode = AttachSeries(_database.Fetch(@"SELECT * FROM Episodes + LEFT JOIN EpisodeFiles ON Episodes.EpisodeFileId = EpisodeFiles.EpisodeFileId + WHERE EpisodeId = @0", id).Single()); + if (episode.EpisodeFileId == 0) + episode.EpisodeFile = null; + + return episode; } public virtual Episode GetEpisode(int seriesId, int seasonNumber, int episodeNumber) { - return AttachSeries(_database.SingleOrDefault("WHERE SeriesId = @0 AND SeasonNumber = @1 AND EpisodeNumber = @2", seriesId, seasonNumber, episodeNumber)); + var episode = AttachSeries(_database.Fetch(@"SELECT * FROM Episodes + LEFT JOIN EpisodeFiles ON Episodes.EpisodeFileId = EpisodeFiles.EpisodeFileId + WHERE Episodes.SeriesId = @0 AND Episodes.SeasonNumber = @1 AND Episodes.EpisodeNumber = @2", seriesId, seasonNumber, episodeNumber).SingleOrDefault()); + + if (episode == null) + return null; + + if (episode.EpisodeFileId == 0) + episode.EpisodeFile = null; + + return episode; } public virtual Episode GetEpisode(int seriesId, DateTime date) { - return AttachSeries(_database.SingleOrDefault("WHERE SeriesId = @0 AND AirDate = @1", seriesId, date.Date)); + var episode = AttachSeries(_database.Fetch(@"SELECT * FROM Episodes + LEFT JOIN EpisodeFiles ON Episodes.EpisodeFileId = EpisodeFiles.EpisodeFileId + WHERE Episodes.SeriesId = @0 AND AirDate = @1", seriesId, date.Date)).SingleOrDefault(); + + if (episode == null) + return null; + + if (episode.EpisodeFileId == 0) + episode.EpisodeFile = null; + + return episode; } public virtual IList GetEpisodeBySeries(long seriesId) { - return AttachSeries(_database.Fetch("WHERE SeriesId = @0", seriesId)); + var episodes = AttachSeries(_database.Fetch(@"SELECT * FROM Episodes + LEFT JOIN EpisodeFiles ON Episodes.EpisodeFileId = EpisodeFiles.EpisodeFileId + WHERE Episodes.SeriesId = @0", seriesId)); + + foreach (var episode in episodes) + { + if (episode.EpisodeFileId == 0) + episode.EpisodeFile = null; + } + + return episodes; } public virtual IList GetEpisodesBySeason(long seriesId, int seasonNumber) { - return AttachSeries(_database.Fetch("WHERE SeriesId = @0 AND SeasonNumber = @1", seriesId, seasonNumber)); + var episodes = AttachSeries(_database.Fetch(@"SELECT * FROM Episodes + LEFT JOIN EpisodeFiles ON Episodes.EpisodeFileId = EpisodeFiles.EpisodeFileId + WHERE Episodes.SeriesId = @0 AND Episodes.SeasonNumber = @1", seriesId, seasonNumber)); + + foreach (var episode in episodes) + { + if (episode.EpisodeFileId == 0) + episode.EpisodeFile = null; + } + + return episodes; } public virtual List GetEpisodes(EpisodeParseResult parseResult) @@ -78,6 +124,12 @@ namespace NzbDrone.Core.Providers episodes.Add(episode); } + foreach (var episode in episodes) + { + if (episode.EpisodeFileId == 0) + episode.EpisodeFile = null; + } + return episodes; } diff --git a/NzbDrone.Core/Providers/HistoryProvider.cs b/NzbDrone.Core/Providers/HistoryProvider.cs index e389ded79..98a1ab883 100644 --- a/NzbDrone.Core/Providers/HistoryProvider.cs +++ b/NzbDrone.Core/Providers/HistoryProvider.cs @@ -25,9 +25,18 @@ namespace NzbDrone.Core.Providers { } - public virtual IEnumerable AllItems() + public virtual List AllItems() { - return _database.Query(""); + return _database.Fetch(""); + } + + public virtual List AllItemsWithRelationships() + { + return _database.Fetch(@" + SELECT History.*, Series.Title as SeriesTitle, Episodes.* FROM History + INNER JOIN Series ON History.SeriesId = Series.SeriesId + INNER JOIN Episodes ON History.EpisodeId = Episodes.EpisodeId + "); } public virtual void Purge() diff --git a/NzbDrone.Core/Repository/Episode.cs b/NzbDrone.Core/Repository/Episode.cs index 38afcc1d3..6e482e2ec 100644 --- a/NzbDrone.Core/Repository/Episode.cs +++ b/NzbDrone.Core/Repository/Episode.cs @@ -19,7 +19,6 @@ namespace NzbDrone.Core.Repository public string Title { get; set; } public DateTime AirDate { get; set; } - public string Overview { get; set; } public Boolean Ignored { get; set; } @@ -33,7 +32,6 @@ namespace NzbDrone.Core.Repository /// public DateTime? GrabDate { get; set; } - [Ignore] public EpisodeStatusType Status { @@ -58,15 +56,12 @@ namespace NzbDrone.Core.Repository } } - [Ignore] public Series Series { get; set; } - - [Ignore] + [ResultColumn] public EpisodeFile EpisodeFile { get; set; } - public override string ToString() { string seriesTitle = Series == null ? "[NULL]" : Series.Title; diff --git a/NzbDrone.Core/Repository/History.cs b/NzbDrone.Core/Repository/History.cs index a062d1c37..d34d6c2b2 100644 --- a/NzbDrone.Core/Repository/History.cs +++ b/NzbDrone.Core/Repository/History.cs @@ -16,5 +16,11 @@ namespace NzbDrone.Core.Repository public DateTime Date { get; set; } public bool IsProper { get; set; } public string Indexer { get; set; } + + [ResultColumn] + public Episode Episode { get; set; } + + [ResultColumn] + public string SeriesTitle { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/HistoryController.cs b/NzbDrone.Web/Controllers/HistoryController.cs index 344af644a..1802396f8 100644 --- a/NzbDrone.Web/Controllers/HistoryController.cs +++ b/NzbDrone.Web/Controllers/HistoryController.cs @@ -46,30 +46,20 @@ namespace NzbDrone.Web.Controllers [GridAction] public ActionResult _AjaxBinding() { - var historyDb = _historyProvider.AllItems(); - - var history = new List(); - - foreach (var item in historyDb) - { - var episode = _episodeProvider.GetEpisode(item.EpisodeId); - var series = _seriesProvider.GetSeries(item.SeriesId); - - history.Add(new HistoryModel + var history = _historyProvider.AllItemsWithRelationships().Select(h => new HistoryModel { - HistoryId = item.HistoryId, - SeasonNumber = episode.SeasonNumber, - EpisodeNumber = episode.EpisodeNumber, - EpisodeTitle = episode.Title, - EpisodeOverview = episode.Overview, - SeriesTitle = series.Title, - NzbTitle = item.NzbTitle, - Quality = item.Quality.ToString(), - IsProper = item.IsProper, - Date = item.Date, - Indexer = item.Indexer + HistoryId = h.HistoryId, + SeasonNumber = h.Episode.SeasonNumber, + EpisodeNumber = h.Episode.EpisodeNumber, + EpisodeTitle = h.Episode.Title, + EpisodeOverview = h.Episode.Overview, + SeriesTitle = h.SeriesTitle, + NzbTitle = h.NzbTitle, + Quality = h.Quality.ToString(), + IsProper = h.IsProper, + Date = h.Date, + Indexer = h.Indexer }); - } return View(new GridModel(history)); }