using System.Text.Json; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; /// /// SOFA composite scoring: current score and paginated history per encounter. /// [ApiController] [Route("api/v1/encounters/{encounterId:guid}/sofa")] [Produces("application/json")] [AuthorizePermission(ClinicalPermissions.EncountersRead)] public class SofaController : ControllerBase { private readonly ISofaService _sofa; public SofaController(ISofaService sofa) => _sofa = sofa; /// /// Returns the latest SOFA score for an encounter, or null data when no score has been computed. /// /// Encounter id. /// The SOFA component scores and total, or null. [HttpGet] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task Current(Guid encounterId) { var score = await _sofa.GetCurrentAsync(encounterId); if (score is null) return Ok(ApiResponse.Ok(null)); return Ok(ApiResponse.Ok(MapResponse(score))); } /// /// Returns cursor-paginated SOFA score history for an encounter. /// /// Encounter id. /// Maximum items per page. /// Opaque cursor from a previous page. /// A page of SOFA scores with an optional next cursor. [HttpGet("history")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task History( Guid encounterId, [FromQuery] int limit = 20, [FromQuery] string? cursor = null) { var page = await _sofa.GetHistoryAsync(encounterId, limit, cursor); return Ok(ApiResponse.Ok(new { items = page.Items.Select(MapResponse), nextCursor = page.NextCursor, hasMore = page.HasMore })); } private static SofaScoreResponse MapResponse(SofaScore score) { SofaStalenessInfo? staleness = null; if (!string.IsNullOrEmpty(score.StalenessFlags)) { var flags = JsonSerializer.Deserialize( score.StalenessFlags, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); if (flags is not null) { staleness = new SofaStalenessInfo( flags.StaleComponents, flags.MissingComponents, flags.UsedSpO2Fallback); } } return new SofaScoreResponse( score.TotalScore, score.RespiratoryScore, score.CoagulationScore, score.LiverScore, score.CardiovascularScore, score.CnsScore, score.RenalScore, score.IsBaseline, score.DeltaFromBaseline, staleness, score.CalculatedAt); } }