165 lines
7.4 KiB
C#
165 lines
7.4 KiB
C#
using System.Reflection;
|
|
using adas_core.Application.Repositories.Interfaces;
|
|
using adas_core.Application.Services.Interfaces;
|
|
using adas_core.Domain.Models;
|
|
using adas_core.Domain.Models.AppSettings;
|
|
using adas_core.Domain.Models.MongoModels;
|
|
using adas_core.Domain.Models.Pumps;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace adas_core.Application.Services;
|
|
|
|
/// <summary>
|
|
/// Implements <see cref="IConfigUnitsService"/> to manage configuration units,
|
|
/// using <paramref name="configUnitsRepository"/> for data persistence,
|
|
/// <paramref name="apiSettings"/> for API configuration values,
|
|
/// and <paramref name="logger"/> for diagnostic logging.
|
|
/// </summary>
|
|
/// <!-- aidoc:v1 sig=cc78d0f -->
|
|
public class ConfigUnitsService(
|
|
IConfigUnitsRepository configUnitsRepository,
|
|
IOptions<ApiSettings> apiSettings,
|
|
ILogger<ConfigUnitsService> logger)
|
|
: IConfigUnitsService
|
|
{
|
|
private static ConfigUnits? _config;
|
|
private static DateTime _nextRefresh = DateTime.MinValue;
|
|
|
|
private readonly bool _configUnitsRequired = apiSettings.Value.ConfigUnitsRequired;
|
|
|
|
private readonly string _key = apiSettings.Value.ConfigUnitsKey ?? "PV1";
|
|
private readonly int? _refreshTimeout = apiSettings.Value.ConfigObservation?.Refresh;
|
|
|
|
/// <summary>
|
|
/// Maps a patient observation using the unit configuration when configuration units are required.
|
|
/// If configuration units are not required, the observation's units are not specified, or no matching configuration is found, the original observation is returned unchanged.
|
|
/// </summary>
|
|
/// <param name="obs">The patient observation to be mapped.</param>
|
|
/// <returns>The mapped observation when a matching unit configuration is resolved; otherwise, the original observation.</returns>
|
|
/// <!-- aidoc:v1 sig=2c52e52 body=2db877e -->
|
|
public async Task<T> Map<T>(T obs) where T : BasePatientObservation
|
|
{
|
|
if (!_configUnitsRequired) return obs;
|
|
|
|
var conf = !string.IsNullOrEmpty(obs.Units) ? await Get(obs.Units) : null;
|
|
|
|
return conf == null ? obs : MapConf(obs, conf);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Maps a <see cref="PumpObservation"/> by processing its pump value properties.
|
|
/// If configuration units are not required, the observation is returned unchanged; otherwise, the pump values are mapped via <c>MapPumpValues</c> and the observation is returned.
|
|
/// </summary>
|
|
/// <param name="obs">The <see cref="PumpObservation"/> to be mapped.</param>
|
|
/// <returns>The mapped <see cref="PumpObservation"/>, returned as-is when units configuration is not required or after pump value mapping otherwise.</returns>
|
|
/// <!-- aidoc:v1 sig=daf9ea9 body=c2e84ce -->
|
|
public async Task<PumpObservation> Map(PumpObservation obs)
|
|
{
|
|
if (!_configUnitsRequired) return obs;
|
|
|
|
//Para cada propiedad de la observación que sea del tipo PumpValue llama a GetByCodeSysAndCode(PumpValue)
|
|
await MapPumpValues(obs);
|
|
|
|
// var conf = !string.IsNullOrEmpty(obs.Units) ? await Get(obs.Units) : null;
|
|
// return conf == null ? obs : MapConf(obs, conf);
|
|
|
|
return obs;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recorre recursivamente las propiedades del objeto para encontrar y convertir PumpValues.
|
|
/// </summary>
|
|
/// <!-- aidoc:v1 sig=01209a6 body=958cccd -->
|
|
private async Task MapPumpValues(object? targetObject)
|
|
{
|
|
if (targetObject == null) return;
|
|
|
|
var properties = targetObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
foreach (var property in properties)
|
|
{
|
|
var propertyValue = property.GetValue(targetObject);
|
|
|
|
if (propertyValue == null) continue;
|
|
|
|
|
|
if (property.PropertyType == typeof(CommonPumpTypes.PumpValue))
|
|
{
|
|
var pumpValue = (CommonPumpTypes.PumpValue)propertyValue;
|
|
|
|
if (string.IsNullOrEmpty(pumpValue.Units)) continue;
|
|
|
|
var conf = await Get(pumpValue.Units);
|
|
if (conf != null) pumpValue.Units = conf.Value;
|
|
}
|
|
// En este caso Syringe es una clase que tienen un pumpValue
|
|
else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
|
|
{
|
|
// Llamada recursiva para inspeccionar las propiedades anidadas
|
|
await MapPumpValues(propertyValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Maps a configuration unit value onto a patient observation. If the observation is not a <see cref="PatientObservation"/>, the original observation is returned unchanged; otherwise, its <c>Units</c> property is assigned from the configuration unit item's value.
|
|
/// </summary>
|
|
/// <param name="obs">The base patient observation to which the configured unit value will be applied.</param>
|
|
/// <param name="conf">The configuration unit item whose <c>Value</c> is used as the unit to assign.</param>
|
|
/// <returns>The input observation, with <c>Units</c> set from <paramref name="conf"/> when applicable, or the unchanged observation when it is not a <see cref="PatientObservation"/>.</returns>
|
|
/// <!-- aidoc:v1 sig=ef286fc body=8674339 -->
|
|
private T MapConf<T>(T obs, ConfigUnitItem conf) where T : BasePatientObservation
|
|
{
|
|
if (obs is not PatientObservation pobs) return obs;
|
|
|
|
logger.LogDebug("Mapping config Unit obs: {obs} to units: {conf}", obs, conf.Value);
|
|
pobs.Units = conf.Value;
|
|
|
|
return obs;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves a configuration unit item by its unique code from the configuration store.
|
|
/// Returns null when the configuration cannot be loaded or when no item matches the specified code.
|
|
/// </summary>
|
|
/// <param name="code">The unique code identifier of the configuration unit item to look up.</param>
|
|
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="ConfigUnitItem"/>, or null if the configuration is unavailable or no item is found.</returns>
|
|
/// <!-- aidoc:v1 sig=b153e30 body=ca52840 -->
|
|
public async Task<ConfigUnitItem?> Get(string code)
|
|
{
|
|
var result = await GetConfig();
|
|
|
|
return result?.Items?.FirstOrDefault(i => i.Code == code);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the <see cref="ConfigUnits"/> configuration associated with the current key, using a time-based cache
|
|
/// to avoid repeated repository calls before the configured refresh timeout elapses. On any failure during the
|
|
/// repository lookup, the error is logged and <c>null</c> is returned.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A <see cref="Task{TResult}"/> containing the cached or freshly fetched <see cref="ConfigUnits"/>, or
|
|
/// <c>null</c> if the repository lookup fails.
|
|
/// </returns>
|
|
/// <!-- aidoc:v1 sig=982237a body=0843e00 -->
|
|
private async Task<ConfigUnits?> GetConfig()
|
|
{
|
|
try
|
|
{
|
|
if (_config != null && DateTime.Now <= _nextRefresh) return _config;
|
|
|
|
_config = await configUnitsRepository.FindById(_key);
|
|
_nextRefresh = _refreshTimeout.HasValue
|
|
? DateTime.Now.AddSeconds(_refreshTimeout.Value)
|
|
: DateTime.MinValue;
|
|
return _config;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "ERROR READ config_units: {_key}: {ex}", _key, ex.Message);
|
|
return null;
|
|
}
|
|
}
|
|
} |