Files
adas-core/adas-core.Application/Services/PatientService.cs
T

3223 lines
144 KiB
C#

using System.Diagnostics;
using adas_core.Application.Exceptions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Application.Subscriptions;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.GroupedObservations;
using adas_core.Domain.Models.Masters;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Responses;
using adas_core.Domain.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Serilog;
using Patient = adas_core.Domain.Models.MongoModels.Patient;
namespace adas_core.Application.Services;
public class PatientService : IPatientService
{
private readonly Lazy<IAdmissionService> _admissionService;
private readonly IAppointmentService _appointmentService;
private readonly ILocalAuditService _auditService;
private readonly IClientMessageService _clientMessageService;
private readonly bool _createPatientWithAdtA08;
private readonly bool _createPatientWithLocation;
private readonly bool _createPatientWithOru;
private readonly bool _cretatePatientWithoutPatientNumber;
private readonly IDiagnosisService _diagnosisService;
private readonly IDischargeService _dischargeService;
private readonly IDisplayConfigService _displayConfigService;
private readonly IDisplayService _displayService;
private readonly bool _findPatientByLocationWithoutId;
private readonly IGroupedObservationService _groupedObservationService;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly string _iccaFacility;
private readonly ListSettings _listSettings;
private readonly ILogger<PatientService> _logger;
private readonly IMasterListServiceFactory _masterListServiceFactory;
private readonly Lazy<IObservationService> _observationService;
private readonly OnArchiveAction _onArchiveAction;
private readonly IPatientArchiveRepository _patientArchiveRepository;
private readonly IPatientCarePlanService _patientCarePlanService;
private readonly IPatientRepository _patientRepository;
private readonly IPoCMappingService _pocMappingService;
private readonly IPointOfCareService _pointOfCareService;
private readonly Lazy<IPumpService> _pumpService;
private readonly bool _pushPatientWithOru;
private readonly IRecordingAlertService _recordingAlertService;
private readonly List<string> _sendingFacility;
private readonly ISubscriberGroupedService _subscriberGroupedService;
private readonly ISubscribersService _subscribersService;
private readonly Lazy<ITreatmentService> _treatmentService;
private readonly IUnitService _unitService;
private readonly bool _updatePatientDataWithAdtA02;
private readonly bool _updatePatientDataWithOru;
private readonly bool _updatePatientLocationWithOru;
public PatientService(
IPatientRepository patientRepository,
IPatientArchiveRepository patientArchiveRepository,
Lazy<IObservationService> observationService,
Lazy<ITreatmentService> treatmentService,
IPoCMappingService pocMappingService,
IDiagnosisService diagnosisService,
IAppointmentService appointmentService,
Lazy<IPumpService> pumpService,
IRecordingAlertService recordingAlertService,
IDischargeService dischargeService,
IOptions<ApiSettings> apiSettings,
IOptions<ListSettings> listSettings,
ILogger<PatientService> logger,
IClientMessageService clientMessageService,
ISubscribersService subscribersService,
ISubscriberGroupedService subscriberGroupedService,
IUnitService unitService,
IDisplayService displayService,
IPointOfCareService pointOfCareService,
Lazy<IAdmissionService> admissionService,
IDisplayConfigService displayConfigService,
IGroupedObservationService groupedObservationService,
IPatientCarePlanService patientCarePlanService,
IHttpContextAccessor httpContextAccessor,
ILocalAuditService auditService, IMasterListServiceFactory masterListServiceFactory)
{
_patientRepository = patientRepository;
_patientArchiveRepository = patientArchiveRepository;
_observationService = observationService;
_treatmentService = treatmentService;
_diagnosisService = diagnosisService;
_appointmentService = appointmentService;
_pumpService = pumpService;
_recordingAlertService = recordingAlertService;
_logger = logger;
_clientMessageService = clientMessageService;
_subscribersService = subscribersService;
_subscriberGroupedService = subscriberGroupedService;
_pocMappingService = pocMappingService;
_dischargeService = dischargeService;
_unitService = unitService;
_displayService = displayService;
_pointOfCareService = pointOfCareService;
_admissionService = admissionService;
_displayConfigService = displayConfigService;
_groupedObservationService = groupedObservationService;
_patientCarePlanService = patientCarePlanService;
_httpContextAccessor = httpContextAccessor;
_auditService = auditService;
_masterListServiceFactory = masterListServiceFactory;
_ = Enum.TryParse(apiSettings.Value.OnArchivePatientAction, out _onArchiveAction);
_createPatientWithOru = apiSettings.Value.CreatePatientWithOru;
_pushPatientWithOru = apiSettings.Value.PushPatientWithOru;
_createPatientWithLocation = apiSettings.Value.CreatePatientWithLocation;
_updatePatientLocationWithOru = apiSettings.Value.UpdatePatientLocationWithOru;
_updatePatientDataWithOru = apiSettings.Value.UpdatePatientDataWithOru;
_findPatientByLocationWithoutId = apiSettings.Value.FindPatientByLocationWithoutId;
_iccaFacility = apiSettings.Value.IccaFacility ?? string.Empty;
_sendingFacility = apiSettings.Value.SendingFacility ?? [];
_cretatePatientWithoutPatientNumber = apiSettings.Value.CreatePatientWithoutPatientNumber;
_createPatientWithAdtA08 = apiSettings.Value.CreatePatientWithAdtA08;
_updatePatientDataWithAdtA02 = apiSettings.Value.UpdatePatientDataWithAdtA02;
_listSettings = listSettings.Value;
}
public async Task Insert(Patient patient)
{
if (!CheckPatient(patient))
return;
await _patientRepository.InsertOneAsync(patient);
_ = SendNewPatientBroadcast(patient);
}
public async Task<Patient?> FindByPatientIdWithLocale(string patientId, LocaleEnum localeEnum)
{
ObjectId.TryParse(patientId, out var id);
var patient = await FindById(id);
if (patient?.UnitId == null) return null;
var unit = await _unitService.FindById(patient.UnitId);
if (unit == null) return patient;
var patientWithLocale = await _masterListServiceFactory.GetPatientTraslated(unit, localeEnum, patient);
return patientWithLocale;
}
public async Task InsertAsync(Patient patient)
{
if (!CheckPatient(patient))
return;
await _patientRepository.InsertOneAsync(patient);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, patient);
_ = SendNewPatientBroadcast(patient);
}
public async Task<bool> Move(Patient patient, ObjectId newPocId, ObjectId oldPocId)
{
var newPoc = await _pointOfCareService.GetInfo(newPocId);
if (newPoc == null)
{
_logger.LogError("Unable to move patient {PatientId} to new poc {NewPocId} is null", patient.Id, newPocId);
return false;
}
if (newPoc.Status != StatusEnum.PointOfCare.InUse &&
newPoc.Status != StatusEnum.PointOfCare.Locked)
{
var oldPoc = await _pointOfCareService.GetInfo(oldPocId);
if (oldPoc == null)
{
_logger.LogError("Unable to move patient {PatientId} from old poc {OldPocId} is null", patient.Id,
oldPocId);
return false;
}
patient.Location = new PatientLocation(newPoc.UnitName, newPoc.Bed, newPoc.Room);
patient.Bed = newPoc.Bed;
patient.UnitString = newPoc.UnitName;
patient.Room = newPoc.Room;
patient.PointOfCareId = newPoc.Id;
patient.UnitId = newPoc.UnitId;
newPoc.Patient = patient;
await Update(patient);
await _pointOfCareService.SetPointOfCareStatus(newPoc.Id, StatusEnum.PointOfCare.InUse);
await _pointOfCareService.SetPointOfCareStatus(oldPoc.Id, StatusEnum.PointOfCare.Available);
_pointOfCareService.CheckNextAdmission(oldPoc.Id);
var currentDischarge = await _dischargeService.GetDischargeByPatientId(patient.Id);
if (currentDischarge != null)
{
_dischargeService.SendDischargeBroadcast(currentDischarge, OperationType.DeleteDischarge);
currentDischarge.PatientLocation = patient.Location;
currentDischarge.PointOfCareId = newPoc.Id;
currentDischarge.UnitId = newPoc.UnitId;
currentDischarge.Patient = patient;
await _dischargeService.UpdateDischargeAsync(currentDischarge);
}
// Enviar en base a la config de display que tenga cada subscriptor
_ = SendLastObsToSubscriberByPoc(newPoc, patient);
// Enviar las grupadas dependiendo del config del subscriptor
_ = SendLastGroupedObsToSubscriberByPoc(newPoc.Id, oldPoc.Id, patient);
return true;
}
_logger.LogError("Unable to move patient {PatientId} to new poc {NewPocId} is in use or locked", patient.Id,
newPocId);
return false;
}
public async Task<Patient?> FindById(ObjectId id, bool withLocation = false)
{
if (id == ObjectId.Empty)
return null;
var patient = await _patientRepository.FindById(id);
if (withLocation && patient != null)
{
if (patient.PointOfCareId.HasValue)
{
var poc = await _pointOfCareService.FindById(patient.PointOfCareId.Value) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
patient.Bed = poc.Bed;
patient.Room = poc.Room;
}
if (patient.UnitId.HasValue)
{
var unit = await _unitService.FindById(patient.UnitId.Value);
patient.UnitString = unit?.Name;
}
}
// var lastObs = await _observationService.Value.FindLastObservations(patient.Id,1);
// if (lastObs.Any())
// {
// patient.LastObservationDate = lastObs.First().Time;
// }
// TODO: check if it can be cached or served faster
return patient;
}
public async Task<Patient?> FindByLocation(PatientLocation? location)
{
if (location?.UnitName == null || location.Bed == null)
return null;
var unit = await _unitService.FindByName(location.UnitName);
if (unit == null) return null;
var poc = await _pointOfCareService.FindByBedAndUnitId(location.Bed, unit.Id);
if (poc == null) return null;
return await FindByUnitAndPocId(unit.Id, poc.Id);
}
public async Task<Patient?> FindByPointOfCareId(ObjectId pocId)
{
return await _patientRepository.FindByPointOfCareId(pocId);
}
public async Task<long> CountPatientsByUnitId(ObjectId unitId)
{
return await _patientRepository.CountByUnitId(unitId);
}
public async Task ArchivePatient(Patient patient)
{
patient.DisTime ??= DateTime.UtcNow;
//var sections = sectionService.FindByPatient(patient.id);
var pocId = patient.PointOfCareId;
_logger.LogDebug("{OnArchiveAction} patient {Patient}", _onArchiveAction, patient);
switch (_onArchiveAction)
{
case OnArchiveAction.Archive:
default:
patient.ArchiveDate = DateTime.UtcNow;
await _patientArchiveRepository.InsertOneAsync(patient);
break;
case OnArchiveAction.Delete:
//No hay archivado
break;
}
await _patientRepository.Delete(patient.Id);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, patient, null);
await ArchivePatientData(patient.Id);
if (pocId.HasValue)
{
_ = SendDeletePatientBroadcast(patient.Id, pocId.Value);
//cambiamos el estado del punto de cuidado a disponible
await _pointOfCareService.SetPointOfCareStatus(pocId.Value, StatusEnum.PointOfCare.Available);
}
}
public async Task ArchivePatientData(ObjectId patientid)
{
_logger.LogDebug("Archiving patient data action: {onArchiveAction} : {patientid}", _onArchiveAction,
patientid);
switch (_onArchiveAction)
{
case OnArchiveAction.Archive:
default:
await _observationService.Value.ArchiveByPatientId(patientid);
await _treatmentService.Value.ArchiveByPatientId(patientid);
await _diagnosisService.ArchiveByPatientId(patientid);
await _appointmentService.ArchiveByPatientId(patientid);
await _pumpService.Value.ArchiveByPatientId(patientid);
await _recordingAlertService.ArchiveByPatientId(patientid);
await _patientCarePlanService.ArchiveByPatientId(patientid);
break;
case OnArchiveAction.Delete:
await _observationService.Value.DeleteByPatientId(patientid);
await _treatmentService.Value.DeleteByPatientId(patientid);
await _diagnosisService.DeleteByPatientId(patientid);
await _appointmentService.DeleteByPatientId(patientid);
await _pumpService.Value.DeleteByPatientId(patientid);
await _recordingAlertService.DeleteByPatientId(patientid);
break;
}
}
public async Task Update(Patient patient)
{
var oldPatient = await _patientRepository.FindById(patient.Id);
try
{
_logger.LogDebug("Update {patient}", patient);
//To solve multi "sin cama" from ICCADB making duplicated keys error on mongodb.
if (patient.Location is { Bed: "Sin cama" }) patient.Location.Bed = ObjectId.GenerateNewId().ToString();
if (string.IsNullOrEmpty(patient.PatientNumber))
//si permite crear pacientes sin patientNumber le asignamos el patient.Id
patient.PatientNumber = !_cretatePatientWithoutPatientNumber ? patient.Id.ToString() : string.Empty;
await _patientRepository.Update(patient);
if (oldPatient != null)
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, patient);
_ = SendPatientUpdateBroadcast(patient);
}
catch (Exception e)
{
_logger.LogError("error updating patient: {patient} exception: {e.Message}", patient, e.Message);
}
}
public async Task UpdateLocation(ObjectId id, PatientLocation? location)
{
try
{
if (location == null)
{
_logger.LogError("PointOfCare is null. Not updated location for patient id: {id} ", id);
return;
}
_logger.LogDebug("Update location id: {id} data: {location}", id, location);
var patient = await _patientRepository.FindById(id);
var auxPatient = await _auditService.DeepCopyAsync(patient);
if (patient == null)
{
_logger.LogError("person is null. Not updated location for patient id: {id} ", id);
return;
}
if (patient.PointOfCareId == null)
{
_logger.LogError("person pocId or unitId is null. Not updated location for patient id: {id} ", id);
return;
}
var oldPoc = await _pointOfCareService.FindById(patient.PointOfCareId.Value);
if (oldPoc == null)
{
_logger.LogError("person old poc not found {pocId}. Not updated location for patient id: {id} ",
patient.PointOfCareId.Value, id);
return;
}
var isRecovered = Enum.TryParse<VirtualPointOfCare>(oldPoc.Bed, out _);
//"DELETED".Equals(patient.Location.UnitName);
//var oldLocation =new PatientLocation(oldUnit?.Name, oldPoc.Bed, oldPoc.Room);
if (isRecovered)
patient.DisTime = null;
var newUnit = await _unitService.FindByName(location.UnitName);
var newPoc = await _pointOfCareService.FindByBedAndUnitId(location.Bed, newUnit?.Id);
if (newUnit == null || newPoc == null)
{
_logger.LogError("person new poc not found {pocId}. Not updated location for patient id: {id} ",
patient.PointOfCareId.Value, id);
return;
}
patient.Location = new PatientLocation(newUnit.Name, newPoc.Bed, newPoc.Room);
patient.PointOfCareId = newPoc.Id;
patient.UnitId = newUnit.Id;
await _patientRepository.UpdateOneAsync(patient.Id, patient);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, auxPatient, patient);
if (isRecovered)
await SendNewPatientBroadcast(patient);
var pocListToNotify = new List<ObjectId> { newPoc.Id };
await _pointOfCareService.SetPointOfCareStatus(newPoc.Id, StatusEnum.PointOfCare.InUse);
pocListToNotify.Add(oldPoc.Id);
_pointOfCareService.CheckNextAdmission(oldPoc.Id);
//Prio location to advertise both locations of movement between sections
_logger.LogDebug(
"SendPatientLocationBroadcast id: {id} location: {location} newSection: {newSection}, oldSection: {oldSection}",
id, location, newPoc.Location.ToString(), oldPoc.Location.ToString());
if (!isRecovered)
_ = SendPatientLocationBroadcast(id, location, pocListToNotify);
if (oldPoc.Id != newPoc.Id)
CheckLocationForWsSubscriber(location, id);
}
catch (Exception ex)
{
_logger.LogError("error updating location for patient id: {id} exception: {exMessage}", id, ex.Message);
}
}
public async Task UpdateAttendingDoctor(ObjectId id, Person doctor)
{
var oldPatient = await _patientRepository.FindById(id) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictOperationIncompleted);
_logger.LogDebug("Update attending doctor id: {id} data: {doctor}", id, doctor);
await _patientRepository.UpdateAttendingDoctor(id, doctor);
var newPatient = await _patientRepository.FindById(id) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictOperationIncompleted);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, newPatient);
_ = SendPatientAttendingDoctorBroadcast(id, doctor);
}
public async Task UpdatePatientData(ObjectId id, string patientNumber, Person data,
bool updatePatientNumber = true)
{
var oldPatient = await _patientRepository.FindById(id) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictOperationIncompleted);
_logger.LogDebug("Update person data id: {id} number: {patientNumber} data: {data}", id, patientNumber,
data);
await _patientRepository.UpdatePatientData(id, patientNumber, data, updatePatientNumber);
var newPatient = await _patientRepository.FindById(id) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictOperationIncompleted);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, newPatient);
_ = SendPatientDataBroadcast(id, data);
}
public async Task UpdatePatientData(ObjectId id, string patientNumber, Patient patient,
bool updatePatientNumber = true)
{
_logger.LogDebug("Update person data id: {id} number: {patientNumber} patient: {patient}", id,
patientNumber, patient);
if (patient.Person != null)
{
var oldPatient = await _patientRepository.FindById(id) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictOperationIncompleted);
await _patientRepository.UpdatePatientData(id, patientNumber, patient.Person, updatePatientNumber);
var newPatient = await _patientRepository.FindById(id) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictOperationIncompleted);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, newPatient);
}
_ = SendPatientBroadcast(patient);
}
public async Task<Patient?> FindByPatientId(string patientId, bool withLocation = false)
{
var patient = await _patientRepository.FindByPatientId(patientId);
if (withLocation && patient != null)
{
if (patient.PointOfCareId.HasValue)
{
var poc = await _pointOfCareService.FindById(patient.PointOfCareId.Value);
patient.Bed = poc?.Bed;
patient.Room = poc?.Room;
}
if (patient.UnitId.HasValue)
{
var unit = await _unitService.FindById(patient.UnitId.Value);
patient.UnitString = unit?.Name;
}
}
return patient;
}
public async Task<Patient?> FindByPatientNumber(string patientNumber, bool withLocation = false)
{
var patient = await _patientRepository.FindByPatientNumber(patientNumber);
if (withLocation && patient != null)
{
if (patient.PointOfCareId.HasValue)
{
var poc = await _pointOfCareService.FindById(patient.PointOfCareId.Value);
patient.Bed = poc?.Bed;
patient.Room = poc?.Room;
}
if (patient.UnitId.HasValue)
{
var unit = await _unitService.FindById(patient.UnitId.Value);
patient.UnitString = unit?.Name;
}
}
return patient;
}
public async Task<Patient?> FindByPatientNumberArchived(string patientNumber)
{
return await _patientArchiveRepository.FindByPatientNumber(patientNumber);
}
public async Task ArchivePatientWithoutObservationsSinceDate(DateTime date)
{
try
{
// Observations not updated since date, archive their patients and data
var ptimes = (await _observationService.Value.FindAllLastPatientObservationTime())
.Where(o => date > o.Value).ToDictionary(o => o.Key, o => o.Value);
(await _pumpService.Value.FindAllLastPatientObservationTime()).Where(o => date > o.Value)
.Where(o => !ptimes.ContainsKey(o.Key) || ptimes[o.Key] > o.Value).ToList()
.ForEach(o => ptimes[o.Key] = o.Value);
//ptimes.ToList().Sort( (o1, o2) => o1.Value.CompareTo(o2.Value));
var foundPatients = new HashSet<Patient>();
var notFoundPatients = new HashSet<ObjectId>();
foreach (var o in ptimes)
{
var patient = await FindById(o.Key);
if (patient != null) foundPatients.Add(patient);
else notFoundPatients.Add(o.Key);
}
var notUpdatedPatients = await _patientRepository.FindPatientsNotUpdatedSince(date);
foreach (var notUpdatedPatient in notUpdatedPatients.Where(p => p.IsInActivePoC()))
{
var obsPatient = await _observationService.Value.FindLastObservations(notUpdatedPatient.Id);
var pumpPatient = await _pumpService.Value.FindLastPumpObservations(notUpdatedPatient.Id);
if (!foundPatients.Contains(notUpdatedPatient) && obsPatient.Count == 0 && pumpPatient.Count == 0)
foundPatients.Add(notUpdatedPatient);
}
if (foundPatients.Count > 0)
{
_logger.LogDebug("Patients found for archive: {foundPatientsCount}", foundPatients.Count);
foreach (var patient in foundPatients)
try
{
await ArchivePatient(patient);
}
catch (Exception ex)
{
_logger.LogError(
"ERROR trying to ArchivePatient {patientId} because {exMessage}", patient.Id,
ex.Message);
}
}
if (notFoundPatients.Count > 0)
{
_logger.LogWarning("Patients not found in patient collection: {notFoundPatientsCount}",
notFoundPatients.Count);
foreach (var patientId in notFoundPatients)
{
_logger.LogWarning("- Deleting orphan data for patient: {patientId}", patientId);
try
{
await ArchivePatientData(patientId);
}
catch (Exception ex)
{
_logger.LogError(
"ERROR trying to ArchivePatientData {patientId} because {exMessage}", patientId,
ex.Message);
}
}
}
}
catch (Exception ex)
{
_logger.LogError(
"ERROR trying to retrieve patients without observations in date range because {exMessage}",
ex.Message);
throw;
}
}
public async Task ArchiveDischargedPatients(int hoursBeforeArchive)
{
var disBeforeDate = DateTime.Now.AddHours(-hoursBeforeArchive);
try
{
var dischargedPatients = (await _patientRepository.FindDischargedPatients())
.Where(patient =>
patient.DisTime != null &&
patient.DisTime.Value < disBeforeDate
).ToList();
_logger.LogDebug(
"ArchiveDischargedPatients older than {HoursBeforeArchive} hours ({DisBeforeDate}). Found {Count} patients for archiving",
hoursBeforeArchive, disBeforeDate, dischargedPatients.Count);
foreach (var patient in dischargedPatients) await ArchivePatient(patient);
}
catch (Exception ex)
{
_logger.LogError("Error archiving discharged patients {Message}", ex.Message);
}
}
public async Task<Patient?> FindPatient(string? patientId, string? patientNumber, PatientLocation? location,
bool findPatientByLocation = false)
{
Patient? patient = null;
if (!string.IsNullOrEmpty(patientNumber))
patient = await FindByPatientNumber(patientNumber, true);
if (patient != null) return patient;
if (!string.IsNullOrEmpty(patientId))
patient = await FindByPatientId(patientId, true);
if (patient != null) return patient;
// No buscamos por localización por defecto
if (!_findPatientByLocationWithoutId && !findPatientByLocation) return patient;
//if location pointOfCare and bed != null
if (location is { UnitName: not null, Bed: not null })
{
var loc = await _pocMappingService.Map(location);
patient = await FindByLocation(loc);
if(patient is not null && loc is not null)
patient.Location = loc;
}
return patient;
}
public async Task<Patient?> GetByPointOfCare(PointOfCare item, bool observations = false,
List<string>? filterObservations = null)
{
try
{
return await _patientRepository.FindByPointOfCareId(item.Id);
}
catch (Exception ex)
{
Debug.WriteLine("ERROR: " + ex.Message);
return null;
}
}
public async Task<Patient?> GetByPointOfCareAndLocale(PointOfCare item, Unit? unit, LocaleEnum? localeEnum)
{
try
{
var patient = await _patientRepository.FindByPointOfCareId(item.Id);
if (unit == null || localeEnum == null) return patient;
return await _masterListServiceFactory.GetPatientTraslated(unit, localeEnum.Value, patient);
}
catch (Exception ex)
{
Debug.WriteLine("ERROR: " + ex.Message);
return null;
}
}
public async Task<Box?> GetBox(PointOfCare poc, bool observations = false,
List<string>? filterObservations = null)
{
Box response = new();
Patient? patient = null;
try
{
patient = await _patientRepository.FindByPointOfCareId(poc.Id);
}
catch (Exception ex)
{
Debug.WriteLine("ERROR: " + ex.Message);
}
response.PointOfCare = poc.UnitName;
response.Bed = poc.Bed;
response.IsVisible = true;
if (patient == null)
{
response.HasPatient = false;
return response;
}
response.HasPatient = true;
response.Patientid = patient.Id;
response.Patient = patient;
response.AttendingDoctor = patient.AttendingDoctor;
if (!observations) return response;
var obs = await _observationService.Value.FindLastObservations(patient.Id, 2, filterObservations);
obs.ForEach(Action);
response.Observations = obs;
return response;
void Action(PatientObservation o)
{
_observationService.Value.MapObservationsByName(o);
}
}
public Task SendNewPatientBroadcast(Patient patient)
{
var subscribers = new List<WsSubscriber>();
// var section = await _sectionService.FindByPointOfCare(patient.UnitString);
// if (section != null)
// {
// subscribers.AddRange(_subscribersService.GetSubscribers().Where(s =>
// (s.SubscriptionType == SubscriptionType.Box && s.Box == patient.Bed && s.Section == section.Id) ||
// (s.SubscriptionType == SubscriptionType.Section && s.Section == section.Id)).ToList());
// }
subscribers.AddRange(_subscribersService.GetSubscribers().Where(s =>
!s.LocationIds.IsNullOrEmpty() && s.LocationIds.Any(c =>
c == patient.PointOfCareId
)).ToList());
foreach (var subscriber in subscribers)
_ = _clientMessageService.SendAsync(subscriber.Id, OperationType.NewPatient, patient);
return Task.CompletedTask;
}
public async Task SendPatientUpdateBroadcast(Patient patient)
{
if (!patient.PointOfCareId.HasValue)
{
_logger.LogError("patient pointOfCareId is null. Not sent patient id: {id} ", patient.Id);
return;
}
var subscribersGroup = _subscribersService.GetSubscribers().Where(s =>
s.LocationIds.Any(c => c == patient.PointOfCareId.Value)).GroupBy(h => h.Locale);
var unit = await _unitService.FindById(patient.UnitId);
foreach (var group in subscribersGroup)
{
var locale = group.Key ?? LocaleEnum.Default;
IEnumerable<WsSubscriber> subscribers = group;
foreach (var subscriber in subscribers)
{
var patientWithLocale = await _masterListServiceFactory.GetPatientTraslated(unit, locale, patient);
_ = _clientMessageService.SendAsync(subscriber.Id, OperationType.UpdatePatient, patientWithLocale);
}
}
}
public Task SaveRequestAsync(ApiRequest apiRequest)
{
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
}
public async Task SaveRequest(ApiRequest apiRequest)
{
if (string.IsNullOrEmpty(apiRequest.PatientNumber) &&
string.IsNullOrEmpty(apiRequest.Location?.UnitName) && apiRequest.Type != "ICCA")
{
_logger.LogDebug("person and PointOfCare are nulls");
throw new ApiRequestException("person and PointOfCare are nulls");
}
_logger.LogDebug("patientNumber: {apiRequestpatientNumber} location: {apiRequestlocation}",
apiRequest.PatientNumber, apiRequest.Location);
if (apiRequest.Location != null)
{
var originalPointOfCare = apiRequest.Location;
apiRequest.Location = await _pocMappingService.Map(originalPointOfCare);
if (apiRequest.Location == null)
{
_logger.LogWarning("PointOfCare not found in Mapping list: {originalPointOfCare}",
originalPointOfCare.ToString());
return;
}
}
if (apiRequest.OldLocation != null)
{
var originalOldPointOfCare = apiRequest.OldLocation;
apiRequest.OldLocation = await _pocMappingService.Map(originalOldPointOfCare);
}
_logger.LogDebug("RequestType: {apiRequesttype}", apiRequest.Type);
var unitConfig = await _unitService.FindByName(apiRequest.Location?.UnitName);
Unit? oldUnitConfig = null;
if (apiRequest.OldLocation != null)
{
var originalOldLocation = await _pocMappingService.Map(apiRequest.OldLocation);
if (originalOldLocation != null)
oldUnitConfig = await _unitService.FindByName(apiRequest.OldLocation?.UnitName);
}
if (apiRequest.Type != "ICCA")
{
if (apiRequest.PatientNumber != null && unitConfig == null && oldUnitConfig == null)
{
var pat = await FindByPatientNumber(apiRequest.PatientNumber);
if (pat is { UnitId: not null }) unitConfig = await _unitService.FindById(pat.UnitId);
}
if (!Hl7Utils.ManageAutoAdt(unitConfig, oldUnitConfig, _logger, "ADT")) return;
}
//TODO Crear para el adt5
try
{
Patient? patient = null;
switch (apiRequest.Type)
{
//* ADT EVENTS
case "ADT_A01":
case "ADT_A05":
{
//* A01 Admit/visit notification
//* A05 Pre-admit a patient
_logger.LogDebug("NEW ADT {apiRequestType} PATIENT NUMBER: {apiRequestPatientNumber}",
apiRequest.Type, apiRequest.PatientNumber ?? "NULL");
patient = await ProcessAdtPatientAdmit(apiRequest);
break;
}
case "ADT_A02":
{
//* A02 Transfer a patient
if (apiRequest.Location != null)
apiRequest.Location = await _pocMappingService.Map(apiRequest.Location);
patient = await FindPatient(apiRequest.PatientId, apiRequest.PatientNumber,
apiRequest.OldLocation);
await ProcessAdtMovePatient(apiRequest, patient);
break;
}
case "ADT_A03":
{
// * A03 Discharge/end visit
// *Ya no archiva, archiva el job del Scheduler service pasado el tiempo establecido por el hospital en SinceDischargeTimeToArchive
// se le mueve a una cama temporal.
_logger.LogDebug("NEW ADT {apiRequestType} PATIENT NUMBER: {apiRequestPatientNumber}",
apiRequest.Type, apiRequest.PatientNumber);
if (apiRequest.Location != null)
apiRequest.Location = await _pocMappingService.Map(apiRequest.Location);
patient = await FindPatient(apiRequest.PatientId, apiRequest.PatientNumber,
apiRequest.Location);
if (patient != null)
await RemovePatientAndSendToVirtualPoc(apiRequest, patient, VirtualPointOfCare.Deleted);
else
_logger.LogDebug("ADT A03 not found patient number {number}", apiRequest.PatientNumber);
break;
}
case "ADT_A04":
{
// * A04 Register a patient
if (apiRequest.Location != null)
apiRequest.Location = await _pocMappingService.Map(apiRequest.Location);
patient = await FindPatient(apiRequest.PatientId, apiRequest.PatientNumber,
apiRequest.Location);
if (patient == null)
patient = await CreatePatientFromRequest(apiRequest);
else
_logger.LogDebug("ADT A04 not found patient number {number}", apiRequest.PatientNumber);
break;
}
case "ADT_A08":
{
//* A08 Update patient information
patient = await FindPatient(apiRequest.PatientId, apiRequest.PatientNumber,
apiRequest.Location);
if (patient != null)
{
await UpdatePatientFromRequest(patient, apiRequest, true);
await Update(patient);
}
else
{
_logger.LogDebug("ADT A08 not found by patient number {number}", apiRequest.PatientNumber);
if (_createPatientWithAdtA08)
{
_logger.LogDebug(
"ProcessAdtPatientAdmit ADT_A08 Because CreatePatientWithADT_A08 is true {apiRequestpatientNumber}",
apiRequest.PatientNumber);
await ProcessAdtPatientAdmit(apiRequest);
}
}
break;
}
// A11 Cancel admit/ visit notification/
// Remove patient
case "ADT_A11":
{
var patientNumber = apiRequest.PatientNumber;
if (patientNumber == null)
{
_logger.LogError("ADT A11 Without patient number {number}", apiRequest.PatientNumber);
}
else
{
patient = await _patientRepository.FindByPatientNumber(patientNumber);
if (patient != null)
await RemovePatientAndSendToVirtualPoc(apiRequest, patient,
VirtualPointOfCare.Cancelled);
else
_logger.LogDebug("ADT A11 not found patient number {number}", apiRequest.PatientNumber);
}
break;
}
//* A12 Cancel patient transfer.
//* Se mueve el patientNumber que viene a la cama que indica, si hay ya un paciente en esa cama ese paciente se mueve a una temporal
case "ADT_A12":
{
var patientNumber = apiRequest.PatientNumber;
if (apiRequest.Location != null)
apiRequest.Location = await _pocMappingService.Map(apiRequest.Location);
if (patientNumber == null)
{
_logger.LogError("ADT A12 Without patient number {number}", apiRequest.PatientNumber);
}
else
{
patient = await _patientRepository.FindByPatientNumber(patientNumber);
await ProcessAdtMovePatient(apiRequest, patient);
}
break;
}
//* A13 Cancel discharge/end visit
//* Recuperamos el paciente, seteamos ArchiveDate a null.Lo movemos a la cama que nos indique.
case "ADT_A13":
{
if (apiRequest.Location != null)
apiRequest.Location = await _pocMappingService.Map(apiRequest.Location);
var patientNumber = apiRequest.PatientNumber;
if (patientNumber == null)
{
_logger.LogError("ADT A13 Without patient number {number}", apiRequest.PatientNumber);
}
else
{
patient = await _patientRepository.FindByPatientNumber(patientNumber);
if (patient != null)
{
if ("".Equals(apiRequest.Location?.Bed?.Replace("\"\"", "")))
{
var unit = await _unitService.FindById(patient.UnitId);
apiRequest.Location = new PatientLocation(unit?.Name,
VirtualPointOfCare.Recovered.ToString(), VirtualPointOfCare.Recovered.ToString());
}
await ProcessAdtMovePatient(apiRequest, patient);
}
else
{
_logger.LogDebug("ADT A13 not found patient number {number}", apiRequest.PatientNumber);
}
}
break;
}
case "ADT_A31":
{
//* ADT_A31 - Update Person Information
patient = await FindPatient(apiRequest.PatientId, apiRequest.PatientNumber,
apiRequest.Location);
if (patient != null)
{
await UpdatePatientFromRequest(patient, apiRequest, true);
await Update(patient);
}
else
{
_logger.LogDebug("ADT A31 not found patient number {number}", apiRequest.PatientNumber);
}
break;
}
//ADT_A39 - Merge Person - Patient Id
//ADT_A40 - Merge patient - Patient Identifier List
case "ADT_A39":
case "ADT_A40":
{
if (apiRequest.Location != null)
apiRequest.Location = await _pocMappingService.Map(apiRequest.Location);
var patientNumber = apiRequest.PatientNumber;
if (patientNumber == null)
{
_logger.LogError("ADT A39 Without patient number {number}", apiRequest.PatientNumber);
}
else
{
patient = await _patientRepository.FindByPatientNumber(patientNumber);
if (patient != null && !string.IsNullOrEmpty(apiRequest.OldPatientNumber))
await MergePatient(patient, apiRequest.OldPatientNumber);
else
_logger.LogDebug("ADT A39 not found patient number {number}", apiRequest.PatientNumber);
}
break;
}
//ADT A_47: actualización de lista de ids
case "ADT_A47":
{
//Para el A47 no envía necesariamente el el location
//Buscamos el paciente por patientNumber
if (!string.IsNullOrEmpty(apiRequest.PatientNumber))
patient = await _patientRepository.FindByPatientNumber(apiRequest.PatientNumber);
if (patient == null)
{
_logger.LogError("{type}: Patient not found in apiRequest: {apirequest}", apiRequest.Type,
apiRequest.ToString());
break;
}
_logger.LogDebug("Updating patient from request. Patient: {patient} ", patient.ToString());
UpdatePatientFromRequestToIdList(patient, apiRequest);
_logger.LogDebug("Updating patient. Patient: {patient}", patient.ToString());
await Update(patient);
_logger.LogDebug("updated patient: {patientId}", patient.Id);
break;
}
//ADT A_44: actualización de patient number
case "ADT_A44":
{
//Cambiar el patientNumber
var oldPatientNumber = apiRequest.OldPatientNumber;
if (oldPatientNumber == null)
{
if (apiRequest.Location == null)
{
//A44 no tiene ni oldPatientNumber ni ubicación
//No se procesa
_logger.LogError("{type} Without old Patient Number and location ", apiRequest.Type);
break;
}
//Buscamos al paciente por su localización
patient = await _patientRepository.FindByLocation(apiRequest.Location);
}
else
{
patient = await _patientRepository.FindByPatientNumber(oldPatientNumber);
}
if (patient == null)
{
_logger.LogDebug("ADT A44 not found patient");
break;
}
_logger.LogDebug("Updating patient from request. Patient: {patient} ", patient.ToString());
await UpdatePatientFromRequest(patient, apiRequest);
_logger.LogDebug("Updating patient. Patient: {patient}", patient.ToString());
await Update(patient);
_logger.LogDebug("updated patient: {patientId}", patient.Id);
break;
}
//* A06 Change an outpatient to an inpatient
//* A07 Change an inpatient to an outpatient
//* A08 Update patient information
//* A09 Patient departing - tracking
//* A10 Patient arriving - tracking
//* A12 Cancel transfer
//* A14 Pending admit
//* A15 Pending transfer
//* A16 Pending discharge
//* A17 Swap patients
//* A18 Merge patient information
//* A19 QRY/ADR - Patient query
//* A20 Bed status update
case "ADT_A06":
case "ADT_A07":
case "ADT_A09":
case "ADT_A10":
case "ADT_A14":
case "ADT_A15":
case "ADT_A16":
case "ADT_A17":
case "ADT_A18":
case "ADT_A19":
case "ADT_A20":
_logger.LogWarning("{apiRequesttype} is not valid for Patients", apiRequest.Type);
break;
//For icca db sync
case "ICCA":
_ = ProcessIccaSync(apiRequest);
break;
default:
throw new ApiRequestException("ApiRequest type " + apiRequest.Type +
" is not valid for Patients");
}
if (patient != null)
{
if (apiRequest.Observations != null && apiRequest.Observations.Any())
_observationService.Value.ProcessObservations(apiRequest.Observations, patient,
apiRequest.MessageTime, apiRequest.ObservationData);
if (apiRequest.Diagnosis != null && apiRequest.Diagnosis.Any())
_ = _diagnosisService.ProcessDiagnosis(apiRequest.Diagnosis, patient, apiRequest.MessageTime);
if (apiRequest.Appointments != null && apiRequest.Appointments.Any())
_ = _appointmentService.ProcessApiRequest(apiRequest, patient);
}
}
catch (Exception ex)
{
_logger.LogError("ERROR saving patient: {exMessage} trace: {exStackTrace}", ex.Message, ex.StackTrace);
}
}
public async Task<Patient?> CreatePatientFromRequest(ApiRequest apiRequest, bool ignoreLocation = false)
{
var patient = new Patient
{
Id = ObjectId.GenerateNewId(),
CreationDate = DateTime.UtcNow
};
await UpdatePatientFromRequest(patient, apiRequest, ignoreLocation);
await _patientRepository.InsertOneAsync(patient);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, patient);
return patient;
}
public async Task<Patient?> FindPatientByApiRequest(ApiRequest apiRequest)
{
Patient? patient = null;
if (apiRequest.Location != null)
{
var originalPointOfCare = apiRequest.Location;
apiRequest.Location = await _pocMappingService.Map(originalPointOfCare);
if (apiRequest.Location == null)
{
_logger.LogDebug("PointOfCare not found in Mapping list: {OriginalPointOfCare} ",
originalPointOfCare);
return patient;
}
}
_logger.LogDebug("RequestType: {apiRequest}", apiRequest.Type);
try
{
switch (apiRequest.Type)
{
//* ORU_R01 - Unsolicited transmission of an observation message
//* ORU_R40 - Unsolicited transmission of an alert observation message
//* AÑADIDO. SI EL FACILITY DEL API REQUEST NO ES EL DE ICCA BUSCA SOLO POR POINT OF CARE Y BED NO POR PATIENT ID.
case "AlarisPump":
case "ORU_R01":
case "ORU_R42":
case "ORU_R40":
_logger.LogDebug("patientNumber: {apiRequestpatientNumber} location: {apiRequestlocation}",
apiRequest.PatientNumber, apiRequest.Location);
// SEARCH PATIENT || SendingFacility.Contains(admRequest.facility)
if (!string.IsNullOrWhiteSpace(apiRequest.PatientNumber) &&
!string.IsNullOrWhiteSpace(apiRequest.Facility) &&
(apiRequest.Facility.Equals(_iccaFacility) || apiRequest.Facility.Equals("obs")))
{
// SEARCH PATIENT BY PATIENT NUMBER
_logger.LogDebug("SEARCH PATIENT BY PATIENT NUMBER");
patient = await FindByPatientNumber(apiRequest.PatientNumber, true);
if (patient == null)
// PATIENT NOT FOUND OR ARCHIVED
_logger.LogDebug("PATIENT NOT FOUND OR ARCHIVED");
}
if (patient == null)
{
_logger.LogDebug("PATIENT {apiRequestpatient}", apiRequest.Patient);
_logger.LogDebug("PATIENT NUMBER {apiRequestpatientNumber}", apiRequest.PatientNumber);
_logger.LogDebug("PATIENT LOCATION {apiRequestlocation} EMPTY {apiRequestlocationIsEmpty}",
apiRequest.Location, apiRequest.Location?.ToStr() ?? "null");
if (!string.IsNullOrEmpty(apiRequest.PatientNumber) &&
!string.IsNullOrWhiteSpace(apiRequest.Facility) &&
(apiRequest.Facility.Equals(_iccaFacility) || _sendingFacility.Contains(apiRequest
.Facility)))
{
_logger.LogDebug("PATIENT LOCATION Find person by PatientNumber");
var patientExists =
await _patientRepository.FindByPatientNumber(apiRequest.PatientNumber);
if (patientExists != null)
{
patient = patientExists;
}
else
{
_logger.LogDebug("PATIENT LOCATION IS EMPTY.Not found Patient by PatientNumber");
if (_createPatientWithOru)
{
if (apiRequest.Location == null || apiRequest.Location.IsEmpty())
{
_logger.LogDebug(
"PATIENT LOCATION IS EMPTY. Generate temporal bed because createPatientWithoutLocation is TRUE");
apiRequest.Location = new PatientLocation(
VirtualPointOfCare.Unknown.ToString(),
VirtualPointOfCare.Unknown.ToString());
}
if (!apiRequest.Location.IsEmpty() &&
string.IsNullOrEmpty(apiRequest.Location.Bed))
apiRequest.Location.Bed = VirtualPointOfCare.Unknown.ToString();
patient = new Patient
{
Id = ObjectId.GenerateNewId(),
PatientNumber = apiRequest.PatientNumber,
Person = apiRequest.Patient,
AttendingDoctor = apiRequest.AttendingDoctor,
Location = apiRequest.Location
};
var patientLocation = await FindPatient(null, null, apiRequest.Location, true);
if (patientLocation != null)
{
_logger.LogDebug(
"PATIENT LOCATION IS OCUPED, Created UNKNOW location {patient}.",
patient);
var pocUnknown =
await _pointOfCareService.FindByBedAndUnitId(
VirtualPointOfCare.Unknown.ToString(),
patientLocation.UnitId);
patient.UnitId = patientLocation.UnitId;
patient.PointOfCareId = pocUnknown?.Id;
var un = await _unitService.FindById(patientLocation.UnitId);
patient.Location = new PatientLocation(
un?.Name ?? VirtualPointOfCare.Unknown.ToString(),
pocUnknown?.Bed ?? VirtualPointOfCare.Unknown.ToString());
}
else
{
var unit = await _unitService.FindByName(apiRequest.Location.UnitName);
var poc =
await _pointOfCareService.FindByBedAndUnitId(apiRequest.Location.Bed,
unit?.Id);
if (unit != null && poc != null)
{
patient.UnitId = poc.UnitId;
patient.PointOfCareId = poc.Id;
patient.Location = new PatientLocation(
unit.Name,
poc.Bed);
}
else
{
patient.Location = new PatientLocation(
VirtualPointOfCare.Unknown.ToString(),
VirtualPointOfCare.Unknown.ToString());
}
}
_logger.LogDebug(
"CREATE PATIENT BECAUSE createPatientWithORU CONFIG IS TRUE {patient}",
patient);
await Insert(patient);
}
}
}
else
{
patient = await FindPatient(null, apiRequest.PatientNumber, apiRequest.Location, true);
if (!string.IsNullOrWhiteSpace(apiRequest.PatientNumber) ||
(Enum.TryParse<VirtualPointOfCare>(apiRequest.Location?.Bed, out var dataPoC) &&
dataPoC == VirtualPointOfCare.UnitData))
{
if (patient == null)
{
if (_createPatientWithLocation)
{
_logger.LogDebug(
"PATIENT LOCATION NOT IS EMPTY. Generate patient because createPatientWithLocation is TRUE");
patient = new Patient
{
Id = ObjectId.GenerateNewId()
};
await UpdatePatientFromRequest(patient, apiRequest);
_logger.LogDebug(
"CREATE PATIENT BECAUSE CreatePatientWithLocation CONFIG IS TRUE {patient}",
patient);
await Insert(patient);
}
}
else
{
if (_pushPatientWithOru && apiRequest.PatientNumber != patient.PatientNumber)
{
var admTime = patient.AdmTime;
var msgTime = apiRequest.MessageTime;
//Si la fecha de admisión es anterior < a la fecha del mensaje (con un patient number distinto)
// estamos recibiendo orus para un paciente que no tenemos aun insertado en esa localización entonces lo creamos desplazando al que está actualmente
if (admTime < msgTime)
{
_logger.LogDebug(
"PATIENT LOCATION IS NOT EMPTY AND PATIENT NUMBER IS NOT THE SAME. Generate patient because CreatePatientWithORU is TRUE AND ADMTIME IS OLDER THAN MSGTIME AND PUSH OLD PATIENT {patient}",
patient);
var unit = await _unitService.FindById(patient.UnitId);
await UpdateLocation(patient.Id,
new PatientLocation(unit?.Name,
VirtualPointOfCare.Pushed.ToString()));
patient = new Patient
{
Id = ObjectId.GenerateNewId()
};
await UpdatePatientFromRequest(patient, apiRequest);
_logger.LogDebug(
"CREATE PATIENT BECAUSE CreatePatientWithORU CONFIG IS TRUE {patient}",
patient);
await Insert(patient);
}
}
}
}
}
}
else
{
// PATIENT FOUND BY PATIENT NUMBER
_logger.LogDebug("PATIENT FOUND BY PATIENT NUMBER");
if (apiRequest.Location is { Bed: not null } && !apiRequest.Location.Equals(patient.Location))
{
//Al cambiar de localizacion set active nueva localizacion set innactive old localizacion
// PATIENT LOCATION HAS CHANGED
_logger.LogDebug("PATIENT LOCATION HAS CHANGED");
if (_updatePatientLocationWithOru)
{
// UPDATE PATIENT LOCATION BECAUSE updatePatientLocationWithORU CONFIG IS TRUE
_logger.LogDebug(
"UPDATE PATIENT LOCATION BECAUSE updatePatientLocationWithORU CONFIG IS TRUE");
patient.Location = apiRequest.Location;
var patientExistsInLocation = await FindByLocation(apiRequest.Location);
if (patientExistsInLocation != null &&
patient.PatientNumber != patientExistsInLocation.PatientNumber)
{
var unit = await _unitService.FindById(patientExistsInLocation.UnitId);
var newLocation = new PatientLocation(unit?.Name,
VirtualPointOfCare.Unknown.ToString());
await UpdateLocation(patientExistsInLocation.Id, newLocation);
}
// ONLY ADTS archive if (patientExistsInLocation != null) patientService.ArchivePatient(patientExistsInLocation);
await ProcessUpdatePatientLocationWithOru(patient, apiRequest);
}
}
}
if (_updatePatientDataWithOru && patient != null)
{
if (apiRequest.AttendingDoctor != null &&
!apiRequest.AttendingDoctor.Equals(patient.AttendingDoctor))
{
// PATIENT DOCTOR CHANGED
_logger.LogDebug("PATIENT FOUND BY LOCATION");
patient.AttendingDoctor = apiRequest.AttendingDoctor;
await UpdateAttendingDoctor(patient.Id, apiRequest.AttendingDoctor);
}
if (apiRequest.Patient != null && !apiRequest.Patient.Equals(patient.Person))
{
// PATIENT DATA CHANGED
_logger.LogDebug("PATIENT DATA CHANGED");
if (apiRequest.Patient?.BirthDate != null)
{
patient.Person ??= new Person();
patient.Person.BirthDate = apiRequest.Patient.BirthDate;
}
if (apiRequest.Patient?.Language != null)
{
patient.Person ??= new Person();
patient.Person.Language = apiRequest.Patient.Language;
}
if (apiRequest.Patient?.FirstName != null)
{
patient.Person ??= new Person();
patient.Person.FirstName = apiRequest.Patient.FirstName;
}
if (apiRequest.Patient?.SecondName != null)
{
patient.Person ??= new Person();
patient.Person.SecondName = apiRequest.Patient.SecondName;
}
if (apiRequest.Patient?.LastName != null)
{
patient.Person ??= new Person();
patient.Person.LastName = apiRequest.Patient.LastName;
}
if (apiRequest.Patient?.Ids is { Count: > 0 })
{
patient.Person ??= new Person();
patient.Person.SetIds(apiRequest.Patient.Ids, apiRequest.MessageTime);
}
//bool updatePatientNumber = false;
//patient.patient = admRequest.patient;
//Solo actualizamos patientNumber de paciente si es de ICCA el mensaje. En central puede haber patient numbers incorrectos.
if (apiRequest.PatientNumber is { Length: > 0 } &&
!string.IsNullOrWhiteSpace(apiRequest.Facility) &&
apiRequest.Facility.Equals(_iccaFacility))
patient.PatientNumber = apiRequest.PatientNumber;
//UpdatePatientData(patient.id, patient.patientNumber, patient.patient);
if (patient.PatientNumber != null)
await UpdatePatientData(patient.Id, patient.PatientNumber, patient);
}
}
break;
}
}
catch (Exception ex)
{
_logger.LogError("ERROR SAVING REQUEST: Exception: {exMessage} ", ex.Message);
return null;
}
return patient;
}
public async Task<List<Patient>> FindAll(bool withLocation = false)
{
var patients = await _patientRepository.FindAll();
if (!withLocation) return patients;
foreach (var patient in patients)
{
if (patient.PointOfCareId.HasValue)
{
var poc = await _pointOfCareService.FindById(patient.PointOfCareId.Value);
patient.Bed = poc?.Bed;
patient.Room = poc?.Room;
}
if (patient.UnitId.HasValue)
{
var unit = await _unitService.FindById(patient.UnitId);
patient.UnitString = unit?.Name;
}
}
return patients;
}
public async Task<List<Patient>> FindInActivePoC()
{
return await _patientRepository.FindInActivePoC();
}
public async Task<List<Patient>> FindInInactivePoC()
{
return await _patientRepository.FindInInactivePoC();
}
public async Task<PaginationResponse<Patient>> GetPaginatedPatients(PaginationFilter filter)
{
var result = _patientRepository.GetPaginatedPatients(filter);
var count = await result.CountDocumentsAsync();
var data = await result.Skip((filter.PageNumber - 1) * filter.PageSize)
.Limit(filter.PageSize)
.ToCursorAsync();
var dataList = await data.ToListAsync();
return new PaginationResponse<Patient>(dataList, filter.PageNumber, filter.PageSize, count);
}
public async Task<Patient?> FindByUnitAndPocId(ObjectId unit, ObjectId pointOfCare)
{
return await _patientRepository.FindByUnitAndPocId(unit, pointOfCare);
}
public async Task<List<Patient>> FindByPointOfCare(string pointOfCare)
{
return await _patientRepository.FindByPointOfCare(pointOfCare);
}
public async Task MergePatient(Patient patient, string oldPatientNumber)
{
var oldLocation = patient.PointOfCareId; // sectionService.FindByPatient(patient.id);
var oldPatient = await FindByPatientNumber(oldPatientNumber);
if (oldPatient == null)
{
_logger.LogDebug(
"the patient has not been merged: {oldPatientNumber} in patient {patientpatientId} , old patient not found ",
oldPatientNumber, patient.PatientId);
return;
}
var oldId = oldPatient.Id;
_logger.LogDebug("merge patient action: {oldPatientNumber} in {patientpatientId}", oldPatientNumber,
patient.PatientId);
//TODO: TO CHECK
_ = _observationService.Value.UpdateManyObjectId("patientid", patient.Id, oldId);
_ = _treatmentService.Value.UpdateManyObjectId("patientid", patient.Id, oldId);
_ = _diagnosisService.UpdateManyObjectId("patientid", patient.Id, oldId);
_ = _appointmentService.UpdateManyObjectId("patientid", patient.Id, oldId);
_ = _pumpService.Value.UpdateManyObjectId("patientid", patient.Id, oldId);
_ = _recordingAlertService.UpdateManyObjectId("patientid", patient.Id, oldId);
_ = _patientCarePlanService.UpdateManyObjectId("patientId", patient.Id, oldId);
_ = _patientRepository.Delete(oldId);
if (oldLocation.HasValue)
{
await UpdatePatientFromMerge(patient, oldPatient);
await SendDeletePatientBroadcast(patient.Id, oldLocation.Value);
_pointOfCareService.CheckNextAdmission(oldPatient.PointOfCareId);
}
}
public async Task DischargeInactivePatients(DateTime sinceDate, int hoursBeforeArchive)
{
try
{
GlobalData.AddData("isCheckingInactivePatients", true);
await ArchiveDischargedPatients(hoursBeforeArchive);
await ArchiveNotUpdatedPatientsInInactivePoCSince(hoursBeforeArchive);
await ArchivePatientWithoutObservationsSinceDate(sinceDate);
}
catch (Exception ex)
{
_logger.LogError("Error discharging inactive patients, error: {exMessage}, trace: {exStackTrace}",
ex.Message, ex.StackTrace);
}
GlobalData.AddData("isCheckingInactivePatients", false);
}
public async Task<Patient?> UpdateOne(Patient updatedPatient)
{
var oldPatient = await _patientRepository.FindById(updatedPatient.Id);
var patient = await _patientRepository.UpdateOne(updatedPatient);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, patient);
return patient;
}
public async Task<Patient?> UpdatePatientAltable(ObjectId patientId, OptionList altable, User? user)
{
var patient = await _patientRepository.FindByPatientId(patientId);
var oldPatient = await _auditService.DeepCopyAsync(patient);
if (patient == null)
{
_logger.LogError("Patient id: {id} not found", patientId);
return null;
}
var aux = patient.Altable;
patient.Altable = altable;
await _patientRepository.Update(patient);
if (oldPatient != null)
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, patient);
await SendPatientBroadcast(patient);
await GenerateNurseObsAndInsert(_listSettings.AltableOptionList.ManualObservationName!,
[altable], patient.Id, user);
//si son iguales no actualiza
if (altable.Name.Equals(aux?.Name))
return patient;
if (!patient.PointOfCareId.HasValue)
return patient;
var poc = await _pointOfCareService.FindById(patient.PointOfCareId.Value);
//Si antes era NotAltable y cambia se crea un registro en discharge
if (aux?.OptionType is null or "NotAltable")
{
var discharge = await _dischargeService.GetDischargeByPatientId(patient.Id);
var unit = await _unitService.FindById(patient.UnitId);
if (discharge == null)
{
var dischargeToInsert = new Discharge
{
DischargeDate = DateTime.UtcNow,
Patient = patient,
PatientId = patientId,
PatientLocation = new PatientLocation(unit?.Name, poc?.Bed, poc?.Room),
PointOfCareId = poc?.Id,
UnitId = poc?.UnitId
};
await _dischargeService.InsertDischarge(dischargeToInsert);
}
else
{
discharge.PatientId = patientId;
discharge.Patient = patient;
discharge.PatientLocation = new PatientLocation(unit?.Name, poc?.Bed, poc?.Room);
discharge.PointOfCareId = poc?.Id;
discharge.UnitId = poc?.UnitId;
await _dischargeService.UpdateDischargeAsync(discharge);
}
}
//Si pasa a ser NotAltable borramos registro en discharge
if ("NotAltable".Equals(altable.OptionType))
{
var discharge = await _dischargeService.GetDischargeByPatientId(patient.Id);
if (discharge != null)
await _dischargeService.DeleteDischargeAsync(discharge);
}
return patient;
}
/// <summary>
/// Delete discharge and archive patient by id.
/// </summary>
/// <param name="id"></param>
/// <param name="archivePatient"></param>
/// <returns></returns>
public async Task ExitPatientById(ObjectId id, bool archivePatient = true)
{
try
{
var patient = await FindById(id);
if (patient == null)
{
_logger.LogError("Patient not found by id: {id}", id);
return;
}
var discharge = await _dischargeService.GetDischargeByPatientId(patient.Id);
if (discharge != null)
{
await _dischargeService.DeleteDischargeAsync(discharge);
_logger.LogInformation("Discharge deleted. {discharge}", discharge);
}
if (archivePatient)
await ArchivePatient(patient);
_logger.LogInformation("ExitPatientById: {id}", id);
_pointOfCareService.CheckNextAdmission(patient.PointOfCareId);
}
catch (Exception ex)
{
_logger.LogError("Exception exiting patient. Exception:{ex}", ex);
throw;
}
}
public async Task<Patient?> UpdatePatientMasterList(ObjectId patientId, MasterListType typeName,
List<OptionList> updatedOptions, User? user, List<OptionList>? carePlanLog)
{
try
{
var patient = await FindById(patientId);
if (patient == null)
{
_logger.LogError("Error updating Patient Master List. Patient not found by Id:{id} ", patientId);
return null;
}
switch (typeName)
{
case MasterListType.TreatmentList:
await GenerateNurseCarePlanAndInsert(MasterListType.TreatmentList, carePlanLog, patient, user);
patient.Treatment = updatedOptions;
break;
case MasterListType.AllergyList:
patient.Allergies = updatedOptions;
await GenerateNurseObsAndInsert(_listSettings.AllergyList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.ProcedureList:
await GenerateNurseCarePlanAndInsert(MasterListType.ProcedureList, carePlanLog, patient, user);
patient.Procedures = updatedOptions;
break;
case MasterListType.TestList:
await GenerateNurseCarePlanAndInsert(MasterListType.TestList, carePlanLog, patient, user);
patient.Tests = updatedOptions;
break;
case MasterListType.DoctorList:
patient.Doctors = updatedOptions;
await GenerateNurseObsAndInsert(_listSettings.DoctorList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.LanguageBarrierList:
patient.LanguageBarrier = updatedOptions;
await GenerateNurseObsAndInsert(_listSettings.LanguageBarrierList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.MobilityOptionList:
patient.Mobility = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.MobilityOptionList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.OriginList:
patient.Origin = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.OriginList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.PatientStatusList:
patient.PatientStatus = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.PatientStatusList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.TherapeuticCeilingList:
patient.TherapeuticCeiling = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.TherapeuticCeilingList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.VisitOptionList:
patient.Visits = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.VisitOptionList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.AccessControlList:
patient.AccessControl = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.AccessControlList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.AltableOptionList:
patient.Altable = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.AltableOptionList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.DiagnosisList:
patient.Diagnosis = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.DiagnosisList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.DischargeStatusList:
patient.DischargeStatus = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.DischargeStatusList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.InsulationList:
patient.Insulation = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.InsulationList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.PassiveSittingList:
patient.PassiveSitting = updatedOptions.FirstOrDefault();
await GenerateNurseObsAndInsert(_listSettings.PassiveSittingList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
case MasterListType.GenericList:
await GenerateNurseObsAndInsert(_listSettings.GenericList.ManualObservationName!,
updatedOptions, patient.Id, user);
break;
default:
await GenerateNurseObsAndInsert(updatedOptions.FirstOrDefault()?.Name ?? "UNKNOWNOBS",
updatedOptions, patient.Id, user);
break;
}
await Update(patient);
return patient;
}
catch (Exception ex)
{
_logger.LogError("Exception updating patient id:{id}. Data: {type} . Exception: {ex}", patientId,
typeName, ex);
return null;
}
}
public async Task GenerateNurseCarePlanAndInsert(MasterListType carePlanType, List<OptionList>? options,
Patient patient, User? user)
{
OptionList? addedItems = null;
OptionList? removedItems = null;
OptionList? updatedItems = null;
OptionList? option = null;
switch (carePlanType)
{
case MasterListType.ProcedureList:
var patientProcedures = patient.Procedures ?? [];
// Encuentra elementos agregados: están en optionsList pero no en patientProcedures
addedItems = options?.FirstOrDefault(opt => patientProcedures.All(proc => proc.Name != opt.Name));
if (addedItems != null)
{
option = addedItems;
break;
}
// Encuentra elementos actualizados: están en ambas listas pero tienen diferencias en sus propiedades
updatedItems = options?
.FirstOrDefault(opt =>
patientProcedures.Any(proc =>
proc.Name == opt.Name && !proc.AreEqualExcludeDescription(proc, opt)));
if (updatedItems != null)
{
option = updatedItems;
option.OptionType = "procedure";
}
// Encuentra elementos eliminados: están en patientProcedures y en optionsList pero tienen una descripción
if (options?.FirstOrDefault()?.Description != null)
{
removedItems = options.FirstOrDefault();
if (removedItems != null) removedItems.OptionType = "procedure";
}
break;
case MasterListType.TestList:
var patientTests = patient.Tests ?? [];
// Encuentra elementos agregados: están en optionsList pero no en patientProcedures
addedItems = options?.FirstOrDefault(opt => patientTests.All(test => test.Name != opt.Name));
if (addedItems != null)
{
option = addedItems;
break;
}
// Encuentra elementos actualizados: están en ambas listas pero tienen diferencias en sus propiedades
updatedItems = options?
.FirstOrDefault(opt =>
patientTests.Any(test => test.Name == opt.Name && !test.AreEqualExcludeDescription(test, opt)));
if (updatedItems != null)
{
option = updatedItems;
option.OptionType = "test";
}
// Encuentra elementos eliminados: están en patientProcedures y en optionsList pero tienen una descripción
if (options?.FirstOrDefault()?.Description != null)
{
removedItems = options.FirstOrDefault();
if (removedItems != null) removedItems.OptionType = "test";
}
break;
case MasterListType.TreatmentList:
var patientTreatment = patient.Treatment ?? [];
// Encuentra elementos agregados: están en optionsList pero no en patientProcedures
addedItems = options?.FirstOrDefault(opt => patientTreatment.All(proc => proc.Name != opt.Name));
if (addedItems != null)
{
option = addedItems;
option.OptionType = "treatment";
break;
}
// Encuentra elementos actualizados: están en ambas listas pero tienen diferencias en sus propiedades
updatedItems = options?
.FirstOrDefault(opt =>
patientTreatment.Any(proc =>
proc.Name == opt.Name && !proc.AreEqualExcludeDescription(proc, opt)));
if (updatedItems != null)
{
option = updatedItems;
option.OptionType = "treatment";
}
// Encuentra elementos eliminados: están en patientProcedures y en optionsList pero tienen una descripción
if (options?.FirstOrDefault()?.Description != null)
{
removedItems = options.FirstOrDefault();
if (removedItems != null) removedItems.OptionType = "treatment";
}
break;
}
ActionsEnum.CrudAction? action = addedItems != null ? ActionsEnum.CrudAction.Create :
updatedItems != null ? ActionsEnum.CrudAction.Update :
removedItems?.Description is "Archive" ? ActionsEnum.CrudAction.Archive : ActionsEnum.CrudAction.Delete;
var patietCarePlan = new PatientCarePlan
{
PatientId = patient.Id,
PatientNumber = patient.PatientNumber,
PointOfCareId = patient.PointOfCareId,
Action = action,
CarePlan =
action is ActionsEnum.CrudAction.Delete or ActionsEnum.CrudAction.Archive ? removedItems : option,
UserId = user?.Id,
Description = action == ActionsEnum.CrudAction.Delete && string.IsNullOrEmpty(removedItems?.Description)
? "No reason given"
: removedItems?.Description
};
await _patientCarePlanService.InsertOneAsync(patietCarePlan);
if (action != ActionsEnum.CrudAction.Delete && action != ActionsEnum.CrudAction.Archive && removedItems != null)
{
var secondaryAction = removedItems.Description is "Archive"
? ActionsEnum.CrudAction.Archive
: ActionsEnum.CrudAction.Delete;
var patietCarePlanSecondaryAction = new PatientCarePlan
{
PatientId = patient.Id,
PatientNumber = patient.PatientNumber,
PointOfCareId = patient.PointOfCareId,
Action = secondaryAction,
CarePlan = removedItems,
UserId = user?.Id,
Description =
secondaryAction == ActionsEnum.CrudAction.Delete && string.IsNullOrEmpty(option?.Description)
? "No reason given"
: option?.Description
};
await _patientCarePlanService.InsertOneAsync(patietCarePlanSecondaryAction);
}
}
public async Task UpdatePatientIncomingData(ObjectId patientId, Patient person)
{
var oldPatient = await _patientRepository.FindByPatientId(patientId) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
var pat = await _patientRepository.UpdatePatientIncomingData(patientId, person) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, pat);
await SendPatientBroadcast(pat);
}
public async Task UpdatePatientIncomingData(ObjectId patientId, Patient person,
PatientIncomeData patientIncomeData, User user)
{
await UpdatePatientIncomingData(patientId, person);
var obs = new PatientObservation
{
Id = ObjectId.GenerateNewId(),
Name = "PatientIncomingData",
Value = patientIncomeData,
PatientId = patientId,
Time = DateTime.Now,
UserId = user.Id
};
await _observationService.Value.InsertNurseObservation(obs);
}
public async Task UpdatePatientMasterListItemChange(UpdateOptionMasterListDto opt, IEnumerable<Unit> unitList,
string typeName)
{
// Filtrar paciente por unidad y valor del item de la lista
var unitIds = unitList.Select(x => x.Id).ToList();
var oldPatientList = await _patientRepository.GetPatientsByUnitIds(unitIds, typeName);
var patientUpdatedList = await _patientRepository.UpdateMasterListOption(unitIds, opt, typeName);
foreach (var patient in patientUpdatedList)
{
var oldPatient = oldPatientList.Find(p => p.Id == patient.Id);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, patient);
await SendPatientBroadcast(patient);
}
}
public async Task DeletePatientMasterListItem(OptionList opt, IEnumerable<Unit> unitList, string typeName)
{
var unitIds = unitList.Select(x => x.Id).ToList();
var patientUpdatedList = await _patientRepository.DeleteMasterListOption(unitIds, opt, typeName);
foreach (var patient in patientUpdatedList)
{
var patientUpdated = await FindById(patient.Id);
if (patientUpdated == null) continue;
await SendPatientBroadcast(patientUpdated);
var obsName = GetManualObservationName(typeName);
var obs = await _observationService.Value.FindLastObservationsByField(patientUpdated.Id,
new List<Field> { new() { Name = obsName } });
if (obs.FirstOrDefault() != null)
{
var newObs = obs.First().Value as List<OptionList>;
newObs?.RemoveAll(c => c.Id == opt.Id);
await GenerateNurseObsAndInsert(obsName, newObs, patient.Id, null);
}
}
}
public async Task UpdatePatientDemographicData(ObjectId patientId, Patient person, User? user)
{
var oldPatient = await _patientRepository.FindByPatientId(patientId) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
var pat = await _patientRepository.UpdatePatientDemographicData(patientId, person);
if (pat != null)
{
await UpdatePatientMasterList(pat.Id, MasterListType.AllergyList, pat.Allergies??[], user, null);
var lbl = pat.LanguageBarrier != null ? pat.LanguageBarrier : null;
var dl = pat.Diagnosis != null ? new List<OptionList> { pat.Diagnosis } : null;
await UpdatePatientMasterList(pat.Id, MasterListType.LanguageBarrierList, lbl??[], user, null);
await UpdatePatientMasterList(pat.Id, MasterListType.DiagnosisList, dl??[], user, null);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, pat);
await SendPatientBroadcast(pat);
}
}
public Task<Patient?> SearchByPatientNumberAndDistinctUnit(string patientNumber, ObjectId unitId)
{
return _patientRepository.SearchByPatientNumberAndDistinctUnit(patientNumber, unitId);
}
public async Task<List<Patient>> FindAllPatientWithFinishedProcedures(int archiveProcedureEndDateAfterMinutes)
{
return await _patientRepository.FindAllPatientWithFinishedProcedures(archiveProcedureEndDateAfterMinutes);
}
public async Task<List<Patient>> FindAllPatientWithFinishedTest(int archiveTestEndDateAfterMinutes)
{
return await _patientRepository.FindAllPatientWithFinishedTests(archiveTestEndDateAfterMinutes);
}
public async Task<List<Patient>> FindAllPatientWithFinishedTreatment(int archiveTreatmentEndDateAfterMinutes)
{
return await _patientRepository.FindAllPatientWithFinishedTreatment(archiveTreatmentEndDateAfterMinutes);
}
private bool CheckPatient(Patient patient)
{
//patient.AdmTime ??= DateTime.UtcNow;
_logger.LogDebug("Inserting patient with patientNumber: {PatientpatientNumber} {Patient}",
patient.PatientNumber, patient);
if (patient.PointOfCareId == null && patient.UnitId == null)
{
_logger.LogWarning(
"Not Inserted patient with patientNumber: {PatientpatientNumber}. PointOfCare is null {Patient}",
patient.PatientNumber, patient);
return false;
}
if (string.IsNullOrEmpty(patient.PatientNumber))
{
_logger.LogWarning("person with patientNumber null or empty: {Patient}", patient);
if (!_cretatePatientWithoutPatientNumber)
{
_logger.LogWarning(
"CretatePatientWithoutPatientNumber: {CretatePatientWithoutPatientNumber}: person not inserted: {Patient} ",
_cretatePatientWithoutPatientNumber, patient.Id);
return false;
}
}
return true;
}
private async Task SendLastGroupedObsToSubscriberByPoc(ObjectId newPoc, ObjectId? oldPoc,
Patient patient)
{
// Enviar y rellenar los subscriptores que necesitan las observaciones agrupadas para este paciente
// Obtén la lista de suscriptores
var relevantSubscribers = _subscribersService.GetByPocId(newPoc);
// Filtra y selecciona los elementos con DisplayId único
var filteredSubscribers = relevantSubscribers
.GroupBy(subscriber => subscriber.DisplayId) // Agrupa por DisplayId
.Where(group => group.Key != null) // Asegurar de que DisplayId no sea null
.Select(group => group.First()) // Selecciona el primer elemento de cada grupo
.ToList(); // Convierte el resultado a una lista
foreach (var sub in filteredSubscribers)
if (sub.DisplayId.HasValue)
{
var display = await _displayService.GetById(sub.DisplayId.Value);
if (display is { DisplayConfigId: not null })
{
var config = await _displayConfigService.GetById(display.DisplayConfigId.Value);
var configGroupField = config.GroupedFieldList;
if (configGroupField != null)
foreach (var groupedField in configGroupField)
{
var groupedObservation = await _groupedObservationService.GenerateGroupedObservation(
patient.Id,
groupedField);
_ = _clientMessageService.SendAsync(sub.Id, OperationType.GroupedObservation,
groupedObservation);
_subscriberGroupedService.CheckOnSubscriptionGroup(groupedField, patient.Id,
"Romance Standard Time",
sub.Id, groupedObservation);
}
}
}
// En este punto los subscriptores que necesitan las obs agrupadas para este paciente ya las tienen
// Y la lista de subscriptorGrouped ya tiene la id del nuevo subscriptor
// Ahora habria que ver que subscriptores ya no necesitan las agrupadas para este paciente
if (oldPoc.HasValue)
{
var relevantSubscribersToDelete = _subscribersService.GetByPocId(oldPoc.Value);
var relevantSubscribersToDeleteFiltered =
relevantSubscribersToDelete.Where(sub => !sub.LocationIds.Contains(newPoc));
foreach (var wsSubscriber in relevantSubscribersToDeleteFiltered)
_subscriberGroupedService.RemoveWsSubscriberPatientIdAndWsId(patient.Id.ToString(), wsSubscriber.Id);
}
}
private async Task SendLastObsToSubscriberByPoc(PointOfCare newPoc, Patient patient)
{
// Obtén la lista de suscriptores
var relevantSubscribers = _subscribersService.GetByPocId(newPoc.Id);
// Filtra y selecciona los elementos con DisplayId único
// var filteredSubscribers = relevantSubscribers
// .GroupBy(subscriber => subscriber.DisplayId) // Agrupa por DisplayId
// .Where(group => group.Key != null) // Asegúrate de que DisplayId no sea null
// .Select(group => group.First()) // Selecciona el primer elemento de cada grupo
// .ToList(); // Convierte el resultado a una lista
foreach (var sub in relevantSubscribers)
if (sub.DisplayId.HasValue)
{
var display = await _displayService.GetById(sub.DisplayId.Value);
if (display is { DisplayConfigId: not null })
{
var config = await _displayConfigService.GetById(display.DisplayConfigId.Value);
var observations =
await _observationService.Value.FindLastObservationsByField(patient.Id, config.FieldList);
if (observations.Count > 0)
{
observations = observations.OrderBy(p => p.Time).ToList();
foreach (var observation in observations)
{
var obs = await _observationService.Value.MapObservationsByName(observation);
if (obs != null)
_ = _clientMessageService.SendAsync(sub.Id, OperationType.Observation, obs);
}
}
}
}
}
private async Task UpdateLocation(ObjectId id, ObjectId unitId, ObjectId pocId)
{
try
{
_logger.LogDebug("Update location id: {id}", id);
var patient = await _patientRepository.FindById(id);
var oldPatient = await _auditService.DeepCopyAsync(patient);
if (patient is not { PointOfCareId: not null })
{
_logger.LogError("person is null. Not updated location for patient id: {id} ", id);
return;
}
var newPoc = await _pointOfCareService.FindById(pocId);
var newUnit = await _unitService.FindById(unitId);
// El valor de bed: 04 daria como resultado NO_BED por eso comprobamos que no sea un int !int.TryParse(newPoc.Bed, out _)
var isMoveToVirtualPoc = newPoc != null &&
!int.TryParse(newPoc.Bed, out _) &&
Enum.TryParse<VirtualPointOfCare>(newPoc.Bed, out _);
var oldLocation = await _pointOfCareService.FindById(patient.PointOfCareId.Value);
var virtualPocOldLocation = VirtualPointOfCare.Unknown;
var comesFromVirtualPoc = oldLocation != null &&
!int.TryParse(oldLocation.Bed, out _) &&
Enum.TryParse(oldLocation.Bed, out virtualPocOldLocation);
if (comesFromVirtualPoc)
{
var isRecovered = VirtualPointOfCare.Deleted.Equals(virtualPocOldLocation);
// Añadir log warning
if (isRecovered)
{
_logger.LogWarning(
"Patient is recovered on UpdateLocation patientId: {id}, oldLocation: {oldLocation}, newLocation: {newPoc}",
patient.Id, oldLocation?.Location.ToString(), newPoc?.Location.ToString());
patient.DisTime = null;
}
}
patient.PointOfCareId = pocId;
patient.UnitId = unitId;
var bed = newPoc?.Bed;
var room = newPoc?.Room;
var location = new PatientLocation(newUnit?.Name, bed, room);
patient.Location = location;
if (isMoveToVirtualPoc && !comesFromVirtualPoc)
patient.Altable = new OptionList
{
IconDefault = "icNotAltable",
OptionType = "NotAltable",
Name = "NotAltable"
};
await _patientRepository.UpdateOneAsync(patient.Id, patient);
if (oldPatient != null)
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, patient);
if (isMoveToVirtualPoc && !comesFromVirtualPoc)
{
await ExitPatientById(patient.Id, false);
_pointOfCareService.CheckNextAdmission(oldLocation?.Id);
}
if (!isMoveToVirtualPoc && comesFromVirtualPoc)
await _pointOfCareService.SetPointOfCareStatus(pocId, StatusEnum.PointOfCare.InUse);
//Prio location to advertise both locations of movement between sections
_logger.LogDebug(
"Move patient: {id} from oldPocId: {oldPocId}, to newPocId: {newPocId}",
id, oldLocation != null ? oldLocation.Id : "NULL", pocId);
if (!isMoveToVirtualPoc && !comesFromVirtualPoc && oldLocation != null)
{
await _pointOfCareService.SetPointOfCareStatus(pocId, StatusEnum.PointOfCare.Available);
await Move(patient, pocId, oldLocation.Id);
}
else
{
_logger.LogDebug(
"Patient: {id} not moved because isMoveToVirtualPoc: {isMoveToVirtualPoc}, comesFromVirtualPoc: {comesFromVirtualPoc}, oldLocationIsNotNull: {oldLocation}",
id, isMoveToVirtualPoc, comesFromVirtualPoc, oldLocation != null);
}
//TODO: Revisar
// CheckLocationForWsSubscriber(location, id);
}
catch (Exception ex)
{
_logger.LogError("error updating location for patient id: {id} exception: {exMessage}", id, ex.Message);
}
}
/// <summary>
/// If location is updated and pointOfCare are DELETED or PUSHED
/// Ws subscriber for GropedObs dont need to keep notificated
/// Else could be that patient from Section A move to Section B
/// clients from the section that is moved dont need to keep notified
/// and other client will ask for grouped obs once receive SendPatientLocationBroadcast
/// </summary>
/// <param name="newLocation">New location where patient is getting updated</param>
/// <param name="patientId">Object Id for patient</param>
private void CheckLocationForWsSubscriber(PatientLocation? newLocation, ObjectId? patientId)
{
if (patientId == null || newLocation == null) return;
try
{
if (newLocation.Bed == VirtualPointOfCare.Pushed.ToString() ||
newLocation.Bed == VirtualPointOfCare.Deleted.ToString() ||
newLocation.Bed == VirtualPointOfCare.Moved.ToString())
_subscriberGroupedService.RemoveGroupedObsByPatientId(patientId.Value.ToString());
else
_subscriberGroupedService.RemoveWsSubscriberByLocation(patientId.Value.ToString(), newLocation);
}
catch (Exception ex)
{
Log.Error(
$"ERROR DELETING PATIENT MOVEMENT. person Id: {patientId} New location: {newLocation.ToStr()}. Exception message: {ex.Message} ex: {ex}");
}
}
/// <summary>method <c>ArchiveNotUpdatedPatientsSince</c> archive all patients who have not updated since in
/// <param>hoursBeforeArchive</param>
/// .
/// </summary>
public async Task ArchiveNotUpdatedPatientsInInactivePoCSince(int hoursBeforeArchive)
{
_logger.LogDebug(
"Archiving not updated and in inactive PoC patients since older than {hoursBeforeArchive} hours",
hoursBeforeArchive);
try
{
// Patients not updated since date
var dischargedPatients =
await _patientRepository.FindPatientsNotUpdatedSince(DateTime.Now.AddHours(-hoursBeforeArchive));
foreach (var patient in dischargedPatients.Where(p => !p.IsInActivePoC())) await ArchivePatient(patient);
}
catch (Exception ex)
{
_logger.LogError("Error archiving not updated and in inactive PoC patients {exMessage}", ex);
}
}
public Task SendPatientLocationBroadcast(object patient, PatientLocation location,
List<ObjectId>? pocs)
{
if (pocs == null || pocs.Count == 0) return Task.CompletedTask;
var subscribers = _subscribersService.GetSubscribers().Where
(s =>
s.LocationIds.Any(pocs.Contains)
).ToList();
subscribers.ForEach(Action);
return Task.CompletedTask;
void Action(WsSubscriber subscriber)
{
_clientMessageService.SendAsync(subscriber.Id,
OperationType.PatientLocation, new Dictionary<string, object>
{
{ "patient", patient },
{ "location", location }
});
}
}
private async Task SendPatientAttendingDoctorBroadcast(ObjectId patientid, Person attendingDoctor)
{
var patient = await FindById(patientid);
if (patient == null) return;
var poc = await _pointOfCareService.FindPoCByPatientId(patient.Id);
if (poc == null) return;
var subscribers = _subscribersService.GetSubscribers().Where(s => s.LocationIds.Any(sb => sb == poc.Id))
.ToList();
subscribers.ForEach(Action);
return;
void Action(WsSubscriber subscriber)
{
_clientMessageService.SendAsync(subscriber.Id,
OperationType.PatientAttendingDoctor, new Dictionary<string, object>
{
{ "id", patientid.ToString() },
{ "attendingDoctor", attendingDoctor }
});
}
}
private async Task SendPatientDataBroadcast(ObjectId patientid, Person data)
{
var patient = await FindById(patientid);
if (patient == null) return;
//var section = await _sectionService.FindByPointOfCare(patient.UnitString);
//if (section == null) return;
var subscribers = _subscribersService.GetSubscribers().Where(s =>
s.LocationIds.Any(c => c == patient.PointOfCareId));
foreach (var subscriber in subscribers)
_ = _clientMessageService.SendAsync(subscriber.Id, OperationType.PatientData, new Dictionary<string, object>
{
{ "id", patientid.ToString() },
{ "data", data }
});
}
private async Task SendPatientBroadcast(Patient patient)
{
var subscribersGroup =
_subscribersService.GetSubscribers()
.Where(s => patient.PointOfCareId != null &&
s.LocationIds.Any(c => c == patient.PointOfCareId.Value)).GroupBy(h => h.Locale);
var unit = await _unitService.FindById(patient.UnitId);
foreach (var group in subscribersGroup)
{
var locale = group.Key ?? LocaleEnum.Default;
IEnumerable<WsSubscriber> subscribers = group;
foreach (var subscriber in subscribers)
{
var patientWithLocale = await _masterListServiceFactory.GetPatientTraslated(unit, locale, patient);
_ = _clientMessageService.SendAsync(subscriber.Id, OperationType.UpdatePatient, patientWithLocale);
}
}
}
private Task SendDeletePatientBroadcast(ObjectId patientid, ObjectId pocId)
{
var subscribers = _subscribersService.GetSubscribers().Where(s => s.LocationIds.Any(x => x == pocId)).ToList();
_logger.LogDebug("Remove grouped obs for patientId: {patientid} on location: {location}", patientid,
pocId);
_subscriberGroupedService.RemoveGroupedObsByPatientId(patientid.ToString());
subscribers.ForEach(Action);
return Task.CompletedTask;
void Action(WsSubscriber subscriber)
{
_clientMessageService.SendAsync(subscriber.Id,
OperationType.DeletePatient, new Dictionary<string, object>
{
{ "id", patientid.ToString() }
});
}
}
private void UpdatePatientFromRequestToIdList(Patient patient, ApiRequest apiRequest)
{
if (apiRequest.AdmTime.HasValue) patient.AdmTime = apiRequest.AdmTime;
if (!string.IsNullOrEmpty(apiRequest.PatientId)) patient.PatientId = apiRequest.PatientId;
if (apiRequest.Patient != null && !apiRequest.Patient.IsEmpty())
{
// Log action to describe
patient.Person ??= new Person();
patient.Person.BirthDate = apiRequest.Patient.BirthDate;
patient.Person.Language = apiRequest.Patient.Language;
patient.Person.FirstName = apiRequest.Patient.FirstName;
patient.Person.Gender = apiRequest.Patient.Gender;
patient.Person.LastName = apiRequest.Patient.LastName;
patient.Person.SecondName = apiRequest.Patient.SecondName;
}
if (apiRequest.Patient?.Ids != null)
{
patient.Person ??= new Person();
patient.Person.SetIds(apiRequest.Patient.Ids,
apiRequest.MessageTime == DateTime.MinValue ? DateTime.UtcNow : apiRequest.MessageTime);
}
if (apiRequest.DisTime.HasValue) patient.DisTime = apiRequest.DisTime;
patient.UpdateDate = DateTime.UtcNow;
}
private async Task RemovePatientAndSendToVirtualPoc(ApiRequest apiRequest, Patient patient,
VirtualPointOfCare virtualPocEnum)
{
var virtualPoc = await _pointOfCareService.FindByBedAndUnitId(virtualPocEnum.ToString(),
patient.UnitId);
if (virtualPoc == null)
_logger.LogError(
"Error getting virtual poc {virtualPocEnum} PATIENT: {patientId} on unit: {patientUnitId} continue with autogenerate pocId",
virtualPocEnum.ToString(), patient.Id, patient.UnitId);
var uni = await _unitService.FindById(patient.UnitId);
var oldLocation = patient.PointOfCareId;
patient.DisTime = apiRequest.DisTime ?? DateTime.UtcNow;
patient.PointOfCareId = virtualPoc?.Id ?? new ObjectId();
patient.UnitId = virtualPoc?.UnitId ?? patient.UnitId;
patient.Location = new PatientLocation(uni?.Name,
virtualPoc?.Bed ?? $"Error on {virtualPocEnum.ToString()}",
virtualPoc?.Room ?? $"Error on {virtualPocEnum.ToString()}");
await _patientRepository.UpdateOneAsync(patient.Id, patient);
_logger.LogDebug("{virtualPocEnum} PATIENT: {patientId} ", virtualPoc?.ToString(), patient.Id);
await ExitPatientById(patient.Id, false);
if (virtualPoc != null && oldLocation.HasValue)
_ = SendLastGroupedObsToSubscriberByPoc(virtualPoc.Id, oldLocation.Value, patient);
_pointOfCareService.CheckNextAdmission(oldLocation);
}
// * To ensure db sync with ICCA we receive their patients data table and sync with our patients.
// * GetByCodeSysAndCode all boxes from all sections and match with iccaPatients
private async Task ProcessIccaSync(ApiRequest apiRequest)
{
//var sections = await _sectionService.GetAll();
// var boxes = sections.Where(box => box.Items != null).SelectMany(s => s.Items.SelectMany(d => d.Boxes))
// ?.ToList();
//var boxes = sections.SelectMany(s => s.items.SelectMany(d => d.boxes)).ToList();
var pocList = await _pointOfCareService.GetAllLocationInfo();
var unitList = await _unitService.GetAll();
foreach (var pointOfCare in pocList)
pointOfCare.Unit = unitList.FirstOrDefault(u => u.Id == pointOfCare.UnitId);
var iccaPatientsFromOtherLocations = apiRequest.IccaPatients?.Select(x => x.DeepCopy()).ToList();
foreach (var poc in pocList)
{
if (poc.Unit == null)
continue;
var iccaPatientOnBox = apiRequest.IccaPatients?.FirstOrDefault(i =>
i.Location.Bed == poc.Bed && i.Location.UnitName == poc.Unit.Name);
if (iccaPatientOnBox != null)
iccaPatientsFromOtherLocations?.RemoveAll(s => s.PatientNumber == iccaPatientOnBox.PatientNumber);
//Find actual patient in poc
var actualPatient = await FindByPointOfCareId(poc.Id);
// 1 Exists in ICCA and not in our DB => create it and if any other patient is on bed move it to Temporal.
if (iccaPatientOnBox != null && actualPatient == null && iccaPatientOnBox.PatientNumber != null)
{
//check if patient exists to only move
var patientInOtherBox = await FindByPatientNumber(iccaPatientOnBox.PatientNumber);
if (patientInOtherBox != null)
await UpdateLocation(patientInOtherBox.Id,
new PatientLocation(poc.Unit.Name, poc.Bed, poc.Room));
else
await Insert(new Patient
{
AdmTime = iccaPatientOnBox.AdmitTime,
PatientId = iccaPatientOnBox.PatientId,
PatientNumber = iccaPatientOnBox.PatientNumber,
Location = new PatientLocation(poc.Unit.Name, poc.Bed, poc.Room),
UnitId = poc.UnitId,
PointOfCareId = poc.Id
});
}
// 2 Exists in ICCA and in our DB => equal patientNumber same person, continue else move actual and create new from icca
else if (iccaPatientOnBox != null && actualPatient != null)
{
if (iccaPatientOnBox.PatientNumber == actualPatient.PatientNumber)
{
//They are the same do anything.
}
else
{
await UpdateLocation(actualPatient.Id,
new PatientLocation(poc.Unit.Name,
VirtualPointOfCare.Moved.ToString(), VirtualPointOfCare.Moved.ToString()));
await Insert(new Patient
{
AdmTime = iccaPatientOnBox.AdmitTime,
PatientId = iccaPatientOnBox.PatientId,
PatientNumber = iccaPatientOnBox.PatientNumber,
Location = new PatientLocation(poc.Unit.Name, poc.Bed, poc.Room),
UnitId = poc.UnitId,
PointOfCareId = poc.Id
});
}
}
// 3 Exists in our DB and not in ICCA => move it to temporal bed to match patients on layout boxes.
else if (iccaPatientOnBox == null && actualPatient != null)
{
//move it to another bed
await UpdateLocation(actualPatient.Id,
new PatientLocation(poc.Unit.Name, VirtualPointOfCare.Unknown.ToString(),
VirtualPointOfCare.Unknown.ToString()));
}
}
if (iccaPatientsFromOtherLocations == null)
return;
foreach (var patientOutOfConfigBoxes in iccaPatientsFromOtherLocations)
{
//Patients from others PointsOfCares or box not in our configured sections. we save them
if (patientOutOfConfigBoxes.PatientNumber == null)
continue;
//check exists
var patientExists = await FindByPatientNumber(patientOutOfConfigBoxes.PatientNumber);
if (patientExists != null) continue;
var patientLocation = await FindByLocation(patientOutOfConfigBoxes.Location);
//move it to another bed
if (patientLocation != null)
{
var unitName = unitList.FirstOrDefault(u => u.Id == patientLocation.UnitId);
await UpdateLocation(patientLocation.Id,
new PatientLocation(unitName?.Name ?? "UNKNOWN", VirtualPointOfCare.Unknown.ToString(),
VirtualPointOfCare.Unknown.ToString()));
}
var pocUnknown = pocList.FirstOrDefault(u => u.Bed == VirtualPointOfCare.Unknown.ToString());
var locationFromRequest = pocList.FirstOrDefault(u =>
u.Bed == patientOutOfConfigBoxes.Location.Bed &&
u.Unit?.Name == patientOutOfConfigBoxes.Location.UnitName);
await Insert(new Patient
{
AdmTime = patientOutOfConfigBoxes.AdmitTime,
PatientId = patientOutOfConfigBoxes.PatientId,
PatientNumber = patientOutOfConfigBoxes.PatientNumber,
PointOfCareId = locationFromRequest?.Id ?? pocUnknown?.Id ?? ObjectId.GenerateNewId(),
UnitId = locationFromRequest?.UnitId ?? pocUnknown?.UnitId ?? ObjectId.GenerateNewId()
});
}
}
/*For adt_a02 || adt_a12
*/
private async Task ProcessAdtMovePatient(ApiRequest apiRequest, Patient? patient)
{
try
{
var patientExistsInLocation = await FindByLocation(apiRequest.Location);
if (patient is { PatientNumber: not null } && apiRequest.Patient != null &&
patient.PatientNumber == apiRequest.PatientNumber && _updatePatientDataWithAdtA02)
{
_logger.LogDebug(
"Debg MovePatient, UpdatePatientDataWithADT_A02 is enabled, patient number found {pn} and apirequest patient number {pnApirequest} are the same update demographic data",
patient.PatientNumber, apiRequest.PatientNumber);
//await UpdatePatientFromRequest(patient, apiRequest, true);
await UpdatePatientData(patient.Id, patient.PatientNumber, apiRequest.Patient, false);
}
if (patientExistsInLocation != null)
{
if (patientExistsInLocation.PatientNumber != apiRequest.PatientNumber)
{
_logger.LogDebug(
"Debug MovePatient, patient to move is null but patientExistsInLocation is not null moving patient: {patientExistsInLocation}, to pointOfCare MOVED",
patientExistsInLocation);
var movedLocationPoc = await _pointOfCareService.FindByBedAndUnitId(
VirtualPointOfCare.Moved.ToString(),
patientExistsInLocation.UnitId);
if (movedLocationPoc != null)
await UpdateLocation(patientExistsInLocation.Id, movedLocationPoc.UnitId, movedLocationPoc.Id);
}
else
{
// Demographic data only
return;
}
}
if (patient != null)
{
//to solve problems when empty beds with not scapped ""
if (apiRequest.Location != null)
{
var bed = apiRequest.Location.Bed?.Replace("\"", "");
apiRequest.Location.Bed = string.IsNullOrEmpty(bed)
? VirtualPointOfCare.Unknown.ToString()
: bed;
if (apiRequest.Location.Bed == VirtualPointOfCare.Unknown.ToString())
{
var unknownLocationPoc = await _pointOfCareService.FindByBedAndUnitId(
VirtualPointOfCare.Unknown.ToString(),
patient.UnitId);
if (unknownLocationPoc != null)
await UpdateLocation(patient.Id, unknownLocationPoc.UnitId, unknownLocationPoc.Id);
}
else
{
var mapLocation = await _pocMappingService.Map(apiRequest.Location);
var newLocationUnit = await _unitService.FindByName(apiRequest.Location.UnitName);
var newLocationPoc = await _pointOfCareService.FindByBedAndUnitId(mapLocation?.Bed,
newLocationUnit?.Id);
if (newLocationPoc != null)
{
await UpdateLocation(patient.Id, newLocationPoc.UnitId, newLocationPoc.Id);
}
else
{
var unknownLocationPoc = await _pointOfCareService.FindByBedAndUnitId(
VirtualPointOfCare.Unknown.ToString(),
patient.UnitId);
if (unknownLocationPoc != null)
{
await UpdateLocation(patient.Id, unknownLocationPoc.UnitId, unknownLocationPoc.Id);
_logger.LogError(
"Unable to find new location on ProcessAdtMovePatient location: {Unit} {Bed}",
apiRequest.Location.UnitName, apiRequest.Location?.Bed);
}
}
}
}
else
{
_logger.LogDebug("Move patient to null location transfering patient to UNKNOWN bed");
var unknownLocationPoc = await _pointOfCareService.FindByBedAndUnitId(
VirtualPointOfCare.Unknown.ToString(),
patient.UnitId);
var unitUnknowPoc = await _unitService.FindById(patient.UnitId);
if (unknownLocationPoc == null || unitUnknowPoc == null)
{
_logger.LogError(
"Unable to find new location on ProcessAdtMovePatient location: Unit: {Unit}, Bed: {Bed}",
unitUnknowPoc?.Name ?? "UNKNOWN", VirtualPointOfCare.Unknown.ToString());
return;
}
apiRequest.Location = new PatientLocation(unitUnknowPoc.Name, unknownLocationPoc.Bed,
unknownLocationPoc.Room);
await UpdateLocation(patient.Id, unitUnknowPoc.Id, unknownLocationPoc.Id);
}
_logger.LogDebug(
"Debug MovePatient, patient not null to move: {patient} Bed: {apiRequestLocationBed}, PointOfCare: {apiRequestLocationPointOfCare}",
patient, apiRequest.Location?.Bed, apiRequest.Location?.UnitName);
}
else
{
// if (patientExistsInLocation != null)
// {
// if (patientExistsInLocation.PatientNumber != apiRequest.PatientNumber)
// {
// _logger.LogDebug(
// "Debug MovePatient, patient to move is null but patientExistsInLocation is not null moving patient: {patientExistsInLocation}, to pointOfCare MOVED",
// patientExistsInLocation);
//
// await UpdateLocation(patientExistsInLocation.Id,movedLocationPoc.UnitId, movedLocationPoc.Id);
// }
// }
patient = await CreatePatientFromRequest(apiRequest);
if (patient != null)
{
var newLocationUnit = await _unitService.FindByName(apiRequest.Location?.UnitName);
var newLocationPoc = await _pointOfCareService.FindByBedAndUnitId(apiRequest.Location?.Bed,
newLocationUnit?.Id);
if (newLocationPoc == null)
{
_logger.LogError("Unable to find new location on ProcessAdtMovePatient location: {Unit} {Bed}",
apiRequest.Location?.UnitName, apiRequest.Location?.Bed);
return;
}
_logger.LogDebug(
"person to move was null on ADT_A02 newPatient: {patient}, proceed to send notification to front",
patient);
await SendNewPatientBroadcast(patient);
await _pointOfCareService.SetPointOfCareStatus(newLocationPoc.Id, StatusEnum.PointOfCare.InUse);
//_pointOfCareService.CheckNextAdmission(newLocationPoc.Id);
}
}
}
catch (Exception e)
{
_logger.LogError("Error on ProcessAdtMovePatient trace: {eStackTrace}, message: {eMessage}",
e.StackTrace, e.Message);
}
}
/// <summary>
/// Process new patient. try to find him by
/// <paramref name="apiRequest.patientNumber"></paramref>
/// or
/// <paramref name="apiRequest.patientId"></paramref>
/// if exits and was not discharged use him.
/// If not insert new one in patients table
/// </summary>
/// <param name="apiRequest"></param>
/// <returns>The Inserted patient or null.</returns>
private async Task<Patient?> ProcessAdtPatientAdmit(ApiRequest apiRequest)
{
Patient? patient;
try
{
if (apiRequest.Location != null)
apiRequest.Location = await _pocMappingService.Map(apiRequest.Location);
patient = await FindPatient(apiRequest.PatientId, apiRequest.PatientNumber,
apiRequest.Location); //Null ADTA01
var unit = await _unitService.FindByName(apiRequest.Location?.UnitName);
if (unit == null)
{
_logger.LogDebug("Process admit patient to unit name: {UnitName} NOT FOUND",
apiRequest.Location?.UnitName);
return null;
}
var poc = await _pointOfCareService.FindByBedAndUnitId(apiRequest.Location?.Bed, unit.Id);
if (poc == null)
{
_logger.LogDebug("Process admit patient to bed name: {BedName} NOT FOUND",
apiRequest.Location?.Bed);
return null;
}
var patientExistsInLocation = await FindByUnitAndPocId(unit.Id, poc.Id);
//if previous patient exists is moved to PUSHED
if (patientExistsInLocation != null)
{
_logger.LogDebug("person Exists InLocation: {PatientExistsInLocation}",
patientExistsInLocation.ToStr());
if (patient == null || patientExistsInLocation.PatientNumber != patient.PatientNumber)
{
_logger.LogDebug("previous patient: {PatientExistsInLocationId} moved to PUSHED ",
patientExistsInLocation.Id);
_logger.LogDebug(
"Remove grouped obs for patientId: {ApiRequestPatientId} patient exist on location for new patient location: {PatientExistsInLocationLocation}, traceId: {ApiRequestTraceId}, traceMessageId: {ApiRequestTraceMessageId}",
apiRequest.PatientId ?? "NULL", patientExistsInLocation.Location,
apiRequest.TraceId ?? "NULL", apiRequest.TraceMessageId ?? "NULL");
var pocPushed =
await _pointOfCareService.FindByBedAndUnitId(VirtualPointOfCare.Pushed.ToString(), unit.Id);
if (pocPushed != null)
{
await UpdateLocation(patientExistsInLocation.Id,
unit.Id, pocPushed.Id);
_ = SendLastGroupedObsToSubscriberByPoc(pocPushed.Id, poc.Id, patientExistsInLocation);
}
else
{
_logger.LogError(
"Unable to update location for existing patient: {Patient} on poc: {VirtualPoc} and unit: {Unit}",
patientExistsInLocation.Id.ToString(), VirtualPointOfCare.Pushed.ToString(),
unit.Id.ToString());
}
} //patient already exists and is the same again, do nothing
else if (patient.PatientNumber == patientExistsInLocation.PatientNumber &&
patient.Location.Bed == patientExistsInLocation.Location.Bed &&
patient.Location.UnitName == patientExistsInLocation.Location.UnitName)
{
_logger.LogDebug("patient: {PatientId} already exist, ignoring ADT", patient.Id);
return null;
}
}
_logger.LogDebug("person dsTime:{PatientDisTime}", patient?.DisTime?.ToString() ?? "NULL");
if (patient is not
{
DisTime: null
}) //If new patient, it returns true because patient not contains a field disTime == null
{
var creationDate = patient?.CreationDate ?? DateTime.UtcNow;
if (patient != null)
{
patient.CreationDate = creationDate;
patient.UnitId = unit.Id;
patient.DisTime = null;
patient.PointOfCareId = poc.Id;
patient.Altable = new OptionList
{ IconDefault = "icNotAltable", Name = "NotAltable", OptionType = "NotAltable" };
}
else
{
patient = new Patient
{
Id = ObjectId.GenerateNewId(),
CreationDate = creationDate,
UnitId = unit.Id,
DisTime = null,
PointOfCareId = poc.Id,
Altable = new OptionList
{ IconDefault = "icNotAltable", Name = "NotAltable", OptionType = "NotAltable" }
};
}
}
_logger.LogDebug("Updating patient from request. person: {Patient} ", patient.ToStr());
await UpdatePatientFromRequest(patient, apiRequest);
if (patient.PatientNumber != null)
{
var patientInAdmission =
await _admissionService.Value.GetAdmissionByPatientNumber(patient.PatientNumber);
if (patientInAdmission != null)
{
await MergePatientWithAdmission(patient, patientInAdmission);
await _admissionService.Value.DeleteAdmissionByIdAsync(patientInAdmission.Id);
}
}
_logger.LogDebug("Inserting patient. person: {Patient}", patient.ToStr());
await Update(patient);
_ = SendNewPatientBroadcast(patient);
await _pointOfCareService.SetPointOfCareStatus(poc.Id, StatusEnum.PointOfCare.InUse);
// Enviar en base a la config de display que tenga cada subscriptor
_ = SendLastObsToSubscriberByPoc(poc, patient);
// Enviar las grupadas dependiendo del config del subscriptor
_ = SendLastGroupedObsToSubscriberByPoc(poc.Id, null, patient);
_logger.LogDebug("Updated patient: {PatientId}", patient.Id);
}
catch (Exception e)
{
_logger.LogError("Exception processing patient admission : {EMessage}", e);
return null;
}
return patient;
}
private async Task MergePatientWithAdmission(Patient patient, Admission patientInAdmission)
{
patient.Insulation = patientInAdmission.Insulation;
if (patient.Insulation != null)
await GenerateNurseObsAndInsert(_listSettings.InsulationList.ManualObservationName!, [patient.Insulation],
patient.Id, null);
patient.Allergies = patientInAdmission.Allergies;
if (patient.Allergies != null)
await GenerateNurseObsAndInsert(_listSettings.AllergyList.ManualObservationName!, patient.Allergies,
patient.Id, null);
patient.Diagnosis = patientInAdmission.Diagnosis;
if (patient.Diagnosis != null)
await GenerateNurseObsAndInsert(_listSettings.DiagnosisList.ManualObservationName!, [patient.Diagnosis],
patient.Id, null);
patient.DiagnosisAux = patientInAdmission.DiagnosisAux;
patient.LanguageBarrier = patientInAdmission.LanguageBarrier;
if (patient.LanguageBarrier != null)
await GenerateNurseObsAndInsert(_listSettings.LanguageBarrierList.ManualObservationName!,
patient.LanguageBarrier, patient.Id, null);
patient.Origin = patientInAdmission.Origin;
if (patient.Origin != null)
await GenerateNurseObsAndInsert(_listSettings.OriginList.ManualObservationName!, [patient.Origin],
patient.Id, null);
patient.OriginAux = patientInAdmission.OriginAux;
}
private async Task UpdatePatientFromRequest(Patient patient, ApiRequest apiRequest,
bool ignoreLocation = false)
{
if (apiRequest.AdmTime.HasValue) patient.AdmTime = apiRequest.AdmTime;
if (!string.IsNullOrEmpty(apiRequest.PatientId)) patient.PatientId = apiRequest.PatientId;
if (apiRequest.Patient != null && !apiRequest.Patient.IsEmpty())
{
// Log action to describe
patient.Person ??= new Person();
patient.Person.BirthDate = apiRequest.Patient.BirthDate;
patient.Person.Language = apiRequest.Patient.Language;
patient.Person.FirstName = apiRequest.Patient.FirstName;
patient.Person.Gender = apiRequest.Patient.Gender;
patient.Person.LastName = apiRequest.Patient.LastName;
patient.Person.SecondName = apiRequest.Patient.SecondName;
}
var patientNumberChanged = true;
if (!string.IsNullOrEmpty(apiRequest.PatientNumber))
{
// Log action to describe
patientNumberChanged = patient.PatientNumber != apiRequest.PatientNumber;
patient.PatientNumber = apiRequest.PatientNumber;
}
if (patientNumberChanged && apiRequest.Patient?.Ids != null)
{
patient.Person ??= new Person();
patient.Person.SetIds(apiRequest.Patient.Ids, apiRequest.MessageTime);
}
var unit = await _unitService.FindByName(apiRequest.Location?.UnitName);
if (unit == null && !ignoreLocation)
_logger.LogWarning(
"WARNING updating patient from request, unit not found by name: {UnitName} for patientId: {PatientId} apiRequest: {Request}",
apiRequest.Location?.UnitName ?? "", patient.Id, apiRequest.ToString());
if (apiRequest.AttendingDoctor != null && !apiRequest.AttendingDoctor.IsEmpty())
patient.AttendingDoctor = apiRequest.AttendingDoctor;
if (apiRequest.Location != null && !apiRequest.Location.IsEmpty() && apiRequest.Location.Bed != null &&
apiRequest.Location.UnitName != null && !ignoreLocation && unit != null)
{
var poc = await _pointOfCareService.FindByBedAndUnitId(apiRequest.Location.Bed, unit.Id);
if (poc != null)
{
patient.Location = new PatientLocation(unit.Name, poc.Bed, poc.Room);
patient.UnitId = unit.Id;
patient.PointOfCareId = poc.Id;
}
else
{
_logger.LogError(
"Error updating patient from request finding poc by bed name: {BedName} and unitId: {UnitId} not found request: {ApiRequest}",
apiRequest.Location?.Bed, unit.Id, apiRequest);
}
}
//To solve multi "sin cama" from ICCADB making duplicated keys error on mongodb.
if (apiRequest.Location != null && patient.Location.Bed != null &&
(patient.Location.Bed.ToLower().Equals("sin cama") || patient.Location.Bed == string.Empty) && unit != null)
{
// Log action to describe
var poc = await _pointOfCareService.FindByBedAndUnitId(VirtualPointOfCare.NoBed.ToString(), unit.Id);
if (poc != null)
{
patient.Location = new PatientLocation(unit.Name, poc.Bed, poc.Room);
patient.UnitId = unit.Id;
patient.PointOfCareId = poc.Id;
}
else
{
_logger.LogError(
"Error updating patient from request with bed empty finding poc by bed name: {BedName} and unitId: {UnitId} not found request: {ApiRequest}",
apiRequest.Location.Bed, unit.Id, apiRequest);
}
}
if (apiRequest.DisTime.HasValue) patient.DisTime = apiRequest.DisTime;
patient.UpdateDate = DateTime.UtcNow;
}
private async Task ProcessUpdatePatientLocationWithOru(Patient patient, ApiRequest apiRequest)
{
await UpdateLocation(patient.Id, apiRequest.Location);
_ = SendPatientUpdateBroadcast(patient);
}
public async Task UpdatePatientFromMerge(Patient patient, Patient oldPatient)
{
var listObsName = _masterListServiceFactory.StringNurseObs();
var obs = await _observationService.Value.FindLastObservations(patient.Id, 1, listObsName);
patient.Visits =
obs.FirstOrDefault(o => o.Name == _listSettings.VisitOptionList.ManualObservationName)?.Value is
List<OptionList> visitList
? visitList.FirstOrDefault()
: null;
patient.AccessControl =
obs.FirstOrDefault(o => o.Name == _listSettings.AccessControlList.ManualObservationName)?.Value is
List<OptionList> accessControList
? accessControList.FirstOrDefault()
: null;
patient.TherapeuticCeiling =
obs.FirstOrDefault(o => o.Name == _listSettings.TherapeuticCeilingList.ManualObservationName)?.Value is
List<OptionList> ceilingList
? ceilingList.FirstOrDefault()
: null;
patient.Mobility =
obs.FirstOrDefault(o => o.Name == _listSettings.MobilityOptionList.ManualObservationName)?.Value is
List<OptionList> mobilityList
? mobilityList.FirstOrDefault()
: null;
patient.PassiveSitting =
obs.FirstOrDefault(o => o.Name == _listSettings.PassiveSittingList.ManualObservationName)?.Value is
List<OptionList> passiveList
? passiveList.FirstOrDefault()
: null;
patient.LanguageBarrier =
obs.FirstOrDefault(o => o.Name == _listSettings.LanguageBarrierList.ManualObservationName)?.Value as List<OptionList>;
patient.Insulation =
obs.FirstOrDefault(o => o.Name == _listSettings.InsulationList.ManualObservationName)?.Value is
List<OptionList> insulationList
? insulationList.FirstOrDefault()
: null;
patient.Doctors =
obs.FirstOrDefault(o => o.Name == _listSettings.DoctorList.ManualObservationName)
?.Value as List<OptionList>;
patient.Allergies =
obs.FirstOrDefault(o => o.Name == _listSettings.AllergyList.ManualObservationName)?.Value as
List<OptionList>;
patient.Altable =
obs.FirstOrDefault(o => o.Name == _listSettings.AltableOptionList.ManualObservationName)?.Value is
List<OptionList> altableList
? altableList.FirstOrDefault()
: null;
var obsPatientIncomingData =
(PatientIncomeData?)obs.FirstOrDefault(o => o.Name == "PatientIncomingData")?.Value;
if (obsPatientIncomingData != null)
{
patient.AdmTime = obsPatientIncomingData.AdmTime;
patient.Origin ??= obsPatientIncomingData.Origin;
patient.OriginAux ??= obsPatientIncomingData.OriginAux;
patient.Diagnosis ??= obsPatientIncomingData.Diagnosis;
patient.DiagnosisAux ??= obsPatientIncomingData.DiagnosisAux;
}
if (patient.Treatment != null)
{
var missingTreatments = oldPatient.Treatment?
.Where(t => patient.Treatment.All(p => p.Name != t.Name))
.ToList();
if (missingTreatments != null)
patient.Treatment.AddRange(missingTreatments);
}
else if (oldPatient.Treatment != null)
{
patient.Treatment = oldPatient.Treatment;
}
if (patient.Procedures != null)
{
var missingProcedures = oldPatient.Procedures?
.Where(t => patient.Procedures.All(p => p.Name != t.Name))
.ToList();
if (missingProcedures != null)
patient.Procedures.AddRange(missingProcedures);
}
else if (oldPatient.Procedures != null)
{
patient.Procedures = oldPatient.Procedures;
}
if (patient.Tests != null)
{
var missingTests = oldPatient.Tests?
.Where(t => patient.Tests.All(p => p.Name != t.Name))
.ToList();
if (missingTests != null)
patient.Tests.AddRange(missingTests);
}
else if (oldPatient.Tests != null)
{
patient.Tests = oldPatient.Tests;
}
await _patientRepository.Update(patient);
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, patient);
await SendPatientBroadcast(patient);
}
private async Task GenerateNurseObsAndInsert(string name, List<OptionList>? value, ObjectId patientId, User? user)
{
var obs = GenerateObs(
name,
value ?? [],
patientId);
obs.UserId = user?.Id;
await _observationService.Value.InsertNurseObservation(obs);
}
private PatientObservation GenerateObs(string name, List<OptionList>? value, ObjectId patientId)
{
var valueList = value ?? [];
return new PatientObservation
{
Name = name,
Value = valueList,
PatientId = patientId,
Time = DateTime.Now,
InsertMode = ObservationEnum.InsertMode.Manual
};
}
public string GetManualObservationName(string masterListNameString)
{
// 1. Obtener el tipo de la clase ListSettings.
var settingsType = typeof(ListSettings);
// 2. Obtener la propiedad con el nombre que coincide con el string del enum.
// El 'masterListNameString' debe coincidir exactamente con el nombre de la propiedad.
var propertyInfo = settingsType.GetProperty(masterListNameString);
if (propertyInfo == null)
// La propiedad no se encontró (el string no coincide con un nombre de propiedad).
return $"Error: Propiedad '{masterListNameString}' no encontrada en ListSettings.";
// 3. Obtener la instancia de ListSettingItem (el valor) de esa propiedad,
// usando la instancia 'settings' proporcionada.
if (propertyInfo.GetValue(_listSettings) is not ListSettingItem listSettingItem)
// Esto no debería suceder si la clase ListSettings está bien definida,
// pero es una buena práctica comprobarlo.
return "Error: El valor de la propiedad no es un ListSettingItem.";
// 4. Devolver el valor de ManualObservationName.
return listSettingItem.ManualObservationName!;
}
private enum OnArchiveAction
{
Delete,
Archive
}
}