decision engine now reports it's own errors rather than just dying.

pull/3113/head
kay.one 12 years ago
parent 9bbbc19869
commit d6d524e624

@ -59,7 +59,6 @@ namespace NzbDrone.Api.Indexers
public Int32 SeasonNumber { get; set; } public Int32 SeasonNumber { get; set; }
public Language Language { get; set; } public Language Language { get; set; }
public DateTime? AirDate { get; set; } public DateTime? AirDate { get; set; }
public String OriginalString { get; set; }
public String SeriesTitle { get; set; } public String SeriesTitle { get; set; }
public int[] EpisodeNumbers { get; set; } public int[] EpisodeNumbers { get; set; }
public Boolean Approved { get; set; } public Boolean Approved { get; set; }

@ -29,7 +29,7 @@ namespace NzbDrone.Core.Test.DataAugmentationFixture.Scene
{ {
Mocker.GetMock<ISceneMappingProxy>().Setup(c => c.Fetch()).Returns(_fakeMappings); Mocker.GetMock<ISceneMappingProxy>().Setup(c => c.Fetch()).Returns(_fakeMappings);
Subject.UpdateMappings(); Subject.Execute(new UpdateSceneMappingCommand());
AssertMappingUpdated(); AssertMappingUpdated();
} }
@ -42,7 +42,7 @@ namespace NzbDrone.Core.Test.DataAugmentationFixture.Scene
Mocker.GetMock<ISceneMappingProxy>().Setup(c => c.Fetch()).Throws(new WebException()); Mocker.GetMock<ISceneMappingProxy>().Setup(c => c.Fetch()).Throws(new WebException());
Subject.UpdateMappings(); Subject.Execute(new UpdateSceneMappingCommand());
AssertNoUpdate(); AssertNoUpdate();
@ -56,7 +56,7 @@ namespace NzbDrone.Core.Test.DataAugmentationFixture.Scene
Mocker.GetMock<ISceneMappingProxy>().Setup(c => c.Fetch()).Returns(new List<SceneMapping>()); Mocker.GetMock<ISceneMappingProxy>().Setup(c => c.Fetch()).Returns(new List<SceneMapping>());
Subject.UpdateMappings(); Subject.Execute(new UpdateSceneMappingCommand());
AssertNoUpdate(); AssertNoUpdate();

@ -3,60 +3,33 @@ using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.DataAugmentation.Scene namespace NzbDrone.Core.DataAugmentation.Scene
{ {
public interface ISceneMappingService public interface ISceneMappingService
{ {
void UpdateMappings(); string GetSceneName(int tvdbId, int seasonNumber = -1);
string GetSceneName(int seriesId, int seasonNumber = -1);
Nullable<int> GetTvDbId(string cleanName); Nullable<int> GetTvDbId(string cleanName);
string GetCleanName(int tvdbId);
} }
public class SceneMappingService : ISceneMappingService,IHandleAsync<ApplicationStartedEvent> public class SceneMappingService : ISceneMappingService,
IHandleAsync<ApplicationStartedEvent>,
IExecute<UpdateSceneMappingCommand>
{ {
private readonly ISceneMappingRepository _repository; private readonly ISceneMappingRepository _repository;
private readonly ISceneMappingProxy _sceneMappingProxy; private readonly ISceneMappingProxy _sceneMappingProxy;
private readonly ISeriesService _seriesService;
private readonly Logger _logger; private readonly Logger _logger;
public SceneMappingService(ISceneMappingRepository repository, ISceneMappingProxy sceneMappingProxy, ISeriesService seriesService, Logger logger) public SceneMappingService(ISceneMappingRepository repository, ISceneMappingProxy sceneMappingProxy, Logger logger)
{ {
_repository = repository; _repository = repository;
_sceneMappingProxy = sceneMappingProxy; _sceneMappingProxy = sceneMappingProxy;
_seriesService = seriesService;
_logger = logger; _logger = logger;
} }
public void UpdateMappings() public string GetSceneName(int tvdbId, int seasonNumber = -1)
{ {
try var mapping = _repository.FindByTvdbId(tvdbId);
{
var mappings = _sceneMappingProxy.Fetch();
if (mappings.Any())
{
_repository.Purge();
_repository.InsertMany(mappings);
}
else
{
_logger.Warn("Received empty list of mapping. will not update.");
}
}
catch (Exception ex)
{
_logger.ErrorException("Failed to Update Scene Mappings:", ex);
}
}
public string GetSceneName(int seriesId, int seasonNumber = -1)
{
var tvDbId = _seriesService.FindByTvdbId(seriesId).TvdbId;
var mapping = _repository.FindByTvdbId(tvDbId);
if (mapping == null) return null; if (mapping == null) return null;
@ -76,21 +49,39 @@ namespace NzbDrone.Core.DataAugmentation.Scene
} }
public string GetCleanName(int tvdbId) public void HandleAsync(ApplicationStartedEvent message)
{ {
var mapping = _repository.FindByTvdbId(tvdbId); if (!_repository.HasItems())
{
UpdateMappings();
}
}
if (mapping == null) return null; private void UpdateMappings()
{
try
{
var mappings = _sceneMappingProxy.Fetch();
return mapping.CleanTitle; if (mappings.Any())
{
_repository.Purge();
_repository.InsertMany(mappings);
} }
else
public void HandleAsync(ApplicationStartedEvent message)
{ {
if (!_repository.HasItems()) _logger.Warn("Received empty list of mapping. will not update.");
}
}
catch (Exception ex)
{ {
UpdateMappings(); _logger.ErrorException("Failed to Update Scene Mappings:", ex);
}
} }
public void Execute(UpdateSceneMappingCommand message)
{
UpdateMappings();
} }
} }
} }

@ -0,0 +1,8 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.DataAugmentation.Scene
{
public class UpdateSceneMappingCommand : ICommand
{
}
}

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog;
using NzbDrone.Core.DecisionEngine.Specifications.Search; using NzbDrone.Core.DecisionEngine.Specifications.Search;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
@ -18,38 +19,26 @@ namespace NzbDrone.Core.DecisionEngine
{ {
private readonly IEnumerable<IRejectWithReason> _specifications; private readonly IEnumerable<IRejectWithReason> _specifications;
private readonly IParsingService _parsingService; private readonly IParsingService _parsingService;
private readonly Logger _logger;
public DownloadDecisionMaker(IEnumerable<IRejectWithReason> specifications, IParsingService parsingService) public DownloadDecisionMaker(IEnumerable<IRejectWithReason> specifications, IParsingService parsingService, Logger logger)
{ {
_specifications = specifications; _specifications = specifications;
_parsingService = parsingService; _parsingService = parsingService;
_logger = logger;
} }
public List<DownloadDecision> GetRssDecision(IEnumerable<ReportInfo> reports) public List<DownloadDecision> GetRssDecision(IEnumerable<ReportInfo> reports)
{ {
return GetDecisions(reports, GetGeneralRejectionReasons).ToList(); return GetDecisions(reports).ToList();
} }
public List<DownloadDecision> GetSearchDecision(IEnumerable<ReportInfo> reports, SearchDefinitionBase searchDefinitionBase) public List<DownloadDecision> GetSearchDecision(IEnumerable<ReportInfo> reports, SearchDefinitionBase searchDefinitionBase)
{ {
return GetDecisions(reports, remoteEpisode => return GetDecisions(reports).ToList();
{
var generalReasons = GetGeneralRejectionReasons(remoteEpisode);
var searchReasons = GetSearchRejectionReasons(remoteEpisode, searchDefinitionBase);
return generalReasons.Union(searchReasons);
}).ToList();
}
private IEnumerable<string> GetGeneralRejectionReasons(RemoteEpisode report)
{
return _specifications
.OfType<IDecisionEngineSpecification>()
.Where(spec => !spec.IsSatisfiedBy(report))
.Select(spec => spec.RejectionReason);
} }
private IEnumerable<DownloadDecision> GetDecisions(IEnumerable<ReportInfo> reports, Func<RemoteEpisode, IEnumerable<string>> decisionCallback) private IEnumerable<DownloadDecision> GetDecisions(IEnumerable<ReportInfo> reports, SearchDefinitionBase searchDefinition = null)
{ {
foreach (var report in reports) foreach (var report in reports)
{ {
@ -62,7 +51,7 @@ namespace NzbDrone.Core.DecisionEngine
if (remoteEpisode.Series != null) if (remoteEpisode.Series != null)
{ {
yield return new DownloadDecision(remoteEpisode, decisionCallback(remoteEpisode).ToArray()); yield return GetDecisionForReport(remoteEpisode, searchDefinition);
} }
else else
{ {
@ -72,12 +61,40 @@ namespace NzbDrone.Core.DecisionEngine
} }
} }
private IEnumerable<string> GetSearchRejectionReasons(RemoteEpisode report, SearchDefinitionBase searchDefinitionBase) private DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchDefinitionBase searchDefinition = null)
{
var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchDefinition))
.Where(c => !string.IsNullOrWhiteSpace(c));
return new DownloadDecision(remoteEpisode, reasons.ToArray());
}
private string EvaluateSpec(IRejectWithReason spec, RemoteEpisode remoteEpisode, SearchDefinitionBase searchDefinitionBase = null)
{
try
{
var searchSpecification = spec as IDecisionEngineSearchSpecification;
if (searchSpecification != null && searchDefinitionBase != null)
{ {
return _specifications if (!searchSpecification.IsSatisfiedBy(remoteEpisode, searchDefinitionBase))
.OfType<IDecisionEngineSearchSpecification>() {
.Where(spec => !spec.IsSatisfiedBy(report, searchDefinitionBase)) return spec.RejectionReason;
.Select(spec => spec.RejectionReason); }
}
var generalSpecification = spec as IDecisionEngineSpecification;
if (generalSpecification != null && !generalSpecification.IsSatisfiedBy(remoteEpisode))
{
return spec.RejectionReason;
}
}
catch (Exception e)
{
_logger.ErrorException("Couldn't evaluate decision", e);
return string.Format("{0}: {1}", spec.GetType().Name, e.Message);
}
return null;
} }
} }
} }

@ -0,0 +1,45 @@
using System.Linq;
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class SingleEpisodeMatchSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public SingleEpisodeMatchSpecification(Logger logger)
{
_logger = logger;
}
public string RejectionReason
{
get
{
return "Episode doesn't match";
}
}
public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchDefinitionBase searchDefinitionBase)
{
var singleEpisodeSpec = searchDefinitionBase as SingleEpisodeSearchDefinition;
if (singleEpisodeSpec == null) return true;
if (singleEpisodeSpec.SeasonNumber != remoteEpisode.ParsedEpisodeInfo.SeasonNumber)
{
_logger.Trace("Season number does not match searched season number, skipping.");
return false;
}
if (!remoteEpisode.Episodes.Select(c => c.EpisodeNumber).Contains(singleEpisodeSpec.EpisodeNumber))
{
_logger.Trace("Episode number does not match searched episode number, skipping.");
return false;
}
return true;
}
}
}

@ -101,8 +101,10 @@ namespace NzbDrone.Core.IndexerSearch
{ {
var spec = new TSpec(); var spec = new TSpec();
var tvdbId = _seriesService.GetSeries(seriesId).TvdbId;
spec.SeriesId = seriesId; spec.SeriesId = seriesId;
spec.SceneTitle = _sceneMapping.GetSceneName(seriesId, seasonNumber); spec.SceneTitle = _sceneMapping.GetSceneName(tvdbId, seasonNumber);
return spec; return spec;
} }

@ -1,33 +0,0 @@
using System;
using System.Linq;
using NzbDrone.Core.DataAugmentation;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Model.Notification;
namespace NzbDrone.Core.Jobs.Implementations
{
public class UpdateSceneMappingsJob : IJob
{
private readonly SceneMappingService _sceneNameMappingService;
public UpdateSceneMappingsJob(SceneMappingService sceneNameMappingService)
{
_sceneNameMappingService = sceneNameMappingService;
}
public string Name
{
get { return "Update Scene Mappings"; }
}
public TimeSpan DefaultInterval
{
get { return TimeSpan.FromHours(6); }
}
public virtual void Start(ProgressNotification notification, dynamic options)
{
_sceneNameMappingService.UpdateMappings();
}
}
}

@ -186,6 +186,7 @@
<Compile Include="DataAugmentation\Scene\SceneMappingService.cs" /> <Compile Include="DataAugmentation\Scene\SceneMappingService.cs" />
<Compile Include="DataAugmentation\Scene\SceneMappingProxy.cs" /> <Compile Include="DataAugmentation\Scene\SceneMappingProxy.cs" />
<Compile Include="DataAugmentation\Scene\SceneMappingRepository.cs" /> <Compile Include="DataAugmentation\Scene\SceneMappingRepository.cs" />
<Compile Include="DataAugmentation\Scene\UpdateSceneMappingCommand.cs" />
<Compile Include="Datastore\Converters\BooleanIntConverter.cs" /> <Compile Include="Datastore\Converters\BooleanIntConverter.cs" />
<Compile Include="Datastore\Converters\QualityIntConverter.cs" /> <Compile Include="Datastore\Converters\QualityIntConverter.cs" />
<Compile Include="Datastore\Converters\Int32Converter.cs" /> <Compile Include="Datastore\Converters\Int32Converter.cs" />
@ -295,7 +296,6 @@
<Compile Include="Jobs\Implementations\RenameSeriesJob.cs" /> <Compile Include="Jobs\Implementations\RenameSeriesJob.cs" />
<Compile Include="Jobs\Implementations\RssSyncJob.cs" /> <Compile Include="Jobs\Implementations\RssSyncJob.cs" />
<Compile Include="Jobs\Implementations\UpdateInfoJob.cs" /> <Compile Include="Jobs\Implementations\UpdateInfoJob.cs" />
<Compile Include="Jobs\Implementations\UpdateSceneMappingsJob.cs" />
<Compile Include="Jobs\Implementations\XemUpdateJob.cs" /> <Compile Include="Jobs\Implementations\XemUpdateJob.cs" />
<Compile Include="Jobs\JobController.cs" /> <Compile Include="Jobs\JobController.cs" />
<Compile Include="Jobs\JobDefinition.cs" /> <Compile Include="Jobs\JobDefinition.cs" />
@ -394,6 +394,7 @@
<Compile Include="Instrumentation\TrimLogsJob.cs" /> <Compile Include="Instrumentation\TrimLogsJob.cs" />
<Compile Include="SeriesStats\SeriesStatistics.cs" /> <Compile Include="SeriesStats\SeriesStatistics.cs" />
<Compile Include="SeriesStats\SeriesStatisticsRepository.cs" /> <Compile Include="SeriesStats\SeriesStatisticsRepository.cs" />
<Compile Include="Tv\SeriesTypes.cs" />
<Compile Include="Update\AppUpdateJob.cs" /> <Compile Include="Update\AppUpdateJob.cs" />
<Compile Include="Model\Xbmc\TvShowResponse.cs" /> <Compile Include="Model\Xbmc\TvShowResponse.cs" />
<Compile Include="Model\Xbmc\TvShow.cs" /> <Compile Include="Model\Xbmc\TvShow.cs" />

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Marr.Data; using Marr.Data;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.MetadataSource.Trakt;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.RootFolders; using NzbDrone.Core.RootFolders;
@ -10,13 +9,6 @@ using NzbDrone.Core.RootFolders;
namespace NzbDrone.Core.Tv namespace NzbDrone.Core.Tv
{ {
public enum SeriesTypes
{
Standard = 0,
Daily = 1,
Anime = 2,
}
public class Series : ModelBase public class Series : ModelBase
{ {
public Series() public Series()

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.SeriesStats;
namespace NzbDrone.Core.Tv namespace NzbDrone.Core.Tv
{ {
@ -13,7 +12,6 @@ namespace NzbDrone.Core.Tv
Series FindByTitle(string cleanTitle); Series FindByTitle(string cleanTitle);
Series FindByTvdbId(int tvdbId); Series FindByTvdbId(int tvdbId);
void SetSeriesType(int seriesId, SeriesTypes seriesTypes); void SetSeriesType(int seriesId, SeriesTypes seriesTypes);
void SetTvRageId(int seriesId, int tvRageId);
} }
public class SeriesRepository : BasicRepository<Series>, ISeriesRepository public class SeriesRepository : BasicRepository<Series>, ISeriesRepository
@ -48,9 +46,5 @@ namespace NzbDrone.Core.Tv
SetFields(new Series { Id = seriesId, SeriesType = seriesType }, s => s.SeriesType); SetFields(new Series { Id = seriesId, SeriesType = seriesType }, s => s.SeriesType);
} }
public void SetTvRageId(int seriesId, int tvRageId)
{
SetFields(new Series { Id = seriesId, TvRageId = tvRageId }, s => s.TvRageId);
}
} }
} }

@ -8,6 +8,7 @@ using NzbDrone.Common;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.MetadataSource; using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
@ -42,18 +43,20 @@ namespace NzbDrone.Core.Tv
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly IProvideSeriesInfo _seriesInfoProxy; private readonly IProvideSeriesInfo _seriesInfoProxy;
private readonly IMessageAggregator _messageAggregator; private readonly IMessageAggregator _messageAggregator;
private readonly ISceneMappingService _sceneMappingService;
private readonly IRootFolderService _rootFolderService; private readonly IRootFolderService _rootFolderService;
private readonly DiskProvider _diskProvider; private readonly DiskProvider _diskProvider;
private readonly Logger _logger; private readonly Logger _logger;
public SeriesService(ISeriesRepository seriesRepository, IConfigService configServiceService, public SeriesService(ISeriesRepository seriesRepository, IConfigService configServiceService,
IProvideSeriesInfo seriesInfoProxy, IMessageAggregator messageAggregator, IProvideSeriesInfo seriesInfoProxy, IMessageAggregator messageAggregator, ISceneMappingService sceneMappingService,
IRootFolderService rootFolderService, DiskProvider diskProvider, Logger logger) IRootFolderService rootFolderService, DiskProvider diskProvider, Logger logger)
{ {
_seriesRepository = seriesRepository; _seriesRepository = seriesRepository;
_configService = configServiceService; _configService = configServiceService;
_seriesInfoProxy = seriesInfoProxy; _seriesInfoProxy = seriesInfoProxy;
_messageAggregator = messageAggregator; _messageAggregator = messageAggregator;
_sceneMappingService = sceneMappingService;
_rootFolderService = rootFolderService; _rootFolderService = rootFolderService;
_diskProvider = diskProvider; _diskProvider = diskProvider;
_logger = logger; _logger = logger;
@ -146,6 +149,13 @@ namespace NzbDrone.Core.Tv
public Series FindByTitle(string title) public Series FindByTitle(string title)
{ {
var tvdbId = _sceneMappingService.GetTvDbId(title);
if (tvdbId.HasValue)
{
return FindByTvdbId(tvdbId.Value);
}
return _seriesRepository.FindByTitle(Parser.Parser.NormalizeTitle(title)); return _seriesRepository.FindByTitle(Parser.Parser.NormalizeTitle(title));
} }

@ -0,0 +1,9 @@
namespace NzbDrone.Core.Tv
{
public enum SeriesTypes
{
Standard = 0,
Daily = 1,
Anime = 2,
}
}

@ -1,5 +1,6 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Api.Indexers;
namespace NzbDrone.Integration.Test namespace NzbDrone.Integration.Test
{ {
@ -9,9 +10,25 @@ namespace NzbDrone.Integration.Test
[Test] [Test]
public void should_only_have_unknown_series_releases() public void should_only_have_unknown_series_releases()
{ {
Releases.All().Should().OnlyContain(c => c.Rejections.Contains("Unknown Series")); var releases = Releases.All();
releases.Should().OnlyContain(c => c.Rejections.Contains("Unknown Series"));
releases.Should().OnlyContain(c=>BeValidRelease(c));
} }
private bool BeValidRelease(ReleaseResource releaseResource)
{
releaseResource.Age.Should().BeGreaterOrEqualTo(-1);
releaseResource.Title.Should().NotBeBlank();
releaseResource.NzbInfoUrl.Should().NotBeBlank();
releaseResource.NzbUrl.Should().NotBeBlank();
releaseResource.SeriesTitle.Should().NotBeBlank();
releaseResource.Size.Should().BeGreaterThan(0);
return true;
}
} }
} }
Loading…
Cancel
Save