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 |