Files
adas-core/adas-core.Infrastructure/Repositories/DisplayConfigRepository.cs
T
2026-06-26 10:29:23 +02:00

977 lines
45 KiB
C#

using adas_core.Application.Exceptions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.DTO.Display;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.GroupedObservations;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Responses;
using adas_core.Domain.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
using Serilog;
using System.Text.RegularExpressions;
namespace adas_core.Infrastructure.Repositories;
/// <summary>
/// Repository implementation for managing DisplayConfig entities in MongoDB.
/// Provides CRUD operations and specialized queries for display configurations.
/// </summary>
public class DisplayConfigRepository : MongoRepository<DisplayConfig>, IDisplayConfigRepository
{
private readonly ApiSettings _apiSettings;
private readonly ILogger<DisplayConfigRepository> _logger;
private readonly IUnitRepository _unitRepository;
/// <summary>
/// Initializes a new instance of the DisplayConfigRepository.
/// </summary>
/// <param name="database">The MongoDB database instance.</param>
/// <param name="apiSettings">API settings containing collection names configuration.</param>
/// <param name="logger">Logger for repository operations.</param>
/// <param name="unitRepository">Repository for unit-related operations.</param>
public DisplayConfigRepository(
IMongoDatabase database,
ApiSettings apiSettings,
ILogger<DisplayConfigRepository> logger,
IUnitRepository unitRepository) : base(database)
{
_apiSettings = apiSettings;
_logger = logger;
_unitRepository = unitRepository;
}
/// <summary>
/// Gets the name of the collection for display configurations.
/// </summary>
/// <returns>The collection name from API settings.</returns>
public override string GetCollectionName()
{
return _apiSettings.DisplaysConfig;
}
/// <summary>
/// Retrieves all display configurations from the database.
/// </summary>
/// <returns>A list of all DisplayConfig entities.</returns>
public async Task<List<DisplayConfig>> GetAll()
{
var result = await Collection.Find(Builders<DisplayConfig>.Filter.Empty).ToListAsync();
return result;
}
/// <summary>
/// Retrieves paginated display configurations with optional filtering.
/// </summary>
/// <param name="filter">The pagination and filtering parameters.</param>
/// <returns>A fluent queryable for DisplayConfigSummary results.</returns>
/// <exception cref="BadRequestException">Thrown when the text filter exceeds 100 characters.</exception>
public IFindFluent<DisplayConfig, DisplayConfigSummary> GetAllPaginated(PaginationFilter filter)
{
var filterBuilder = Builders<DisplayConfig>.Filter;
var sort = Builders<DisplayConfig>.Sort.Ascending("hospital");
var filters = new List<FilterDefinition<DisplayConfig>>();
if (filter.FilteredRequest == null)
return CreateFindFluentMinimal(filters, sort);
if (!string.IsNullOrWhiteSpace(filter.FilteredRequest.Text))
{
var textFilter = filter.FilteredRequest.Text;
if (textFilter.Length > 100)
throw new BadRequestException("Text filter too long");
var textFilterEscaped = Regex.Escape(textFilter);
var orFilters = new List<FilterDefinition<DisplayConfig>>
{
filterBuilder.Regex(p => p.Hospital, new BsonRegularExpression(textFilterEscaped, "i"))
};
// búsqueda por Id solo si es válido
if (ObjectId.TryParse(textFilter, out var id))
{
orFilters.Add(filterBuilder.Eq("_id", id));
}
filters.Add(filterBuilder.Or(orFilters));
}
if (filter.FilteredRequest.DisplayType != null)
{
filters.Add(filterBuilder.Eq(d => d.Type, filter.FilteredRequest.DisplayType));
}
return CreateFindFluentMinimal(filters, sort);
}
/// <summary>
/// Retrieves all display configurations of a specific type.
/// </summary>
/// <param name="type">The display type to filter by.</param>
/// <returns>A list of DisplayConfig entities matching the specified type.</returns>
public async Task<List<DisplayConfig>> GetByType(DisplayConfigEnums.DisplayType type)
{
var filter = Builders<DisplayConfig>.Filter.Eq(p => p.Type, type);
var result = await Collection.Find(filter).ToListAsync();
return result;
}
/// <summary>
/// Retrieves a display configuration by its ID with related configurations populated.
/// Performs aggregation to include CardConfig, DetailConfig, ChartConfig, and rotating layout data.
/// </summary>
/// <param name="id">The ObjectId of the display configuration to retrieve.</param>
/// <returns>The DisplayConfig with related data, or null if not found.</returns>
public async Task<DisplayConfig?> GetById(ObjectId id)
{
try
{
var aggregate = Collection.Aggregate()
.Match(Builders<DisplayConfig>.Filter.Eq(p => p.Id, id))
// CardConfig
.Lookup(
_apiSettings.DisplayCardConfig,
"cardConfigId",
"_id",
"cardConfig"
)
.Unwind("cardConfig", new AggregateUnwindOptions<BsonDocument>
{
PreserveNullAndEmptyArrays = true
})
// DetailConfig
.Lookup(
_apiSettings.DisplayDetailConfig,
"detailConfigId",
"_id",
"detailConfig"
)
.Unwind("detailConfig", new AggregateUnwindOptions<BsonDocument>
{
PreserveNullAndEmptyArrays = true
})
// ChartConfig
.Lookup(
_apiSettings.DisplayChartConfig,
"chartConfigIdList",
"_id",
"chartConfig"
)
// Lookup para cardRotatingLayout.dataId
.Lookup(
_apiSettings.DisplayCardConfig,
"cardRotatingLayout.dataId",
"_id",
"rotatingLayoutData"
)
// Enriquecer cada elemento del array
.AppendStage<BsonDocument>(new BsonDocument("$addFields",
new BsonDocument("cardRotatingLayout",
new BsonDocument("$map",
new BsonDocument
{
{ "input", "$cardRotatingLayout" },
{ "as", "item" },
{
"in",
new BsonDocument("$mergeObjects", new BsonArray
{
"$$item",
new BsonDocument("data",
new BsonDocument("$arrayElemAt", new BsonArray
{
new BsonDocument("$filter", new BsonDocument
{
{ "input", "$rotatingLayoutData" },
{ "as", "d" },
{
"cond",
new BsonDocument("$eq", new BsonArray
{
"$$d._id",
"$$item.dataId"
})
}
}),
0
})
)
})
}
})
)
))
// limpiar auxiliar
.AppendStage<BsonDocument>(new BsonDocument("$unset", "rotatingLayoutData"))
.As<DisplayConfig>();
return await aggregate.FirstOrDefaultAsync();
}
catch (Exception ex)
{
_logger.LogError("Error on config display repository on GetById Exception: {ex}", ex.Message);
return null;
}
}
/// <summary>
/// Retrieves the default display configuration for a given type.
/// Default configurations are identified by having "Default" as the hospital name.
/// </summary>
/// <param name="type">The display type to search for.</param>
/// <returns>The default DisplayConfig for the specified type, or null if not found.</returns>
public async Task<DisplayConfig?> GetDefault(DisplayConfigEnums.DisplayType type)
{
var filter = Builders<DisplayConfig>.Filter.And(
Builders<DisplayConfig>.Filter.Eq(p => p.Type, type),
Builders<DisplayConfig>.Filter.Eq(p => p.Hospital, "Default")
);
var result = await Collection.FindAsync(filter);
return result.FirstOrDefault();
}
/// <summary>
/// Inserts a new display configuration and returns the inserted document.
/// </summary>
/// <param name="config">The DisplayConfig to insert.</param>
/// The inserted DisplayConfig, or null if insertion fails.
/// <returns></returns>
public async Task<DisplayConfig?> InsertOneAsyncAndReturn(DisplayConfig config)
{
try
{
await Collection.InsertOneAsync(config);
return config;
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return null;
}
}
/// <summary>
/// Updates the smart display configuration for a specific display.
/// </summary>
/// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
/// <param name="newDisplayConfig">The new SmartDisplay configuration to apply.</param>
/// <returns>The updated SmartDisplay configuration, or null if update fails.</returns>
public async Task<SmartDisplay?> UpdateSmartDisplay(ObjectId displayConfigId, SmartDisplay? newDisplayConfig)
{
try
{
if (newDisplayConfig == null) return null;
var filter = Builders<DisplayConfig>.Filter.Eq("Id", displayConfigId);
var update = Builders<DisplayConfig>.Update
.Set(c => ((SmartDisplay)c).Pumps, newDisplayConfig.Pumps)
.Set(c => ((SmartDisplay)c).HasCameras, newDisplayConfig.HasCameras)
.Set(c => ((SmartDisplay)c).HasSound, newDisplayConfig.HasSound)
.Set(c => ((SmartDisplay)c).CamerasAreActive, newDisplayConfig.CamerasAreActive)
.Set(c => ((SmartDisplay)c).IsRotationEnabled, newDisplayConfig.IsRotationEnabled)
.Set(c => ((SmartDisplay)c).CanChangeCameraMode, newDisplayConfig.CanChangeCameraMode)
.Set(c => ((SmartDisplay)c).FieldList, newDisplayConfig.FieldList)
.Set(c => ((SmartDisplay)c).ChartConfig, newDisplayConfig.ChartConfig)
.Set(c => ((SmartDisplay)c).GraphLayout, newDisplayConfig.GraphLayout)
.Set(c => ((SmartDisplay)c).SensorList, newDisplayConfig.SensorList)
.Set(c => ((SmartDisplay)c).AlarmFieldList, newDisplayConfig.AlarmFieldList)
.Set(c => c.HomeConfig, newDisplayConfig.HomeConfig)
.Set(c => c.DetailConfigId, newDisplayConfig.DetailConfigId)
.Set(c => ((SmartDisplay)c).CameraStreamType, newDisplayConfig.CameraStreamType)
.Set(c => ((SmartDisplay)c).ColorConfig, newDisplayConfig.ColorConfig)
.Set(c => ((SmartDisplay)c).RequestGroupedFieldList, newDisplayConfig.RequestGroupedFieldList)
.Set(c => c.Hospital, newDisplayConfig.Hospital)
// .Set(c => c.CardConfig, newDisplayConfig.CardConfig)
.Set(c => c.DisplaySectionIdList, newDisplayConfig.DisplaySectionIdList)
;
var c = await Collection.FindOneAndUpdateAsync(filter, update,
new FindOneAndUpdateOptions<DisplayConfig, DisplayConfig> { ReturnDocument = ReturnDocument.After });
return c as SmartDisplay;
}
catch (Exception e)
{
_logger.LogError($"Error on config display repository on UpdateSmartDisplay Exception: {e.Message}");
return null;
}
}
/// <summary>
/// Updates the color configuration for a specific display.
/// </summary>
/// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
/// <param name="colorConfig">The new ColorConfig to apply.</param>
/// <returns>True if the update was successful; otherwise, false.</returns>
/// <exception cref="Exception">Throws an exception if MongoDB update fails.</exception>
public async Task<bool> UpdateConfigColor(ObjectId objectIdConfigDisplay, ColorConfig colorConfig)
{
var filter = Builders<DisplayConfig>.Filter.Eq("Id", objectIdConfigDisplay);
var updateDefinition = new List<UpdateDefinition<DisplayConfig>>();
if (colorConfig.Level != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.level", colorConfig.Level));
if (colorConfig.Text != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.text", colorConfig.Text));
if (colorConfig.Arrow != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.arrow", colorConfig.Arrow));
if (colorConfig.Indicator != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.indicator", colorConfig.Indicator));
if (colorConfig.Graph != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.graph", colorConfig.Graph));
if (colorConfig.BoxNumber != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.boxNumber", colorConfig.BoxNumber));
if (colorConfig.BoxStatusColor != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set("colorConfig.boxStatusColor", colorConfig.BoxStatusColor));
if (colorConfig.Test != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.test", colorConfig.Test));
if (colorConfig.Therapy != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.therapy", colorConfig.Therapy));
if (colorConfig.Procedure != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.procedure", colorConfig.Procedure));
var update = Builders<DisplayConfig>.Update.Combine(updateDefinition);
try
{
var result = await Collection.UpdateOneAsync(filter, update);
if (result.ModifiedCount > 0) return true; // Retorna el objeto actualizado si la modificación fue exitosa
return false; // Retorna nulo si no se encontró el documento o no se modificó
}
catch (Exception ex)
{
Log.Error("Error UpdateConfigColor: {name}. Exception: {ex}", objectIdConfigDisplay, ex);
return false;
}
}
/// <summary>
/// Updates the header configuration for a specific display.
/// </summary>
/// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
/// <param name="headerConfig">The new HeaderConfig to apply.</param>
/// <returns>True if the update was successful; otherwise, false.</returns>
/// <exception cref="Exception">Throws an exception if MongoDB update fails.</exception>
public async Task<bool> UpdateHeaderConfig(ObjectId objectIdConfigDisplay, HeaderConfig headerConfig)
{
var filter = Builders<DisplayConfig>.Filter.Eq("Id", objectIdConfigDisplay);
var updateDefinition = new List<UpdateDefinition<DisplayConfig>>();
if (headerConfig.PartnerLogo != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set("headerConfig.partnerLogo", headerConfig.PartnerLogo));
if (headerConfig.CompanyLogo != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set("headerConfig.companyLogo", headerConfig.CompanyLogo));
if (headerConfig.CenterLogo != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set("headerConfig.centerLogo", headerConfig.CenterLogo));
if (headerConfig.MeddisLogo != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set("headerConfig.meddisLogo", headerConfig.MeddisLogo));
if (headerConfig.UnitName != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.unitName", headerConfig.UnitName));
if (headerConfig.Cameras != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.cameras", headerConfig.Cameras));
if (headerConfig.Sensors != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.sensors", headerConfig.Sensors));
if (headerConfig.Fullscreen != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set("headerConfig.fullscreen", headerConfig.Fullscreen));
if (headerConfig.Sounds != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.sounds", headerConfig.Sounds));
if (headerConfig.Sidebar != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.sidebar", headerConfig.Sidebar));
if (headerConfig.CurrentDateTime != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.currentDateTime",
headerConfig.CurrentDateTime));
if (headerConfig.SectionTitle != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set("headerConfig.sectionTitle", headerConfig.SectionTitle));
var update = Builders<DisplayConfig>.Update.Combine(updateDefinition);
try
{
var result = await Collection.UpdateOneAsync(filter, update);
if (result.ModifiedCount > 0) return true;
return false;
}
catch (Exception ex)
{
Log.Error("Error UpdateConfigColor: {name}. Exception: {ex}", objectIdConfigDisplay, ex);
return false;
}
}
/// <summary>
/// Updates the home banner configuration for a specific display.
/// </summary>
/// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
/// <param name="bannerItems">The list of banner items to set.</param>
/// <returns>True if the update was successful; otherwise, false.</returns>
/// <exception cref="Exception">Throws an exception if MongoDB update fails.</exception>
public async Task<bool> UpdateSetHomeBanner(ObjectId objectIdConfigDisplay, List<BannerItem> bannerItems)
{
var filter = Builders<DisplayConfig>.Filter.Eq("Id", objectIdConfigDisplay);
var update = Builders<DisplayConfig>.Update
.Set(c => ((DisplayNurse)c).HomeBanner, bannerItems);
try
{
var result = await Collection.UpdateOneAsync(filter, update);
if (result.ModifiedCount > 0) return true; // Retorna el objeto actualizado si la modificación fue exitosa
return false; // Retorna nulo si no se encontró el documento o no se modificó
}
catch (Exception ex)
{
Log.Error("Error UpdateConfigColor: {name}. Exception: {ex}", objectIdConfigDisplay, ex);
return false;
}
}
/// <summary>
/// Updates the base configuration for a specific display.
/// </summary>
/// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
/// <param name="baseConfig">The base DisplayConfig with updated values.</param>
/// <returns>True if the update was successful; otherwise, false.</returns>
/// <exception cref="Exception">Throws an exception if MongoDB update fails.</exception>
public async Task<bool> UpdateBaseConfig(ObjectId objectIdConfigDisplay, DisplayConfig baseConfig)
{
var filter = Builders<DisplayConfig>.Filter.Eq("Id", objectIdConfigDisplay);
// TODO NO ESPERA EL CARDCONFIG
var (updateDefinition, _) = GetBaseUpdateDefinition(baseConfig);
var update = Builders<DisplayConfig>.Update.Combine(updateDefinition);
try
{
var result = await Collection.UpdateOneAsync(filter, update);
if (result.ModifiedCount > 0)
// await UpdateFieldList(objectIdConfigDisplay, GenerateFieldListFromStrig(fieldList));
return true; // Retorna el objeto actualizado si la modificación fue exitosa
return false; // Retorna nulo si no se encontró el documento o no se modificó
}
catch (Exception ex)
{
Log.Error("Error UpdateBaseConfig: {name}. Exception: {ex}", objectIdConfigDisplay, ex);
return false;
}
}
/// <summary>
/// Updates the nurse display configuration with additional observation fields.
/// </summary>
/// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
/// <param name="newDisplayConfig">The new DisplayNurseDto configuration to apply.</param>
/// <param name="nurseObs">List of observation names to mark as "last".</param>
/// <returns>The updated DisplayNurse configuration, or null if update fails.</returns>
public async Task<DisplayNurse?> UpdateDisplayNurse(ObjectId displayConfigId, DisplayNurseDto? newDisplayConfig,
List<string> nurseObs)
{
try
{
if (newDisplayConfig == null) return null;
var filter = Builders<DisplayConfig>.Filter.Eq("Id", displayConfigId);
var updateDefinition = new List<UpdateDefinition<DisplayConfig>>();
if (newDisplayConfig.CardConfigId != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set(c => c.CardConfigId, newDisplayConfig.CardConfigId));
if (newDisplayConfig.DetailConfigId != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set(c => c.DetailConfigId, newDisplayConfig.DetailConfigId));
if (newDisplayConfig.HomeConfig != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set(c => c.HomeConfig, newDisplayConfig.HomeConfig));
if (newDisplayConfig.Hospital != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.Hospital, newDisplayConfig.Hospital));
if (newDisplayConfig.HeaderConfig != null)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set(c => c.HeaderConfig, newDisplayConfig.HeaderConfig));
if (newDisplayConfig.HomeBanner != null && newDisplayConfig.HomeBanner.Count != 0)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => ((DisplayNurse)c).HomeBanner,
newDisplayConfig.HomeBanner));
if (newDisplayConfig.ColorConfig != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => ((DisplayNurse)c).ColorConfig,
newDisplayConfig.ColorConfig));
if (newDisplayConfig.FormConfig != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => ((DisplayNurse)c).FormConfig,
newDisplayConfig.FormConfig));
var originalList = ExtractObservationFields(newDisplayConfig, nurseObs);
if (originalList.Count > 0)
updateDefinition.Add(
Builders<DisplayConfig>.Update.Set(c => ((DisplayNurse)c).FieldList, originalList));
var update = Builders<DisplayConfig>.Update.Combine(updateDefinition);
var c = await Collection.FindOneAndUpdateAsync(filter, update,
new FindOneAndUpdateOptions<DisplayConfig, DisplayConfig> { ReturnDocument = ReturnDocument.After });
if (c == null)
{
_logger.LogError("Error on config display repository on UpdateDisplayNurse unable to FindOneAndUpdate");
return null;
}
return c as DisplayNurse;
}
catch (Exception e)
{
_logger.LogError("Error on config display repository on UpdateDisplayNurse Exception: {eMessage}",
e.Message);
return null;
}
}
/// <summary>
/// Updates the hospital name for a specific display configuration.
/// </summary>
/// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
/// <param name="name">The new hospital name.</param>
/// <returns>True if the update was successful; otherwise, false.</returns>
public async Task<bool> UpdateDisplayConfigHospitalName(ObjectId objectIdConfigDisplay, string name)
{
var filter = Builders<DisplayConfig>.Filter.Eq(c => c.Id, objectIdConfigDisplay);
var update = Builders<DisplayConfig>.Update.Set(c => c.Hospital, name);
var updatedConfig = await Collection.FindOneAndUpdateAsync(
filter,
update,
new FindOneAndUpdateOptions<DisplayConfig> { ReturnDocument = ReturnDocument.After }
);
if (updatedConfig == null)
{
_logger.LogError("Error in UpdateDisplayConfigHospitalName: Unable to find and update DisplayConfig.");
return false;
}
return true;
}
/// <summary>
/// Updates the field list for a specific display configuration.
/// </summary>
/// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
/// <param name="fields">The new list of fields to set.</param>
/// <returns>True if the update was successful; otherwise, false.</returns>
public async Task<bool> UpdateFieldList(ObjectId objectIdConfigDisplay, List<Field> fields)
{
var filter = Builders<DisplayConfig>.Filter.Eq(x => x.Id, objectIdConfigDisplay);
var update = Builders<DisplayConfig>.Update.Set(x => x.FieldList, fields);
var result = await Collection.UpdateOneAsync(filter, update);
return result.ModifiedCount > 0;
}
/// <summary>
/// Gets the default display configuration for a unit based on its ID and display type.
/// </summary>
/// <param name="unitId">The ObjectId of the unit.</param>
/// <param name="displayType">The type of display configuration to retrieve.</param>
/// <returns>The default DisplayConfig for the unit, or null if not found.</returns>
public async Task<DisplayConfig?> GetDefaultByUnitIdAndType(ObjectId unitId,
DisplayConfigEnums.DisplayType displayType)
{
var unit = await _unitRepository.FindById(unitId);
switch (displayType)
{
case DisplayConfigEnums.DisplayType.DisplayNurse:
if (unit?.Configuration.PlanDisplayConfiguration == null) return null;
return await GetById(unit.Configuration.PlanDisplayConfiguration.Value) as DisplayNurse;
case DisplayConfigEnums.DisplayType.SmartDisplay:
if (unit?.Configuration.SmartDisplayConfiguration == null) return null;
return await GetById(unit.Configuration.SmartDisplayConfiguration.Value) as SmartDisplay;
case DisplayConfigEnums.DisplayType.StandarDisplay:
if (unit?.Configuration.StandarDisplayConfiguration == null) return null;
return await GetById(unit.Configuration.StandarDisplayConfiguration.Value) as StandarDisplay;
default: return null;
}
}
/// <summary>
/// Deletes a display configuration by its ID.
/// </summary>
/// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to delete.</param>
/// <returns>The deleted DisplayConfig, or null if not found.</returns>
public async Task<DisplayConfig?> DeleteDisplayConfig(ObjectId objectIdConfigDisplay)
{
return await DeleteAsync(objectIdConfigDisplay);
}
/// <summary>
/// Retrieves all display configurations in compact format (minimal response).
/// </summary>
/// <returns>A list of DisplayConfigMinimalResponse containing Id, Hospital, and Type.</returns>
public Task<List<DisplayConfigMinimalResponse>> GetAllCompact()
{
var filter = Builders<DisplayConfig>.Filter.Empty;
return Collection
.Find(filter)
.Project(d => new DisplayConfigMinimalResponse
{
Id = d.Id,
Hospital = d.Hospital ?? "",
Type = d.Type
}).ToListAsync();
}
/// <summary>
/// Retrieves all display configuration IDs associated with a specific card configuration.
/// </summary>
/// <param name="cardConfigId">The ObjectId of the card configuration.</param>
/// <returns>A list of ObjectIds for display configurations using the specified card config.</returns>
public async Task<List<ObjectId>> GetAllByCardConfigId(ObjectId cardConfigId)
{
var filter = Builders<DisplayConfig>.Filter.Eq(d => d.CardConfigId, cardConfigId);
var result = await Collection.Find(filter)
.Project(d => d.Id)
.ToListAsync();
return result;
}
/// <summary>
/// Retrieves all display configuration IDs associated with a card config, including rotating layouts.
/// </summary>
/// <param name="cardConfigId">The ObjectId of the card configuration.</param>
/// <returns>A list of ObjectIds for display configurations using the specified card config in main or rotating layout.</returns>
public async Task<List<ObjectId>> GetAllByCardConfigIdAndRotating(ObjectId cardConfigId)
{
var mainFilter = Builders<DisplayConfig>.Filter.Eq(d => d.CardConfigId, cardConfigId);
var rotatingFilter = Builders<DisplayConfig>.Filter.ElemMatch(
"cardRotatingLayout",
Builders<BsonDocument>.Filter.Eq("dataId", cardConfigId)
);
var combinedFilter = Builders<DisplayConfig>.Filter.Or(mainFilter, rotatingFilter);
var result = await Collection.Find(combinedFilter)
.Project(d => d.Id)
.ToListAsync();
return result;
}
/// <summary>
/// Updates the card configuration ID for a specific display.
/// </summary>
/// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
/// <param name="resultId">The new card configuration ID to set.</param>
/// <returns>True if the update was successful; otherwise, false.</returns>
public async Task<bool> UpdateCardConfigId(ObjectId? displayConfigId, ObjectId? resultId)
{
var filter = Builders<DisplayConfig>.Filter.Eq(x => x.Id, displayConfigId);
var update = Builders<DisplayConfig>.Update.Set(x => x.CardConfigId, resultId);
var result = await Collection.UpdateOneAsync(filter, update);
return result.ModifiedCount > 0;
}
/// <summary>
/// Adds a new chart configuration ID to a smart display's chart config list.
/// </summary>
/// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
/// <param name="newChartIdToAdd">The new chart configuration ID to add.</param>
/// <returns>True if the update was successful; otherwise, false.</returns>
public async Task<bool> AddChartId(ObjectId? displayConfigId, ObjectId newChartIdToAdd)
{
var filter = Builders<DisplayConfig>.Filter.Eq(x => x.Id, displayConfigId);
var update = Builders<DisplayConfig>.Update
.AddToSet(c => ((SmartDisplay)c).ChartConfigIdList, newChartIdToAdd);
var result = await Collection.UpdateOneAsync(filter, update);
return result.ModifiedCount > 0;
}
/// <summary>
/// Removes a deleted chart configuration ID from all display configurations.
/// </summary>
/// <param name="deletedId">The ObjectId of the chart configuration that was deleted.</param>
/// <returns>The MongoDB UpdateResult indicating the number of modified documents.</returns>
/// <exception cref="Exception">Throws an exception if MongoDB operation fails.</exception>
public async Task<UpdateResult> UpdateDeletedChartConfig(ObjectId deletedId)
{
try
{
var filter = Builders<DisplayConfig>.Filter.AnyEq("chartConfigIdList", deletedId);
var update = Builders<DisplayConfig>.Update.Pull("chartConfigIdList", deletedId);
return await Collection.UpdateManyAsync(filter, update);
}
catch (Exception ex)
{
_logger.LogError("Error actualizando referencias de ChartConfig borrado: {ex}", ex.Message);
throw;
}
}
/// <summary>
/// Gets a chart configuration by ID. This method is not implemented.
/// </summary>
/// <param name="objectIdConfigChart">The ObjectId of the chart configuration.</param>
/// <returns>Always throws NotImplementedException.</returns>
/// <exception cref="NotImplementedException">This method is not implemented.</exception>
public Task<ChartConfig> GetChartConfig(ObjectId objectIdConfigChart)
{
throw new NotImplementedException();
}
/// <summary>
/// Updates the detail configuration ID for a specific display.
/// </summary>
/// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
/// <param name="resultId">The new detail configuration ID to set.</param>
/// <returns>True if the update was successful; otherwise, false.</returns>
public async Task<bool> UpdateDetailConfigId(ObjectId? displayConfigId, ObjectId? resultId)
{
var filter = Builders<DisplayConfig>.Filter.Eq(x => x.Id, displayConfigId);
var update = Builders<DisplayConfig>.Update.Set(x => x.DetailConfigId, resultId);
var result = await Collection.UpdateOneAsync(filter, update);
return result.ModifiedCount > 0;
}
/// <summary>
/// Retrieves all display configuration IDs associated with a specific detail configuration.
/// </summary>
/// <param name="baseConfigId">The ObjectId of the detail configuration.</param>
/// <returns>A list of ObjectIds for display configurations using the specified detail config.</returns>
public async Task<List<ObjectId>> GetAllByCardDetailConfigId(ObjectId baseConfigId)
{
var filter = Builders<DisplayConfig>.Filter.Eq(d => d.DetailConfigId, baseConfigId);
var result = await Collection.Find(filter)
.Project(d => d.Id)
.ToListAsync();
return result;
}
/// <summary>
/// Performs initial data load by creating default configurations for each display type if they don't exist.
/// </summary>
/// <remarks>
/// This method runs on application startup to ensure default configurations exist for:
/// - DisplayNurse
/// - SmartDisplay
/// - StandarDisplay
/// </remarks>
public sealed override async Task InsertInitialLoad()
{
// 1. Obtener todos los valores y castear al tipo IEnumerable<DisplayType>
var allTypes = (DisplayConfigEnums.DisplayType[])Enum.GetValues(typeof(DisplayConfigEnums.DisplayType));
// 2. Usar LINQ para filtrar y convertir de nuevo a un array (o lista)
var displayTypesToIterate = allTypes
.Where(dt => dt != DisplayConfigEnums.DisplayType.Unknown) // Filtra el valor 'Unknown'
.ToArray();
foreach (var type in displayTypesToIterate)
{
var defaultConfigByType = await GetDefault(type);
if (defaultConfigByType == null)
switch (type)
{
case DisplayConfigEnums.DisplayType.DisplayNurse:
var nurse = new DisplayNurse
{
Hospital = "Default",
Type = DisplayConfigEnums.DisplayType.DisplayNurse,
ColorConfig = new ColorConfig(),
FormConfig = new FormConfig
{
Admission = new FormItemOverview { Nhc = true },
Demographic = new FormItemOverview { Nhc = true },
Discharge = new FormItemOverview { Nhc = true },
IncomeInfo = new FormItemOverview { Nhc = true }
},
HomeBanner = []
};
await InsertOneAsyncAndReturn(nurse);
break;
case DisplayConfigEnums.DisplayType.SmartDisplay:
var smart = new SmartDisplay
{
Hospital = "Default",
Type = DisplayConfigEnums.DisplayType.SmartDisplay,
ColorConfig = new ColorConfig()
};
await InsertOneAsyncAndReturn(smart);
break;
case DisplayConfigEnums.DisplayType.StandarDisplay:
var standar = new StandarDisplay
{
Type = DisplayConfigEnums.DisplayType.StandarDisplay,
Hospital = "Default"
};
await InsertOneAsyncAndReturn(standar);
break;
}
}
// Si alguno no existe crearlos
}
/// <summary>
/// Extracts observation field names from a DisplayNurseDto configuration.
/// </summary>
/// <param name="newDisplayConfig">The DisplayNurseDto configuration to extract fields from.</param>
/// <param name="nurseObs">List of observation names to mark as "last" priority.</param>
/// <returns>A list of Field objects with extracted observation names.</returns>
private List<Field> ExtractObservationFields(DisplayNurseDto newDisplayConfig, List<string> nurseObs)
{
var fieldSet = new HashSet<string>();
var regex = new Regex(@"""ManualObservationName""\s*:\s*\[\s*((?:""[^""]*""\s*,?\s*)+)\]",
RegexOptions.Compiled);
// --- 1. CardConfig ---
if (newDisplayConfig.CardConfig?.Rows is { Count: > 0 })
FillHashSet(JsonConvert.SerializeObject(newDisplayConfig.CardConfig.Rows), regex, fieldSet);
if (newDisplayConfig.CardConfig?.Rows is { Count: > 0 })
foreach (var row in newDisplayConfig.CardConfig.Rows)
ExtractFromCells(row.Cells, fieldSet);
// --- 2. DetailConfig ---
if (newDisplayConfig.DetailConfig?.NurseRows is { Count: > 0 })
FillHashSet(JsonConvert.SerializeObject(newDisplayConfig.DetailConfig?.NurseRows), regex, fieldSet);
if (newDisplayConfig.DetailConfig?.NurseRows is { Count: > 0 })
foreach (var row in newDisplayConfig.DetailConfig.NurseRows)
ExtractFromDetailsCells(row.Cells, fieldSet);
// --- Result: convert to List<Field> ---
return fieldSet
.Distinct()
.Select(name => new Field { Name = name, Last = nurseObs.Contains(name) ? 1 : 2 })
.ToList();
}
/// <summary>
/// Fills a hash set with observation names extracted from JSON using regex.
/// </summary>
/// <param name="newDisplayConfig">The JSON string to search for observation names.</param>
/// <param name="regex">The regex pattern to match observation name arrays.</param>
/// <param name="fieldSet">The hash set to populate with field names.</param>
private void FillHashSet(string newDisplayConfig, Regex regex, HashSet<string> fieldSet)
{
var matches = regex.Matches(newDisplayConfig);
foreach (Match match in matches)
if (match.Groups.Count > 1)
{
var arrayContent = match.Groups[1].Value;
var items = Regex.Matches(arrayContent, @"""([^""]+)""");
foreach (Match item in items) fieldSet.Add(item.Groups[1].Value);
}
}
/// <summary>
/// Recursively extracts observation names from cell configurations.
/// </summary>
/// <param name="cells">The list of cells to extract from.</param>
/// <param name="fieldSet">The hash set to populate with field names.</param>
private void ExtractFromCells(List<Cell>? cells, HashSet<string> fieldSet)
{
if (cells is null)
return;
foreach (var cell in cells)
{
// 1. Extraer ObservationName
if (cell.ObservationName is { Count: > 0 })
foreach (var obsName in cell.ObservationName)
fieldSet.Add(obsName);
// 2. Recursión: sub-observaciones
if (cell.SubObs is { Count: > 0 })
ExtractFromCells(cell.SubObs, fieldSet);
}
}
/// <summary>
/// Recursively extracts observation names from detail cell configurations.
/// </summary>
/// <param name="cells">The list of detail cells to extract from.</param>
/// <param name="fieldSet">The hash set to populate with field names.</param>
private void ExtractFromDetailsCells(List<CellDetails>? cells, HashSet<string> fieldSet)
{
if (cells is null)
return;
foreach (var cell in cells)
{
// 1. Extraer ObservationName
if (cell.ObservationName is { Count: > 0 })
foreach (var obsName in cell.ObservationName)
fieldSet.Add(obsName);
// 2. Recursión: sub-observaciones
if (cell.Cells is { Count: > 0 })
ExtractFromDetailsCells(cell.Cells, fieldSet);
}
}
/// <summary>
/// Creates a minimal fluent query for paginated display configs with optional filters.
/// </summary>
/// <param name="filters">List of filter definitions to apply.</param>
/// <param name="sort">Sort definition for the query results.</param>
/// <returns>A fluent queryable for DisplayConfigSummary.</returns>
private IFindFluent<DisplayConfig, DisplayConfigSummary> CreateFindFluentMinimal(
List<FilterDefinition<DisplayConfig>> filters,
SortDefinition<DisplayConfig> sort)
{
var combinedFilter = filters.Any()
? Builders<DisplayConfig>.Filter.And(filters)
: Builders<DisplayConfig>.Filter.Empty;
return Collection
.Find(combinedFilter)
.Sort(sort)
.Project(d => new DisplayConfigSummary
{
Id = d.Id,
Name = d.Hospital ?? "",
Type = d.Type
});
}
/// <summary>
/// Generates update definitions from a base DisplayConfig.
/// </summary>
/// <param name="baseConfig">The base DisplayConfig with values to update.</param>
/// <returns>A tuple containing list of update definitions and list of field names.</returns>
private (List<UpdateDefinition<DisplayConfig>> Updates, List<string> Fields) GetBaseUpdateDefinition(
DisplayConfig baseConfig)
{
var fieldList = new List<string>();
var updateDefinition = new List<UpdateDefinition<DisplayConfig>>();
// if (baseConfig.CardConfig != null)
// {
// updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.CardConfig, baseConfig.CardConfig));
// fieldList.AddRange(baseConfig.CardConfig.GetAllObservationNames());
// }
if (baseConfig.DetailConfig != null)
{
updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.DetailConfig, baseConfig.DetailConfig));
fieldList.AddRange(baseConfig.DetailConfig.GetAllObservationNames());
}
if (baseConfig.HomeConfig != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.HomeConfig, baseConfig.HomeConfig));
if (baseConfig.Hospital != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.Hospital, baseConfig.Hospital));
if (baseConfig.HeaderConfig != null)
updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.HeaderConfig, baseConfig.HeaderConfig));
return (updateDefinition, fieldList);
}
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member