diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeAllowedSpecificationFixture .cs b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeAllowedSpecificationFixture .cs index 7dbcddc91..c958dc1a6 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeAllowedSpecificationFixture .cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeAllowedSpecificationFixture .cs @@ -41,13 +41,11 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { new ProfileFormatItem { - Id = 1, Format = _customFormatOne, Score = 50 }, new ProfileFormatItem { - Id = 1, Format = _customFormatTwo, Score = 100 } diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupQualityProfileFormatItemsFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupQualityProfileFormatItemsFixture.cs index 53d1f0e5b..14bb29f3a 100644 --- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupQualityProfileFormatItemsFixture.cs +++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupQualityProfileFormatItemsFixture.cs @@ -21,6 +21,9 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers { Mocker.SetConstant( new QualityProfileFormatItemsCleanupRepository(Mocker.Resolve(), Mocker.Resolve())); + + Mocker.SetConstant( + new CustomFormatRepository(Mocker.Resolve(), Mocker.Resolve())); } [Test] @@ -30,7 +33,12 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers .With(h => h.Items = Qualities.QualityFixture.GetDefaultQualities()) .With(h => h.MinFormatScore = 50) .With(h => h.CutoffFormatScore = 100) - .With(h => h.FormatItems = Builder.CreateListOfSize(1).Build().ToList()) + .With(h => h.FormatItems = new List + { + Builder.CreateNew() + .With(c => c.Format = new CustomFormat("My Custom Format") { Id = 0 }) + .Build() + }) .BuildNew(); Db.Insert(qualityProfile); @@ -62,9 +70,10 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers .With(h => h.CutoffFormatScore = cutoffFormatScore) .With(h => h.FormatItems = new List { - Builder.CreateNew().With(f => f.Id = customFormat.Id).Build() + Builder.CreateNew() + .With(c => c.Format = customFormat) + .Build() }) - .BuildNew(); Db.Insert(qualityProfile); @@ -77,5 +86,49 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers result.First().MinFormatScore.Should().Be(minFormatScore); result.First().CutoffFormatScore.Should().Be(cutoffFormatScore); } + + [Test] + public void should_add_missing_custom_formats() + { + var minFormatScore = 50; + var cutoffFormatScore = 100; + + var customFormat1 = Builder.CreateNew() + .With(h => h.Id = 1) + .With(h => h.Name = "Custom Format 1") + .With(h => h.Specifications = new List()) + .BuildNew(); + + var customFormat2 = Builder.CreateNew() + .With(h => h.Id = 2) + .With(h => h.Name = "Custom Format 2") + .With(h => h.Specifications = new List()) + .BuildNew(); + + Db.Insert(customFormat1); + Db.Insert(customFormat2); + + var qualityProfile = Builder.CreateNew() + .With(h => h.Items = Qualities.QualityFixture.GetDefaultQualities()) + .With(h => h.MinFormatScore = minFormatScore) + .With(h => h.CutoffFormatScore = cutoffFormatScore) + .With(h => h.FormatItems = new List + { + Builder.CreateNew() + .With(c => c.Format = customFormat1) + .Build() + }) + .BuildNew(); + + Db.Insert(qualityProfile); + + Subject.Clean(); + var result = AllStoredModels; + + result.Should().HaveCount(1); + result.First().FormatItems.Should().HaveCount(2); + result.First().MinFormatScore.Should().Be(minFormatScore); + result.First().CutoffFormatScore.Should().Be(cutoffFormatScore); + } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupQualityProfileFormatItems.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupQualityProfileFormatItems.cs index 6c04ce114..ace84ca29 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupQualityProfileFormatItems.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupQualityProfileFormatItems.cs @@ -1,35 +1,64 @@ using System.Collections.Generic; using System.Linq; -using Dapper; using NzbDrone.Common.Extensions; +using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities; namespace NzbDrone.Core.Housekeeping.Housekeepers { public class CleanupQualityProfileFormatItems : IHousekeepingTask { - private readonly IMainDatabase _database; private readonly IQualityProfileFormatItemsCleanupRepository _repository; + private readonly ICustomFormatRepository _customFormatRepository; - public CleanupQualityProfileFormatItems(IMainDatabase database, IQualityProfileFormatItemsCleanupRepository repository) + public CleanupQualityProfileFormatItems(IQualityProfileFormatItemsCleanupRepository repository, + ICustomFormatRepository customFormatRepository) { - _database = database; _repository = repository; + _customFormatRepository = customFormatRepository; } public void Clean() { - var customFormatIds = GetCustomFormatIds(); + var test = _customFormatRepository.All(); + var customFormats = _customFormatRepository.All().ToDictionary(c => c.Id); var profiles = _repository.All(); var updatedProfiles = new List(); foreach (var profile in profiles) { - var formatItems = profile.FormatItems.Where(f => customFormatIds.Contains(f.Id)).ToList(); + var formatItems = new List(); - if (formatItems.Count != profile.FormatItems.Count) + // Make sure the profile doesn't include formats that have been removed + profile.FormatItems.ForEach(p => + { + if (p.Format != null && customFormats.ContainsKey(p.Format.Id)) + { + formatItems.Add(p); + } + }); + + // Make sure the profile includes all available formats + foreach (var customFormat in customFormats) + { + if (formatItems.None(f => f.Format.Id == customFormat.Key)) + { + formatItems.Insert(0, new ProfileFormatItem + { + Format = customFormat.Value, + Score = 0 + }); + } + } + + var previousIds = profile.FormatItems.Select(i => i.Format.Id).ToList(); + var ids = formatItems.Select(i => i.Format.Id).ToList(); + + // Update the profile if any formats were added or removed + if (ids.Except(previousIds).Any() || previousIds.Except(ids).Any()) { profile.FormatItems = formatItems; @@ -48,14 +77,6 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers _repository.SetFields(updatedProfiles, p => p.FormatItems, p => p.MinFormatScore, p => p.CutoffFormatScore); } } - - private HashSet GetCustomFormatIds() - { - using (var mapper = _database.OpenConnection()) - { - return new HashSet(mapper.Query("SELECT Id FROM CustomFormats")); - } - } } public interface IQualityProfileFormatItemsCleanupRepository : IBasicRepository diff --git a/src/NzbDrone.Core/Profiles/ProfileFormatItem.cs b/src/NzbDrone.Core/Profiles/ProfileFormatItem.cs index 30b3582ee..7f498d172 100644 --- a/src/NzbDrone.Core/Profiles/ProfileFormatItem.cs +++ b/src/NzbDrone.Core/Profiles/ProfileFormatItem.cs @@ -1,4 +1,3 @@ -using System.Text.Json.Serialization; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Datastore; @@ -6,8 +5,6 @@ namespace NzbDrone.Core.Profiles { public class ProfileFormatItem : IEmbeddedDocument { - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public int Id { get; set; } public CustomFormat Format { get; set; } public int Score { get; set; } } diff --git a/src/NzbDrone.Core/Profiles/Qualities/QualityProfileService.cs b/src/NzbDrone.Core/Profiles/Qualities/QualityProfileService.cs index aaa657456..ae7446511 100644 --- a/src/NzbDrone.Core/Profiles/Qualities/QualityProfileService.cs +++ b/src/NzbDrone.Core/Profiles/Qualities/QualityProfileService.cs @@ -221,7 +221,6 @@ namespace NzbDrone.Core.Profiles.Qualities var formatItems = _formatService.All().Select(format => new ProfileFormatItem { - Id = format.Id, Score = 0, Format = format }).ToList();