=== Summary: 675 files | 7 generated | 484 fresh | 4605 untracked | 4268 adopted | 355 marked | 466 validated-ok | 2+0 stale (sig+body) | 0 skipped | 0 failed | elapsed 11:08:11.442 (40091.44s) ===

This commit is contained in:
julian
2026-06-28 02:50:05 -07:00
parent a19fb90902
commit 586f02a2ca
654 changed files with 5260 additions and 0 deletions
@@ -17,6 +17,7 @@ namespace adas_core.Application.Services;
/// <summary>
/// Provides a concrete implementation of <see cref="IGroupedObservationService"/> for managing and exposing grouped observation data.
/// </summary>
/// <!-- aidoc:v1 sig=d252f21 -->
public class GroupedObservationService : IGroupedObservationService
{
private readonly CacheSettings? _cacheSettings;
@@ -61,6 +62,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="cacheIsChecked">When <c>true</c>, the cache is bypassed and observations are always recomputed; when <c>false</c>, results are obtained through the cache with the configured TTL.</param>
/// <param name="ct">Token used to cancel the asynchronous operation.</param>
/// <returns>A <see cref="Task{GroupedObservation}"/> containing the patient id, group, name, and the list of computed <see cref="GroupedObservationObs"/> entries.</returns>
/// <!-- aidoc:v1 sig=17d649b body=5ba5a2e -->
public async Task<GroupedObservation> GenerateGroupedObservation(
ObjectId patientId,
GroupedField groupedField,
@@ -357,6 +359,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="obs">The new patient observation to be incorporated into the grouped observation.</param>
/// <param name="timeZoneId">The time zone identifier used when a full recalculation is required. Defaults to "Romance Standard Time".</param>
/// <returns>A task that resolves to the generated <see cref="GroupedObservation"/> for the patient, either from an incrementally updated cache entry or from a full recalculation.</returns>
/// <!-- aidoc:v1 sig=63421dc body=6b8c46b -->
public async Task<GroupedObservation> GenerateGroupedObservation(
ObjectId patientId,
GroupedField groupedField,
@@ -408,6 +411,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="num">The maximum number of recent observations to return. Defaults to 2.</param>
/// <param name="filterObservations">An optional list of observation identifiers used to restrict the result set. When null, no filtering is applied.</param>
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientObservation"/> entries representing the patient's most recent observations.</returns>
/// <!-- aidoc:v1 sig=6f6283b body=92c1221 -->
public async Task<List<PatientObservation>> FindLastObservations(ObjectId patientId, int num = 2,
List<string>? filterObservations = null)
{
@@ -420,6 +424,7 @@ public class GroupedObservationService : IGroupedObservationService
/// </summary>
/// <param name="ws">The grouped subscriber context that supplies the group, names, regularity, max window, and cache key used to derive the next observations.</param>
/// <returns>A <see cref="GroupedObservation"/> containing the resulting observations adjusted to the configured time window based on <see cref="GroupedObservationEnum.Regularity"/> (Minute, Second, or default hour-based).</returns>
/// <!-- aidoc:v1 sig=b957e8f body=3a63489 -->
public async Task<GroupedObservation> CreateNextEmptyObs(WsSubscriberGrouped ws)
{
var gf = new GroupedField
@@ -525,6 +530,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="result">The existing grouped observation documents to be completed with missing intervals.</param>
/// <param name="groupedField">The grouping configuration that defines the names, maximum number of intervals, regularity and result types to use when generating the placeholders.</param>
/// <returns>A list of <see cref="BsonDocument"/> entries ordered by time, containing the original data and any added zero-valued filler entries for missing intervals.</returns>
/// <!-- aidoc:v1 sig=b15a145 body=b74a0fe -->
public List<BsonDocument> FillHours(List<BsonDocument> result, GroupedField groupedField)
{
var now = DateTime.UtcNow;
@@ -725,6 +731,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="groupedField">The grouped field containing either a list of names to iterate over or a single name used as fallback when the list is empty.</param>
/// <param name="patientId">The identifier of the patient whose observations are being calculated.</param>
/// <returns>A task that returns a list of BsonDocument containing the calculated last filled observations.</returns>
/// <!-- aidoc:v1 sig=685bef7 body=8a6adf3 -->
public async Task<List<BsonDocument>> CalculateLastFilledObservations(List<BsonDocument> result,
GroupedField groupedField, ObjectId patientId)
{
@@ -751,6 +758,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="patientId">The patient identifier used to query the last observation before the earliest expected date.</param>
/// <param name="name">The name assigned to the generated BsonDocument <c>_id</c> entries.</param>
/// <returns>The result list with missing time-slot entries filled using the last-filled value, ordered by time.</returns>
/// <!-- aidoc:v1 sig=3627386 body=09800ac -->
private async Task<List<BsonDocument>> LastFilledCalc(List<BsonDocument> result, GroupedField groupedField,
ObjectId patientId, string? name)
{
@@ -875,6 +883,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="shiftGroupObservations">The list of shift observations to be aggregated.</param>
/// <param name="groupedField">The grouped field configuration whose Result property determines whether a Sum aggregation is applied.</param>
/// <returns>A list of <see cref="BsonDocument"/> containing the aggregated shift observations, or the original list when no Sum operation is required.</returns>
/// <!-- aidoc:v1 sig=37e3bfd body=e1601ec -->
public List<BsonDocument> CalculateShiftObservations(List<BsonDocument> shiftGroupObservations,
GroupedField groupedField)
{
@@ -923,6 +932,7 @@ public class GroupedObservationService : IGroupedObservationService
/// grouped into half-hour intervals and enriched with a "halfhour" field in place.</param>
/// <returns>The same <see cref="List{BsonDocument}"/> instance with the "halfhour" field populated
/// according to the half-hour grouping rules.</returns>
/// <!-- aidoc:v1 sig=9daf918 body=efd69f0 -->
public List<BsonDocument> CalculateHalfHourObservations(List<BsonDocument> result)
{
//HalfHour añadir que se rellenen las horas que no existen
@@ -1000,6 +1010,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="result">The list of aggregated BsonDocuments to enrich; each document is modified in place to include the day and shift elements.</param>
/// <param name="groupedField">The grouping configuration providing the ordered list of shift start times used to determine the assigned shift.</param>
/// <returns>The same <paramref name="result"/> list with day and shift elements added to each successfully processed document.</returns>
/// <!-- aidoc:v1 sig=f6df652 body=c517b3e -->
public List<BsonDocument> GenerateShiftObservations(List<BsonDocument> result, GroupedField groupedField)
{
result.ForEach(item =>
@@ -1095,6 +1106,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="obs">The patient observation to match against or insert into the group.</param>
/// <param name="groupedField">The grouped field providing the regularity, result aggregation types, maximum retention count, and shift configuration.</param>
/// <returns>The updated list of grouped observations, limited to entries whose time is within the allowed date range.</returns>
/// <!-- aidoc:v1 sig=cca5083 body=147be01 -->
private List<GroupedObservationObs> LocateCurrentObsInGroup(List<GroupedObservationObs> group,
PatientObservation obs, GroupedField groupedField)
{
@@ -1241,6 +1253,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="currentObsDate">The observation date whose time of day is evaluated against the configured shifts.</param>
/// <param name="groupedFieldStartTimeShift">The list of shift start time strings used to build the shift intervals; must contain at least one entry to produce a result.</param>
/// <returns>The zero-based index of the matching shift, -1 if no shift matches, or null if <paramref name="groupedFieldStartTimeShift"/> is empty.</returns>
/// <!-- aidoc:v1 sig=d71f06d body=ef631e3 -->
private int? GetShift(DateTime currentObsDate, List<string> groupedFieldStartTimeShift)
{
if (groupedFieldStartTimeShift.Count == 0) return null;
@@ -1264,6 +1277,7 @@ public class GroupedObservationService : IGroupedObservationService
/// </summary>
/// <param name="groupedFieldStartTimeShift">A list of time span string representations to be parsed and adjusted.</param>
/// <returns>A list of TimeSpan values where each entry is the original time plus one minute, or the subsequent time span if adding one minute would exceed it.</returns>
/// <!-- aidoc:v1 sig=1b7da10 body=741054c -->
private List<TimeSpan> GetTimeSpan(List<string> groupedFieldStartTimeShift)
{
List<TimeSpan> timeSpans = [];
@@ -1294,6 +1308,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="min">The optional minimum bound used in the status evaluation.</param>
/// <param name="max">The optional maximum bound used in the status evaluation.</param>
/// <returns>A task that resolves to the <see cref="StatusEnum.Type"/> representing the status of the grouped observation.</returns>
/// <!-- aidoc:v1 sig=8c95413 body=a79f912 -->
private async Task<StatusEnum.Type> GroupedObservationStatus(GroupedField groupedField,
GroupedObservationEnum.Result result, string name, object value, double? min, double? max)
{
@@ -1306,6 +1321,7 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="obs">The grouped observation containing the timestamp of the last recorded observation.</param>
/// <param name="gf">The grouped field whose regularity (second, minute, hour, or day) defines the time unit used in the calculation.</param>
/// <returns>A tuple containing the number of times left to create an empty observation (the ceiling of the elapsed time in the selected unit) and the multiplier factor associated with the regularity unit.</returns>
/// <!-- aidoc:v1 sig=dcb9cc9 body=13579ae -->
private (int timesLeft, int multiplierFactor) TimesLeftToCreateEmptyObs(GroupedObservationObs obs, GroupedField gf)
{
var currentDateTime = DateTime.Now;
@@ -1346,6 +1362,8 @@ public class GroupedObservationService : IGroupedObservationService
/// <param name="gf">The grouped field providing the list of result property names to copy and the regularity used to determine shift handling.</param>
/// <param name="seconsToAdd">The number of seconds to add to the source observation's time for the new observation.</param>
/// <returns>A new <see cref="GroupedObservationObs"/> populated with values derived from <paramref name="sourceObs"/>; when the regularity is <see cref="GroupedObservationEnum.Regularity.Shift"/>, the shift date and shift are also assigned.</returns>
/// <!-- aidoc-review:v1 severity=high kind=wrong_returns
/// "The <returns> description states the new observation is 'populated with values derived from sourceObs', but the code sets each property in gf.Result to a defaultValue of (0, sourceObs.Time) and never copies the actual values from sourceObs. The retrieved sourceObs value is only used to mutate sourceObs (resetting its inner Time), not to populate lastObs." -->
private GroupedObservationObs CreateNextObs(GroupedObservationObs sourceObs, GroupedField gf, int seconsToAdd)
{
var lastObs = new GroupedObservationObs