Fixed: Negative preferred word scores being trumped by 0 scores without any matches

This commit is contained in:
Mark McDowall 2022-02-24 19:11:09 -08:00
parent 66af08a830
commit acdf02d569
3 changed files with 94 additions and 31 deletions

View File

@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -15,26 +17,33 @@ namespace NzbDrone.Core.Test.MediaFiles
{ {
private Series _series; private Series _series;
private EpisodeFile _episodeFile; private EpisodeFile _episodeFile;
private readonly KeyValuePair<string, int> _positiveScore = new KeyValuePair<string, int>("Positive", 10);
private readonly KeyValuePair<string, int> _negativeScore = new KeyValuePair<string, int>("Negative", -10);
private KeyValuePair<string, int> _neutralScore = new KeyValuePair<string, int>("Neutral", 0);
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_series = Builder<Series>.CreateNew().Build(); _series = Builder<Series>.CreateNew().Build();
_episodeFile = Builder<EpisodeFile>.CreateNew().Build(); _episodeFile = Builder<EpisodeFile>.CreateNew().Build();
Mocker.GetMock<IPreferredWordService>()
.Setup(s => s.GetMatchingPreferredWordsAndScores(It.IsAny<Series>(), It.IsAny<string>(), 0))
.Returns(new List<KeyValuePair<string, int>>());
} }
private void GivenPreferredWordScore(string title, int score) private void GivenPreferredWordScore(string title, params KeyValuePair<string, int>[] matches)
{ {
Mocker.GetMock<IPreferredWordService>() Mocker.GetMock<IPreferredWordService>()
.Setup(s => s.Calculate(It.IsAny<Series>(), title, 0)) .Setup(s => s.GetMatchingPreferredWordsAndScores(It.IsAny<Series>(), title, 0))
.Returns(score); .Returns(matches.ToList());
} }
[Test] [Test]
public void should_return_score_for_relative_file_name_when_it_is_higher_than_scene_name() public void should_return_score_for_relative_file_name_when_it_is_higher_than_scene_name()
{ {
GivenPreferredWordScore(_episodeFile.SceneName, 10); GivenPreferredWordScore(_episodeFile.SceneName, _positiveScore);
GivenPreferredWordScore(_episodeFile.RelativePath, 20); GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore, _positiveScore);
Subject.Calculate(_series, _episodeFile).Should().Be(20); Subject.Calculate(_series, _episodeFile).Should().Be(20);
} }
@ -45,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFiles
_episodeFile.SceneName = null; _episodeFile.SceneName = null;
_episodeFile.RelativePath = null; _episodeFile.RelativePath = null;
GivenPreferredWordScore(_episodeFile.Path, 20); GivenPreferredWordScore(_episodeFile.Path, _positiveScore, _positiveScore);
Subject.Calculate(_series, _episodeFile).Should().Be(20); Subject.Calculate(_series, _episodeFile).Should().Be(20);
} }
@ -55,7 +64,7 @@ namespace NzbDrone.Core.Test.MediaFiles
{ {
_episodeFile.SceneName = null; _episodeFile.SceneName = null;
GivenPreferredWordScore(_episodeFile.RelativePath, 20); GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore, _positiveScore);
Subject.Calculate(_series, _episodeFile).Should().Be(20); Subject.Calculate(_series, _episodeFile).Should().Be(20);
} }
@ -63,17 +72,17 @@ namespace NzbDrone.Core.Test.MediaFiles
[Test] [Test]
public void should_return_score_for_scene_name_when_higher_than_relative_file_name() public void should_return_score_for_scene_name_when_higher_than_relative_file_name()
{ {
GivenPreferredWordScore(_episodeFile.SceneName, 50); GivenPreferredWordScore(_episodeFile.SceneName, _positiveScore, _positiveScore, _positiveScore);
GivenPreferredWordScore(_episodeFile.RelativePath, 20); GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore, _positiveScore);
Subject.Calculate(_series, _episodeFile).Should().Be(50); Subject.Calculate(_series, _episodeFile).Should().Be(30);
} }
[Test] [Test]
public void should_return_score_for_relative_file_if_available() public void should_return_score_for_relative_file_if_available()
{ {
GivenPreferredWordScore(_episodeFile.RelativePath, 20); GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore, _positiveScore);
GivenPreferredWordScore(_episodeFile.Path, 50); GivenPreferredWordScore(_episodeFile.Path, _positiveScore, _positiveScore, _positiveScore);
Subject.Calculate(_series, _episodeFile).Should().Be(20); Subject.Calculate(_series, _episodeFile).Should().Be(20);
} }
@ -86,12 +95,12 @@ namespace NzbDrone.Core.Test.MediaFiles
_episodeFile.OriginalFilePath = Path.Combine(folderName, fileName); _episodeFile.OriginalFilePath = Path.Combine(folderName, fileName);
GivenPreferredWordScore(_episodeFile.RelativePath, 20); GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore);
GivenPreferredWordScore(_episodeFile.Path, 50); GivenPreferredWordScore(_episodeFile.Path, _positiveScore, _positiveScore);
GivenPreferredWordScore(folderName, 60); GivenPreferredWordScore(folderName, _positiveScore, _positiveScore, _positiveScore);
GivenPreferredWordScore(fileName, 50); GivenPreferredWordScore(fileName, _positiveScore, _positiveScore);
Subject.Calculate(_series, _episodeFile).Should().Be(60); Subject.Calculate(_series, _episodeFile).Should().Be(30);
} }
[Test] [Test]
@ -102,12 +111,42 @@ namespace NzbDrone.Core.Test.MediaFiles
_episodeFile.OriginalFilePath = Path.Combine(folderName, fileName); _episodeFile.OriginalFilePath = Path.Combine(folderName, fileName);
GivenPreferredWordScore(_episodeFile.RelativePath, 20); GivenPreferredWordScore(_episodeFile.RelativePath, _positiveScore);
GivenPreferredWordScore(_episodeFile.Path, 50); GivenPreferredWordScore(_episodeFile.Path, _positiveScore, _positiveScore);
GivenPreferredWordScore(folderName, 40); GivenPreferredWordScore(folderName, _positiveScore, _positiveScore);
GivenPreferredWordScore(fileName, 50); GivenPreferredWordScore(fileName, _positiveScore, _positiveScore, _positiveScore);
Subject.Calculate(_series, _episodeFile).Should().Be(50); Subject.Calculate(_series, _episodeFile).Should().Be(30);
}
[Test]
public void should_return_negative_score_if_0_result_has_no_matches()
{
var folderName = "folder-name";
var fileName = "file-name";
_episodeFile.OriginalFilePath = Path.Combine(folderName, fileName);
GivenPreferredWordScore(_episodeFile.RelativePath, _negativeScore);
GivenPreferredWordScore(fileName);
Subject.Calculate(_series, _episodeFile).Should().Be(-10);
}
[Test]
public void should_return_0_score_if_0_result_has_matches()
{
var folderName = "folder-name";
var fileName = "file-name";
_episodeFile.OriginalFilePath = Path.Combine(folderName, fileName);
GivenPreferredWordScore(_episodeFile.RelativePath, _negativeScore);
GivenPreferredWordScore(_episodeFile.Path, _negativeScore);
GivenPreferredWordScore(folderName, _negativeScore);
GivenPreferredWordScore(fileName, _neutralScore);
Subject.Calculate(_series, _episodeFile).Should().Be(0);
} }
} }
} }

View File

@ -32,7 +32,7 @@ namespace NzbDrone.Core.MediaFiles
if (episodeFile.SceneName.IsNotNullOrWhiteSpace()) if (episodeFile.SceneName.IsNotNullOrWhiteSpace())
{ {
scores.Add(_preferredWordService.Calculate(series, episodeFile.SceneName, 0)); AddScoreIfApplicable(series, episodeFile.SceneName, scores);
} }
else else
{ {
@ -49,7 +49,7 @@ namespace NzbDrone.Core.MediaFiles
var isLast = i == segments.Count - 1; var isLast = i == segments.Count - 1;
var segment = isLast ? Path.GetFileNameWithoutExtension(segments[i]) : segments[i]; var segment = isLast ? Path.GetFileNameWithoutExtension(segments[i]) : segments[i];
scores.Add(_preferredWordService.Calculate(series, segment, 0)); AddScoreIfApplicable(series, segment, scores);
} }
} }
else else
@ -60,11 +60,11 @@ namespace NzbDrone.Core.MediaFiles
// Calculate using RelativePath or Path, but not both // Calculate using RelativePath or Path, but not both
if (episodeFile.RelativePath.IsNotNullOrWhiteSpace()) if (episodeFile.RelativePath.IsNotNullOrWhiteSpace())
{ {
scores.Add(_preferredWordService.Calculate(series, episodeFile.RelativePath, 0)); AddScoreIfApplicable(series, episodeFile.RelativePath, scores);
} }
else if (episodeFile.Path.IsNotNullOrWhiteSpace()) else if (episodeFile.Path.IsNotNullOrWhiteSpace())
{ {
scores.Add(_preferredWordService.Calculate(series, episodeFile.Path, 0)); AddScoreIfApplicable(series, episodeFile.Path, scores);
} }
// Return the highest score, this will allow media info in file names to be used to improve preferred word scoring. // Return the highest score, this will allow media info in file names to be used to improve preferred word scoring.
@ -72,5 +72,22 @@ namespace NzbDrone.Core.MediaFiles
return scores.MaxOrDefault(); return scores.MaxOrDefault();
} }
private void AddScoreIfApplicable(Series series, string title, List<int> scores)
{
var score = 0;
_logger.Trace("Calculating preferred word score for '{0}'", title);
var matchingPairs = _preferredWordService.GetMatchingPreferredWordsAndScores(series, title, 0);
// Only add the score if there are matching terms, uncalculated
if (matchingPairs.Any())
{
score = matchingPairs.Sum(p => p.Value);
scores.Add(score);
}
_logger.Trace("Calculated preferred word score for '{0}': {1} ({2} match(es))", title, score, matchingPairs.Count);
}
} }
} }

View File

@ -10,6 +10,7 @@ namespace NzbDrone.Core.Profiles.Releases
public interface IPreferredWordService public interface IPreferredWordService
{ {
int Calculate(Series series, string title, int indexerId); int Calculate(Series series, string title, int indexerId);
List<KeyValuePair<string, int>> GetMatchingPreferredWordsAndScores(Series series, string title, int indexerId);
PreferredWordMatchResults GetMatchingPreferredWords(Series series, string title); PreferredWordMatchResults GetMatchingPreferredWords(Series series, string title);
} }
@ -30,6 +31,16 @@ namespace NzbDrone.Core.Profiles.Releases
{ {
_logger.Trace("Calculating preferred word score for '{0}'", title); _logger.Trace("Calculating preferred word score for '{0}'", title);
var matchingPairs = GetMatchingPreferredWordsAndScores(series, title, indexerId);
var score = matchingPairs.Sum(p => p.Value);
_logger.Trace("Calculated preferred word score for '{0}': {1} ({2} match(es))", title, score, matchingPairs.Count);
return score;
}
public List<KeyValuePair<string, int>> GetMatchingPreferredWordsAndScores(Series series, string title, int indexerId)
{
var releaseProfiles = _releaseProfileService.EnabledForTags(series.Tags, indexerId); var releaseProfiles = _releaseProfileService.EnabledForTags(series.Tags, indexerId);
var matchingPairs = new List<KeyValuePair<string, int>>(); var matchingPairs = new List<KeyValuePair<string, int>>();
@ -46,11 +57,7 @@ namespace NzbDrone.Core.Profiles.Releases
} }
} }
var score = matchingPairs.Sum(p => p.Value); return matchingPairs;
_logger.Trace("Calculated preferred word score for '{0}': {1}", title, score);
return score;
} }
public PreferredWordMatchResults GetMatchingPreferredWords(Series series, string title) public PreferredWordMatchResults GetMatchingPreferredWords(Series series, string title)