1262 lines
53 KiB
C#
1262 lines
53 KiB
C#
using System.Globalization;
|
|
using adas_core.Application.Repositories.Interfaces;
|
|
using adas_core.Application.Services.Interfaces;
|
|
using adas_core.Application.Subscriptions;
|
|
using adas_core.Domain.Enums;
|
|
using adas_core.Domain.Models;
|
|
using adas_core.Domain.Models.AppSettings;
|
|
using adas_core.Domain.Models.GroupedObservations;
|
|
using adas_core.Domain.Utils;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using MongoDB.Bson;
|
|
using static adas_core.Domain.Models.GroupedObservation;
|
|
|
|
namespace adas_core.Application.Services;
|
|
|
|
public class GroupedObservationService : IGroupedObservationService
|
|
{
|
|
private readonly CacheSettings? _cacheSettings;
|
|
private readonly ICacheService _cacheService;
|
|
private readonly IConfigObservationService _configObservationService;
|
|
private readonly ILogger<GroupedObservationService> _logger;
|
|
private readonly IObservationRepository _observationRepository;
|
|
|
|
public GroupedObservationService(
|
|
IObservationRepository observationRepository,
|
|
IConfigObservationService configObservationService,
|
|
ILogger<GroupedObservationService> logger,
|
|
ICacheService cacheService,
|
|
IOptions<ApiSettings> apiSettings,
|
|
IOptions<CacheSettings> cacheSettings)
|
|
{
|
|
_observationRepository = observationRepository;
|
|
_configObservationService = configObservationService;
|
|
_logger = logger;
|
|
_cacheService = cacheService;
|
|
_cacheSettings = cacheSettings.Value;
|
|
}
|
|
|
|
|
|
// PUBLIC API (Use cases)
|
|
public async Task<GroupedObservation> GenerateGroupedObservation(
|
|
ObjectId patientId,
|
|
GroupedField groupedField,
|
|
string timeZoneId = "Romance Standard Time",
|
|
bool cacheIsChecked = false,
|
|
CancellationToken ct = default)
|
|
{
|
|
// Loader que calcula TODA la lista de observaciones agrupadas
|
|
async Task<List<GroupedObservationObs>> ComputeObservationsAsync()
|
|
{
|
|
var result = await _observationRepository.AggregatedPatientGroupedObservations(patientId, groupedField);
|
|
|
|
var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
|
|
|
result = FillHours(result, groupedField);
|
|
|
|
if (groupedField.StartTimeShift.Count > 0 &&
|
|
groupedField.Regularity == GroupedObservationEnum.Regularity.Shift)
|
|
{
|
|
var shiftGroupObservations = GenerateShiftObservations(result, groupedField);
|
|
result = CalculateShiftObservations(shiftGroupObservations, groupedField);
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.HalfHour))
|
|
{
|
|
result = CalculateHalfHourObservations(result);
|
|
result = FillHours(result, groupedField);
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.LastFilled))
|
|
result = await CalculateLastFilledObservations(result, groupedField, patientId);
|
|
|
|
var list = new List<GroupedObservationObs>();
|
|
|
|
foreach (var it in result.ToList())
|
|
{
|
|
var id = it.TryGetElement("_id", out var _) ? it.GetElement("_id").Value.AsBsonDocument : null;
|
|
var name = id?.Get("name")?.AsString;
|
|
if (name is null or "_id")
|
|
{
|
|
var idValue = id?.Get("value") as BsonDocument;
|
|
name = idValue?.Get("name")?.AsString;
|
|
}
|
|
|
|
var uTime = it.Get("time")?.ToUniversalTime();
|
|
if (!uTime.HasValue)
|
|
{
|
|
_logger.LogError("Error getting time to universal time. Bson document: {it}", it);
|
|
continue;
|
|
}
|
|
|
|
var obsTime = TimeZoneInfo.ConvertTimeFromUtc(uTime.Value, tz);
|
|
obsTime = DateTime.SpecifyKind(obsTime, DateTimeKind.Local);
|
|
|
|
var obs = new GroupedObservationObs
|
|
{
|
|
Time = obsTime,
|
|
Name = name ?? string.Empty
|
|
};
|
|
|
|
if (it.TryGetValue("shift", out var shiftValue) && it.TryGetValue("day", out _))
|
|
{
|
|
obs.Shift = shiftValue.ToInt32();
|
|
obs.ShiftDate = obs.Time;
|
|
}
|
|
|
|
if (id != null && id.Contains("min")) obs.MinAlert = id.Get("min")?.ToDouble();
|
|
if (id != null && id.Contains("max")) obs.MaxAlert = id.Get("max")?.ToDouble();
|
|
if (id != null && it.Contains("isFilled")) obs.IsFilled = (bool)it.Get("isFilled");
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.First))
|
|
{
|
|
var g = !it.Contains("first") ? null : it.Get("first")?.AsBsonDocument;
|
|
if (g != null)
|
|
{
|
|
var value = g.Get("value")?.ToObject();
|
|
var min = g.Get("min")?.ToDouble();
|
|
var max = g.Get("max")?.ToDouble();
|
|
var uTim = g.Get("time")?.ToUniversalTime();
|
|
if (!uTim.HasValue) { _logger.LogError("Error getting utc time in 'first': {it}", it); goto addObs; }
|
|
|
|
var time = TimeZoneInfo.ConvertTimeFromUtc(uTim.Value, tz);
|
|
time = DateTime.SpecifyKind(time, DateTimeKind.Local);
|
|
if (string.IsNullOrEmpty(name) || value == null)
|
|
{ _logger.LogError("Invalid 'first' grouped obs (name/value) patient:{patientId}", patientId); goto addObs; }
|
|
|
|
obs.First = new GroupedObservationObsValue(
|
|
value, time,
|
|
await GroupedObservationStatus(groupedField, GroupedObservationEnum.Result.First, name, value, min, max)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.Last))
|
|
{
|
|
var g = !it.Contains("last") ? null : it.Get("last")?.AsBsonDocument;
|
|
if (g != null)
|
|
{
|
|
var value = g.Get("value")?.ToObject();
|
|
var min = g.Get("min")?.ToDouble();
|
|
var max = g.Get("max")?.ToDouble();
|
|
var uTim = g.Get("time")?.ToUniversalTime();
|
|
if (!uTim.HasValue) { _logger.LogError("Error getting utc time in 'last': {it}", it); goto addObs; }
|
|
|
|
var time = TimeZoneInfo.ConvertTimeFromUtc(uTim.Value, tz);
|
|
time = DateTime.SpecifyKind(time, DateTimeKind.Local);
|
|
if (string.IsNullOrEmpty(name) || value == null)
|
|
{ _logger.LogError("Invalid 'last' grouped obs (name/value) patient:{patientId}", patientId); goto addObs; }
|
|
|
|
obs.Last = new GroupedObservationObsValue(
|
|
value, time,
|
|
await GroupedObservationStatus(groupedField, GroupedObservationEnum.Result.Last, name, value, min, max)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.Min))
|
|
{
|
|
var g = !it.Contains("min") ? null : it.Get("min")?.AsBsonDocument;
|
|
if (g != null)
|
|
{
|
|
var value = g.Get("value")?.ToObject();
|
|
var min = g.Get("min")?.ToDouble();
|
|
var max = g.Get("max")?.ToDouble();
|
|
var uTim = g.Get("time")?.ToUniversalTime();
|
|
if (!uTim.HasValue) { _logger.LogError("Error getting utc time in 'min': {it}", it); goto addObs; }
|
|
|
|
var time = TimeZoneInfo.ConvertTimeFromUtc(uTim.Value, tz);
|
|
time = DateTime.SpecifyKind(time, DateTimeKind.Local);
|
|
if (string.IsNullOrEmpty(name) || value == null)
|
|
{ _logger.LogError("Invalid 'min' grouped obs (name/value) patient:{patientId}", patientId); goto addObs; }
|
|
|
|
obs.Min = new GroupedObservationObsValue(
|
|
value, time,
|
|
await GroupedObservationStatus(groupedField, GroupedObservationEnum.Result.Min, name, value, min, max)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.Max))
|
|
{
|
|
var g = !it.Contains("max") ? null : it.Get("max")?.AsBsonDocument;
|
|
if (g != null)
|
|
{
|
|
var value = g.Get("value")?.ToObject();
|
|
var min = g.Get("min")?.ToDouble();
|
|
var max = g.Get("max")?.ToDouble();
|
|
var uTim = g.Get("time")?.ToUniversalTime();
|
|
if (!uTim.HasValue) { _logger.LogError("Error getting utc time in 'max': {it}", it); goto addObs; }
|
|
|
|
var time = TimeZoneInfo.ConvertTimeFromUtc(uTim.Value, tz);
|
|
time = DateTime.SpecifyKind(time, DateTimeKind.Local);
|
|
if (string.IsNullOrEmpty(name) || value == null)
|
|
{ _logger.LogError("Invalid 'max' grouped obs (name/value) patient:{patientId}", patientId); goto addObs; }
|
|
|
|
obs.Max = new GroupedObservationObsValue(
|
|
value, time,
|
|
await GroupedObservationStatus(groupedField, GroupedObservationEnum.Result.Max, name, value, min, max)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.Sum))
|
|
{
|
|
var value = !it.Contains("sum") ? null : it.Get("sum")?.ToObject();
|
|
if (value != null && !string.IsNullOrEmpty(name))
|
|
{
|
|
obs.Sum = new GroupedObservationObsValue(
|
|
value, null,
|
|
await GroupedObservationStatus(groupedField, GroupedObservationEnum.Result.Sum, name, value, null, null)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.Average))
|
|
{
|
|
var value = !it.Contains("average") ? null : it.Get("average")?.ToObject();
|
|
if (value != null && !string.IsNullOrEmpty(name))
|
|
{
|
|
obs.Average = new GroupedObservationObsValue(
|
|
value, null,
|
|
await GroupedObservationStatus(groupedField, GroupedObservationEnum.Result.Average, name, value, null, null)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.Count))
|
|
{
|
|
var value = !it.Contains("count") ? null : it.Get("count")?.AsNullableInt32;
|
|
if (value != null && !string.IsNullOrEmpty(name))
|
|
{
|
|
obs.Count = new GroupedObservationObsValue(
|
|
value, null,
|
|
await GroupedObservationStatus(groupedField, GroupedObservationEnum.Result.Count, name, value, null, null)
|
|
);
|
|
}
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.LastFilled))
|
|
{
|
|
var g = !it.Contains("lastfilled") ? null : it.Get("lastfilled")?.AsBsonDocument;
|
|
if (g != null)
|
|
{
|
|
var value = g.Get("value")?.ToObject();
|
|
var min = g.Get("min")?.ToDouble();
|
|
var max = g.Get("max")?.ToDouble();
|
|
var uTim = g.Get("time")?.ToUniversalTime();
|
|
if (!uTim.HasValue) { _logger.LogError("Error getting utc time in 'lastfilled': {it}", it); goto addObs; }
|
|
|
|
var time = TimeZoneInfo.ConvertTimeFromUtc(uTim.Value, tz);
|
|
time = DateTime.SpecifyKind(time, DateTimeKind.Local);
|
|
|
|
if (value != null && !string.IsNullOrEmpty(name))
|
|
{
|
|
obs.LastFilled = new GroupedObservationObsValue(
|
|
value, time,
|
|
await GroupedObservationStatus(groupedField, GroupedObservationEnum.Result.LastFilled, name, value, min, max)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.HalfHour))
|
|
{
|
|
var g = !it.Contains("halfhour") ? null : it.Get("halfhour")?.AsBsonDocument;
|
|
if (g != null)
|
|
{
|
|
var value = g.Get("value")?.ToObject();
|
|
var min = g.Get("min")?.ToDouble();
|
|
var max = g.Get("max")?.ToDouble();
|
|
var uTim = g.Get("time")?.ToUniversalTime();
|
|
if (!uTim.HasValue) { _logger.LogError("Error getting utc time in 'halfhour': {it}", it); goto addObs; }
|
|
|
|
var time = TimeZoneInfo.ConvertTimeFromUtc(uTim.Value, tz);
|
|
time = DateTime.SpecifyKind(time, DateTimeKind.Local);
|
|
|
|
if (value != null && !string.IsNullOrEmpty(name))
|
|
{
|
|
obs.HalfHour = new GroupedObservationObsValue(
|
|
value, time,
|
|
await GroupedObservationStatus(groupedField, GroupedObservationEnum.Result.Last, name, value, min, max)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
addObs:
|
|
_logger.LogTrace("PatientId: {patientId}. Add to grouped observation: {obs}", patientId, obs);
|
|
list.Add(obs);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
// caché explícita por clave + TTL ---
|
|
if (!cacheIsChecked)
|
|
{
|
|
var (key, ttl) = CacheKeys.GroupedObsKeyWithTtl(_cacheSettings, patientId, groupedField.Name ?? string.Empty);
|
|
|
|
var observations = await _cacheService.GetOrSetObjectAsync(
|
|
key,
|
|
ComputeObservationsAsync,
|
|
ttl,
|
|
ct);
|
|
|
|
return new GroupedObservation
|
|
{
|
|
PatientId = patientId,
|
|
Group = groupedField.Group,
|
|
Name = groupedField.Name,
|
|
Observations = observations
|
|
};
|
|
}
|
|
|
|
// Re-cálculo forzado (sin tocar caché)
|
|
var computed = await ComputeObservationsAsync();
|
|
|
|
return new GroupedObservation
|
|
{
|
|
PatientId = patientId,
|
|
Group = groupedField.Group,
|
|
Name = groupedField.Name,
|
|
Observations = computed
|
|
};
|
|
}
|
|
|
|
public async Task<GroupedObservation> GenerateGroupedObservation(
|
|
ObjectId patientId,
|
|
GroupedField groupedField,
|
|
List<GroupedObservationObs> wsgLastGroupedObservationObs,
|
|
PatientObservation obs,
|
|
string timeZoneId = "Romance Standard Time")
|
|
{
|
|
|
|
// Estos resultados requieren recálculo completo (no incremental)
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.Average) ||
|
|
groupedField.Result.Contains(GroupedObservationEnum.Result.HalfHour) ||
|
|
groupedField.Result.Contains(GroupedObservationEnum.Result.Sum))
|
|
{
|
|
return await GenerateGroupedObservation(patientId, groupedField, timeZoneId, true);
|
|
}
|
|
|
|
// Clave consistente con CacheDispatcher / backends
|
|
var cacheKey = $"GroupedObs:{patientId}:{groupedField.Name}";
|
|
|
|
// Intentar leer caché actual
|
|
var cached = await _cacheService.GetObjectAsync<List<GroupedObservationObs>>(cacheKey, true);
|
|
if (cached != null)
|
|
{
|
|
_logger.LogInformation("Return cached grouped obs (incremental)");
|
|
|
|
// Actualizar lista en memoria
|
|
var updated = LocateCurrentObsInGroup(cached, obs, groupedField);
|
|
|
|
// Persistir nueva lista (SET incremental)
|
|
await _cacheService.SetObjectAsync(cacheKey, updated, true);
|
|
|
|
return new GroupedObservation
|
|
{
|
|
PatientId = patientId,
|
|
Group = groupedField.Group,
|
|
Name = groupedField.Name,
|
|
Observations = updated
|
|
};
|
|
}
|
|
// Si MISS, recálculo completo y salida
|
|
return await GenerateGroupedObservation(patientId, groupedField, timeZoneId, true);
|
|
}
|
|
|
|
public async Task<List<PatientObservation>> FindLastObservations(ObjectId patientId, int num = 2,
|
|
List<string>? filterObservations = null)
|
|
{
|
|
var result = await _observationRepository.AggregatedPatientLastObservations(patientId, num, filterObservations);
|
|
return result;
|
|
}
|
|
|
|
public async Task<GroupedObservation> CreateNextEmptyObs(WsSubscriberGrouped ws)
|
|
{
|
|
var gf = new GroupedField
|
|
{
|
|
Max = ws.Max,
|
|
Since = ws.Since,
|
|
StartTimeShift = ws.StartTimeShift,
|
|
Regularity = ws.Regularity,
|
|
Result = ws.Result,
|
|
Names = ws.Names,
|
|
Name = ws.Names.First(),
|
|
Group = ws.Group.FirstOrDefault().Value
|
|
};
|
|
|
|
// Mantenemos el uso del HashCode
|
|
var currentObsList = await _cacheService.GetObjectAsync<List<GroupedObservationObs>>(ws.HashCode, false);
|
|
|
|
if (currentObsList is { Count: > 0 })
|
|
{
|
|
var orderCurrentObsList = currentObsList.OrderBy(c => c.Time).ToList();
|
|
|
|
foreach (var name in gf.Names)
|
|
{
|
|
var obs = orderCurrentObsList.LastOrDefault(c => c.Name == name)
|
|
?? orderCurrentObsList.LastOrDefault();
|
|
if (obs != null)
|
|
{
|
|
var (timesLeft, multiplierFactor) = TimesLeftToCreateEmptyObs(obs, gf);
|
|
for (var i = 1; i <= timesLeft; i++)
|
|
orderCurrentObsList.Add(CreateNextObs(obs, gf, i * multiplierFactor));
|
|
}
|
|
}
|
|
|
|
// Ajuste de ventana temporal a mostrar según Regularity
|
|
DateTime dateLimit;
|
|
var requireMinutes = false;
|
|
var requireSeconds = false;
|
|
|
|
switch (gf.Regularity)
|
|
{
|
|
case GroupedObservationEnum.Regularity.Minute:
|
|
dateLimit = new DateTime(
|
|
DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
|
|
DateTime.Now.Hour, DateTime.Now.Minute, 0
|
|
).AddMinutes(gf.Max * -1);
|
|
requireMinutes = true;
|
|
break;
|
|
|
|
case GroupedObservationEnum.Regularity.Second:
|
|
dateLimit = new DateTime(
|
|
DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
|
|
DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second
|
|
).AddSeconds(gf.Max * -1);
|
|
requireMinutes = true;
|
|
requireSeconds = true;
|
|
break;
|
|
|
|
default:
|
|
dateLimit = new DateTime(
|
|
DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
|
|
DateTime.Now.Hour, 0, 0
|
|
).AddHours(gf.Max * -1);
|
|
break;
|
|
}
|
|
|
|
var cacheResult = orderCurrentObsList
|
|
.Where(c =>
|
|
new DateTime(
|
|
c.Time.Year, c.Time.Month, c.Time.Day, c.Time.Hour,
|
|
requireMinutes ? c.Time.Minute : 0,
|
|
requireSeconds ? c.Time.Second : 0
|
|
) >= dateLimit)
|
|
.ToList();
|
|
|
|
// Persistimos la lista incrementada en la misma clave para no romper el flujo actual.
|
|
await _cacheService.SetObjectAsync(ws.HashCode, cacheResult, false);
|
|
|
|
return new GroupedObservation
|
|
{
|
|
PatientId = ws.PatientId,
|
|
Group = gf.Group,
|
|
Name = gf.Name,
|
|
Observations = cacheResult
|
|
};
|
|
}
|
|
|
|
|
|
// Si no hay caché o hay MISS → recálculo completo (sin usar caché en esta ruta).
|
|
return await GenerateGroupedObservation(ws.PatientId, gf, ws.TimeZoneId, true);
|
|
}
|
|
|
|
|
|
// PRIVATE: High-level composition steps
|
|
public List<BsonDocument> FillHours(List<BsonDocument> result, GroupedField groupedField)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
//var resultNumber = groupedField.Max;
|
|
var names = groupedField.Names.Count > 1 ? groupedField.Names : [groupedField.Name ?? string.Empty];
|
|
|
|
if (result.Count >= groupedField.Max * names.Count)
|
|
return result; //We have data from each hour/day/minute, dont need to calc.
|
|
List<BsonDocument> fillResult = [];
|
|
//var first = result.FirstOrDefault();
|
|
foreach (var name in names)
|
|
{
|
|
var filteredDocuments = result.Where(doc => doc["_id"]["name"] == name).ToList();
|
|
if (filteredDocuments.Count >= groupedField.Max)
|
|
{
|
|
filteredDocuments = filteredDocuments.OrderBy(e => e.Get("time")?.ToLocalTime()).ToList();
|
|
fillResult.AddRange(filteredDocuments);
|
|
continue;
|
|
}
|
|
|
|
var expectedDate = DateTime.MinValue;
|
|
var inverseCounter = 0;
|
|
for (var i = groupedField.Max; i != 0; i--)
|
|
{
|
|
var expectedMatch = true;
|
|
|
|
var value = filteredDocuments.ElementAtOrDefault(inverseCounter);
|
|
|
|
DateTime? time = null;
|
|
|
|
if (value == null)
|
|
expectedMatch = false;
|
|
else
|
|
//Estamos recogiendo la hora en UTC
|
|
time = value.Get("time")?.ToUniversalTime();
|
|
|
|
|
|
switch (groupedField.Regularity)
|
|
{
|
|
case GroupedObservationEnum.Regularity.Day:
|
|
expectedDate = now.AddDays(-i);
|
|
if (!time.HasValue || expectedDate.Day != time.Value.Day) expectedMatch = false;
|
|
break;
|
|
case GroupedObservationEnum.Regularity.Hour:
|
|
case GroupedObservationEnum.Regularity.Shift:
|
|
expectedDate = now.AddHours(-i);
|
|
if (!time.HasValue || expectedDate.Hour != time.Value.Hour) expectedMatch = false;
|
|
break;
|
|
case GroupedObservationEnum.Regularity.Minute:
|
|
expectedDate = now.AddMinutes(-i);
|
|
if (!time.HasValue || expectedDate.Minute != time.Value.Minute) expectedMatch = false;
|
|
break;
|
|
case GroupedObservationEnum.Regularity.Second:
|
|
expectedDate = now.AddSeconds(-i);
|
|
if (!time.HasValue || expectedDate.Second != time.Value.Second) expectedMatch = false;
|
|
break;
|
|
}
|
|
|
|
|
|
if (!expectedMatch)
|
|
{
|
|
var newEntry = new BsonDocument
|
|
{
|
|
{
|
|
"_id", new BsonDocument
|
|
{
|
|
{ "year", expectedDate.Year },
|
|
{ "month", expectedDate.Month },
|
|
{ "day", expectedDate.Day },
|
|
{ "hour", expectedDate.Hour },
|
|
{ "minute", expectedDate.Minute },
|
|
{ "name", groupedField.Name }
|
|
}
|
|
},
|
|
{ "time", new BsonDateTime(expectedDate) }, //TODO loop del result entero
|
|
//{ Result.HalfHour.ToString().ToLower(), new BsonDocument { { "value", 0 }, { "min",0 }, { "max",0 }, { "time", new BsonDateTime(expectedDate) } } }
|
|
// { groupedField.regularity.ToString().ToLower(), new BsonDocument { { "value", 0 }, { "min",0 }, { "max",0 }, { "time", new BsonDateTime(expectedDate) } } }
|
|
{ "isFilled", true } //Marcamos como hora añadida
|
|
};
|
|
|
|
foreach (var r in groupedField.Result)
|
|
switch (r)
|
|
{
|
|
case GroupedObservationEnum.Result.LastFilled:
|
|
case GroupedObservationEnum.Result.LastAll:
|
|
continue;
|
|
case GroupedObservationEnum.Result.Sum:
|
|
case GroupedObservationEnum.Result.Average:
|
|
case GroupedObservationEnum.Result.Count:
|
|
newEntry.Add(new BsonElement(r.ToString().ToLower(), 0));
|
|
continue;
|
|
case GroupedObservationEnum.Result.HalfHour:
|
|
newEntry.Add("all",
|
|
new BsonArray
|
|
{
|
|
new BsonDocument
|
|
{
|
|
{ "value", 0 }, { "min", 0 }, { "max", 0 },
|
|
{ "time", new BsonDateTime(expectedDate) }
|
|
}
|
|
});
|
|
continue;
|
|
default:
|
|
newEntry.Add(r.ToString().ToLower(),
|
|
new BsonDocument
|
|
{
|
|
{ "value", 0 }, { "min", 0 }, { "max", 0 },
|
|
{ "time", new BsonDateTime(expectedDate) }
|
|
});
|
|
continue;
|
|
}
|
|
|
|
if (groupedField.Result.Count != 1 ||
|
|
!groupedField.Result.Contains(GroupedObservationEnum.Result.LastFilled))
|
|
filteredDocuments.Add(newEntry);
|
|
|
|
filteredDocuments = filteredDocuments.OrderBy(e => e.Get("time")?.ToLocalTime()).ToList();
|
|
}
|
|
|
|
inverseCounter++;
|
|
}
|
|
|
|
if (groupedField.Max <= filteredDocuments.Count)
|
|
{
|
|
var value = filteredDocuments.ElementAtOrDefault(inverseCounter);
|
|
|
|
if (value == null)
|
|
{
|
|
var newEntry = new BsonDocument
|
|
{
|
|
{
|
|
"_id", new BsonDocument
|
|
{
|
|
{ "year", DateTime.UtcNow.Year },
|
|
{ "month", DateTime.UtcNow.Month },
|
|
{ "day", DateTime.UtcNow.Day },
|
|
{ "hour", DateTime.UtcNow.Hour },
|
|
{ "minute", DateTime.UtcNow.Minute },
|
|
{ "name", groupedField.Name }
|
|
}
|
|
},
|
|
{ "time", new BsonDateTime(DateTime.UtcNow) }, //TODO loop del result entero
|
|
{ "isFilled", true } //Marcamos como hora añadida
|
|
};
|
|
|
|
foreach (var r in groupedField.Result)
|
|
switch (r)
|
|
{
|
|
case GroupedObservationEnum.Result.LastFilled:
|
|
case GroupedObservationEnum.Result.LastAll:
|
|
continue;
|
|
case GroupedObservationEnum.Result.Sum:
|
|
case GroupedObservationEnum.Result.Average:
|
|
case GroupedObservationEnum.Result.Count:
|
|
newEntry.Add(new BsonElement(r.ToString().ToLower(), 0));
|
|
continue;
|
|
case GroupedObservationEnum.Result.HalfHour:
|
|
newEntry.Add("all",
|
|
new BsonArray
|
|
{
|
|
new BsonDocument
|
|
{
|
|
{ "value", 0 }, { "min", 0 }, { "max", 0 },
|
|
{ "time", new BsonDateTime(expectedDate) }
|
|
}
|
|
});
|
|
continue;
|
|
default:
|
|
newEntry.Add(r.ToString().ToLower(),
|
|
new BsonDocument
|
|
{
|
|
{ "value", 0 }, { "min", 0 }, { "max", 0 },
|
|
{ "time", new BsonDateTime(expectedDate) }
|
|
});
|
|
continue;
|
|
}
|
|
|
|
if (groupedField.Result.Count != 1 ||
|
|
!groupedField.Result.Contains(GroupedObservationEnum.Result.LastFilled))
|
|
filteredDocuments.Add(newEntry);
|
|
|
|
filteredDocuments = filteredDocuments.OrderBy(e => e.Get("time")?.ToLocalTime()).ToList();
|
|
}
|
|
|
|
fillResult.AddRange(filteredDocuments);
|
|
}
|
|
}
|
|
|
|
fillResult = fillResult.OrderBy(e => e.Get("time")?.ToLocalTime()).ToList();
|
|
|
|
return fillResult;
|
|
}
|
|
|
|
public async Task<List<BsonDocument>> CalculateLastFilledObservations(List<BsonDocument> result,
|
|
GroupedField groupedField, ObjectId patientId)
|
|
{
|
|
List<BsonDocument> resultToReturn = [];
|
|
if (groupedField.Names.Any())
|
|
foreach (var name in groupedField.Names)
|
|
{
|
|
var resultFiltedByName = result.FindAll(b => b.Get("_id")?.AsBsonDocument.Get("name") == name);
|
|
var resultCalculated = await LastFilledCalc(resultFiltedByName, groupedField, patientId, name);
|
|
resultToReturn.AddRange(resultCalculated);
|
|
}
|
|
else
|
|
return await LastFilledCalc(result, groupedField, patientId, groupedField.Name);
|
|
|
|
|
|
return resultToReturn;
|
|
}
|
|
|
|
private async Task<List<BsonDocument>> LastFilledCalc(List<BsonDocument> result, GroupedField groupedField,
|
|
ObjectId patientId, string? name)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
|
|
if (result.Count == groupedField.Max)
|
|
return result; //We have data from each hour/day/minute, dont need to calc.
|
|
|
|
var first = result.FirstOrDefault();
|
|
|
|
|
|
object? lastObservationValue = null;
|
|
|
|
var expectedFirstDate = groupedField.Regularity switch
|
|
{
|
|
GroupedObservationEnum.Regularity.Day => now.AddDays(-groupedField.Max),
|
|
GroupedObservationEnum.Regularity.Hour => now.AddHours(-groupedField.Max),
|
|
GroupedObservationEnum.Regularity.Minute => now.AddMinutes(-groupedField.Max),
|
|
GroupedObservationEnum.Regularity.Second => now.AddSeconds(-groupedField.Max),
|
|
_ => DateTime.MinValue
|
|
};
|
|
|
|
//first > fecha minima tenemos que buscar el ultimo valor
|
|
if (first == null || first.Get("time")?.ToUniversalTime().CompareTo(expectedFirstDate) > 0)
|
|
{
|
|
var lastObservation =
|
|
await _observationRepository.FindLastObservationBeforeDate(patientId, groupedField.Name,
|
|
expectedFirstDate);
|
|
|
|
if (lastObservation != null) lastObservationValue = lastObservation.Value;
|
|
}
|
|
|
|
var expectedDate = DateTime.MinValue;
|
|
var inverseCounter = 0;
|
|
for (var i = groupedField.Max; i != 0; i--)
|
|
{
|
|
var expectedMatch = true;
|
|
|
|
var value = result.ElementAtOrDefault(inverseCounter);
|
|
|
|
DateTime? time = null;
|
|
|
|
if (value == null)
|
|
expectedMatch = false;
|
|
else
|
|
time = value.Get("time")?.ToUniversalTime();
|
|
|
|
|
|
switch (groupedField.Regularity)
|
|
{
|
|
case GroupedObservationEnum.Regularity.Day:
|
|
expectedDate = now.AddDays(-i);
|
|
if (!time.HasValue || expectedDate.Day != time.Value.Day) expectedMatch = false;
|
|
break;
|
|
case GroupedObservationEnum.Regularity.Hour:
|
|
expectedDate = now.AddHours(-i);
|
|
if (!time.HasValue || expectedDate.Hour != time.Value.Hour) expectedMatch = false;
|
|
break;
|
|
case GroupedObservationEnum.Regularity.Minute:
|
|
expectedDate = now.AddMinutes(-i);
|
|
if (!time.HasValue || expectedDate.Minute != time.Value.Minute) expectedMatch = false;
|
|
break;
|
|
case GroupedObservationEnum.Regularity.Second:
|
|
expectedDate = now.AddSeconds(-i);
|
|
if (!time.HasValue || expectedDate.Second != time.Value.Second) expectedMatch = false;
|
|
break;
|
|
}
|
|
|
|
|
|
if (!expectedMatch)
|
|
{
|
|
var lastResult = result.ElementAtOrDefault(inverseCounter - 1);
|
|
object? lastFilledValue;
|
|
if (lastResult == null)
|
|
{
|
|
lastFilledValue = lastObservationValue;
|
|
}
|
|
else
|
|
{
|
|
var lastfilled = GroupedObservationEnum.Result.LastFilled.ToString().ToLower();
|
|
lastFilledValue = lastResult.Get(lastfilled)?.AsBsonDocument.Get("value");
|
|
}
|
|
|
|
lastFilledValue ??= 0;
|
|
|
|
var newEntry = new BsonDocument
|
|
{
|
|
{
|
|
"_id", new BsonDocument
|
|
{
|
|
{ "year", expectedDate.Year },
|
|
{ "month", expectedDate.Month },
|
|
{ "day", expectedDate.Day },
|
|
{ "hour", expectedDate.Hour },
|
|
{ "minute", expectedDate.Minute },
|
|
{ "name", name }
|
|
}
|
|
},
|
|
{ "time", new BsonDateTime(expectedDate) },
|
|
{
|
|
GroupedObservationEnum.Result.LastFilled.ToString().ToLower(),
|
|
new BsonDocument
|
|
{
|
|
{ "value", lastFilledValue.ToString() }, { "min", 0 }, { "max", 0 },
|
|
{ "time", new BsonDateTime(expectedDate) }
|
|
}
|
|
}
|
|
};
|
|
result.Add(newEntry);
|
|
result = result.OrderBy(e => e.Get("time")?.ToLocalTime()).ToList();
|
|
}
|
|
|
|
inverseCounter++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public List<BsonDocument> CalculateShiftObservations(List<BsonDocument> shiftGroupObservations,
|
|
GroupedField groupedField)
|
|
{
|
|
var shiftGrouped = new List<BsonDocument>();
|
|
|
|
if (groupedField.Result.Contains(GroupedObservationEnum.Result.Sum))
|
|
{
|
|
var shiftGroups =
|
|
shiftGroupObservations.GroupBy(shift => new
|
|
{ V1 = shift.GetElement("shift").ToString(), V = shift.GetElement("day").ToString() })
|
|
.Select(s => s.ToList())
|
|
.ToList();
|
|
|
|
|
|
shiftGroups.ForEach(shiftGroup =>
|
|
{
|
|
var shiftSum = shiftGroup.Select(shift => shift.Get("sum")?.ToInt32()).Sum();
|
|
var shiftToAdd = shiftGroup.FirstOrDefault();
|
|
|
|
shiftToAdd?.SetElement(new BsonElement("sum", shiftSum));
|
|
if (shiftGroup.Select(shift => shift.Get("isFilled")).Any(s => s == false))
|
|
shiftToAdd?.SetElement(new BsonElement("isFilled", false));
|
|
|
|
if (shiftToAdd != null)
|
|
shiftGrouped.Add(shiftToAdd);
|
|
});
|
|
|
|
|
|
return shiftGrouped;
|
|
}
|
|
|
|
return shiftGroupObservations;
|
|
}
|
|
|
|
//MIGUEL: EJEMPLO SI LAS 12 TIENEN 4 VALORES 12:15 => 5, 12:20 => 10, 12:45 => 20 Y LAS 13 NO TIENE VALOR. 12 DEBERÍA 5 Y 13 DEBERÍA SER
|
|
// ACTUALMENTE A LAS 12 MOSTRARIA EL ULTIMO DE LA MEDIA HORA Y A LAS 13?
|
|
public List<BsonDocument> CalculateHalfHourObservations(List<BsonDocument> result)
|
|
{
|
|
//HalfHour añadir que se rellenen las horas que no existen
|
|
for (var i = 0; i < result.Count; i++)
|
|
{
|
|
var document = result[i];
|
|
BsonDocument? nextDocument = null;
|
|
|
|
if (i + 1 < result.Count)
|
|
nextDocument = result[i + 1];
|
|
|
|
document.TryGetValue("all", out var docvalues);
|
|
|
|
|
|
List<BsonValue?> groups = [];
|
|
|
|
if (docvalues != null)
|
|
groups = docvalues.AsBsonArray.ToList().GroupBy(x =>
|
|
{
|
|
var stamp = x.AsBsonDocument.Get("time")?.ToLocalTime();
|
|
if (!stamp.HasValue) return null;
|
|
stamp = stamp.Value.AddMinutes(-(stamp.Value.Minute % 30));
|
|
stamp = stamp.Value.AddMilliseconds(-stamp.Value.Millisecond - 1000 * stamp.Value.Second);
|
|
return stamp;
|
|
}).Select(g =>
|
|
g.OrderByDescending(c => c.AsBsonDocument.Get("time")?.ToLocalTime()).FirstOrDefault())
|
|
.ToList();
|
|
|
|
|
|
if (groups.Count > 0)
|
|
{
|
|
if (nextDocument == null)
|
|
{
|
|
document.Set("halfhour", groups.Last());
|
|
break;
|
|
}
|
|
|
|
if (groups.Count > 1)
|
|
{
|
|
var nextDocumentValues = nextDocument.Get("all")?.AsBsonArray.ToList();
|
|
|
|
if (nextDocumentValues == null || nextDocumentValues.Count == 0
|
|
|| (nextDocumentValues.Count == 1 &&
|
|
nextDocument.Get("all")?.AsBsonArray.ToList()[0].AsBsonDocument
|
|
.Get("value")?.ToString() ==
|
|
"0")) //El siguiente registro no tiene tomas
|
|
{
|
|
nextDocument.Set("halfhour", groups.LastOrDefault());
|
|
document.Set("halfhour", groups.FirstOrDefault());
|
|
}
|
|
else
|
|
{
|
|
document.Set("halfhour", groups.LastOrDefault());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (document.Get("halfhour")?.AsBsonDocument == null)
|
|
document.Set("halfhour", groups.LastOrDefault());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!document.TryGetValue("halfhour", out _)) document.Set("halfhour", new BsonDocument());
|
|
}
|
|
}
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
public List<BsonDocument> GenerateShiftObservations(List<BsonDocument> result, GroupedField groupedField)
|
|
{
|
|
result.ForEach(item =>
|
|
{
|
|
DateTime? obsDateTime = null;
|
|
var id = item.Get("_id")?.AsBsonDocument;
|
|
if (id == null)
|
|
return;
|
|
|
|
|
|
var timeStr = id.Get("hour")?.ToString()?.PadLeft(2, '0') ?? "00";
|
|
if (id.TryGetValue("minute", out _))
|
|
{
|
|
timeStr += ":" + id.Get("minute")?.ToString()?.PadLeft(2, '0');
|
|
if (DateTime.TryParseExact(timeStr, "HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None,
|
|
out var r1))
|
|
obsDateTime = r1;
|
|
}
|
|
else
|
|
{
|
|
if (DateTime.TryParseExact(timeStr, "HH", CultureInfo.InvariantCulture, DateTimeStyles.None,
|
|
out var r2))
|
|
obsDateTime = r2;
|
|
}
|
|
|
|
|
|
obsDateTime = obsDateTime?.ToLocalTime();
|
|
int shift;
|
|
int day;
|
|
//Return all dates in shift array before obsDateTime, and take last, that will be the closest shift date.
|
|
var expectedHour = obsDateTime?.Hour.ToString().Length < 2
|
|
? $"0{obsDateTime.Value.Hour}"
|
|
: obsDateTime?.Hour.ToString();
|
|
|
|
var expectedMinutes = obsDateTime?.Minute.ToString().Length < 2
|
|
? $"0{obsDateTime.Value.Minute}"
|
|
: obsDateTime?.Minute.ToString();
|
|
|
|
|
|
var shiftSelected = groupedField.StartTimeShift.LastOrDefault
|
|
(i => DateTime.ParseExact(i, "HH:mm", CultureInfo.InvariantCulture).Ticks <= DateTime
|
|
.ParseExact($"{expectedHour}:{expectedMinutes}", "HH:mm", CultureInfo.InvariantCulture)
|
|
.Ticks);
|
|
|
|
var havemin = id.TryGetValue("minute", out var minuteV);
|
|
var year = id.Get("year")?.ToInt32();
|
|
var month = id.Get("month")?.ToInt32();
|
|
var dayy = id.Get("day")?.ToInt32();
|
|
var hour = id.Get("hour")?.ToInt32();
|
|
|
|
if (!year.HasValue || !month.HasValue || !dayy.HasValue || !hour.HasValue)
|
|
{
|
|
_logger.LogError("Error getting dateTime from Bson: {id}", id);
|
|
return;
|
|
}
|
|
|
|
var dateUtc = new DateTime(year.Value, month.Value, dayy.Value, hour.Value, havemin ? minuteV.ToInt32() : 0,
|
|
0);
|
|
|
|
if (!groupedField.StartTimeShift.Any())
|
|
{
|
|
_logger.LogError(
|
|
"Error generating shift observations. StartTimeShift List in null or empty. {groupedField}",
|
|
groupedField);
|
|
return;
|
|
}
|
|
|
|
if (shiftSelected == null)
|
|
{
|
|
day = dateUtc.ToLocalTime().AddDays(-1).Day;
|
|
shift = groupedField.StartTimeShift.Count - 1;
|
|
}
|
|
else
|
|
{
|
|
day = dateUtc.ToLocalTime().Day;
|
|
shift = groupedField.StartTimeShift.IndexOf(shiftSelected);
|
|
}
|
|
|
|
item.Add(new BsonElement("day", day));
|
|
item.Add(new BsonElement("shift", shift));
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
// PRIVATE: Incremental item adjustment
|
|
private List<GroupedObservationObs> LocateCurrentObsInGroup(List<GroupedObservationObs> group,
|
|
PatientObservation obs, GroupedField groupedField)
|
|
{
|
|
// Check if we need to modify the current value or add new one
|
|
var specifyMinutes =
|
|
groupedField.Regularity is GroupedObservationEnum.Regularity.Minute
|
|
or GroupedObservationEnum.Regularity.Second;
|
|
var specifySeconds = groupedField.Regularity == GroupedObservationEnum.Regularity.Second;
|
|
var obsTime = new DateTime(obs.Time.Year, obs.Time.Month, obs.Time.Day, obs.Time.Hour,
|
|
specifyMinutes ? obs.Time.Minute : 0,
|
|
specifySeconds ? obs.Time.Second : 0);
|
|
var obsIndexInThisHour = group.FindIndex(c =>
|
|
new DateTime(c.Time.Year, c.Time.Month, c.Time.Day, c.Time.Hour,
|
|
specifyMinutes ? c.Time.Minute : 0,
|
|
specifySeconds ? c.Time.Second : 0) == obsTime && c.Name == obs.Name);
|
|
if (obsIndexInThisHour >= 0)
|
|
{
|
|
// Obs to modify found
|
|
var valueIsParsed = double.TryParse(obs.Value.ToString(), out var valueParsed);
|
|
if (!valueIsParsed)
|
|
return group;
|
|
group[obsIndexInThisHour].IsFilled = false;
|
|
group[obsIndexInThisHour].Time = obsTime;
|
|
foreach (var result in groupedField.Result)
|
|
switch (result)
|
|
{
|
|
case GroupedObservationEnum.Result.Max:
|
|
var valueMaxIsParsed = double.TryParse(group[obsIndexInThisHour].Max?.Value.ToString(),
|
|
out var valueMaxParsed);
|
|
if (!valueMaxIsParsed) continue;
|
|
//actual value is higher, is relevant
|
|
if (group[obsIndexInThisHour].Max?.Value != null && valueParsed > valueMaxParsed)
|
|
{
|
|
group[obsIndexInThisHour].Max!.Value = obs.Value;
|
|
group[obsIndexInThisHour].Max!.Time = obs.Time;
|
|
group[obsIndexInThisHour].Max!.Type = obs.Status;
|
|
group[obsIndexInThisHour].Max!.Type = obs.Status;
|
|
}
|
|
|
|
break;
|
|
case GroupedObservationEnum.Result.Min:
|
|
var valueMinIsParsed = double.TryParse(group[obsIndexInThisHour].Max?.Value.ToString(),
|
|
out var valueMinParsed);
|
|
if (!valueMinIsParsed) continue;
|
|
if (group[obsIndexInThisHour].Max?.Value != null && valueParsed < valueMinParsed)
|
|
{
|
|
group[obsIndexInThisHour].Min!.Value = obs.Value;
|
|
group[obsIndexInThisHour].Min!.Time = obs.Time;
|
|
group[obsIndexInThisHour].Min!.Type = obs.Status;
|
|
}
|
|
|
|
break;
|
|
case GroupedObservationEnum.Result.Last:
|
|
if (group[obsIndexInThisHour].Last?.Time < obs.Time)
|
|
{
|
|
group[obsIndexInThisHour].Last!.Value = obs.Value;
|
|
group[obsIndexInThisHour].Last!.Time = obs.Time;
|
|
group[obsIndexInThisHour].Last!.Type = obs.Status;
|
|
}
|
|
|
|
break;
|
|
case GroupedObservationEnum.Result.LastFilled:
|
|
if (group[obsIndexInThisHour].LastFilled?.Time < obs.Time)
|
|
{
|
|
group[obsIndexInThisHour].LastFilled!.Value = obs.Value;
|
|
group[obsIndexInThisHour].LastFilled!.Time = obs.Time;
|
|
group[obsIndexInThisHour].LastFilled!.Type = obs.Status;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Delete obs autofilled and in time of the new obs
|
|
var obsIndexInThisHourToRemove = group.FindIndex(c =>
|
|
new DateTime(c.Time.Year, c.Time.Month, c.Time.Day, c.Time.Hour,
|
|
specifyMinutes ? c.Time.Minute : 0,
|
|
specifySeconds ? c.Time.Second : 0) == obsTime && c.IsFilled);
|
|
if (obsIndexInThisHourToRemove >= 0)
|
|
group.RemoveAt(obsIndexInThisHourToRemove);
|
|
// Obs to modify not found add it
|
|
var ngo = new GroupedObservationObs
|
|
{
|
|
Name = obs.Name ?? string.Empty,
|
|
MinAlert = obs.Min,
|
|
MaxAlert = obs.Max,
|
|
Time = obsTime,
|
|
IsFilled = false,
|
|
ShiftDate = obs.Time,
|
|
Shift = GetShift(obs.Time, groupedField.StartTimeShift)
|
|
};
|
|
|
|
foreach (var result in groupedField.Result)
|
|
switch (result)
|
|
{
|
|
case GroupedObservationEnum.Result.Max:
|
|
ngo.Max = new GroupedObservationObsValue(obs.Value, obs.Time, obs.Status);
|
|
break;
|
|
case GroupedObservationEnum.Result.Min:
|
|
ngo.Min = new GroupedObservationObsValue(obs.Value, obs.Time, obs.Status);
|
|
break;
|
|
case GroupedObservationEnum.Result.Last:
|
|
ngo.Last = new GroupedObservationObsValue(obs.Value, obs.Time, obs.Status);
|
|
break;
|
|
case GroupedObservationEnum.Result.LastFilled:
|
|
ngo.LastFilled = new GroupedObservationObsValue(obs.Value, obs.Time, obs.Status);
|
|
break;
|
|
}
|
|
|
|
group.Add(ngo);
|
|
}
|
|
|
|
// Check if we have more obs than we require to show
|
|
DateTime dateLimit;
|
|
switch (groupedField.Regularity)
|
|
{
|
|
case GroupedObservationEnum.Regularity.Minute:
|
|
dateLimit = new DateTime(obs.Time.Year, obs.Time.Month, obs.Time.Day, obs.Time.Hour, obs.Time.Minute, 0)
|
|
.AddMinutes(groupedField.Max * -1);
|
|
break;
|
|
case GroupedObservationEnum.Regularity.Second:
|
|
dateLimit = new DateTime(obs.Time.Year, obs.Time.Month, obs.Time.Day, obs.Time.Hour, obs.Time.Minute,
|
|
obs.Time.Second).AddSeconds(groupedField.Max * -1);
|
|
break;
|
|
default:
|
|
dateLimit =
|
|
new DateTime(obs.Time.Year, obs.Time.Month, obs.Time.Day, obs.Time.Hour, 0, 0).AddHours(
|
|
groupedField.Max * -1);
|
|
break;
|
|
}
|
|
|
|
return group.Where(c =>
|
|
new DateTime(c.Time.Year, c.Time.Month, c.Time.Day, c.Time.Hour, specifyMinutes ? c.Time.Minute : 0,
|
|
specifySeconds ? c.Time.Second : 0) >= dateLimit).ToList();
|
|
}
|
|
|
|
|
|
|
|
// PRIVATE: Low-level utilities
|
|
private int? GetShift(DateTime currentObsDate, List<string> groupedFieldStartTimeShift)
|
|
{
|
|
if (groupedFieldStartTimeShift.Count == 0) return null;
|
|
var listTime = GetTimeSpan(groupedFieldStartTimeShift);
|
|
var dateTimeSpan = currentObsDate.TimeOfDay;
|
|
|
|
for (var i = 0; i < listTime.Count; i++)
|
|
{
|
|
var nextIndex = (i + 1) % listTime.Count;
|
|
var nextTimeSpan = listTime[nextIndex];
|
|
var currentTimeSpan = listTime[i];
|
|
|
|
if (dateTimeSpan >= currentTimeSpan && dateTimeSpan < nextTimeSpan) return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
private List<TimeSpan> GetTimeSpan(List<string> groupedFieldStartTimeShift)
|
|
{
|
|
List<TimeSpan> timeSpans = [];
|
|
timeSpans.AddRange(groupedFieldStartTimeShift.Select(TimeSpan.Parse));
|
|
|
|
List<TimeSpan> newTimeSpans = [];
|
|
|
|
for (var i = 0; i < timeSpans.Count; i++)
|
|
{
|
|
var nextIndex = (i + 1) % timeSpans.Count;
|
|
var nextTimeSpan = timeSpans[nextIndex];
|
|
var currentTimeSpan = timeSpans[i];
|
|
var addedTimeSpan = currentTimeSpan.Add(TimeSpan.FromMinutes(1));
|
|
|
|
newTimeSpans.Add(addedTimeSpan < nextTimeSpan ? addedTimeSpan : nextTimeSpan);
|
|
}
|
|
|
|
return newTimeSpans;
|
|
}
|
|
|
|
private async Task<StatusEnum.Type> GroupedObservationStatus(GroupedField groupedField,
|
|
GroupedObservationEnum.Result result, string name, object value, double? min, double? max)
|
|
{
|
|
return await _configObservationService.GroupedObservationStatus(groupedField, result, name, value, min, max);
|
|
}
|
|
|
|
private (int timesLeft, int multiplierFactor) TimesLeftToCreateEmptyObs(GroupedObservationObs obs, GroupedField gf)
|
|
{
|
|
var currentDateTime = DateTime.Now;
|
|
var timeElapsed = currentDateTime - obs.Time;
|
|
double timeToCurrent;
|
|
var multipliesFactor = 1;
|
|
// Calcula el tiempo transcurrido en la unidad deseada
|
|
switch (gf.Regularity)
|
|
{
|
|
case GroupedObservationEnum.Regularity.Second:
|
|
timeToCurrent = timeElapsed.TotalSeconds;
|
|
break;
|
|
|
|
case GroupedObservationEnum.Regularity.Minute:
|
|
timeToCurrent = timeElapsed.TotalMinutes;
|
|
multipliesFactor = 60;
|
|
break;
|
|
|
|
case GroupedObservationEnum.Regularity.Day:
|
|
timeToCurrent = timeElapsed.TotalDays;
|
|
multipliesFactor = 86400;
|
|
break;
|
|
|
|
default:
|
|
timeToCurrent = timeElapsed.TotalHours;
|
|
multipliesFactor = 3600;
|
|
break;
|
|
}
|
|
|
|
var timesLeft = (int)Math.Ceiling(timeToCurrent);
|
|
return (timesLeft, multipliesFactor);
|
|
}
|
|
|
|
private GroupedObservationObs CreateNextObs(GroupedObservationObs sourceObs, GroupedField gf, int seconsToAdd)
|
|
{
|
|
var lastObs = new GroupedObservationObs
|
|
{
|
|
Time = sourceObs.Time.AddSeconds(seconsToAdd),
|
|
Name = sourceObs.Name,
|
|
IsFilled = true
|
|
};
|
|
//Log.Debug("Result list:", string.Join(',', Result));
|
|
//Se rellena el tiempo con la hora de la obs anterior por que no hay dato de la actual
|
|
GroupedObservationObsValue defaultValue = new(0, sourceObs.Time);
|
|
foreach (var r in gf.Result)
|
|
{
|
|
//Log.Debug("Processing {r}", r.ToString());
|
|
//var props = typeof(GroupedObservationObs).GetProperties();
|
|
//Log.Debug("GroupedObservationObs Poperties list: {list}", string.Join(',', props.ToList()));
|
|
|
|
var propInfo = typeof(GroupedObservationObs).GetProperty(r.ToString());
|
|
if (propInfo == null)
|
|
{
|
|
_logger.LogError("Error Creating Next Obs. Getting property: {R} not found. SourceObs:{Obs}",
|
|
r.ToString(), sourceObs);
|
|
continue;
|
|
}
|
|
|
|
propInfo.SetValue(lastObs, defaultValue);
|
|
|
|
var gObsValue = propInfo.GetValue(sourceObs);
|
|
if (gObsValue == null)
|
|
{
|
|
_logger.LogWarning("Error Creating Next Obs.Getting property value: {R} not found. SourceObs:{Obs}",
|
|
r.ToString(), sourceObs);
|
|
continue;
|
|
}
|
|
|
|
var prevValue = (GroupedObservationObsValue)gObsValue;
|
|
|
|
prevValue.Time = sourceObs.Time;
|
|
|
|
propInfo.SetValue(sourceObs, prevValue);
|
|
}
|
|
|
|
if (gf.Regularity == GroupedObservationEnum.Regularity.Shift)
|
|
{
|
|
lastObs.ShiftDate = lastObs.Time;
|
|
lastObs.Shift = GetShift(lastObs.ShiftDate, gf.StartTimeShift);
|
|
}
|
|
|
|
return lastObs;
|
|
}
|
|
} |