New: 'Seasons Monitored Status' Custom Filter to replace 'Has Unmonitored Season'

Closes #6896
This commit is contained in:
jbstark 2024-07-28 16:57:22 -07:00 committed by GitHub
parent a80f5b794b
commit 6dd85a5af9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 517 additions and 12 deletions

View File

@ -13,6 +13,7 @@ import LanguageFilterBuilderRowValue from './LanguageFilterBuilderRowValue';
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
import QualityFilterBuilderRowValueConnector from './QualityFilterBuilderRowValueConnector';
import QualityProfileFilterBuilderRowValueConnector from './QualityProfileFilterBuilderRowValueConnector';
import SeasonsMonitoredStatusFilterBuilderRowValue from './SeasonsMonitoredStatusFilterBuilderRowValue';
import SeriesFilterBuilderRowValue from './SeriesFilterBuilderRowValue';
import SeriesStatusFilterBuilderRowValue from './SeriesStatusFilterBuilderRowValue';
import SeriesTypeFilterBuilderRowValue from './SeriesTypeFilterBuilderRowValue';
@ -79,6 +80,9 @@ function getRowValueConnector(selectedFilterBuilderProp) {
case filterBuilderValueTypes.QUALITY_PROFILE:
return QualityProfileFilterBuilderRowValueConnector;
case filterBuilderValueTypes.SEASONS_MONITORED_STATUS:
return SeasonsMonitoredStatusFilterBuilderRowValue;
case filterBuilderValueTypes.SERIES:
return SeriesFilterBuilderRowValue;

View File

@ -0,0 +1,35 @@
import React from 'react';
import translate from 'Utilities/String/translate';
import FilterBuilderRowValue from './FilterBuilderRowValue';
const seasonsMonitoredStatusList = [
{
id: 'all',
get name() {
return translate('SeasonsMonitoredAll');
}
},
{
id: 'partial',
get name() {
return translate('SeasonsMonitoredPartial');
}
},
{
id: 'none',
get name() {
return translate('SeasonsMonitoredNone');
}
}
];
function SeasonsMonitoredStatusFilterBuilderRowValue(props) {
return (
<FilterBuilderRowValue
tagList={seasonsMonitoredStatusList}
{...props}
/>
);
}
export default SeasonsMonitoredStatusFilterBuilderRowValue;

View File

@ -8,6 +8,7 @@ export const LANGUAGE = 'language';
export const PROTOCOL = 'protocol';
export const QUALITY = 'quality';
export const QUALITY_PROFILE = 'qualityProfile';
export const SEASONS_MONITORED_STATUS = 'seasonsMonitoredStatus';
export const SERIES = 'series';
export const SERIES_STATUS = 'seriesStatus';
export const SERIES_TYPES = 'seriesType';

View File

@ -202,20 +202,33 @@ export const filterPredicates = {
return predicate(hasMissingSeason, filterValue);
},
hasUnmonitoredSeason: function(item, filterValue, type) {
seasonsMonitoredStatus: function(item, filterValue, type) {
const predicate = filterTypePredicates[type];
const { seasons = [] } = item;
const hasUnmonitoredSeason = seasons.some((season) => {
const {
seasonNumber,
monitored
} = season;
const { monitoredCount, unmonitoredCount } = seasons.reduce((acc, { seasonNumber, monitored }) => {
if (seasonNumber <= 0) {
return acc;
}
return seasonNumber > 0 && !monitored;
});
if (monitored) {
acc.monitoredCount++;
} else {
acc.unmonitoredCount++;
}
return predicate(hasUnmonitoredSeason, filterValue);
return acc;
}, { monitoredCount: 0, unmonitoredCount: 0 });
let seasonsMonitoredStatus = 'partial';
if (monitoredCount === 0) {
seasonsMonitoredStatus = 'none';
} else if (unmonitoredCount === 0) {
seasonsMonitoredStatus = 'all';
}
return predicate(seasonsMonitoredStatus, filterValue);
}
};
@ -383,10 +396,10 @@ export const filterBuilderProps = [
valueType: filterBuilderValueTypes.BOOL
},
{
name: 'hasUnmonitoredSeason',
label: () => translate('HasUnmonitoredSeason'),
name: 'seasonsMonitoredStatus',
label: () => translate('SeasonsMonitoredStatus'),
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.BOOL
valueType: filterBuilderValueTypes.SEASONS_MONITORED_STATUS
},
{
name: 'year',

View File

@ -0,0 +1,372 @@
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Newtonsoft.Json;
using NUnit.Framework;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Migration
{
[TestFixture]
public class add_monitored_seasons_filterFixture : MigrationTest<add_monitored_seasons_filter>
{
[Test]
public void equal_both_becomes_equal_every_option()
{
var filter = new FilterSettings210
{
key = "hasUnmonitoredSeason",
value = new List<object> { true, false },
type = "equal"
};
var filtersJson = new List<FilterSettings210> { filter };
var filtersString = filtersJson.ToJson();
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("CustomFilters").Row(new
{
Id = 1,
Type = "series",
Label = "Is Both",
Filters = filtersString
});
});
var items = db.Query<FilterDefinition210>("SELECT * FROM \"CustomFilters\"");
items.Should().HaveCount(1);
items.First().Type.Should().Be("series");
items.First().Label.Should().Be("Is Both");
var filters = JsonConvert.DeserializeObject<List<FilterSettings210>>(items.First().Filters);
filters[0].key.Should().Be("seasonsMonitoredStatus");
filters[0].value.Should().BeEquivalentTo(new List<object> { "all", "partial", "none" });
filters[0].type.Should().Be("equal");
}
[Test]
public void notEqual_both_becomes_notEqual_every_option()
{
var filter = new FilterSettings210
{
key = "hasUnmonitoredSeason",
value = new List<object> { true, false },
type = "notEqual"
};
var filtersJson = new List<FilterSettings210> { filter };
var filtersString = filtersJson.ToJson();
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("CustomFilters").Row(new
{
Id = 1,
Type = "series",
Label = "Is Both",
Filters = filtersString
});
});
var items = db.Query<FilterDefinition210>("SELECT * FROM \"CustomFilters\"");
items.Should().HaveCount(1);
items.First().Type.Should().Be("series");
items.First().Label.Should().Be("Is Both");
var filters = JsonConvert.DeserializeObject<List<FilterSettings210>>(items.First().Filters);
filters[0].key.Should().Be("seasonsMonitoredStatus");
filters[0].value.Should().BeEquivalentTo(new List<object> { "all", "partial", "none" });
filters[0].type.Should().Be("notEqual");
}
[Test]
public void equal_true_becomes_notEqual_all()
{
var filter = new FilterSettings210
{
key = "hasUnmonitoredSeason",
value = new List<object> { true },
type = "equal"
};
var filtersJson = new List<FilterSettings210> { filter };
var filtersString = filtersJson.ToJson();
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("CustomFilters").Row(new
{
Id = 1,
Type = "series",
Label = "Is Both",
Filters = filtersString
});
});
var items = db.Query<FilterDefinition210>("SELECT * FROM \"CustomFilters\"");
items.Should().HaveCount(1);
items.First().Type.Should().Be("series");
items.First().Label.Should().Be("Is Both");
var filters = JsonConvert.DeserializeObject<List<FilterSettings210>>(items.First().Filters);
filters[0].key.Should().Be("seasonsMonitoredStatus");
filters[0].value.Should().BeEquivalentTo(new List<object> { "all" });
filters[0].type.Should().Be("notEqual");
}
[Test]
public void notEqual_true_becomes_equal_all()
{
var filter = new FilterSettings210
{
key = "hasUnmonitoredSeason",
value = new List<object> { true },
type = "notEqual"
};
var filtersJson = new List<FilterSettings210> { filter };
var filtersString = filtersJson.ToJson();
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("CustomFilters").Row(new
{
Id = 1,
Type = "series",
Label = "Is Both",
Filters = filtersString
});
});
var items = db.Query<FilterDefinition210>("SELECT * FROM \"CustomFilters\"");
items.Should().HaveCount(1);
items.First().Type.Should().Be("series");
items.First().Label.Should().Be("Is Both");
var filters = JsonConvert.DeserializeObject<List<FilterSettings210>>(items.First().Filters);
filters[0].key.Should().Be("seasonsMonitoredStatus");
filters[0].value.Should().BeEquivalentTo(new List<object> { "all" });
filters[0].type.Should().Be("equal");
}
[Test]
public void equal_false_becomes_equal_all()
{
var filter = new FilterSettings210
{
key = "hasUnmonitoredSeason",
value = new List<object> { false },
type = "equal"
};
var filtersJson = new List<FilterSettings210> { filter };
var filtersString = filtersJson.ToJson();
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("CustomFilters").Row(new
{
Id = 1,
Type = "series",
Label = "Is Both",
Filters = filtersString
});
});
var items = db.Query<FilterDefinition210>("SELECT * FROM \"CustomFilters\"");
items.Should().HaveCount(1);
items.First().Type.Should().Be("series");
items.First().Label.Should().Be("Is Both");
var filters = JsonConvert.DeserializeObject<List<FilterSettings210>>(items.First().Filters);
filters[0].key.Should().Be("seasonsMonitoredStatus");
filters[0].value.Should().BeEquivalentTo(new List<object> { "all" });
filters[0].type.Should().Be("equal");
}
[Test]
public void notEqual_false_becomes_notEqual_all()
{
var filter = new FilterSettings210
{
key = "hasUnmonitoredSeason",
value = new List<object> { false },
type = "notEqual"
};
var filtersJson = new List<FilterSettings210> { filter };
var filtersString = filtersJson.ToJson();
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("CustomFilters").Row(new
{
Id = 1,
Type = "series",
Label = "Is Both",
Filters = filtersString
});
});
var items = db.Query<FilterDefinition210>("SELECT * FROM \"CustomFilters\"");
items.Should().HaveCount(1);
items.First().Type.Should().Be("series");
items.First().Label.Should().Be("Is Both");
var filters = JsonConvert.DeserializeObject<List<FilterSettings210>>(items.First().Filters);
filters[0].key.Should().Be("seasonsMonitoredStatus");
filters[0].value.Should().BeEquivalentTo(new List<object> { "all" });
filters[0].type.Should().Be("notEqual");
}
[Test]
public void missing_hasUnmonitored_unchanged()
{
var filter = new FilterSettings210
{
key = "monitored",
value = new List<object> { false },
type = "equal"
};
var filtersJson = new List<FilterSettings210> { filter };
var filtersString = filtersJson.ToJson();
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("CustomFilters").Row(new
{
Id = 1,
Type = "series",
Label = "Is Both",
Filters = filtersString
});
});
var items = db.Query<FilterDefinition210>("SELECT * FROM \"CustomFilters\"");
items.Should().HaveCount(1);
items.First().Type.Should().Be("series");
items.First().Label.Should().Be("Is Both");
var filters = JsonConvert.DeserializeObject<List<FilterSettings210>>(items.First().Filters);
filters[0].key.Should().Be("monitored");
filters[0].value.Should().BeEquivalentTo(new List<object> { false });
filters[0].type.Should().Be("equal");
}
[Test]
public void has_hasUnmonitored_not_in_first_entry()
{
var filter1 = new FilterSettings210
{
key = "monitored",
value = new List<object> { false },
type = "equal"
};
var filter2 = new FilterSettings210
{
key = "hasUnmonitoredSeason",
value = new List<object> { true },
type = "equal"
};
var filtersJson = new List<FilterSettings210> { filter1, filter2 };
var filtersString = filtersJson.ToJson();
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("CustomFilters").Row(new
{
Id = 1,
Type = "series",
Label = "Is Both",
Filters = filtersString
});
});
var items = db.Query<FilterDefinition210>("SELECT * FROM \"CustomFilters\"");
items.Should().HaveCount(1);
items.First().Type.Should().Be("series");
items.First().Label.Should().Be("Is Both");
var filters = JsonConvert.DeserializeObject<List<FilterSettings210>>(items.First().Filters);
filters[0].key.Should().Be("monitored");
filters[0].value.Should().BeEquivalentTo(new List<object> { false });
filters[0].type.Should().Be("equal");
filters[1].key.Should().Be("seasonsMonitoredStatus");
filters[1].value.Should().BeEquivalentTo(new List<object> { "all" });
filters[1].type.Should().Be("notEqual");
}
[Test]
public void has_umonitored_is_empty()
{
var filter = new FilterSettings210
{
key = "hasUnmonitoredSeason",
value = new List<object> { },
type = "equal"
};
var filtersJson = new List<FilterSettings210> { filter };
var filtersString = filtersJson.ToJson();
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("CustomFilters").Row(new
{
Id = 1,
Type = "series",
Label = "Is Both",
Filters = filtersString
});
});
var items = db.Query<FilterDefinition210>("SELECT * FROM \"CustomFilters\"");
items.Should().HaveCount(1);
items.First().Type.Should().Be("series");
items.First().Label.Should().Be("Is Both");
var filters = JsonConvert.DeserializeObject<List<FilterSettings210>>(items.First().Filters);
filters[0].key.Should().Be("seasonsMonitoredStatus");
filters[0].value.Should().BeEquivalentTo(new List<object> { });
filters[0].type.Should().Be("equal");
}
}
public class FilterDefinition210
{
public int Id { get; set; }
public string Type { get; set; }
public string Label { get; set; }
public string Filters { get; set; }
}
public class FilterSettings210
{
public string key { get; set; }
public List<object> value { get; set; }
public string type { get; set; }
}
}

View File

@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Data;
using Dapper;
using FluentMigrator;
using Newtonsoft.Json.Linq;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(210)]
public class add_monitored_seasons_filter : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(ChangeHasUnmonitoredSeason);
}
private void ChangeHasUnmonitoredSeason(IDbConnection conn, IDbTransaction tran)
{
var updated = new List<object>();
using (var getUnmonitoredSeasonFilter = conn.CreateCommand())
{
getUnmonitoredSeasonFilter.Transaction = tran;
getUnmonitoredSeasonFilter.CommandText = "SELECT \"Id\", \"Filters\" FROM \"CustomFilters\" WHERE \"Type\" = 'series'";
using (var reader = getUnmonitoredSeasonFilter.ExecuteReader())
{
while (reader.Read())
{
var id = reader.GetInt32(0);
var filters = JArray.Parse(reader.GetString(1));
foreach (var filter in filters)
{
if (filter["key"]?.ToString() == "hasUnmonitoredSeason")
{
var value = filter["value"].ToString();
var type = filter["type"].ToString();
filter["key"] = "seasonsMonitoredStatus";
if (value.Contains("true") && value.Contains("false"))
{
filter["value"] = new JArray("all", "partial", "none");
}
else if (value.Contains("true"))
{
filter["type"] = type == "equal" ? "notEqual" : "equal";
filter["value"] = new JArray("all");
}
else if (value.Contains("false"))
{
filter["value"] = new JArray("all");
}
else
{
filter["value"] = new JArray();
}
}
}
updated.Add(new
{
Filters = filters.ToJson(),
Id = id
});
}
}
}
var updateSql = "UPDATE \"CustomFilters\" SET \"Filters\" = @Filters WHERE \"Id\" = @Id";
conn.Execute(updateSql, updated, transaction: tran);
}
}
}

View File

@ -1784,6 +1784,10 @@
"SeasonPremiere": "Season Premiere",
"SeasonPremieresOnly": "Season Premieres Only",
"Seasons": "Seasons",
"SeasonsMonitoredAll": "All",
"SeasonsMonitoredPartial": "Partial",
"SeasonsMonitoredNone": "None",
"SeasonsMonitoredStatus": "Seasons Monitored",
"SecretToken": "Secret Token",
"Security": "Security",
"Seeders": "Seeders",