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; /// /// Returns alert quality metric snapshots for a time range, optionally filtered by alert type. /// [HttpGet("api/v1/alerts/quality-metrics")] [AuthorizePermission(ClinicalPermissions.AnalyticsRead)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status400BadRequest)] public async Task 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.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.Fail( 400, "'from' must be before 'to'.", "INVALID_DATE_RANGE")); } var items = await _metrics.ListAsync(parsedType, periodStart, periodEnd); return Ok(ApiResponse.Ok(new { periodStart, periodEnd, items })); } /// /// Returns aggregate alert quality rates across all alert types for a time range. /// [HttpGet("api/v1/alerts/quality-metrics/summary")] [AuthorizePermission(ClinicalPermissions.AnalyticsRead)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task 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.Ok(summary)); } }