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; /// /// Repository implementation for managing DisplayConfig entities in MongoDB. /// Provides CRUD operations and specialized queries for display configurations. /// public class DisplayConfigRepository : MongoRepository, IDisplayConfigRepository { private readonly ApiSettings _apiSettings; private readonly ILogger _logger; private readonly IUnitRepository _unitRepository; /// /// Initializes a new instance of the DisplayConfigRepository. /// /// The MongoDB database instance. /// API settings containing collection names configuration. /// Logger for repository operations. /// Repository for unit-related operations. public DisplayConfigRepository( IMongoDatabase database, ApiSettings apiSettings, ILogger logger, IUnitRepository unitRepository) : base(database) { _apiSettings = apiSettings; _logger = logger; _unitRepository = unitRepository; } /// /// Gets the name of the collection for display configurations. /// /// The collection name from API settings. public override string GetCollectionName() { return _apiSettings.DisplaysConfig; } /// /// Retrieves all display configurations from the database. /// /// A list of all DisplayConfig entities. public async Task> GetAll() { var result = await Collection.Find(Builders.Filter.Empty).ToListAsync(); return result; } /// /// Retrieves paginated display configurations with optional filtering. /// /// The pagination and filtering parameters. /// A fluent queryable for DisplayConfigSummary results. /// Thrown when the text filter exceeds 100 characters. public IFindFluent GetAllPaginated(PaginationFilter filter) { var filterBuilder = Builders.Filter; var sort = Builders.Sort.Ascending("hospital"); var filters = new List>(); 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> { 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); } /// /// Retrieves all display configurations of a specific type. /// /// The display type to filter by. /// A list of DisplayConfig entities matching the specified type. public async Task> GetByType(DisplayConfigEnums.DisplayType type) { var filter = Builders.Filter.Eq(p => p.Type, type); var result = await Collection.Find(filter).ToListAsync(); return result; } /// /// Retrieves a display configuration by its ID with related configurations populated. /// Performs aggregation to include CardConfig, DetailConfig, ChartConfig, and rotating layout data. /// /// The ObjectId of the display configuration to retrieve. /// The DisplayConfig with related data, or null if not found. public async Task GetById(ObjectId id) { try { var aggregate = Collection.Aggregate() .Match(Builders.Filter.Eq(p => p.Id, id)) // CardConfig .Lookup( _apiSettings.DisplayCardConfig, "cardConfigId", "_id", "cardConfig" ) .Unwind("cardConfig", new AggregateUnwindOptions { PreserveNullAndEmptyArrays = true }) // DetailConfig .Lookup( _apiSettings.DisplayDetailConfig, "detailConfigId", "_id", "detailConfig" ) .Unwind("detailConfig", new AggregateUnwindOptions { 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(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(new BsonDocument("$unset", "rotatingLayoutData")) .As(); return await aggregate.FirstOrDefaultAsync(); } catch (Exception ex) { _logger.LogError("Error on config display repository on GetById Exception: {ex}", ex.Message); return null; } } /// /// Retrieves the default display configuration for a given type. /// Default configurations are identified by having "Default" as the hospital name. /// /// The display type to search for. /// The default DisplayConfig for the specified type, or null if not found. public async Task GetDefault(DisplayConfigEnums.DisplayType type) { var filter = Builders.Filter.And( Builders.Filter.Eq(p => p.Type, type), Builders.Filter.Eq(p => p.Hospital, "Default") ); var result = await Collection.FindAsync(filter); return result.FirstOrDefault(); } /// /// Inserts a new display configuration and returns the inserted document. /// /// The DisplayConfig to insert. /// The inserted DisplayConfig, or null if insertion fails. /// public async Task InsertOneAsyncAndReturn(DisplayConfig config) { try { await Collection.InsertOneAsync(config); return config; } catch (Exception ex) { _logger.LogError(ex.Message); return null; } } /// /// Updates the smart display configuration for a specific display. /// /// The ObjectId of the display configuration to update. /// The new SmartDisplay configuration to apply. /// The updated SmartDisplay configuration, or null if update fails. public async Task UpdateSmartDisplay(ObjectId displayConfigId, SmartDisplay? newDisplayConfig) { try { if (newDisplayConfig == null) return null; var filter = Builders.Filter.Eq("Id", displayConfigId); var update = Builders.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 { ReturnDocument = ReturnDocument.After }); return c as SmartDisplay; } catch (Exception e) { _logger.LogError($"Error on config display repository on UpdateSmartDisplay Exception: {e.Message}"); return null; } } /// /// Updates the color configuration for a specific display. /// /// The ObjectId of the display configuration to update. /// The new ColorConfig to apply. /// True if the update was successful; otherwise, false. /// Throws an exception if MongoDB update fails. public async Task UpdateConfigColor(ObjectId objectIdConfigDisplay, ColorConfig colorConfig) { var filter = Builders.Filter.Eq("Id", objectIdConfigDisplay); var updateDefinition = new List>(); if (colorConfig.Level != null) updateDefinition.Add(Builders.Update.Set("colorConfig.level", colorConfig.Level)); if (colorConfig.Text != null) updateDefinition.Add(Builders.Update.Set("colorConfig.text", colorConfig.Text)); if (colorConfig.Arrow != null) updateDefinition.Add(Builders.Update.Set("colorConfig.arrow", colorConfig.Arrow)); if (colorConfig.Indicator != null) updateDefinition.Add(Builders.Update.Set("colorConfig.indicator", colorConfig.Indicator)); if (colorConfig.Graph != null) updateDefinition.Add(Builders.Update.Set("colorConfig.graph", colorConfig.Graph)); if (colorConfig.BoxNumber != null) updateDefinition.Add(Builders.Update.Set("colorConfig.boxNumber", colorConfig.BoxNumber)); if (colorConfig.BoxStatusColor != null) updateDefinition.Add( Builders.Update.Set("colorConfig.boxStatusColor", colorConfig.BoxStatusColor)); if (colorConfig.Test != null) updateDefinition.Add(Builders.Update.Set("colorConfig.test", colorConfig.Test)); if (colorConfig.Therapy != null) updateDefinition.Add(Builders.Update.Set("colorConfig.therapy", colorConfig.Therapy)); if (colorConfig.Procedure != null) updateDefinition.Add(Builders.Update.Set("colorConfig.procedure", colorConfig.Procedure)); var update = Builders.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; } } /// /// Updates the header configuration for a specific display. /// /// The ObjectId of the display configuration to update. /// The new HeaderConfig to apply. /// True if the update was successful; otherwise, false. /// Throws an exception if MongoDB update fails. public async Task UpdateHeaderConfig(ObjectId objectIdConfigDisplay, HeaderConfig headerConfig) { var filter = Builders.Filter.Eq("Id", objectIdConfigDisplay); var updateDefinition = new List>(); if (headerConfig.PartnerLogo != null) updateDefinition.Add( Builders.Update.Set("headerConfig.partnerLogo", headerConfig.PartnerLogo)); if (headerConfig.CompanyLogo != null) updateDefinition.Add( Builders.Update.Set("headerConfig.companyLogo", headerConfig.CompanyLogo)); if (headerConfig.CenterLogo != null) updateDefinition.Add( Builders.Update.Set("headerConfig.centerLogo", headerConfig.CenterLogo)); if (headerConfig.MeddisLogo != null) updateDefinition.Add( Builders.Update.Set("headerConfig.meddisLogo", headerConfig.MeddisLogo)); if (headerConfig.UnitName != null) updateDefinition.Add(Builders.Update.Set("headerConfig.unitName", headerConfig.UnitName)); if (headerConfig.Cameras != null) updateDefinition.Add(Builders.Update.Set("headerConfig.cameras", headerConfig.Cameras)); if (headerConfig.Sensors != null) updateDefinition.Add(Builders.Update.Set("headerConfig.sensors", headerConfig.Sensors)); if (headerConfig.Fullscreen != null) updateDefinition.Add( Builders.Update.Set("headerConfig.fullscreen", headerConfig.Fullscreen)); if (headerConfig.Sounds != null) updateDefinition.Add(Builders.Update.Set("headerConfig.sounds", headerConfig.Sounds)); if (headerConfig.Sidebar != null) updateDefinition.Add(Builders.Update.Set("headerConfig.sidebar", headerConfig.Sidebar)); if (headerConfig.CurrentDateTime != null) updateDefinition.Add(Builders.Update.Set("headerConfig.currentDateTime", headerConfig.CurrentDateTime)); if (headerConfig.SectionTitle != null) updateDefinition.Add( Builders.Update.Set("headerConfig.sectionTitle", headerConfig.SectionTitle)); var update = Builders.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; } } /// /// Updates the home banner configuration for a specific display. /// /// The ObjectId of the display configuration to update. /// The list of banner items to set. /// True if the update was successful; otherwise, false. /// Throws an exception if MongoDB update fails. public async Task UpdateSetHomeBanner(ObjectId objectIdConfigDisplay, List bannerItems) { var filter = Builders.Filter.Eq("Id", objectIdConfigDisplay); var update = Builders.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; } } /// /// Updates the base configuration for a specific display. /// /// The ObjectId of the display configuration to update. /// The base DisplayConfig with updated values. /// True if the update was successful; otherwise, false. /// Throws an exception if MongoDB update fails. public async Task UpdateBaseConfig(ObjectId objectIdConfigDisplay, DisplayConfig baseConfig) { var filter = Builders.Filter.Eq("Id", objectIdConfigDisplay); // TODO NO ESPERA EL CARDCONFIG var (updateDefinition, _) = GetBaseUpdateDefinition(baseConfig); var update = Builders.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; } } /// /// Updates the nurse display configuration with additional observation fields. /// /// The ObjectId of the display configuration to update. /// The new DisplayNurseDto configuration to apply. /// List of observation names to mark as "last". /// The updated DisplayNurse configuration, or null if update fails. public async Task UpdateDisplayNurse(ObjectId displayConfigId, DisplayNurseDto? newDisplayConfig, List nurseObs) { try { if (newDisplayConfig == null) return null; var filter = Builders.Filter.Eq("Id", displayConfigId); var updateDefinition = new List>(); if (newDisplayConfig.CardConfigId != null) updateDefinition.Add( Builders.Update.Set(c => c.CardConfigId, newDisplayConfig.CardConfigId)); if (newDisplayConfig.DetailConfigId != null) updateDefinition.Add( Builders.Update.Set(c => c.DetailConfigId, newDisplayConfig.DetailConfigId)); if (newDisplayConfig.HomeConfig != null) updateDefinition.Add( Builders.Update.Set(c => c.HomeConfig, newDisplayConfig.HomeConfig)); if (newDisplayConfig.Hospital != null) updateDefinition.Add(Builders.Update.Set(c => c.Hospital, newDisplayConfig.Hospital)); if (newDisplayConfig.HeaderConfig != null) updateDefinition.Add( Builders.Update.Set(c => c.HeaderConfig, newDisplayConfig.HeaderConfig)); if (newDisplayConfig.HomeBanner != null && newDisplayConfig.HomeBanner.Count != 0) updateDefinition.Add(Builders.Update.Set(c => ((DisplayNurse)c).HomeBanner, newDisplayConfig.HomeBanner)); if (newDisplayConfig.ColorConfig != null) updateDefinition.Add(Builders.Update.Set(c => ((DisplayNurse)c).ColorConfig, newDisplayConfig.ColorConfig)); if (newDisplayConfig.FormConfig != null) updateDefinition.Add(Builders.Update.Set(c => ((DisplayNurse)c).FormConfig, newDisplayConfig.FormConfig)); var originalList = ExtractObservationFields(newDisplayConfig, nurseObs); if (originalList.Count > 0) updateDefinition.Add( Builders.Update.Set(c => ((DisplayNurse)c).FieldList, originalList)); var update = Builders.Update.Combine(updateDefinition); var c = await Collection.FindOneAndUpdateAsync(filter, update, new FindOneAndUpdateOptions { 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; } } /// /// Updates the hospital name for a specific display configuration. /// /// The ObjectId of the display configuration to update. /// The new hospital name. /// True if the update was successful; otherwise, false. public async Task UpdateDisplayConfigHospitalName(ObjectId objectIdConfigDisplay, string name) { var filter = Builders.Filter.Eq(c => c.Id, objectIdConfigDisplay); var update = Builders.Update.Set(c => c.Hospital, name); var updatedConfig = await Collection.FindOneAndUpdateAsync( filter, update, new FindOneAndUpdateOptions { ReturnDocument = ReturnDocument.After } ); if (updatedConfig == null) { _logger.LogError("Error in UpdateDisplayConfigHospitalName: Unable to find and update DisplayConfig."); return false; } return true; } /// /// Updates the field list for a specific display configuration. /// /// The ObjectId of the display configuration to update. /// The new list of fields to set. /// True if the update was successful; otherwise, false. public async Task UpdateFieldList(ObjectId objectIdConfigDisplay, List fields) { var filter = Builders.Filter.Eq(x => x.Id, objectIdConfigDisplay); var update = Builders.Update.Set(x => x.FieldList, fields); var result = await Collection.UpdateOneAsync(filter, update); return result.ModifiedCount > 0; } /// /// Gets the default display configuration for a unit based on its ID and display type. /// /// The ObjectId of the unit. /// The type of display configuration to retrieve. /// The default DisplayConfig for the unit, or null if not found. public async Task 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; } } /// /// Deletes a display configuration by its ID. /// /// The ObjectId of the display configuration to delete. /// The deleted DisplayConfig, or null if not found. public async Task DeleteDisplayConfig(ObjectId objectIdConfigDisplay) { return await DeleteAsync(objectIdConfigDisplay); } /// /// Retrieves all display configurations in compact format (minimal response). /// /// A list of DisplayConfigMinimalResponse containing Id, Hospital, and Type. public Task> GetAllCompact() { var filter = Builders.Filter.Empty; return Collection .Find(filter) .Project(d => new DisplayConfigMinimalResponse { Id = d.Id, Hospital = d.Hospital ?? "", Type = d.Type }).ToListAsync(); } /// /// Retrieves all display configuration IDs associated with a specific card configuration. /// /// The ObjectId of the card configuration. /// A list of ObjectIds for display configurations using the specified card config. public async Task> GetAllByCardConfigId(ObjectId cardConfigId) { var filter = Builders.Filter.Eq(d => d.CardConfigId, cardConfigId); var result = await Collection.Find(filter) .Project(d => d.Id) .ToListAsync(); return result; } /// /// Retrieves all display configuration IDs associated with a card config, including rotating layouts. /// /// The ObjectId of the card configuration. /// A list of ObjectIds for display configurations using the specified card config in main or rotating layout. public async Task> GetAllByCardConfigIdAndRotating(ObjectId cardConfigId) { var mainFilter = Builders.Filter.Eq(d => d.CardConfigId, cardConfigId); var rotatingFilter = Builders.Filter.ElemMatch( "cardRotatingLayout", Builders.Filter.Eq("dataId", cardConfigId) ); var combinedFilter = Builders.Filter.Or(mainFilter, rotatingFilter); var result = await Collection.Find(combinedFilter) .Project(d => d.Id) .ToListAsync(); return result; } /// /// Updates the card configuration ID for a specific display. /// /// The ObjectId of the display configuration to update. /// The new card configuration ID to set. /// True if the update was successful; otherwise, false. public async Task UpdateCardConfigId(ObjectId? displayConfigId, ObjectId? resultId) { var filter = Builders.Filter.Eq(x => x.Id, displayConfigId); var update = Builders.Update.Set(x => x.CardConfigId, resultId); var result = await Collection.UpdateOneAsync(filter, update); return result.ModifiedCount > 0; } /// /// Adds a new chart configuration ID to a smart display's chart config list. /// /// The ObjectId of the display configuration to update. /// The new chart configuration ID to add. /// True if the update was successful; otherwise, false. public async Task AddChartId(ObjectId? displayConfigId, ObjectId newChartIdToAdd) { var filter = Builders.Filter.Eq(x => x.Id, displayConfigId); var update = Builders.Update .AddToSet(c => ((SmartDisplay)c).ChartConfigIdList, newChartIdToAdd); var result = await Collection.UpdateOneAsync(filter, update); return result.ModifiedCount > 0; } /// /// Removes a deleted chart configuration ID from all display configurations. /// /// The ObjectId of the chart configuration that was deleted. /// The MongoDB UpdateResult indicating the number of modified documents. /// Throws an exception if MongoDB operation fails. public async Task UpdateDeletedChartConfig(ObjectId deletedId) { try { var filter = Builders.Filter.AnyEq("chartConfigIdList", deletedId); var update = Builders.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; } } /// /// Gets a chart configuration by ID. This method is not implemented. /// /// The ObjectId of the chart configuration. /// Always throws NotImplementedException. /// This method is not implemented. public Task GetChartConfig(ObjectId objectIdConfigChart) { throw new NotImplementedException(); } /// /// Updates the detail configuration ID for a specific display. /// /// The ObjectId of the display configuration to update. /// The new detail configuration ID to set. /// True if the update was successful; otherwise, false. public async Task UpdateDetailConfigId(ObjectId? displayConfigId, ObjectId? resultId) { var filter = Builders.Filter.Eq(x => x.Id, displayConfigId); var update = Builders.Update.Set(x => x.DetailConfigId, resultId); var result = await Collection.UpdateOneAsync(filter, update); return result.ModifiedCount > 0; } /// /// Retrieves all display configuration IDs associated with a specific detail configuration. /// /// The ObjectId of the detail configuration. /// A list of ObjectIds for display configurations using the specified detail config. public async Task> GetAllByCardDetailConfigId(ObjectId baseConfigId) { var filter = Builders.Filter.Eq(d => d.DetailConfigId, baseConfigId); var result = await Collection.Find(filter) .Project(d => d.Id) .ToListAsync(); return result; } /// /// Performs initial data load by creating default configurations for each display type if they don't exist. /// /// /// This method runs on application startup to ensure default configurations exist for: /// - DisplayNurse /// - SmartDisplay /// - StandarDisplay /// public sealed override async Task InsertInitialLoad() { // 1. Obtener todos los valores y castear al tipo IEnumerable 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 } /// /// Extracts observation field names from a DisplayNurseDto configuration. /// /// The DisplayNurseDto configuration to extract fields from. /// List of observation names to mark as "last" priority. /// A list of Field objects with extracted observation names. private List ExtractObservationFields(DisplayNurseDto newDisplayConfig, List nurseObs) { var fieldSet = new HashSet(); 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 --- return fieldSet .Distinct() .Select(name => new Field { Name = name, Last = nurseObs.Contains(name) ? 1 : 2 }) .ToList(); } /// /// Fills a hash set with observation names extracted from JSON using regex. /// /// The JSON string to search for observation names. /// The regex pattern to match observation name arrays. /// The hash set to populate with field names. private void FillHashSet(string newDisplayConfig, Regex regex, HashSet 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); } } /// /// Recursively extracts observation names from cell configurations. /// /// The list of cells to extract from. /// The hash set to populate with field names. private void ExtractFromCells(List? cells, HashSet 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); } } /// /// Recursively extracts observation names from detail cell configurations. /// /// The list of detail cells to extract from. /// The hash set to populate with field names. private void ExtractFromDetailsCells(List? cells, HashSet 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); } } /// /// Creates a minimal fluent query for paginated display configs with optional filters. /// /// List of filter definitions to apply. /// Sort definition for the query results. /// A fluent queryable for DisplayConfigSummary. private IFindFluent CreateFindFluentMinimal( List> filters, SortDefinition sort) { var combinedFilter = filters.Any() ? Builders.Filter.And(filters) : Builders.Filter.Empty; return Collection .Find(combinedFilter) .Sort(sort) .Project(d => new DisplayConfigSummary { Id = d.Id, Name = d.Hospital ?? "", Type = d.Type }); } /// /// Generates update definitions from a base DisplayConfig. /// /// The base DisplayConfig with values to update. /// A tuple containing list of update definitions and list of field names. private (List> Updates, List Fields) GetBaseUpdateDefinition( DisplayConfig baseConfig) { var fieldList = new List(); var updateDefinition = new List>(); // if (baseConfig.CardConfig != null) // { // updateDefinition.Add(Builders.Update.Set(c => c.CardConfig, baseConfig.CardConfig)); // fieldList.AddRange(baseConfig.CardConfig.GetAllObservationNames()); // } if (baseConfig.DetailConfig != null) { updateDefinition.Add(Builders.Update.Set(c => c.DetailConfig, baseConfig.DetailConfig)); fieldList.AddRange(baseConfig.DetailConfig.GetAllObservationNames()); } if (baseConfig.HomeConfig != null) updateDefinition.Add(Builders.Update.Set(c => c.HomeConfig, baseConfig.HomeConfig)); if (baseConfig.Hospital != null) updateDefinition.Add(Builders.Update.Set(c => c.Hospital, baseConfig.Hospital)); if (baseConfig.HeaderConfig != null) updateDefinition.Add(Builders.Update.Set(c => c.HeaderConfig, baseConfig.HeaderConfig)); return (updateDefinition, fieldList); } } #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member