Add hide unmonitored toggle for quality scores (#535)

This commit is contained in:
Flaminel
2026-04-01 13:28:43 +03:00
committed by GitHub
parent 868406c95c
commit 4903b3137b
5 changed files with 48 additions and 11 deletions

View File

@@ -77,6 +77,24 @@ public class CustomFormatScoreControllerTests : IDisposable
body.GetProperty("Items")[0].GetProperty("Title").GetString().ShouldBe("Below Cutoff");
}
[Fact]
public async Task GetCustomFormatScores_WithHideUnmonitoredTrue_ExcludesUnmonitoredItems()
{
var radarr = SeekerTestDataFactory.AddRadarrInstance(_dataContext);
AddScoreEntry(radarr.Id, 1, "Monitored Movie", currentScore: 100, cutoffScore: 500, isMonitored: true);
AddScoreEntry(radarr.Id, 2, "Unmonitored Movie", currentScore: 200, cutoffScore: 500, isMonitored: false);
AddScoreEntry(radarr.Id, 3, "Another Monitored", currentScore: 300, cutoffScore: 500, isMonitored: true);
var result = await _controller.GetCustomFormatScores(hideUnmonitored: true);
var body = GetResponseBody(result);
body.GetProperty("TotalCount").GetInt32().ShouldBe(2);
var items = body.GetProperty("Items");
items.GetArrayLength().ShouldBe(2);
items[0].GetProperty("Title").GetString().ShouldBe("Another Monitored");
items[1].GetProperty("Title").GetString().ShouldBe("Monitored Movie");
}
[Fact]
public async Task GetCustomFormatScores_WithSearchFilter_ReturnsMatchingTitlesOnly()
{
@@ -314,7 +332,8 @@ public class CustomFormatScoreControllerTests : IDisposable
int currentScore,
int cutoffScore,
InstanceType itemType = InstanceType.Radarr,
DateTime? lastSynced = null)
DateTime? lastSynced = null,
bool isMonitored = true)
{
_dataContext.CustomFormatScoreEntries.Add(new CustomFormatScoreEntry
{
@@ -327,6 +346,7 @@ public class CustomFormatScoreControllerTests : IDisposable
CurrentScore = currentScore,
CutoffScore = cutoffScore,
QualityProfileName = "HD",
IsMonitored = isMonitored,
LastSyncedAt = lastSynced ?? DateTime.UtcNow
});
_dataContext.SaveChanges();

View File

@@ -28,7 +28,8 @@ public sealed class CustomFormatScoreController : ControllerBase
[FromQuery] Guid? instanceId = null,
[FromQuery] string? search = null,
[FromQuery] string sortBy = "title",
[FromQuery] bool hideMet = false)
[FromQuery] bool hideMet = false,
[FromQuery] bool hideUnmonitored = false)
{
if (page < 1) page = 1;
if (pageSize < 1) pageSize = 50;
@@ -53,6 +54,11 @@ public sealed class CustomFormatScoreController : ControllerBase
query = query.Where(e => e.CurrentScore < e.CutoffScore);
}
if (hideUnmonitored)
{
query = query.Where(e => e.IsMonitored);
}
int totalCount = await query.CountAsync();
var items = await (sortBy == "date"

View File

@@ -99,12 +99,13 @@ export class CfScoreApi {
return this.http.get<CfScoreUpgradesResponse>('/api/seeker/cf-scores/upgrades', { params });
}
getScores(page = 1, pageSize = 50, search?: string, instanceId?: string, sortBy?: string, hideMet?: boolean): Observable<CfScoreEntriesResponse> {
getScores(page = 1, pageSize = 50, search?: string, instanceId?: string, sortBy?: string, hideMet?: boolean, hideUnmonitored?: boolean): Observable<CfScoreEntriesResponse> {
const params: Record<string, string | number | boolean> = { page, pageSize };
if (search) params['search'] = search;
if (instanceId) params['instanceId'] = instanceId;
if (sortBy) params['sortBy'] = sortBy;
if (hideMet) params['hideMet'] = true;
if (hideUnmonitored) params['hideUnmonitored'] = true;
return this.http.get<CfScoreEntriesResponse>('/api/seeker/cf-scores', { params });
}

View File

@@ -23,6 +23,11 @@
[checked]="hideMet()"
(checkedChange)="onHideMetChange($event)"
/>
<app-toggle
label="Hide unmonitored"
[checked]="hideUnmonitored()"
(checkedChange)="onHideUnmonitoredChange($event)"
/>
</div>
<div class="toolbar__actions">
<app-button variant="ghost" size="sm" (clicked)="refresh()">

View File

@@ -54,6 +54,7 @@ export class QualityTabComponent implements OnInit {
readonly sortBy = signal<string>('title');
readonly hideMet = signal(false);
readonly hideUnmonitored = signal(false);
readonly sortOptions: SelectOption[] = [
{ label: 'Title', value: 'title' },
{ label: 'Last Synced', value: 'date' },
@@ -93,7 +94,7 @@ export class QualityTabComponent implements OnInit {
loadScores(): void {
this.loading.set(true);
this.api.getScores(this.currentPage(), this.pageSize(), this.searchQuery() || undefined, this.selectedInstanceId() || undefined, this.sortBy(), this.hideMet()).subscribe({
this.api.getScores(this.currentPage(), this.pageSize(), this.searchQuery() || undefined, this.selectedInstanceId() || undefined, this.sortBy(), this.hideMet(), this.hideUnmonitored()).subscribe({
next: (result) => {
this.items.set(result.items);
this.totalRecords.set(result.totalCount);
@@ -122,9 +123,7 @@ export class QualityTabComponent implements OnInit {
}
onInstanceFilterChange(value: string): void {
this.selectedInstanceId.set(value);
this.currentPage.set(1);
this.loadScores();
this.applyFilterChange(this.selectedInstanceId, value);
}
private loadStats(): void {
@@ -140,13 +139,19 @@ export class QualityTabComponent implements OnInit {
}
onSortChange(value: string): void {
this.sortBy.set(value);
this.currentPage.set(1);
this.loadScores();
this.applyFilterChange(this.sortBy, value);
}
onHideMetChange(value: boolean): void {
this.hideMet.set(value);
this.applyFilterChange(this.hideMet, value);
}
onHideUnmonitoredChange(value: boolean): void {
this.applyFilterChange(this.hideUnmonitored, value);
}
private applyFilterChange<T>(setter: { set: (v: T) => void }, value: T): void {
setter.set(value);
this.currentPage.set(1);
this.loadScores();
}