using adas_core.Application.Services.Interfaces; using adas_core.Domain.Enums; using adas_core.Domain.Models; using adas_core.Domain.Models.AppSettings; using adas_core.Domain.Models.Pumps; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MongoDB.Bson; namespace adas_core.Application.Customizations.H12O.UCIN; public class CalculatedObservations : ICalculatedObservations { private readonly List _complexityObservations = ["Respiratory", "IntravenousLines", "Medication", "Surgery", "Weight_Newborn", "Weight_Current", "Monitor"]; private readonly List _ecmo = []; private readonly List _electroencephalogram = []; private readonly List _highFrequencyVentilation = []; //private readonly List _shift = []; private readonly Tuple>[] _intraVenousLinesTypeValue = [ Tuple.Create(IntraVenousLineTypes.Artery, 5, new List { "Catéter arterial", "Catéter UMBILICAL arteria" }), Tuple.Create(IntraVenousLineTypes.CentralVein, 4, new List { "Catéter venoso CENTRAL", "Catéter UMBILICAL vena" }), Tuple.Create(IntraVenousLineTypes.Picc, 3, new List { "Catéter PICC" }), Tuple.Create(IntraVenousLineTypes.MiddleLine, 2, new List { "Catéter línea media", "Catéter EPICUTÁNEO PERIFÉRICO" }), Tuple.Create(IntraVenousLineTypes.Peripheral, 1, new List { "Catéter Venoso PERIFÉRICO" }) ]; private readonly List _invasiveVentilation = []; private readonly ILogger _logger; private readonly List _medicationBolus = []; //private readonly Lazy medicineService; private readonly IMedicineService _medicineService; private readonly List _nonInvasiveVentilation = []; //private readonly List Oni = new List(); private readonly List _notesIndicatingMedication = []; private readonly Lazy _observationService; private readonly List _oniCodes = []; private readonly List _regionalBrainSaturation = []; private readonly List _respiratory = []; private readonly Tuple>[] _respiratoryTypeValue = [ // Tuple.Create(RespiratoryTypes.INO, 10, new List{ ""}), Tuple.Create(RespiratoryTypes.Vafo, 5, new List { "V.A.F.O." }), Tuple.Create(RespiratoryTypes.Vmc, 3, new List { "V.M.C." }), Tuple.Create(RespiratoryTypes.Vmni, 2, new List { "V.N.I. Ciclada", "CPAP" }), Tuple.Create(RespiratoryTypes.NasalCannulas, 1, new List { "Alto Flujo", "Bajo Flujo" }), Tuple.Create(RespiratoryTypes.None, 0, new List { "Sin assistance respiratoria" }) ]; private readonly List _rxaStatus = ["Concluido"]; private readonly List _surgery = []; private readonly List _surgeryText = []; private readonly List _transcutanous = []; private readonly Lazy _treatmentService; public CalculatedObservations(IServiceProvider serviceProvider) { _observationService = serviceProvider.GetRequiredService>(); //observationService; _medicineService = serviceProvider.GetRequiredService(); //medicineService; _treatmentService = serviceProvider.GetRequiredService>(); //treatmentService; var apiSettings = serviceProvider.GetRequiredService>(); //apiSettings; _logger = serviceProvider.GetRequiredService>(); var electroencephalogram = apiSettings.Value.Electroencephalogram ?? null; electroencephalogram?.ForEach(x => _electroencephalogram.Add(x.Trim())); var medicationBolus = apiSettings.Value.MedicationBolus ?? null; medicationBolus?.ForEach(x => _medicationBolus.Add(x.Trim())); var transcutaneous = apiSettings.Value.Transcutaneous ?? null; transcutaneous?.ForEach(x => _transcutanous.Add(x.Trim())); var regionalBrainSaturation = apiSettings.Value.RegionalBrainSaturation ?? null; regionalBrainSaturation?.ForEach(x => _regionalBrainSaturation.Add(x.Trim())); var surgery = apiSettings.Value.Surgery ?? null; surgery?.ForEach(x => _surgery.Add(x.Trim())); var surgeryText = apiSettings.Value.SurgeryText ?? null; surgeryText?.ForEach(x => _surgeryText.Add(x.Trim())); var respiratory = apiSettings.Value.Respiratory ?? null; respiratory?.ForEach(x => _respiratory.Add(x.Trim())); //inoCode = Respiratory.LastOrDefault(); var oni = apiSettings.Value.Oni ?? null; oni?.ForEach(x => _oniCodes.Add(x.Trim())); var highFrequencyVentilation = apiSettings.Value.HighFrequencyVentilation ?? null; highFrequencyVentilation?.ForEach(x => _highFrequencyVentilation.Add(x.Trim())); var invasiveVentilation = apiSettings.Value.InvasiveVentilation ?? null; invasiveVentilation?.ForEach(x => _invasiveVentilation.Add(x.Trim())); var nonInvasiveVentilation = apiSettings.Value.NonInvasiveVentilation ?? null; nonInvasiveVentilation?.ForEach(x => _nonInvasiveVentilation.Add(x.Trim())); //var shift = apiSettings.Value.Shift ?? null; //shift?.ForEach(x => _shift.Add(x.Trim())); var notesIndicatingMedication = apiSettings.Value.NotesIndicatingMedication ?? null; notesIndicatingMedication?.ForEach(x => _notesIndicatingMedication.Add(x.Trim())); var ecmo = apiSettings.Value.Ecmo ?? null; ecmo?.ForEach(x => _ecmo.Add(x.Trim())); } public async Task Map(T obs, bool onlyByName) where T : BasePatientObservation { if (string.IsNullOrEmpty(obs.Name)) return obs; //Llega Vías o algo en Vías dictionary //Solo tenemos en cuenta las observaciones de ICA if (!string.IsNullOrEmpty(obs.Code) && _respiratory.Contains(obs.Code) && obs.CodingSystem == "SNM") { _logger.LogDebug("Mapping observation Respiratorio {obs}", obs); await CalculateAsistResp(obs); } if (obs.Name is "AirPressure_Mean" or "FiO2" or "PaO2") await CalculateOxygenationIndex(obs); //Solo tenemos en cuenta las observaciones de Central if (obs.Name == "Resp_Mode") { _logger.LogDebug("Mapping Resp mode observation {obs}", obs); await CalculateVentilationMode(obs); } if (obs.Name is "TAm" or "Age_Gestational_Fixed" or "Age_Gestational") await CalculateTAmAlert(obs); if (obs.Name is "Weight_Newborn" or "Weight_Current") if (obs is PatientObservation { Expired: false }) obs = (T)await ParseWeight(obs); if (obs is { Name: "Temp_Incubator" } or { Name: "Temp_Patient" }) await CalculateTempGradient(obs); if (obs is { Name: "IntravenousLinesObs" }) { var intraObs = await CalculateIntravenousLineObservation(obs); if (intraObs != null) obs = (T)intraObs; } if (obs.Name is "pCO2tc" or "Transcutaneous_O2") if (obs.Code != null && _regionalBrainSaturation.Contains(obs.Code)) await CalculateMonitor(obs); if (obs.Name == "Surgery") await CheckSurgeryExpired(obs); if (obs.Name != null && _complexityObservations.Contains(obs.Name)) { _logger.LogDebug("Mapping observation Complexity {obs}", obs); await CalculateComplexity(obs); } if (obs.Name != null && (obs.Name.Equals("Diuresis_Hour") || obs.Name.Equals("Drainages_Hour") || obs.Name.Equals("Hydric_Balance") || obs.Name.Equals("IV_Entries_Hour") || obs.Name.Equals("Enteral_Entries_Hour"))) obs = (T)await CheckObsExistsAndIncrementTime(obs); if (obs.Name != null && (obs.Name.Equals("Temp_Patient") || obs.Name.Equals("Temp_Incubator") )) obs = (T)await CheckObsWithSameTimeExistsAndIncrementTime(obs); return obs; } public async Task Map(PatientTreatment treatment) { if (treatment.SingleDose) treatment = CalculateSingleDoseEndDate(treatment); var tempTreatment = treatment; await CheckTreatmentMedicines(tempTreatment); if (tempTreatment.RequestedGiveCodes.Any()) { if (tempTreatment.RequestedGiveCodes.Any(t => _medicationBolus.Contains(t.Identifier))) //remove && t.codingSystem == "FTPCS" not match always { _logger.LogDebug("Medication Bolus detected. Creating calculated observation"); await CalculateBolus(tempTreatment); } if (tempTreatment.RequestedGiveCodes.Any(t => _electroencephalogram.Contains(t.Identifier))) await CalculateMonitor(tempTreatment); if (tempTreatment.RequestedGiveCodes.Any(t => _surgery.Contains(t.Identifier) && _surgeryText.Contains(t.Text))) await CalculateSurgery(tempTreatment); if (treatment.RequestedGiveCodes.Any(t => _ecmo.Contains(t.Identifier))) //If ecmo treatment is NW recalculate complexity to show ECMO if is DC recalculate complexity await CalculateComplexityOnEcmo(treatment); if (tempTreatment.RequestedGiveCodes.Any(t => _oniCodes.Contains(t.Identifier))) { if (tempTreatment.OrderControl == OrderControlType.Nw) { var oniObservation = new PatientObservation { PatientId = treatment.PatientId, Code = "84481000140102", Name = "ONi", CodingSystem = "MDC", Value = tempTreatment.RequestedGiveAmountMinimum ?? '-', Time = tempTreatment.OrderTime ?? DateTime.Now, Units = "ppm" }; await _observationService.Value.InsertObservation(oniObservation, mapObs: false); } if (treatment.OrderControl == OrderControlType.Dc) { var lastOnis = await _observationService.Value.FindLastObservations(treatment.PatientId, 1, ["ONi"]); var lastOni = lastOnis.FirstOrDefault(); if (lastOni == null) return treatment; lastOni.Expired = true; await _observationService.Value.UpdateObservation(lastOni); await _observationService.Value.InsertObservation(lastOni, false); } } } return treatment; } public async Task Map(PumpObservation pumpObservation) { return await Task.FromResult(pumpObservation); } public async Task CalculateMedicineObservation(List activeMedicines, ObjectId patientId) { await CalculateMedicineObservation(activeMedicines, [], new PatientTreatment { PatientId = patientId }); } public async Task CalculateActiveBolus(ObjectId patientId) { // SIN RXA /* * var activeBolus = treatmentService.Value.GetActiveTreatmentsByPatient(patientId) .FindAll(p => p.requestedGiveCodes.Any(r => MedicationBolus.Contains(r.identifier)) && p.orderTime > DateTime.Now.AddHours(-12) && p.boloPom); var bolusObs = new PatientObservation() { patientid = patientId, time = DateTime.Now, //code = "MedicationBolus", //treatment.requestedGiveCode.identifier, //codingSystem = "ADAS", //treatment.requestedGiveCode.codingSystem, name = "OpiateBoluses", codingSystem = "ADAS", value = activeBolus.Count }; var lastBolus = observationService.Value.FindLastObservations(patientId, 1, new List { "OpiateBoluses" }).FirstOrDefault(); if (lastBolus != null && int.Parse(lastBolus.value.ToString()) == int.Parse(bolusObs.value.ToString())) { return; } observationService.Value.InsertObservation(bolusObs); */ //Por RXA var activeBolus = await GetActiveBolus(patientId, null); var bolusObs = new PatientObservation { PatientId = patientId, Time = DateTime.Now, //code = "MedicationBolus", //treatment.requestedGiveCode.identifier, //codingSystem = "ADAS", //treatment.requestedGiveCode.codingSystem, Name = "OpiateBoluses", CodingSystem = "ADAS", Value = activeBolus.Count }; var lastsBolus = await _observationService.Value.FindLastObservations(patientId, 1, ["OpiateBoluses"]); var lastBolus = lastsBolus.FirstOrDefault(); if (lastBolus != null && int.TryParse(lastBolus.Value.ToString(), out var lastbolus) && int.TryParse(bolusObs.Value.ToString(), out var bolusobs) && lastbolus == bolusobs) return; await _observationService.Value.InsertObservation(bolusObs, mapObs: false); } public async Task> GetActiveTreatmentsByPatient(ObjectId id) { var activeTreatments = await _treatmentService.Value.GetActiveTreatmentsByPatient(id); return activeTreatments; } public Task Map(PatientDiagnosis diagnosis) { return Task.FromResult(diagnosis); } /* * Designed to fix observations like intravenous with the possibility to receive multiple intravenous observations in the same hl7 message. * Sometimes ADAS calculates many in the same second. causing inconsistencies when retrieving last observation. */ public async Task FixTimeInconsistencyWithLast(PatientObservation newObservation) { if (string.IsNullOrEmpty(newObservation.Name)) { _logger.LogError("Observation name is null or empty: {newObservation}", newObservation); return null; } var lastObservations = await _observationService.Value.FindLastObservations(newObservation.PatientId, 1, [newObservation.Name]); var lastObservation = lastObservations.FirstOrDefault(); if (lastObservation != null && DateTime.Compare( new DateTime(lastObservation.Time.Year, lastObservation.Time.Month, lastObservation.Time.Day, lastObservation.Time.Hour, lastObservation.Time.Minute, lastObservation.Time.Second), new DateTime(newObservation.Time.Year, newObservation.Time.Month, newObservation.Time.Day, newObservation.Time.Hour, newObservation.Time.Minute, newObservation.Time.Second) ) >= 0) newObservation.Time = lastObservation.Time.AddSeconds(1); newObservation.Id = ObjectId.GenerateNewId(); return newObservation; } public Task> PreMapList(List listToInsert) { return Task.FromResult(listToInsert); } public Task MapSourceAlarm(PatientObservation obs, PatientObservationAlarm alarmToInsert) { return Task.FromResult(obs); } public Task SendAlarm(PatientObservation obs, string name, AlarmEnum.Name? code) { throw new NotImplementedException(); } public async Task CheckSurgeryExpired(BasePatientObservation obs) { try { var pobs = (PatientObservation)obs; if (!pobs.Expired) return; var surgeryObs = new PatientObservation { PatientId = obs.PatientId, Name = "Surgery", Min = 0, Max = 5, CodingSystem = "ADAS", Time = obs.Time.AddSeconds(1), Value = 0, Expires = pobs.Expires }; _logger.LogDebug("CheckSurgeryExpired. Inserting: {obs}", surgeryObs.ToString()); await _observationService.Value.InsertObservation(surgeryObs, mapObs: false); } catch (Exception e) { Console.WriteLine(e); throw; } } /* *Used for observations that can come with the same time, the last which enter is the newest. So increment time so that the system can identify which is the last. */ public async Task CheckObsExistsAndIncrementTime(BasePatientObservation obs) { var pobs = (PatientObservation)obs; var lastObsFromHour = await _observationService.Value.FindLastBeforeDate(pobs.PatientId, pobs.Time.AddMinutes(59), obs.Name); if (lastObsFromHour != null && DateTime.Compare(new DateTime(lastObsFromHour.Time.Year, lastObsFromHour.Time.Month, lastObsFromHour.Time.Day, lastObsFromHour.Time.Hour, 0, 0) , new DateTime(pobs.Time.Year, pobs.Time.Month, pobs.Time.Day, pobs.Time.Hour, 0, 0)) == 0) pobs.Time = lastObsFromHour.Time.AddSeconds(1); return pobs; } public async Task CheckObsWithSameTimeExistsAndIncrementTime(BasePatientObservation obs) { var pobs = (PatientObservation)obs; var existObsWithSameTime = await _observationService.Value.FindAnyWithSameDate(pobs.PatientId, pobs.Time, pobs.Name); if (existObsWithSameTime != null && existObsWithSameTime.Any()) pobs.Time = pobs.Time.AddSeconds(1); return pobs; } /// /// Oxygenation index is calculated => PMAP x FiO2 x 100 / PaO2 /// the method is called when it receives pmap or fio or pao2 and tries to take the other values to perform the /// calculation if they are not /// in database then does nothing. /// public async Task CalculateOxygenationIndex(BasePatientObservation obs) { var toSearchList = new List(); PatientObservation? airPressMeanObs = null; PatientObservation? fiO2Obs = null; PatientObservation? paO2Obs = null; try { toSearchList.AddRange(new List { "AirPressure_Mean", "FiO2", "PaO2" }); var values = await _observationService.Value.FindLastObservations(obs.PatientId, 1, toSearchList); switch (obs.Name) { case "AirPressure_Mean": airPressMeanObs = (PatientObservation)obs; fiO2Obs = values.FirstOrDefault(o => o.Name == "FiO2"); paO2Obs = values.FirstOrDefault(o => o.Name == "PaO2"); break; case "FiO2": fiO2Obs = (PatientObservation)obs; airPressMeanObs = values.FirstOrDefault(o => o.Name == "AirPressure_Mean"); paO2Obs = values.FirstOrDefault(o => o.Name == "PaO2"); break; case "PaO2": paO2Obs = (PatientObservation)obs; fiO2Obs = values.FirstOrDefault(p => p.Name == "FiO2"); airPressMeanObs = values.FirstOrDefault(o => o.Name == "AirPressure_Mean"); break; } //Controlar que fio2 no haya expirada al recogerla if (fiO2Obs != null && paO2Obs != null && airPressMeanObs != null) if (int.TryParse(airPressMeanObs.Value.ToString(), out var airPressureMean) && int.TryParse(fiO2Obs.Value.ToString(), out var fio2) && int.TryParse(paO2Obs.Value.ToString(), out var pao2)) { if (pao2 == 0) { _logger.LogWarning( "Calculate Oxygenation Index observation. PaO2: {pao2}, observation: {obsName}", pao2, obs.Name); return; } var oxygenationIndexValue = airPressureMean * fio2 * 100 / pao2; var oxygenationIndexObs = new PatientObservation { PatientId = obs.PatientId, Name = "Oxygenation_Index", CodingSystem = "ADAS", Value = oxygenationIndexValue, Time = DateTime.Now, Expires = 10 //Expires in 10 seg because its FiO2 expire time. }; await _observationService.Value.InsertObservation(oxygenationIndexObs, mapObs: false); } } catch (Exception ex) { _logger.LogError("Error calculate Oxygenation Index observation. Exception: {exMessage}", ex.Message); } } private static PatientTreatment CalculateSingleDoseEndDate(PatientTreatment treatment) { /* Deprecated calc finish treatment date on shift end. Now every treatment single dose is last 8 Hours DateTime now = DateTime.UtcNow; //Turno actual, tenemos que poner como fecha fin la fecha de inicio del turno siguiente var shiftNow = Shift.Where( i => DateTime.ParseExact(i, "HH:mm", CultureInfo.InvariantCulture).Ticks < DateTime.Now.Ticks ).LastOrDefault(); var index = Shift.IndexOf(shiftNow); string endDateShift; if (Shift.Count <= index + 1) endDateShift = Shift[0]; else endDateShift = Shift[index + 1]; var HourMinutes = endDateShift.Split(':'); if (now.Hour.CompareTo(int.Parse(HourMinutes[0])) > 0) now = now.AddDays(1); DateTime finisTreatmentDate = new DateTime(now.Year, now.Month, now.Day, int.Parse(HourMinutes[0]), int.Parse(HourMinutes[1]), 0); */ treatment.EndTime = treatment.OrderTime?.AddHours(8) ?? DateTime.Now.AddHours(8); return treatment; } public async Task CheckTreatmentMedicines(PatientTreatment treatment) { //sI TIENE UNA NOTA CON NPT SON DE TIPO NUTRICIÓN PARENTERAL Y SUMAN 1 var listNotesCode = treatment.Notes.Select(note => note.Comment).ToList(); if (treatment.RequestedGiveCodes.Any()) listNotesCode.AddRange(treatment.RequestedGiveCodes.Select(treatmentCodes => treatmentCodes.Identifier)); //var medicines = medicineService.Value.GetByCodeOrNote(listNotesCode); var medicines = await _medicineService.GetByCodeOrNote(listNotesCode); var parentalNutritionMedicine = await CalculateParentalNutritionMedicine(treatment); medicines.Add(parentalNutritionMedicine); //If not detect any medicine in medicine table, check if contáis NTE with, if it's true, it's a medicine. /* NTE|||Medicación|^OrderDefinition\r NTE|||Perfusiones|^OrderDefinition\r NTE|||SUEROS Y HEMODER|^OrderDefinition\r */ if (medicines is { Count: 0 }) { var isMedicine = treatment.Notes.Any(n => _notesIndicatingMedication.Contains(n.Comment)); if (isMedicine && treatment.RequestedGiveCodes is { Count: > 0 }) treatment.RequestedGiveCodes.ForEach(c => { if (!string.IsNullOrEmpty(c.Identifier)) medicines.Add(new Medicine { Codes = [c.Identifier], Name = c.Text }); }); } if (medicines.Any()) { if (!string.IsNullOrEmpty(treatment.RequestedGiveTreatment)) try { medicines = [ new Medicine { Name = treatment.RequestedGiveTreatment, Type = medicines.FindAll(t => t.Type.Any()) .Select(m => m.Type.Aggregate((x, y) => x + "," + y)).Distinct().ToList(), Codes = medicines.FindAll(t => t.Codes.Any()) .Select(m => m.Codes.Aggregate((x, y) => x + "," + y)).Distinct().ToList(), Group = medicines.FindAll(t => t.Group.Any()) .Select(m => m.Group.Aggregate((x, y) => x + "," + y)).Distinct().ToList(), Notes = medicines.FindAll(t => t.Notes.Any()) .Select(m => m.Notes.Aggregate((x, y) => x + "," + y)).Distinct().ToList() } ]; } catch (Exception ex) { _logger.LogError("Error Checking Treatment Medicines. {medicines} . Exception {ex}", string.Join(",", medicines), ex); } var _ = await GetActiveTreatmentsByPatient(treatment.PatientId); var activeTreatments = _.ToList(); //var activeMedicines = medicineService.Value.GetMedicinesOfTreatments(activeTreatments); var __ = await _medicineService.GetMedicinesOfTreatments(activeTreatments); var activeMedicines = __.ToList(); var activeNptTreatments = activeTreatments.FindAll(t => t?.Notes.FirstOrDefault(n => n.Comment == "NPT") != null); if (activeNptTreatments.Count > 0) { //activeMedicines.AddRange((IEnumerable)activeNPTTreatments.Select(async s => await CalculateParentalNutritionMedicine(s)).Where(parentalMedicine => parentalMedicine != null)); //activeMedicines.AddRange(await Task.WhenAll(activeNPTTreatments.Select(async s => await CalculateParentalNutritionMedicine(s))).Where(parentalMedicine => parentalMedicine != null)); var tasks = activeNptTreatments.Select(async s => await CalculateParentalNutritionMedicine(s)); var taskResults = await Task.WhenAll(tasks); var filteredMedicines = taskResults.ToList(); if (filteredMedicines.Any()) filteredMedicines.ForEach(m => { activeMedicines.Add(m); }); } await CalculateMedicineObservation(activeMedicines, medicines, treatment); } } private static Task CalculateParentalNutritionMedicine(PatientTreatment? treatment) { List medicineType = []; Note? commentType = null; if (treatment?.Notes.FirstOrDefault(n => n.Comment == "NPT") != null) { commentType = treatment.Notes.FirstOrDefault(n => n.CommentType == "formularybaseformulation"); if (treatment.Notes.FirstOrDefault(n => n.Comment is "LÍPIDOS NEONATALES AL 20%" or "LÍPIDOS NEONATALES AL 20% CON...") != null) medicineType = [nameof(MedicineEnum.Types.ParenteralNutritionLipids)]; else medicineType = [nameof(MedicineEnum.Types.ParenteralNutrition)]; } var medicine = new Medicine { Type = medicineType, Name = commentType != null ? commentType.Comment : "UnNamed" }; return Task.FromResult(medicine); } /* * Cada vez que entra un tratamiento recalcular medicación y riesgo de medicación: recuperamos todos los tratamientos activos del paciente, * siendo activos: todos aquellos que no han sido cancelados DC Y SIENDO NW NUEVO, * Medicación: Si treatment.orderControl = NW entra nuevo sumamos 1 si no hay ninguno de ese tipo ya activo * si entra treatment.orderControl = DC restamos 1 si no hay ninguno de ese tipo medición.type * y */ public async Task CalculateMedicineObservation(List activeMedicines, List medicines, PatientTreatment treatment) { var medicinesWithType = activeMedicines.FindAll(m => m.Type.Any()); foreach (var medicine in medicines) if (treatment.OrderControl is OrderControlType.Nw or OrderControlType.Xo) { if (treatment.StartTime != null && DateTime.UtcNow.CompareTo(treatment.StartTime) < 0) return; if (treatment.OrderControl == OrderControlType.Nw) activeMedicines.Add(medicine); if (treatment.OrderControl == OrderControlType.Xo && activeMedicines.All(m => m.Name != medicine.Name)) activeMedicines.Add(medicine); if (medicinesWithType != null && medicinesWithType.All(m => m.Type != medicine.Type)) medicinesWithType.Add(medicine); } else if (treatment.OrderControl == OrderControlType.Dc) { if (medicinesWithType != null) { medicinesWithType.Remove(medicine); activeMedicines.RemoveAll(m => m.Name == medicine.Name); } } var medicineTypes = medicinesWithType!.SelectMany(m => m.Type.Select(t => new { type = t })) .GroupBy(x => x.type).Count(); var medicineObs = new PatientObservation { PatientId = treatment.PatientId, Name = "Medication", CodingSystem = "ADAS", Value = medicineTypes, Time = DateTime.Now }; var lastMedicationsObs = await _observationService.Value.FindLastObservations(treatment.PatientId, 1, ["Medication"]); var lastMedicationObs = lastMedicationsObs.FirstOrDefault(); if (lastMedicationObs == null || !lastMedicationObs.Value.Equals(medicineObs.Value)) await _observationService.Value.InsertObservation(medicineObs, mapObs: false); await CalculateErMedication(activeMedicines, treatment); } private async Task CalculateErMedication(List activeMedicines, PatientTreatment treatment) { //var activeTreatments = treatmentService.Value.GetActiveTreatmentsByPatient(treatment.patientid); var result = await CalculateErMedicineLevels(activeMedicines); var erMedicationObs = new PatientObservation { Name = "ERMedication", CodingSystem = "ADAS", PatientId = treatment.PatientId, Time = DateTime.Now, Min = 0, Max = 5, Value = result }; var lastErMedicationsObsList = await _observationService.Value.FindLastObservations(treatment.PatientId, 1, ["ERMedication"]); var lastErMedicationObs = lastErMedicationsObsList.FirstOrDefault(); if (lastErMedicationObs == null || !lastErMedicationObs.Value.Equals(erMedicationObs.Value)) await _observationService.Value.InsertObservation(erMedicationObs, mapObs: false); } private static Task CalculateErMedicineLevels(List activeMedicines) { var ironVitaminDCodes = new List { "374424002", "175041000140104" }; //Aquí a cambiar que la nutrición parenteral siempre sume 4 no 5 quitar de medicinas de 5 solo suma 5 en caso de ser //Los lípidos son dos órdenes de NP con el segmento RXO6.2 (NPT: 1 ml LÍPIDOS NEONATALES AL 20% CON MEDICAMENTOS 1 o //NPT: 1 ml LÍPIDOS NEONATALES AL 20% 1) var medicines5Points = activeMedicines.Where(m => m.Type.Contains(nameof(MedicineEnum.Types.Pge1)) || m.Type.Contains(nameof(MedicineEnum.Types.Insulin)) || m.Group.Contains(nameof(MedicineEnum.Group.DoubleSignature)) || m.Group.Contains(nameof(MedicineEnum.Group.Vasoactive)) || m.Type.Contains(nameof(MedicineEnum.Types.ParenteralNutritionLipids)) ) .ToList(); if (medicines5Points.Count > 2 || activeMedicines.FirstOrDefault(m => m.Type.Contains(nameof(MedicineEnum.Types.ParenteralNutritionLipids))) != null) return Task.FromResult(5); if (medicines5Points.Count is > 0 and <= 2 || activeMedicines.FirstOrDefault(m => m.Type.Contains(nameof(MedicineEnum.Types.ParenteralNutrition))) != null ) return Task.FromResult(4); var medicines2Points = activeMedicines.Where(m => m.Group.Contains(nameof(MedicineEnum.Group.Metabolic)) ).ToList(); if (medicines2Points.Count > 0) return Task.FromResult(2); //All medicines without type/group and not vitamin D or Iron var medicines1Point = activeMedicines.FindAll(m => !m.Codes.Any(c => ironVitaminDCodes.Contains(c))); if (medicines1Point.Count > 0) return Task.FromResult(1); var ironVitaminDMedicines = activeMedicines.Where(m => m.Codes.Any(c => ironVitaminDCodes.Contains(c)) ).ToList(); if (ironVitaminDMedicines.Count == activeMedicines.Count || activeMedicines.Count == 0) return Task.FromResult(0); return Task.FromResult(1); } //TODO revisar con los valores reales cuando se sepan public async Task CalculateVentilationMode(BasePatientObservation obs) { var pobs = (PatientObservation)obs; var respTypeObs = new PatientObservation { Name = "Resp_Type", CodingSystem = "ADAS", PatientId = obs.PatientId, Time = obs.Time }; var strValue = pobs.Value.ToString(); respTypeObs.Value = _highFrequencyVentilation.Contains(strValue ?? string.Empty) ? nameof(RespirationType.HighFrequencyVentilation) : _invasiveVentilation.Contains(strValue ?? string.Empty) ? respTypeObs.Value = nameof(RespirationType.Invasive) : _nonInvasiveVentilation.Contains(strValue ?? string.Empty) ? respTypeObs.Value = nameof(RespirationType.NonInvasive) : nameof(RespirationType.None); await _observationService.Value.InsertIfChanged("Resp_Type", respTypeObs); } public async Task CalculateAsistResp(BasePatientObservation obs) { //Comprobar si hay un resp_type más nuevo y si no lo hay ignorar var pobs = (PatientObservation)obs; var lastsAsistResp = await _observationService.Value.FindLastObservations(obs.PatientId, 1, ["Resp_Mode"]); var lastAsistResp = lastsAsistResp.FirstOrDefault(); if (lastAsistResp != null && lastAsistResp.Time.CompareTo(obs.Time) > 0 && lastAsistResp.Name != "ONi") return obs; var calculatedRespiratory = new PatientObservation { PatientId = obs.PatientId, Time = DateTime.Now, CodingSystem = "ADAS", Name = "Respiratory", Value = 0 }; if (obs.Name == "ONi") { //find last assist resp obs if (lastAsistResp != null) { var respiratoryValue = _respiratoryTypeValue.FirstOrDefault(r => r.Item3.Contains(lastAsistResp.Value)); if (respiratoryValue != null) calculatedRespiratory.Value = respiratoryValue.Item2; } } else { var respiratoryValue = _respiratoryTypeValue.FirstOrDefault(r => r.Item3.Contains(pobs.Value)); if (respiratoryValue != null) calculatedRespiratory.Value = respiratoryValue.Item2; } var isIno = !string.IsNullOrEmpty(obs.Code) && _oniCodes.Contains(obs.Code); if (isIno && !pobs.Expired) calculatedRespiratory.Value = 10; //Check if last ino exist and is not expired var lastsIno = await _observationService.Value.FindLastObservations(obs.PatientId, 1, ["ONi"]); var lastIno = lastsIno.FirstOrDefault(); if (lastIno is { Expired: false }) calculatedRespiratory.Value = 10; await _observationService.Value.InsertObservation(calculatedRespiratory, mapObs: false); return obs; } private async Task CalculateComplexityOnEcmo(PatientTreatment treatment) { var ecmoObs = new PatientObservation { PatientId = treatment.PatientId, Name = "ECMO" }; switch (treatment.OrderControl) { case OrderControlType.Nw: ecmoObs.Value = OrderControlType.Nw; break; case OrderControlType.Dc: var haveEcmo = await _treatmentService.Value.GetActiveTreatmentsByPatient(treatment.PatientId); ecmoObs.Value = haveEcmo.Any(t => t != null && t.RequestedGiveCodes.Any(r => _ecmo.Contains(r.Identifier) && t.PlacerOrder?.EntityIdentifier != treatment.PlacerOrder?.EntityIdentifier)) ? OrderControlType.Xo : OrderControlType.Dc; break; default: return; } await CalculateComplexity(ecmoObs); } /** * Calculamos la complejidad basándonos en el valor de PESO/VÍAS/ASSIST.RESP/MONIT./CIRUGÍA/MEDICACIÓN * cuando entra una nueva observación que afecte a complejidad, se recalcula de 0 para no tener que estar pendiente de qué puntos corresponden a qué observación * La complejidad va de 0 a 5. Guardamos la observación que nos llega y recalculamos complejidad. */ public async Task CalculateComplexity(BasePatientObservation obs, bool ignoreObs = false) { try { var logUid = Guid.NewGuid(); var complexity = new PatientObservation { PatientId = obs.PatientId, Time = DateTime.UtcNow, CodingSystem = "ADAS", Name = "Complexity", Value = 0 }; var complexityValue = 0; var activeTreatments = await _treatmentService.Value.GetActiveTreatmentsByPatient(obs.PatientId); var haveEcmo = activeTreatments.Any(t => t != null && t.RequestedGiveCodes.Any(r => _ecmo.Contains(r.Identifier))); var pobs = obs as PatientObservation; var complexityObservations = await _observationService.Value.FindLastObservations(obs.PatientId, 1, _complexityObservations); //Nos acaba de entrar, la usamos por delante de la última de base de datos //21-02-22 No es correcto, solo remover si el tiempo de esa observación que entra es más nuevo que la de bd var actualObsInBd = complexityObservations.FirstOrDefault(o => o.Name == obs.Name); if (actualObsInBd != null && obs.Time > actualObsInBd.Time) complexityObservations.RemoveAll(it => it.Name == obs.Name); if (!ignoreObs && pobs is { Expired: false }) complexityObservations.Add(pobs); foreach (var observation in complexityObservations) switch (observation.Name) { case "Weight_Current": // if(observation.expired) break; A este no le importa que esté expirado, lo tiene que hacer siempre. var weightCurrentValue = await CalculateWeight(observation); _logger.LogDebug( "complexity for patient {patientid} id log{logUID} => Weight_Current plus complexity: {WeightCurrentValue}", observation.PatientId, logUid, weightCurrentValue); complexityValue += weightCurrentValue; break; case "Weight_Newborn": //Only count newborn weight when weight current don't exist. if (complexityObservations.FirstOrDefault(o => o.Name == "Weight_Current") == null) { var weightNewbornValue = await CalculateWeight(observation); _logger.LogDebug( "complexity for patient {patientid} id log{logUID} => Weight_Newborn plus complexity: {Weight_NewbornValue}", observation.PatientId, logUid, weightNewbornValue); complexityValue += weightNewbornValue; } break; case "Medication": if (observation.Expired) break; var value = observation.Value.ToString(); if (!int.TryParse(value, out var valueParsed)) valueParsed = 0; _logger.LogDebug( "complexity for patient {patientid} id log{logUID} => Medication plus complexity: {valueParsed}", observation.PatientId, logUid, valueParsed); complexityValue += valueParsed; break; case "Surgery": if (observation.Expired) break; var surgeryValue = Convert.ToInt32(observation.Value); _logger.LogDebug( "complexity for patient {patientid} id log{logUID} => Surgery plus complexity: {surgeryValue}", observation.PatientId, logUid, surgeryValue); complexityValue += surgeryValue; break; case "IntravenousLines": if (observation.Expired) break; var intravenousLinesValue = Convert.ToInt32(observation.Value); _logger.LogDebug( "complexity for patient {patientid} id log{logUID} => IntravenousLines plus complexity: {IntravenousLinesValue}", observation.PatientId, logUid, intravenousLinesValue); complexityValue += intravenousLinesValue; break; case "Monitor": if (observation.Expired) continue; var monitorValue = Convert.ToInt32(observation.Value); _logger.LogDebug( "complexity for patient {patientid} id log{logUID} => monitor plus complexity: {monitorValue}", observation.PatientId, logUid, monitorValue); complexityValue += monitorValue; break; case "Respiratory": if (observation.Expired) break; var respiratoryValue = Convert.ToInt32(observation.Value); _logger.LogDebug( "complexity for patient {patientid} id log{logUID} => respiratory plus complexity: {respiratoryValue}", observation.PatientId, logUid, respiratoryValue); complexityValue += respiratoryValue; break; } complexity.Value = complexityValue; if (haveEcmo && obs.Name != "ECMO") //complexity.value = $"ECMO ({complexityValue})"; complexity.Value = $"{complexityValue} ECMO"; if (complexityObservations.Any(o => o.Name == "ECMO")) { var ecmoObs = complexityObservations.FirstOrDefault(o => o.Name == "ECMO"); if (Enum.TryParse(typeof(OrderControlType), ecmoObs?.Value.ToString(), out var treatmentControl)) switch (treatmentControl) { case OrderControlType.Dc: break; //case OrderControlType.XO: //case OrderControlType.NW: default: //complexity.value = $"ECMO ({complexityValue})"; complexity.Value = $"{complexityValue} ECMO"; //var pObs = await FixTimeInconsistencyWithLast(complexity); if (pobs != null) await _observationService.Value.InsertObservation(pobs); break; } } var lastsObsComplexity = await _observationService.Value.FindLastObservations(obs.PatientId, 1, [complexity.Name]); var lastObsComplexity = lastsObsComplexity.FirstOrDefault(); if (lastObsComplexity is not null) { var lastObsParsed = int.TryParse(lastObsComplexity.Value.ToString(), out var lastObsValue); if (lastObsParsed && int.TryParse(complexity.Value.ToString(), out var intValue) && lastObsValue == intValue) return obs; } var fixedTimeComplexity = await FixTimeInconsistencyWithLast(complexity); _logger.LogDebug( "complexity for patient {patientid} id log{logUID} => inserting complexity value: {complexityValue} for patient: {complexityPatientid} at time {fixedTimeComplexityTime}", complexity.PatientId, logUid, complexity.Value, complexity.PatientId, fixedTimeComplexity?.Time); if (fixedTimeComplexity != null) await _observationService.Value.InsertObservation(fixedTimeComplexity, mapObs: false); return obs; } catch (Exception ex) { _logger.LogError("Error calculating complexity. Exception: {ex}", ex); return obs; } } /// /// Calculate intravenous line when new intravenous observation enters. Get all intravenous obs and filtering actives. /// Si existe ya un catéter del mismo tipo, apuntando a la misma localización con una fecha que ya existe y no la ha /// sido retirada y vuelve a venir /// una inserción, no hacemos caso porque es una actualización. /// /// /// public async Task CalculateIntravenousLineObservation(BasePatientObservation obs) { try { var pobs = (PatientObservation)obs; var intravenousLineValue = (PatientIntravenousLinesValue)pobs.Value; //Check for doctor miss click on ICCA. sometimes they put insert with remove time //when it has a remove time always is remove, never insert if (intravenousLineValue is { Action: "Insertado", RemoveTime: not null }) intravenousLineValue.Action = "Retirado"; //GetByCodeSysAndCode active Intravenous observations //var intraVenousLineObservations = observationService.Value.FindLastObservations(obs.patientid, 1, new List { obs.name }); List activeIntraVenousLineObservations = []; try { activeIntraVenousLineObservations = await _observationService.Value.FindLastIntravenousLinesObservationByLocation(obs.PatientId); } catch (Exception ex) { _logger.LogWarning( "Cannot transform to PatientObservation value to PatientIntravenousLinesValue {exMessage}", ex.Message); } if (activeIntraVenousLineObservations.Any()) activeIntraVenousLineObservations = activeIntraVenousLineObservations.FindAll(v => v != null && ((PatientIntravenousLinesValue)v.Value).Action == "Insertado"); /* var activeIntravenousLineObservations = intraVenousLineObservations.GroupBy(obs => (((PatientIntravenousLinesValue)obs.value).type, ((PatientIntravenousLinesValue)obs.value).insertTime, ((PatientIntravenousLinesValue)obs.value).location)) .Where(grp => grp.All(o => ((PatientIntravenousLinesValue)o.value).RemoveTime == null)) .SelectMany(group => group).ToList(); */ //If intravenous obs have remove time, remove it from activeIntravenousLines before calculating. if (intravenousLineValue.RemoveTime != null) { activeIntraVenousLineObservations.RemoveAll(o => o != null && ((PatientIntravenousLinesValue)o.Value).Location == intravenousLineValue.Location); } else { //If insert come with same data than other insert and same insertTime is update. Don't need to register it again as insert.is updated var intravenousLineBeforeUpdate = activeIntraVenousLineObservations.FirstOrDefault(o => o != null && ((PatientIntravenousLinesValue)o.Value).Location == intravenousLineValue.Location && Equals(((PatientIntravenousLinesValue)o.Value).InsertTime, intravenousLineValue.InsertTime) && ((PatientIntravenousLinesValue)o.Value).Type == intravenousLineValue.Type && intravenousLineValue.RemoveTime == null); if (intravenousLineBeforeUpdate != null) { //If duration changed Update register if (((PatientIntravenousLinesValue)intravenousLineBeforeUpdate.Value).Duration == intravenousLineValue.Duration) return null; ((PatientIntravenousLinesValue)intravenousLineBeforeUpdate.Value).Duration = intravenousLineValue.Duration ?? string.Empty; await _observationService.Value.UpdateObservation(intravenousLineBeforeUpdate); return null; } if (!activeIntraVenousLineObservations.Any(o => o != null && ((PatientIntravenousLinesValue)o.Value).Location == intravenousLineValue.Location && ((PatientIntravenousLinesValue)o.Value).Type == intravenousLineValue.Type)) activeIntraVenousLineObservations.Add(pobs); } //Generate calculad intravenousLine obs //var intraVenousLineScore2 = activeIntraVenousLineObservations.Select(activeObservation => IntraVenousLinesTypeValue.Where(i => i.Item3.Contains(((PatientIntravenousLinesValue)activeObservation.value).type))).Select(typeValue => typeValue.FirstOrDefault()?.Item2??0).Sum(); // Step 1: Select active observations and cast to PatientIntravenousLinesValue var activeObservations = activeIntraVenousLineObservations.Select(activeObservation => (PatientIntravenousLinesValue?)activeObservation?.Value); // Step 2: Match active observations with type-value mappings // We use .Replace("\u00A0", " ") on type to avoid special codification for spaces between characters var matchedValues = activeObservations.Select(activeObservation => _intraVenousLinesTypeValue.Where(i => activeObservation is { Type: not null } && i.Item3.Contains(activeObservation.Type.Replace("\u00A0", " ")))) .ToList(); // Step 3: Extract the values from matched tuples var values = 0; matchedValues.ForEach(typeValue => values += typeValue.FirstOrDefault()?.Item2 ?? 0); // Step 4: Calculate the sum var intraVenousLineScore = values; if (intraVenousLineScore > 10) intraVenousLineScore = 10; var intravenousObs = new PatientObservation { PatientId = obs.PatientId, Name = "IntravenousLines", Min = 0, CodingSystem = "ADAS", Max = 10, Time = DateTime.UtcNow, Value = intraVenousLineScore }; var ob = await FixTimeInconsistencyWithLast(intravenousObs); if (ob != null) await _observationService.Value.InsertObservation(ob, mapObs: false); } catch (InvalidCastException ex) { _logger.LogError("CalculateIntravenousLineObservation {obs}: {exMessage}", obs, ex.Message); } return obs; } private async Task CalculateWeight(BasePatientObservation obs) { var complexityValueOfWeight = 0; if (obs.Units == "kg") obs = await ParseWeight(obs); var pobs = (PatientObservation)obs; if (!double.TryParse(pobs.Value.ToString(), out var valueParsed)) return complexityValueOfWeight; complexityValueOfWeight = valueParsed switch { < 750 => 7, >= 750 and <= 999 => 5, >= 1000 and <= 1249 => 2, >= 1250 and <= 1999 => 1, >= 2000 => 0, _ => complexityValueOfWeight }; return complexityValueOfWeight; } public Task ParseWeight(BasePatientObservation obs) { if (obs is not PatientObservation pobs || !double.TryParse(pobs.Value.ToString(), out var dValue)) { _logger.LogError("Error casting weight Observation {obs}:", obs); return Task.FromResult(obs); } pobs.Units = "gr"; pobs.Value = dValue * 1000; return Task.FromResult(obs); } //Monitor observation can be a observation or treatment //TODO como saber si tiene fecha de fin o frecuencia public async Task CalculateMonitor(object monitorObservation) { var monitorValue = 0; BasePatientObservation? pobs = null; ObjectId patientId = new(); PatientTreatment? tobs = null; if (monitorObservation.GetType() == typeof(PatientObservation)) { pobs = (BasePatientObservation)monitorObservation; patientId = pobs.PatientId; } else if (monitorObservation.GetType() == typeof(PatientTreatment)) { tobs = (PatientTreatment)monitorObservation; patientId = tobs.PatientId; } //Observations in last 30 min are active var activeMonitorObservationsList = await _observationService.Value.FindLastObservations(patientId, 1, ["Saturation_RegBrain", "Saturation_RegSomatic", "Transcutaneous_O2"]); if (!activeMonitorObservationsList.Any()) { _logger.LogWarning("Active Monitor Observation List is null or empty for patientId: {patientId}", patientId); return; } var activeMonitorObservations = activeMonitorObservationsList.FindAll(o => !o.Expired); if (pobs != null && !((PatientObservation)pobs).Expired) activeMonitorObservations.Add((PatientObservation)pobs); if (activeMonitorObservations.Any(o => o.Code != null && _transcutanous.Contains(o.Code))) monitorValue += 1; if (activeMonitorObservations.Any(o => o.Code != null && _regionalBrainSaturation.Contains(o.Code))) monitorValue += 1; //Patient treatment var activeTreatments = await _treatmentService.Value.GetActiveTreatmentsByPatient(patientId); activeTreatments = activeTreatments.ToList().FindAll(p => p != null && p.RequestedGiveCodes.Any(r => _electroencephalogram.Contains(r.Identifier) && r.Text.Contains("Monitor EEGa"))); if (tobs != null && tobs.OrderControl != OrderControlType.Dc && activeTreatments.Any()) monitorValue += 1; else if (tobs is { OrderControl: OrderControlType.Nw }) monitorValue += 1; //Generate calculad monitor obs var monitorObs = new PatientObservation { PatientId = patientId, Name = "Monitor", CodingSystem = "ADAS", Min = 0, Max = 3, Time = DateTime.Now, Value = monitorValue }; await _observationService.Value.InsertObservation(monitorObs, mapObs: false); } private async Task CalculateSurgery(PatientTreatment treatment) { var surgeryScore = 0; var activeTreatments = await _treatmentService.Value.GetActiveTreatmentsByPatient(treatment.PatientId); //Filter only surgery treatments var activeSurgeryTreatments = activeTreatments.ToList().FindAll(t => t != null && t.RequestedGiveCodes.Any(code => _surgery.Contains(code.Identifier) && _surgeryText.Contains(code.Text))); switch (treatment.OrderControl) { //Refactor solo con los tratamientos activos de cirugía, un DC cancela a su entityIdentifier correspondiente. case OrderControlType.Nw: activeSurgeryTreatments.Add(treatment); break; case OrderControlType.Dc: activeSurgeryTreatments.RemoveAll(t => t != null && t.PlacerOrder?.EntityIdentifier == treatment.PlacerOrder?.EntityIdentifier); break; } if (activeSurgeryTreatments.Count > 0) surgeryScore = 5; var surgeryObs = new PatientObservation { PatientId = treatment.PatientId, Name = "Surgery", Min = 0, Max = 5, CodingSystem = "ADAS", Time = DateTime.Now, Value = surgeryScore }; await _observationService.Value.InsertObservation(surgeryObs, mapObs: false); } private async Task> GetActiveBolus(ObjectId patientId, PatientTreatment? newTreatment) { var treatmentsProcessed = new List(); var activeTreatments = await _treatmentService.Value.GetBolusTreatments(patientId); if (newTreatment != null) activeTreatments.Add(newTreatment); //Agrupar por namespaceID y quedarme con el último message time que será el último que han reecho. //Si ese último es concluido suma 1. var treatmentsGroupedByNamespaceId = activeTreatments .GroupBy(t => t.PlacerOrder?.NamespaceId) //.Select(group => group.OrderByDescending(t => t.messageTime)).ToList(); .Select(grp => grp.ToList()) .ToList(); //.Select(g => g.FirstOrDefault()).ToList(); foreach (var treatmentGroup in treatmentsGroupedByNamespaceId) { var modificatedBolus = treatmentGroup.FindAll(t => t.RequestedGiveCodesStatus.Any(r => r.EndAdministrationTime != DateTime.MinValue)); //Eliminamos todos aquellos que han sido modificados con posterioridad, esto nos lo indica cuando tiene endAdministrationTime != null treatmentGroup.RemoveAll(t => modificatedBolus.Any(m => m.RequestedGiveCodesStatus.FirstOrDefault()?.EndAdministrationTime == t.RequestedGiveCodesStatus.FirstOrDefault()?.AdministrationTime)); /* treatmentGroup.RemoveAll(t => t.requestedGiveCodesStatus .Any(r => r.endAdministrationTime == null && modificatedBolus.FirstOrDefault(m => m.requestedGiveCodesStatus.Any(code => code.endAdministrationTime == r.administrationTime)) != null)); */ //modificatedBolus.Any(b => // b.requestedGiveCodesStatus.Any(code => code.endAdministrationTime == r.administrationTime)))); //Opción B a veces se cancelan sin endTime. Simplemente, agrupar por administrationTime y quedarme con el último por time del mensaje. treatmentGroup.RemoveAll(t => !t.RequestedGiveCodesStatus.Any()); var groupByAdmTime = treatmentGroup.GroupBy(t => t.RequestedGiveCodesStatus.FirstOrDefault()!.AdministrationTime) .Select(grp => grp.OrderByDescending(t => t.MessageTime).First()); treatmentsProcessed.AddRange(groupByAdmTime); //todos los que tengan startOfAdministration y no tenga esa fecha ninguno de modificatedBolus en endOfTreatment } return treatmentsProcessed .FindAll(p => p.RequestedGiveCodes.Any(r => _medicationBolus.Contains(r.Identifier)) && p.RequestedGiveCodesStatus.FirstOrDefault() != null && p.RequestedGiveCodesStatus.FirstOrDefault()!.AdministrationTime?.ToUniversalTime() > DateTime.UtcNow.AddHours(-12) && p.RequestedGiveCodesStatus.FirstOrDefault()!.AdministrationTime?.ToUniversalTime() <= DateTime.UtcNow && p.RequestedGiveCodesStatus.Any(rxa => rxa.Status != null && _rxaStatus.Contains(rxa.Status))); } private async Task CalculateBolus(PatientTreatment treatment) { var activeBolus = await GetActiveBolus(treatment.PatientId, treatment); var bolusObs = new PatientObservation { PatientId = treatment.PatientId, Time = DateTime.Now, Name = "OpiateBoluses", CodingSystem = "ADAS", Value = activeBolus.Count }; await _observationService.Value.InsertObservation(bolusObs); } /* * En rojo si TAm < Edad Gestacional en semanas en la primera semana y luego edad corregida */ public async Task CalculateTAmAlert(BasePatientObservation obs) { var pobs = (PatientObservation)obs; var tamStatus = StatusEnum.Type.Ok; var groupedObservations = new List { pobs }; var gestationalWeeksToCheck = 0; if (obs.Name == "Age_Gestational") groupedObservations = await _observationService.Value.FindLastObservations(obs.PatientId, 1, ["Age_Gestational_Fixed", "TAm"]); if (obs.Name == "Age_Gestational_Fixed") groupedObservations = await _observationService.Value.FindLastObservations(obs.PatientId, 1, ["Age_Gestational", "TAm"]); if (obs.Name == "TAm") groupedObservations = await _observationService.Value.FindLastObservations(obs.PatientId, 1, ["Age_Gestational", "Age_Gestational_Fixed"]); var ageGestational = groupedObservations.FirstOrDefault(o => o.Name == "Age_Gestational"); if (ageGestational != null && int.TryParse(ageGestational.Value.ToString(), out var ageParsed) && ageParsed < 2) { gestationalWeeksToCheck = Convert.ToInt32(ageGestational.Value); } else { var ageGestationalFixed = groupedObservations.FirstOrDefault(o => o.Name == "Age_Gestational_Fixed"); if (ageGestationalFixed != null) gestationalWeeksToCheck = Convert.ToInt32(ageGestationalFixed.Value); //var gestationalWeeks = int.Parse(ageGestationalFixed.value.ToString()); //if (Convert.ToInt32(pobs.value) < gestationalWeeks) TAmStatus = ObservationStatus.Alert; } var tamObs = groupedObservations.FirstOrDefault(o => o.Name == "TAm"); if (tamObs != null) if (Convert.ToInt32(tamObs.Value) < gestationalWeeksToCheck) tamStatus = StatusEnum.Type.Alert; if (pobs.Name == "TAm") { pobs.Status = tamStatus; } else { if (tamObs != null) { tamObs.Id = ObjectId.GenerateNewId(); tamObs.Time = DateTime.Now; tamObs.Status = tamStatus; await _observationService.Value.InsertObservation(tamObs, mapObs: false); } } } /*Entra temp axilar o incubadora. Restamos la axilar a la incubadora. Solo se calcula si están los dos valores. */ public async Task CalculateTempGradient(BasePatientObservation obs) { List tempRetrievedesFromBd; PatientObservation? tempRetrievedFromBd = null; var pobs = (PatientObservation)obs; if ("Temp_Patient".Equals(pobs.Name)) { tempRetrievedesFromBd = await _observationService.Value.FindLastObservations(pobs.PatientId, 1, ["Temp_Incubator"]); tempRetrievedFromBd = tempRetrievedesFromBd.FirstOrDefault(); } if ("Temp_Incubator".Equals(pobs.Name)) { tempRetrievedesFromBd = await _observationService.Value.FindLastObservations(pobs.PatientId, 1, ["Temp_Patient"]); tempRetrievedFromBd = tempRetrievedesFromBd.FirstOrDefault(); } //Now Temp Gradient is only calculated when temp_Patient and Temp_Incubator come in last 10 min. //if (tempRetrievedFromBd != null && DateTime.UtcNow.CompareTo(tempRetrievedFromBd.time.ToUniversalTime().AddMinutes(10)) <= 0) if (tempRetrievedFromBd == null) return; var difFechas = pobs.Time.ToUniversalTime() - tempRetrievedFromBd.Time.ToUniversalTime(); //if (tempRetrievedFromBd != null && pobs.time.ToUniversalTime().CompareTo(tempRetrievedFromBd.time.ToUniversalTime().AddMinutes(10)) <= 0) if (Math.Abs(difFechas.TotalMinutes) <= 10 && double.TryParse(tempRetrievedFromBd.Value.ToString(), out var tempRetrievedFromBdParsed) && double.TryParse(pobs.Value.ToString(), out var pobsParsed)) { double tempGradientValue; if ("Temp_Patient".Equals(pobs.Name)) tempGradientValue = tempRetrievedFromBdParsed - pobsParsed; else tempGradientValue = pobsParsed - tempRetrievedFromBdParsed; var tempGradientObs = new PatientObservation { PatientId = obs.PatientId, Name = "Temp_Gradient", CodingSystem = "ADAS", Time = obs.Time, Value = tempGradientValue }; var obsToInsert = (PatientObservation)await CheckObsWithSameTimeExistsAndIncrementTime(tempGradientObs); obsToInsert.Id = ObjectId.GenerateNewId(); await _observationService.Value.InsertObservation(obsToInsert, mapObs: false); } } private enum IntraVenousLineTypes { Artery, CentralVein, Picc, MiddleLine, Peripheral } // ReSharper disable once UnusedMember.Local private enum RespiratoryTypes { Ino, Vafo, Vmc, Vmni, NasalCannulas, None } }