3223 lines
144 KiB
C#
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
|
|
}
|
|
} |