fix: strict_negative_scores works again

The removal of the markdown parsing logic in v2.0 accidentally also
deleted the logic responsible for handling this property. The code has
been refactored to introduce a "filter pipeline" system that handles
include/exclude filtering as well as strict negative score support.
pull/76/head
Robert Dailey 2 years ago
parent fcef5ccec2
commit 5140272705

@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Fixed
- Sonarr: `strict_negative_scores` works again (broke in v2.0 release)
## [2.0.0] - 2022-05-13
This release contains **BREAKING CHANGES**. See the [Upgrade Guide] for required changes you need to

@ -3,6 +3,7 @@
<PackageReference Update="Autofac" Version="6.*" />
<PackageReference Update="Autofac.Extensions.DependencyInjection" Version="7.*" />
<PackageReference Update="Autofac.Extras.AggregateService" Version="6.*" />
<PackageReference Update="Autofac.Extras.Ordering" Version="4.*" />
<PackageReference Update="AutofacContrib.NSubstitute" Version="7.*" />
<PackageReference Update="AutoFixture" Version="4.*" />
<PackageReference Update="AutoFixture.AutoNSubstitute" Version="4.*" />

@ -2,6 +2,7 @@
using System.Reflection;
using Autofac;
using Autofac.Core.Activators.Reflection;
using Autofac.Extras.Ordering;
using CliFx;
using CliFx.Infrastructure;
using Common;
@ -79,6 +80,9 @@ public static class CompositionRoot
public static IContainer Setup(ContainerBuilder builder)
{
// Needed for Autofac.Extras.Ordering
builder.RegisterSource<OrderedRegistrationSource>();
builder.RegisterType<FileSystem>().As<IFileSystem>();
builder.RegisterType<FileUtilities>().As<IFileUtilities>();
builder.RegisterType<SystemConsole>().As<IConsole>().SingleInstance();

@ -12,12 +12,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac.Extras.AggregateService" />
<PackageReference Include="Autofac" />
<PackageReference Include="Autofac.Extras.AggregateService" />
<PackageReference Include="Autofac.Extras.Ordering" />
<PackageReference Include="CliFx" />
<PackageReference Include="Serilog" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Serilog" />
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="YamlDotNet" />
</ItemGroup>

@ -0,0 +1,59 @@
using FluentAssertions;
using NUnit.Framework;
using TestLibrary.AutoFixture;
using TrashLib.Sonarr.Config;
using TrashLib.Sonarr.ReleaseProfile;
using TrashLib.Sonarr.ReleaseProfile.Filters;
namespace TrashLib.Tests.Sonarr.ReleaseProfile.Filters;
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class StrictNegativeScoresFilterTest
{
private static readonly ReleaseProfileData TestProfile = new()
{
Preferred = new[]
{
new PreferredTermData
{
Score = -1,
Terms = new[]
{
new TermData
{
TrashId = "abc",
Term = "a"
}
}
}
}
};
[Test, AutoMockData]
public void Preferred_with_negative_scores_is_treated_as_ignored_when_strict_negative_scores_enabled(
StrictNegativeScoresFilter sut)
{
var config = new ReleaseProfileConfig
{
StrictNegativeScores = true
};
var result = sut.Transform(TestProfile, config);
result.Preferred.Should().BeEmpty();
result.Ignored.Should().BeEquivalentTo(TestProfile.Preferred.First().Terms);
}
[Test, AutoMockData]
public void Preferred_and_ignored_untouched_when_strict_negative_scores_disabled(StrictNegativeScoresFilter sut)
{
var config = new ReleaseProfileConfig
{
StrictNegativeScores = false
};
var result = sut.Transform(TestProfile, config);
result.Should().BeSameAs(TestProfile);
}
}

@ -0,0 +1,8 @@
using TrashLib.Sonarr.Config;
namespace TrashLib.Sonarr.ReleaseProfile.Filters;
public interface IReleaseProfileFilter
{
ReleaseProfileData Transform(ReleaseProfileData profile, ReleaseProfileConfig config);
}

@ -0,0 +1,8 @@
using TrashLib.Sonarr.Config;
namespace TrashLib.Sonarr.ReleaseProfile.Filters;
public interface IReleaseProfileFilterPipeline
{
ReleaseProfileData Process(ReleaseProfileData profile, ReleaseProfileConfig config);
}

@ -0,0 +1,28 @@
using Serilog;
using TrashLib.Sonarr.Config;
namespace TrashLib.Sonarr.ReleaseProfile.Filters;
public class IncludeExcludeFilter : IReleaseProfileFilter
{
private readonly ILogger _log;
private readonly ReleaseProfileDataFilterer _filterer;
public IncludeExcludeFilter(ILogger log)
{
_log = log;
_filterer = new ReleaseProfileDataFilterer(log);
}
public ReleaseProfileData Transform(ReleaseProfileData profile, ReleaseProfileConfig config)
{
if (config.Filter == null)
{
return profile;
}
_log.Debug("This profile will be filtered");
var newProfile = _filterer.FilterProfile(profile, config.Filter);
return newProfile ?? profile;
}
}

@ -0,0 +1,23 @@
using TrashLib.Sonarr.Config;
namespace TrashLib.Sonarr.ReleaseProfile.Filters;
public class ReleaseProfileFilterPipeline : IReleaseProfileFilterPipeline
{
private readonly IOrderedEnumerable<IReleaseProfileFilter> _filters;
public ReleaseProfileFilterPipeline(IOrderedEnumerable<IReleaseProfileFilter> filters)
{
_filters = filters;
}
public ReleaseProfileData Process(ReleaseProfileData profile, ReleaseProfileConfig config)
{
foreach (var filter in _filters)
{
profile = filter.Transform(profile, config);
}
return profile;
}
}

@ -0,0 +1,31 @@
using Serilog;
using TrashLib.Sonarr.Config;
namespace TrashLib.Sonarr.ReleaseProfile.Filters;
public class StrictNegativeScoresFilter : IReleaseProfileFilter
{
private readonly ILogger _log;
public StrictNegativeScoresFilter(ILogger log)
{
_log = log;
}
public ReleaseProfileData Transform(ReleaseProfileData profile, ReleaseProfileConfig config)
{
if (!config.StrictNegativeScores)
{
return profile;
}
_log.Debug("Negative scores will be strictly ignored");
var splitPreferred = profile.Preferred.ToLookup(x => x.Score < 0);
return profile with
{
Ignored = profile.Ignored.Concat(splitPreferred[true].SelectMany(x => x.Terms)).ToList(),
Preferred = splitPreferred[false].ToList()
};
}
}

@ -5,14 +5,16 @@ using TrashLib.ExceptionTypes;
using TrashLib.Sonarr.Api;
using TrashLib.Sonarr.Api.Objects;
using TrashLib.Sonarr.Config;
using TrashLib.Sonarr.ReleaseProfile.Filters;
using TrashLib.Sonarr.ReleaseProfile.Guide;
namespace TrashLib.Sonarr.ReleaseProfile;
internal class ReleaseProfileUpdater : IReleaseProfileUpdater
public class ReleaseProfileUpdater : IReleaseProfileUpdater
{
private readonly ISonarrApi _api;
private readonly ISonarrCompatibility _compatibility;
private readonly IReleaseProfileFilterPipeline _pipeline;
private readonly ISonarrGuideService _guide;
private readonly ILogger _log;
@ -20,12 +22,14 @@ internal class ReleaseProfileUpdater : IReleaseProfileUpdater
ILogger logger,
ISonarrGuideService guide,
ISonarrApi api,
ISonarrCompatibility compatibility)
ISonarrCompatibility compatibility,
IReleaseProfileFilterPipeline pipeline)
{
_log = logger;
_guide = guide;
_api = api;
_compatibility = compatibility;
_pipeline = pipeline;
}
public async Task Process(bool isPreview, SonarrConfiguration config)
@ -33,7 +37,6 @@ internal class ReleaseProfileUpdater : IReleaseProfileUpdater
var profilesFromGuide = _guide.GetReleaseProfileData();
var filteredProfiles = new List<(ReleaseProfileData Profile, IReadOnlyCollection<string> Tags)>();
var filterer = new ReleaseProfileDataFilterer(_log);
var configProfiles = config.ReleaseProfiles.SelectMany(x => x.TrashIds.Select(y => (TrashId: y, Config: x)));
foreach (var (trashId, configProfile) in configProfiles)
@ -49,15 +52,7 @@ internal class ReleaseProfileUpdater : IReleaseProfileUpdater
_log.Debug("Found Release Profile: {ProfileName} ({TrashId})", selectedProfile.Name,
selectedProfile.TrashId);
if (configProfile.Filter != null)
{
_log.Debug("This profile will be filtered");
var newProfile = filterer.FilterProfile(selectedProfile, configProfile.Filter);
if (newProfile is not null)
{
selectedProfile = newProfile;
}
}
selectedProfile = _pipeline.Process(selectedProfile, configProfile);
if (isPreview)
{

@ -1,8 +1,10 @@
using Autofac;
using Autofac.Extras.Ordering;
using TrashLib.Sonarr.Api;
using TrashLib.Sonarr.Config;
using TrashLib.Sonarr.QualityDefinition;
using TrashLib.Sonarr.ReleaseProfile;
using TrashLib.Sonarr.ReleaseProfile.Filters;
using TrashLib.Sonarr.ReleaseProfile.Guide;
namespace TrashLib.Sonarr;
@ -21,6 +23,14 @@ public class SonarrAutofacModule : Module
builder.RegisterType<LocalRepoReleaseProfileJsonParser>().As<ISonarrGuideService>();
builder.RegisterType<SonarrReleaseProfileCompatibilityHandler>()
.As<ISonarrReleaseProfileCompatibilityHandler>();
builder.RegisterType<ReleaseProfileFilterPipeline>().As<IReleaseProfileFilterPipeline>();
// Release Profile Filters (ORDER MATTERS!)
builder.RegisterTypes(
typeof(IncludeExcludeFilter),
typeof(StrictNegativeScoresFilter))
.As<IReleaseProfileFilter>()
.OrderByRegistration();
// Quality Definition Support
builder.RegisterType<SonarrQualityDefinitionUpdater>().As<ISonarrQualityDefinitionUpdater>();

@ -1,18 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="AutoMapper" />
<PackageReference Include="Autofac" />
<PackageReference Include="Autofac.Extras.AggregateService" />
<PackageReference Include="Autofac.Extras.Ordering" />
<PackageReference Include="AutoMapper" />
<PackageReference Include="CliFx" />
<PackageReference Include="FluentValidation" />
<PackageReference Include="Flurl" />
<PackageReference Include="Flurl.Http" />
<PackageReference Include="morelinq" />
<PackageReference Include="Newtonsoft.Json.Schema" />
<PackageReference Include="Serilog" />
<PackageReference Include="System.Data.HashFunction.FNV" />
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="System.Reactive" />
<PackageReference Include="morelinq" />
</ItemGroup>
<ItemGroup>

Loading…
Cancel
Save