notify doctors work, audit work, new product advisory sprints

This commit is contained in:
master
2026-01-13 08:36:29 +02:00
parent b8868a5f13
commit 9ca7cb183e
343 changed files with 24492 additions and 3544 deletions

View File

@@ -1,3 +1,4 @@
using System.Globalization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -5,8 +6,8 @@ namespace StellaOps.Evidence.Budgets;
public interface IEvidenceBudgetService
{
BudgetCheckResult CheckBudget(Guid scanId, EvidenceItem item);
BudgetStatus GetBudgetStatus(Guid scanId);
Task<BudgetCheckResult> CheckBudgetAsync(Guid scanId, EvidenceItem item, CancellationToken ct);
Task<BudgetStatus> GetBudgetStatusAsync(Guid scanId, CancellationToken ct);
Task<PruneResult> PruneToFitAsync(Guid scanId, long targetBytes, CancellationToken ct);
}
@@ -26,10 +27,11 @@ public sealed class EvidenceBudgetService : IEvidenceBudgetService
_logger = logger;
}
public BudgetCheckResult CheckBudget(Guid scanId, EvidenceItem item)
public async Task<BudgetCheckResult> CheckBudgetAsync(Guid scanId, EvidenceItem item, CancellationToken ct)
{
ArgumentNullException.ThrowIfNull(item);
var budget = _options.CurrentValue;
var currentUsage = GetCurrentUsage(scanId);
var currentUsage = await GetCurrentUsageAsync(scanId, ct);
var issues = new List<string>();
@@ -37,7 +39,9 @@ public sealed class EvidenceBudgetService : IEvidenceBudgetService
var projectedTotal = currentUsage.TotalBytes + item.SizeBytes;
if (projectedTotal > budget.MaxScanSizeBytes)
{
issues.Add($"Would exceed total budget: {projectedTotal:N0} > {budget.MaxScanSizeBytes:N0} bytes");
issues.Add(
$"Would exceed total budget: {projectedTotal.ToString("N0", CultureInfo.InvariantCulture)} > " +
$"{budget.MaxScanSizeBytes.ToString("N0", CultureInfo.InvariantCulture)} bytes");
}
// Check per-type budget
@@ -47,7 +51,9 @@ public sealed class EvidenceBudgetService : IEvidenceBudgetService
var projectedType = typeUsage + item.SizeBytes;
if (projectedType > typeLimit)
{
issues.Add($"Would exceed {item.Type} budget: {projectedType:N0} > {typeLimit:N0} bytes");
issues.Add(
$"Would exceed {item.Type} budget: {projectedType.ToString("N0", CultureInfo.InvariantCulture)} > " +
$"{typeLimit.ToString("N0", CultureInfo.InvariantCulture)} bytes");
}
}
@@ -66,10 +72,10 @@ public sealed class EvidenceBudgetService : IEvidenceBudgetService
};
}
public BudgetStatus GetBudgetStatus(Guid scanId)
public async Task<BudgetStatus> GetBudgetStatusAsync(Guid scanId, CancellationToken ct)
{
var budget = _options.CurrentValue;
var usage = GetCurrentUsage(scanId);
var usage = await GetCurrentUsageAsync(scanId, ct);
return new BudgetStatus
{
@@ -92,13 +98,10 @@ public sealed class EvidenceBudgetService : IEvidenceBudgetService
};
}
public async Task<PruneResult> PruneToFitAsync(
Guid scanId,
long targetBytes,
CancellationToken ct)
public async Task<PruneResult> PruneToFitAsync(Guid scanId, long targetBytes, CancellationToken ct)
{
var budget = _options.CurrentValue;
var usage = GetCurrentUsage(scanId);
var usage = await GetCurrentUsageAsync(scanId, ct);
if (usage.TotalBytes <= targetBytes)
{
@@ -113,6 +116,8 @@ public sealed class EvidenceBudgetService : IEvidenceBudgetService
var candidates = items
.Where(i => !budget.AlwaysPreserve.Contains(i.Type))
.OrderBy(i => GetPrunePriority(i))
.ThenBy(i => i.CreatedAt.UtcDateTime.Ticks)
.ThenBy(i => i.Id)
.ToList();
long prunedBytes = 0;
@@ -158,15 +163,15 @@ public sealed class EvidenceBudgetService : IEvidenceBudgetService
};
}
private UsageStats GetCurrentUsage(Guid scanId)
private async Task<UsageStats> GetCurrentUsageAsync(Guid scanId, CancellationToken ct)
{
// Implementation to calculate current usage from repository
var items = _repository.GetByScanIdAsync(scanId, CancellationToken.None)
.GetAwaiter().GetResult();
var items = await _repository.GetByScanIdAsync(scanId, ct);
var totalBytes = items.Sum(i => i.SizeBytes);
var byType = items
.GroupBy(i => i.Type)
.OrderBy(g => g.Key)
.ToDictionary(g => g.Key, g => g.Sum(i => i.SizeBytes));
return new UsageStats