feat(stats): add percentage and stale counters to completeness cards
- Show percentage of total assets on missing metadata/thumbnail/preview/phash cards - Add stale counts for thumbnails and previews (generated at wrong resolution vs current settings) - Add h-100 to all completeness cards for consistent row heights
This commit is contained in:
@@ -88,10 +88,18 @@ public class StatsDto {
|
||||
/// </summary>
|
||||
public int AssetsMissingThumbnail { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the number of assets whose thumbnail was generated at a different size than the current setting.
|
||||
/// </summary>
|
||||
public int AssetsMissingThumbnailStale { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the number of assets missing a generated preview.
|
||||
/// </summary>
|
||||
public int AssetsMissingPreviews { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the number of assets whose preview was generated at a different size than the current setting.
|
||||
/// </summary>
|
||||
public int AssetsMissingPreviewsStale { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the number of assets missing a perceptual hash.
|
||||
/// </summary>
|
||||
public int AssetsMissingPhash { get; set; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Butter.Dtos.Stats;
|
||||
using Butter.Settings;
|
||||
using Butter.Types;
|
||||
using Lactose.Context;
|
||||
using System.Collections;
|
||||
@@ -6,7 +7,7 @@ using System.Collections;
|
||||
namespace Lactose.Repositories;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class StatsRepository(LactoseDbContext context) : IStatsRepository {
|
||||
public class StatsRepository(LactoseDbContext context, ISettingsRepository settingsRepo) : IStatsRepository {
|
||||
/// <inheritdoc />
|
||||
public StatsDto GetStats() {
|
||||
var dto = new StatsDto();
|
||||
@@ -56,12 +57,27 @@ public class StatsRepository(LactoseDbContext context) : IStatsRepository {
|
||||
dto.AssetsMissingMetadata = context.Assets.Count(a =>
|
||||
a.Type == EAssetType.Image && a.ResolutionWidth == 0 && a.DeletedAt == null);
|
||||
|
||||
var thumbnailSizeSetting = settingsRepo.Get(Settings.ThumbnailSize.AsString());
|
||||
var previewSizeSetting = settingsRepo.Get(Settings.PreviewSize.AsString());
|
||||
_ = int.TryParse(thumbnailSizeSetting?.Value, out var expectedThumbnailSize);
|
||||
_ = int.TryParse(previewSizeSetting?.Value, out var expectedPreviewSize);
|
||||
|
||||
dto.AssetsMissingThumbnail = context.Assets.Count(a =>
|
||||
(a.ThumbnailPath == null || a.ThumbnailPath == "") && a.DeletedAt == null);
|
||||
|
||||
dto.AssetsMissingThumbnailStale = expectedThumbnailSize > 0
|
||||
? context.Assets.Count(a =>
|
||||
a.ThumbnailPath != null && a.ThumbnailPath != "" && a.ThumbnailSize != expectedThumbnailSize && a.DeletedAt == null)
|
||||
: 0;
|
||||
|
||||
dto.AssetsMissingPreviews = context.Assets.Count(a =>
|
||||
(a.PreviewPath == null || a.PreviewPath == "") && a.DeletedAt == null);
|
||||
|
||||
dto.AssetsMissingPreviewsStale = expectedPreviewSize > 0
|
||||
? context.Assets.Count(a =>
|
||||
a.PreviewPath != null && a.PreviewPath != "" && a.PreviewSize != expectedPreviewSize && a.DeletedAt == null)
|
||||
: 0;
|
||||
|
||||
var emptyHash = new BitArray(64);
|
||||
dto.AssetsMissingPhash = context.Assets.Count(a =>
|
||||
a.Hash == emptyHash && a.DeletedAt == null);
|
||||
|
||||
@@ -38,14 +38,14 @@
|
||||
|
||||
@* ---- Data completeness indicators ---- *@
|
||||
<div class="row row-cols-2 row-cols-md-4 row-cols-lg-4 g-3 mb-4">
|
||||
<div class="col"><div class="card border-warning"><div class="card-body text-center py-3"><h5>@_stats.AssetsMissingMetadata.ToString("N0")</h5><small class="text-warning"><i class="bi bi-exclamation-triangle"></i> Missing Metadata</small></div></div></div>
|
||||
<div class="col"><div class="card border-warning"><div class="card-body text-center py-3"><h5>@_stats.AssetsMissingThumbnail.ToString("N0")</h5><small class="text-warning"><i class="bi bi-exclamation-triangle"></i> Missing Thumbnail</small></div></div></div>
|
||||
<div class="col"><div class="card border-warning"><div class="card-body text-center py-3"><h5>@_stats.AssetsMissingPreviews.ToString("N0")</h5><small class="text-warning"><i class="bi bi-exclamation-triangle"></i> Missing Previews</small></div></div></div>
|
||||
<div class="col"><div class="card border-warning"><div class="card-body text-center py-3"><h5>@_stats.AssetsMissingPhash.ToString("N0")</h5><small class="text-warning"><i class="bi bi-exclamation-triangle"></i> Missing pHash</small></div></div></div>
|
||||
<div class="col"><div class="card border-info"><div class="card-body text-center py-3"><h5>@_stats.AssetsWithNoPerson.ToString("N0")</h5><small class="text-info"><i class="bi bi-info-circle"></i> No Person</small></div></div></div>
|
||||
<div class="col"><div class="card border-info"><div class="card-body text-center py-3"><h5>@_stats.AssetsWithNoAlbum.ToString("N0")</h5><small class="text-info"><i class="bi bi-info-circle"></i> No Album</small></div></div></div>
|
||||
<div class="col"><div class="card border-danger"><div class="card-body text-center py-3"><h5>@_stats.AlbumsMissingCover.ToString("N0")</h5><small class="text-danger"><i class="bi bi-image"></i> Albums Missing Cover</small></div></div></div>
|
||||
<div class="col"><div class="card border-secondary"><div class="card-body text-center py-3"><h5>@_stats.CosplayersMissingCover.ToString("N0")</h5><small class="text-muted"><i class="bi bi-person-badge"></i> Cosplayers Missing Cover</small></div></div></div>
|
||||
<div class="col"><div class="card border-warning h-100"><div class="card-body text-center py-3"><h5>@_stats.AssetsMissingMetadata.ToString("N0")</h5><small class="text-muted">@PercentOfTotal(_stats.AssetsMissingMetadata)</small><br/><small class="text-warning"><i class="bi bi-exclamation-triangle"></i> Missing Metadata</small></div></div></div>
|
||||
<div class="col"><div class="card border-warning h-100"><div class="card-body text-center py-3"><h5>@_stats.AssetsMissingThumbnail.ToString("N0")</h5><small class="text-muted">@PercentOfTotal(_stats.AssetsMissingThumbnail)</small><br/><small class="text-warning"><i class="bi bi-exclamation-triangle"></i> Missing Thumbnail</small><br/><small class="text-danger">@_stats.AssetsMissingThumbnailStale.ToString("N0") stale</small></div></div></div>
|
||||
<div class="col"><div class="card border-warning h-100"><div class="card-body text-center py-3"><h5>@_stats.AssetsMissingPreviews.ToString("N0")</h5><small class="text-muted">@PercentOfTotal(_stats.AssetsMissingPreviews)</small><br/><small class="text-warning"><i class="bi bi-exclamation-triangle"></i> Missing Previews</small><br/><small class="text-danger">@_stats.AssetsMissingPreviewsStale.ToString("N0") stale</small></div></div></div>
|
||||
<div class="col"><div class="card border-warning h-100"><div class="card-body text-center py-3"><h5>@_stats.AssetsMissingPhash.ToString("N0")</h5><small class="text-muted">@PercentOfTotal(_stats.AssetsMissingPhash)</small><br/><small class="text-warning"><i class="bi bi-exclamation-triangle"></i> Missing pHash</small></div></div></div>
|
||||
<div class="col"><div class="card border-info h-100"><div class="card-body text-center py-3"><h5>@_stats.AssetsWithNoPerson.ToString("N0")</h5><small class="text-info"><i class="bi bi-info-circle"></i> No Person</small></div></div></div>
|
||||
<div class="col"><div class="card border-info h-100"><div class="card-body text-center py-3"><h5>@_stats.AssetsWithNoAlbum.ToString("N0")</h5><small class="text-info"><i class="bi bi-info-circle"></i> No Album</small></div></div></div>
|
||||
<div class="col"><div class="card border-danger h-100"><div class="card-body text-center py-3"><h5>@_stats.AlbumsMissingCover.ToString("N0")</h5><small class="text-danger"><i class="bi bi-image"></i> Albums Missing Cover</small></div></div></div>
|
||||
<div class="col"><div class="card border-secondary h-100"><div class="card-body text-center py-3"><h5>@_stats.CosplayersMissingCover.ToString("N0")</h5><small class="text-muted"><i class="bi bi-person-badge"></i> Cosplayers Missing Cover</small></div></div></div>
|
||||
</div>
|
||||
|
||||
@* ---- Duplicate / Orphan / Visibility ---- *@
|
||||
@@ -195,6 +195,9 @@
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private string PercentOfTotal(int count) =>
|
||||
_stats?.TotalAssets > 0 ? $"{(double)count / _stats.TotalAssets * 100:F1}%" : "0%";
|
||||
|
||||
private static string FormatBytes(long bytes) => bytes switch {
|
||||
>= 1_073_741_824 => $"{bytes / 1_073_741_824.0:F2} GB",
|
||||
>= 1_048_576 => $"{bytes / 1_048_576.0:F2} MB",
|
||||
|
||||
Reference in New Issue
Block a user