Run initial test for Climate Resilience Verification Suite

Add first part of Alert Quality Analytics
This commit is contained in:
voltsrage
2026-06-24 03:01:00 +08:00
parent 032cd1d240
commit 185dc93fa1
50 changed files with 4116 additions and 55 deletions
@@ -0,0 +1,73 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Produces("application/json")]
[Authorize]
public class AlertQualityMetricsController : ControllerBase
{
private readonly IAlertQualityMetricsService _metrics;
public AlertQualityMetricsController(IAlertQualityMetricsService metrics) =>
_metrics = metrics;
/// <summary>
/// Returns alert quality metric snapshots for a time range, optionally filtered by alert type.
/// </summary>
[HttpGet("api/v1/alerts/quality-metrics")]
[AuthorizePermission(ClinicalPermissions.AnalyticsRead)]
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> List(
[FromQuery] string? alertType,
[FromQuery] DateTimeOffset? from,
[FromQuery] DateTimeOffset? to)
{
AlertType? parsedType = null;
if (!string.IsNullOrEmpty(alertType))
{
try
{
parsedType = AlertTypeExtensions.FromDbString(alertType);
}
catch (ArgumentOutOfRangeException)
{
return BadRequest(ApiResponse<object>.Fail(
400, "Invalid alert type filter.", "INVALID_ALERT_TYPE"));
}
}
var periodEnd = to ?? DateTimeOffset.UtcNow;
var periodStart = from ?? periodEnd.AddDays(-7);
if (periodStart >= periodEnd)
{
return BadRequest(ApiResponse<object>.Fail(
400, "'from' must be before 'to'.", "INVALID_DATE_RANGE"));
}
var items = await _metrics.ListAsync(parsedType, periodStart, periodEnd);
return Ok(ApiResponse<object>.Ok(new
{
periodStart,
periodEnd,
items
}));
}
/// <summary>
/// Returns aggregate alert quality rates across all alert types for a time range.
/// </summary>
[HttpGet("api/v1/alerts/quality-metrics/summary")]
[AuthorizePermission(ClinicalPermissions.AnalyticsRead)]
[ProducesResponseType(typeof(ApiResponse<AlertQualitySummaryResponse>), StatusCodes.Status200OK)]
public async Task<IActionResult> Summary(
[FromQuery] DateTimeOffset? from,
[FromQuery] DateTimeOffset? to)
{
var periodEnd = to ?? DateTimeOffset.UtcNow;
var periodStart = from ?? periodEnd.AddDays(-7);
var summary = await _metrics.GetSummaryAsync(periodStart, periodEnd);
return Ok(ApiResponse<AlertQualitySummaryResponse>.Ok(summary));
}
}