973 lines
40 KiB
C#
973 lines
40 KiB
C#
using adas_core.Application.Repositories.Interfaces;
|
|
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.Masters;
|
|
using adas_core.Domain.Models.MongoModels;
|
|
using Microsoft.Extensions.Options;
|
|
using MongoDB.Bson;
|
|
using MongoDB.Driver;
|
|
using Serilog;
|
|
using System.Text.RegularExpressions;
|
|
using MongoUtils = adas_core.Infrastructure.Utils.MongoUtils;
|
|
|
|
namespace adas_core.Infrastructure.Repositories;
|
|
|
|
public class PatientRepository : MongoRepository<Patient>, IPatientRepository
|
|
{
|
|
private readonly ApiSettings _apiSettings;
|
|
|
|
public PatientRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database) : base(database)
|
|
{
|
|
if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
|
|
_apiSettings = apiSettings.Value;
|
|
} //For testing
|
|
|
|
// public PatientRepository(IOptions<ApiSettings> apiSettings, IOptions<DatabaseSettings> dbSetting) :
|
|
// base(dbSetting)
|
|
// {
|
|
// if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
|
|
// _apiSettings = apiSettings.Value;
|
|
// }
|
|
|
|
public override string GetCollectionName()
|
|
{
|
|
return _apiSettings.Patients ?? "patients";
|
|
}
|
|
|
|
public async Task<Patient?> FindById(ObjectId id)
|
|
{
|
|
try
|
|
{
|
|
var filter = Builders<Patient>.Filter.Eq(p => p.Id, id);
|
|
var result = await Collection.FindAsync(filter);
|
|
|
|
return await result.FirstOrDefaultAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error("Error searching patient by id: {id}. Exception: {ex}", id, ex);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public async Task<Patient?> FindByPointOfCareId(ObjectId id)
|
|
{
|
|
try
|
|
{
|
|
var filter = Builders<Patient>.Filter.Eq(p => p.PointOfCareId, id);
|
|
var result = await Collection.FindAsync(filter);
|
|
|
|
return await result.FirstOrDefaultAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error("Error searching patient by id: {id}. Exception: {ex}", id, ex);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public async Task<Patient> FindByUnitAndPocId(ObjectId unit, ObjectId pointOfCare)
|
|
{
|
|
try
|
|
{
|
|
var filter = Builders<Patient>.Filter.And(
|
|
Builders<Patient>.Filter.Eq(p => p.PointOfCareId, pointOfCare),
|
|
Builders<Patient>.Filter.Eq(p => p.UnitId, unit)
|
|
);
|
|
var result = await Collection.FindAsync(filter);
|
|
|
|
return await result.FirstOrDefaultAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error("Error searching patient by unitId: {unitid} pointOfCareid: {id}. Exception: {ex}", unit,
|
|
pointOfCare, ex);
|
|
return null!;
|
|
}
|
|
}
|
|
|
|
//Deprecated
|
|
public async Task<Patient?> FindByLocation(PatientLocation? location)
|
|
{
|
|
if (location == null)
|
|
return null;
|
|
// Primero encontrar la unidad
|
|
|
|
var filterBuilder = Builders<Patient>.Filter;
|
|
var filter = filterBuilder.Empty;
|
|
|
|
if (!string.IsNullOrEmpty(location.UnitName) && !string.IsNullOrEmpty(location.Bed))
|
|
filter = filterBuilder.And(
|
|
filterBuilder.Eq(p => p.UnitString, location.UnitName),
|
|
filterBuilder.Eq(p => p.Bed, location.Bed)
|
|
);
|
|
else if (!string.IsNullOrEmpty(location.Bed)) filter = filterBuilder.Eq(p => p.Bed, location.Bed);
|
|
|
|
var result = await Collection.Find(filter).Limit(1).FirstOrDefaultAsync();
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
public override async Task InsertOneAsync(Patient patient)
|
|
{
|
|
try
|
|
{
|
|
patient.CreationDate = DateTime.UtcNow;
|
|
await base.InsertOneAsync(patient);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine(e);
|
|
|
|
var patientAux = await FindByLocation(patient.Location);
|
|
if (patientAux != null && patientAux.PatientNumber == patient.PatientNumber)
|
|
{
|
|
Log.Warning("Exception trying to insert an existing patient: {patient}. Exception {e}", patient, e);
|
|
}
|
|
else
|
|
{
|
|
Log.Error("Exception trying to insert patient: {patient}. Exception {e}", patient, e);
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task Update(Patient patient)
|
|
{
|
|
patient.UpdateDate = DateTime.UtcNow;
|
|
await UpdateOneAsync(patient.Id, patient);
|
|
}
|
|
|
|
public async Task Delete(ObjectId id)
|
|
{
|
|
var filter = Builders<Patient>.Filter.Eq(x => x.Id, id);
|
|
await Collection.DeleteOneAsync(filter, null);
|
|
}
|
|
|
|
public async Task UpdateLocation(ObjectId id, PatientLocation location)
|
|
{
|
|
var patient = await FindById(id);
|
|
if (patient == null)
|
|
{
|
|
Log.Warning("Patient not found for updating location: {id}", id);
|
|
return;
|
|
}
|
|
|
|
patient.UpdateDate = DateTime.UtcNow;
|
|
patient.Location = location;
|
|
await UpdateOneAsync(patient.Id, patient);
|
|
/*var filterBuilder = Builders<Patient>.Filter;
|
|
var filter = filterBuilder.Eq(p => p.Id, id);
|
|
|
|
var update = Builders<Patient>.Update
|
|
.Set(p => p.Bed, location?.Bed)
|
|
.Set(p => p.UnitString, location?.UnitName)
|
|
.Set(p => p.UpdateDate, DateTime.UtcNow)
|
|
.AddToSet(p => p.HistoricalLocations, new KeyValuePair<string, PatientLocation>(DateTime.UtcNow.ToString("o"), location));
|
|
|
|
var result = await Collection.UpdateOneAsync(filter, update);
|
|
|
|
Log.Debug("Update location result: {result}", result);*/
|
|
}
|
|
|
|
public async Task UpdateLocation(ObjectId id, ObjectId location)
|
|
{
|
|
var patient = await FindById(id);
|
|
if (patient == null)
|
|
{
|
|
Log.Warning("Patient not found for updating location: {id}", id);
|
|
return;
|
|
}
|
|
|
|
patient.UpdateDate = DateTime.UtcNow;
|
|
patient.PointOfCareId = location;
|
|
await UpdateOneAsync(patient.Id, patient);
|
|
}
|
|
|
|
public async Task UpdateAttendingDoctor(ObjectId id, Person attendingDoctor)
|
|
{
|
|
var update = Builders<Patient>.Update
|
|
.Set(p => p.AttendingDoctor, attendingDoctor)
|
|
.Set(p => p.UpdateDate, DateTime.UtcNow);
|
|
|
|
await Collection.UpdateOneAsync(p => p.Id == id, update);
|
|
}
|
|
|
|
public async Task UpdatePatientData(ObjectId id, string patientNumber, Person data, bool updatePatientNumber = true)
|
|
{
|
|
var filterBuilder = Builders<Patient>.Filter;
|
|
var updateBuilder = Builders<Patient>.Update.Set(p => p.Person, data).Set(p => p.UpdateDate, DateTime.UtcNow);
|
|
|
|
var filter = filterBuilder.Eq(p => p.Id, id);
|
|
var update = updateBuilder;
|
|
|
|
if (updatePatientNumber) update = update.Set(p => p.PatientNumber, patientNumber);
|
|
|
|
await Collection.UpdateOneAsync(filter, update);
|
|
}
|
|
|
|
|
|
public async Task<Patient?> FindByPatientNumber(string patientNumber)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(patientNumber)) return null;
|
|
|
|
//Último paciente admitido
|
|
var patient = await Collection.Find(Builders<Patient>.Filter.And(
|
|
Builders<Patient>.Filter.Eq(p => p.DisTime, null),
|
|
Builders<Patient>.Filter.Eq(p => p.PatientNumber, patientNumber)
|
|
))
|
|
.Sort(Builders<Patient>.Sort.Descending(p => p.AdmTime))
|
|
.Limit(1)
|
|
.FirstOrDefaultAsync();
|
|
|
|
|
|
//Último con fecha de alta más reciente
|
|
patient ??= await Collection.Find(Builders<Patient>.Filter.And(
|
|
Builders<Patient>.Filter.Ne(p => p.DisTime, null),
|
|
Builders<Patient>.Filter.Eq(p => p.PatientNumber, patientNumber)
|
|
))
|
|
.Sort(Builders<Patient>.Sort.Descending(p => p.DisTime))
|
|
.Limit(1)
|
|
.FirstOrDefaultAsync();
|
|
|
|
return patient;
|
|
}
|
|
|
|
public async Task<Patient?> SearchByPatientNumberAndDistinctUnit(string patientNumber, ObjectId unitId)
|
|
{
|
|
try
|
|
{
|
|
if (string.IsNullOrWhiteSpace(patientNumber)) return null;
|
|
|
|
// Paciente ubicado en un PoC pero en diferente unidad
|
|
var patient = await Collection.Find(Builders<Patient>.Filter.And(
|
|
Builders<Patient>.Filter.Eq(p => p.PatientNumber, patientNumber),
|
|
Builders<Patient>.Filter.Ne(p => p.UnitId, unitId)
|
|
)).ToListAsync();
|
|
// Si hay mas de una coincidencia devolvemos null por que puede no estar completo el patientNumber
|
|
return patient.Count > 1 ? null : patient.FirstOrDefault();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(
|
|
"Error Search By Patient Number And Distinct Unit on patient repository patientNumber: {patientNumber}, unitId: {unitId}, Excepción: {e}",
|
|
patientNumber, unitId, e.Message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public async Task<List<Patient>> FindAllPatientWithFinishedProcedures(int archiveProcedureEndDateAfterMinutes)
|
|
{
|
|
var currentDateTime = DateTime.UtcNow;
|
|
|
|
// Filtro para documentos que contienen al menos un procedimiento con opción "procedure"
|
|
var filter = Builders<Patient>.Filter.ElemMatch(
|
|
x => x.Procedures,
|
|
procedure =>
|
|
//procedure.OptionType == "procedure" &&
|
|
procedure.EndDate.HasValue
|
|
);
|
|
|
|
// Ejecutar la consulta inicial y traer los documentos
|
|
var patientsWithProcedures = await Collection.Find(filter).ToListAsync();
|
|
|
|
// Aplicar el filtro adicional en memoria
|
|
var patientsWithFinishedProcedures = patientsWithProcedures.Where(patient =>
|
|
patient.Procedures != null &&
|
|
patient.Procedures.Any(procedure =>
|
|
procedure is
|
|
{
|
|
//OptionType: "procedure",
|
|
EndDate: not null
|
|
} &&
|
|
procedure.EndDate.Value.AddMinutes(archiveProcedureEndDateAfterMinutes) < currentDateTime
|
|
)
|
|
).ToList();
|
|
|
|
return patientsWithFinishedProcedures;
|
|
}
|
|
|
|
public async Task<List<Patient>> FindAllPatientWithFinishedTests(int archiveTestEndDateAfterMinutes)
|
|
{
|
|
var currentDateTime = DateTime.UtcNow;
|
|
|
|
// Filtro para documentos que contienen al menos un procedimiento con opción "procedure"
|
|
var filter = Builders<Patient>.Filter.ElemMatch(
|
|
x => x.Tests,
|
|
procedure => //procedure.OptionType == "test" &&
|
|
procedure.EndDate.HasValue
|
|
);
|
|
|
|
// Ejecutar la consulta inicial y traer los documentos
|
|
var patientsWithTests = await Collection.Find(filter).ToListAsync();
|
|
|
|
// Aplicar el filtro adicional en memoria
|
|
var patientsWithFinishedTests = patientsWithTests.Where(patient =>
|
|
patient.Tests != null &&
|
|
patient.Tests.Any(procedure =>
|
|
procedure is
|
|
{
|
|
//OptionType: "test",
|
|
EndDate: not null
|
|
} &&
|
|
procedure.EndDate.Value.AddMinutes(archiveTestEndDateAfterMinutes) < currentDateTime
|
|
)
|
|
).ToList();
|
|
|
|
return patientsWithFinishedTests;
|
|
}
|
|
|
|
public async Task<List<Patient>> FindAllPatientWithFinishedTreatment(int archiveTreatmentEndDateAfterMinutes)
|
|
{
|
|
var currentDateTime = DateTime.UtcNow;
|
|
|
|
// Filtro para documentos que contienen al menos un procedimiento con opción "procedure"
|
|
var filter = Builders<Patient>.Filter.ElemMatch(
|
|
x => x.Treatment,
|
|
treatment => treatment.EndDate.HasValue
|
|
);
|
|
|
|
// Ejecutar la consulta inicial y traer los documentos
|
|
var patientsWithTreatment = await Collection.Find(filter).ToListAsync();
|
|
|
|
// Aplicar el filtro adicional en memoria
|
|
var patientsWithFinishedTreatments = patientsWithTreatment.Where(patient =>
|
|
patient.Treatment != null &&
|
|
patient.Treatment.Any(treatment =>
|
|
treatment.EndDate.HasValue &&
|
|
treatment.EndDate.Value.AddMinutes(archiveTreatmentEndDateAfterMinutes) < currentDateTime
|
|
)
|
|
).ToList();
|
|
|
|
return patientsWithFinishedTreatments;
|
|
}
|
|
|
|
public async Task<List<Patient>> UpdateMasterListOption(List<ObjectId> unitIds, UpdateOptionMasterListDto opt,
|
|
string typeName)
|
|
{
|
|
var isParsed = Enum.TryParse<MasterListType>(typeName, out var parsedTypeName);
|
|
if (isParsed)
|
|
{
|
|
// Tener en cuenta los datos auxiliares ya que pueden ser texto libre o asignarse el establecido en la lista
|
|
// originAux / diagnosisAux modificar en caso de ser el mismo que el padre
|
|
// Notificar a todos los fronts con el nuevo valor de cada paciente por el id de los poc's afectados(?)
|
|
var filterUnit = Builders<Patient>.Filter.In("unitId", unitIds);
|
|
switch (parsedTypeName)
|
|
{
|
|
case MasterListType.DiagnosisList:
|
|
|
|
var diagnosisFilter = Builders<Patient>.Filter.Eq(
|
|
"diagnosis.name", opt.OldOption?.Name
|
|
);
|
|
var filterUpdateDiagnosis = Builders<Patient>.Filter.And(filterUnit, diagnosisFilter);
|
|
var updateDiagnosis = Builders<Patient>.Update
|
|
.Set("diagnosis.name", opt.UpdatedOption?.Name)
|
|
.Set("diagnosis.description", opt.UpdatedOption?.Description);
|
|
await Collection.UpdateManyAsync(filterUpdateDiagnosis, updateDiagnosis);
|
|
|
|
await Collection.UpdateManyAsync(Builders<Patient>.Filter.And(filterUnit,
|
|
Builders<Patient>.Filter.Eq(
|
|
"diagnosisAux", opt.OldOption?.Name
|
|
)), Builders<Patient>.Update
|
|
.Set("diagnosisAux", opt.UpdatedOption?.Name));
|
|
|
|
// Filtro para devolver los documentos actualizados (OR entre diagnosis.name y diagnosisAux)
|
|
var diagnosisFilterToReturn =
|
|
Builders<Patient>.Filter.Eq("diagnosis.name", opt.UpdatedOption?.Name);
|
|
var diagnosisAuxFilterToReturn =
|
|
Builders<Patient>.Filter.Eq("diagnosisAux", opt.UpdatedOption?.Name);
|
|
var filterToReturn = Builders<Patient>.Filter.And(
|
|
filterUnit,
|
|
Builders<Patient>.Filter.Or(diagnosisFilterToReturn, diagnosisAuxFilterToReturn)
|
|
);
|
|
|
|
// Devolver los documentos actualizados
|
|
var updatedDocuments = await Collection.Find(filterToReturn).ToListAsync();
|
|
return updatedDocuments;
|
|
case MasterListType.DoctorList:
|
|
// Filtro para encontrar el elemento en el array `doctors` que coincida con el nombre
|
|
var doctorFilter = Builders<Patient>.Filter.ElemMatch(
|
|
"doctors",
|
|
Builders<Patient>.Filter.Eq("name", opt.OldOption?.Name)
|
|
);
|
|
var filterUpdateDoctor = Builders<Patient>.Filter.And(filterUnit, doctorFilter);
|
|
|
|
// Actualizar todos los elementos de la lista `doctors` que coincidan
|
|
var updateDoctor = Builders<Patient>.Update.Set(
|
|
"doctors.$[nameElem].name", opt.UpdatedOption?.Name
|
|
);
|
|
|
|
// Definir filtros únicos para cada campo utilizado
|
|
var arrayFilters = new List<ArrayFilterDefinition>
|
|
{
|
|
new BsonDocumentArrayFilterDefinition<BsonDocument>(
|
|
new BsonDocument("nameElem.name", opt.OldOption?.Name))
|
|
};
|
|
|
|
// Agregar filtro para `optionType` solo si es necesario
|
|
if (!string.IsNullOrEmpty(opt.OldOption?.OptionType))
|
|
{
|
|
updateDoctor = updateDoctor.Set(
|
|
"doctors.$[typeElem].optionType", opt.UpdatedOption?.OptionType
|
|
);
|
|
|
|
arrayFilters.Add(
|
|
new BsonDocumentArrayFilterDefinition<BsonDocument>(
|
|
new BsonDocument("typeElem.optionType", opt.OldOption.OptionType)));
|
|
}
|
|
|
|
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
|
|
|
|
// Ejecutar la actualización
|
|
await Collection.UpdateManyAsync(filterUpdateDoctor, updateDoctor, updateOptions);
|
|
|
|
// Devolver los documentos actualizados
|
|
var doctorFilterToReturn = Builders<Patient>.Filter.ElemMatch(
|
|
"doctors",
|
|
Builders<Patient>.Filter.Eq("name", opt.UpdatedOption?.Name)
|
|
);
|
|
|
|
var filterToReturnDoctor = Builders<Patient>.Filter.And(filterUnit, doctorFilterToReturn);
|
|
|
|
var updatedDoctorDocuments = await Collection.Find(filterToReturnDoctor).ToListAsync();
|
|
|
|
return updatedDoctorDocuments;
|
|
case MasterListType.InsulationList:
|
|
break;
|
|
case MasterListType.OriginList:
|
|
var originFilter = Builders<Patient>.Filter.Eq(
|
|
"origin.name", opt.OldOption?.Name
|
|
);
|
|
var filterUpdateOrigin = Builders<Patient>.Filter.And(filterUnit, originFilter);
|
|
var updateOrigin = Builders<Patient>.Update
|
|
.Set("origin.name", opt.UpdatedOption?.Name);
|
|
await Collection.UpdateManyAsync(filterUpdateOrigin, updateOrigin);
|
|
|
|
await Collection.UpdateManyAsync(Builders<Patient>.Filter.And(filterUnit,
|
|
Builders<Patient>.Filter.Eq(
|
|
"originAux", opt.OldOption?.Name
|
|
)), Builders<Patient>.Update
|
|
.Set("originAux", opt.UpdatedOption?.Name));
|
|
|
|
// Filtro para devolver los documentos actualizados (OR entre diagnosis.name y diagnosisAux)
|
|
var originFilterToReturn = Builders<Patient>.Filter.Eq("origin.name", opt.UpdatedOption?.Name);
|
|
var originAuxFilterToReturn = Builders<Patient>.Filter.Eq("originAux", opt.UpdatedOption?.Name);
|
|
var originfilterToReturn = Builders<Patient>.Filter.And(
|
|
filterUnit,
|
|
Builders<Patient>.Filter.Or(originFilterToReturn, originAuxFilterToReturn)
|
|
);
|
|
|
|
// Devolver los documentos actualizados
|
|
var updatedDocumentsorigin = await Collection.Find(originfilterToReturn).ToListAsync();
|
|
return updatedDocumentsorigin;
|
|
|
|
case MasterListType.DoctorTypeList:
|
|
case MasterListType.AllergyList:
|
|
case MasterListType.DestinationList:
|
|
case MasterListType.ProcedureList:
|
|
case MasterListType.ServiceList:
|
|
case MasterListType.TreatmentList:
|
|
case MasterListType.AltableOptionList:
|
|
case MasterListType.DischargeStatusList:
|
|
case MasterListType.InternalDestinationList:
|
|
case MasterListType.LanguageBarrierList:
|
|
case MasterListType.MobilityOptionList:
|
|
case MasterListType.PassiveSittingList:
|
|
case MasterListType.PatientStatusList:
|
|
case MasterListType.TherapeuticCeilingList:
|
|
case MasterListType.VisitOptionList:
|
|
case MasterListType.AccessControlList:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
public async Task<List<Patient>> GetPatientsByUnitIds(List<ObjectId> unitIds, string typeName)
|
|
{
|
|
var isParsed = Enum.TryParse<MasterListType>(typeName, out _);
|
|
if (!isParsed) return [];
|
|
|
|
var filterUnit = Builders<Patient>.Filter.In("unitId", unitIds);
|
|
return await Collection.Find(filterUnit).ToListAsync();
|
|
}
|
|
|
|
public async Task<long> CountByUnitId(ObjectId unitId)
|
|
{
|
|
try
|
|
{
|
|
var filter = Builders<Patient>.Filter.Eq(p => p.UnitId, unitId);
|
|
return await Collection.CountDocumentsAsync(filter);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Logger.Error(e.Message);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
public async Task<IEnumerable<Patient>> DeleteMasterListOption(List<ObjectId> unitIds, OptionList opt,
|
|
string typeName)
|
|
{
|
|
var isParsed = Enum.TryParse<MasterListType>(typeName, out var parsedTypeName);
|
|
if (isParsed)
|
|
{
|
|
// Tener en cuenta los datos auxiliares ya que pueden ser texto libre o asignarse el establecido en la lista
|
|
// originAux / diagnosisAux modificar en caso de ser el mismo que el padre
|
|
// Notificar a todos los fronts con el nuevo valor de cada paciente por el id de los poc's afectados(?)
|
|
var filterUnit = Builders<Patient>.Filter.In("unitId", unitIds);
|
|
switch (parsedTypeName)
|
|
{
|
|
case MasterListType.DiagnosisList:
|
|
|
|
// Filtro para devolver los documentos actualizados (OR entre diagnosis.name y diagnosisAux)
|
|
var diagnosisFilterToReturn = Builders<Patient>.Filter.Eq("diagnosis.name", opt.Name);
|
|
var diagnosisAuxFilterToReturn = Builders<Patient>.Filter.Eq("diagnosisAux", opt.Name);
|
|
var filterToReturn = Builders<Patient>.Filter.And(
|
|
filterUnit,
|
|
Builders<Patient>.Filter.Or(diagnosisFilterToReturn, diagnosisAuxFilterToReturn)
|
|
);
|
|
|
|
// Devolver los documentos actualizados
|
|
var updatedDocuments = await Collection.Find(filterToReturn).ToListAsync();
|
|
|
|
var diagnosisFilter = Builders<Patient>.Filter.Eq(
|
|
"diagnosis.name", opt.Name
|
|
);
|
|
var filterUpdateDiagnosis = Builders<Patient>.Filter.And(filterUnit, diagnosisFilter);
|
|
var updateDiagnosis = Builders<Patient>.Update
|
|
.Set(x => x.Diagnosis, null);
|
|
await Collection.UpdateManyAsync(filterUpdateDiagnosis, updateDiagnosis);
|
|
|
|
await Collection.UpdateManyAsync(Builders<Patient>.Filter.And(filterUnit,
|
|
Builders<Patient>.Filter.Eq(
|
|
"diagnosisAux", opt.Name
|
|
)), Builders<Patient>.Update
|
|
.Set(x => x.DiagnosisAux, null));
|
|
|
|
|
|
return updatedDocuments;
|
|
case MasterListType.DoctorList:
|
|
// Devolvemos los datos
|
|
var doctorFilterToReturn = Builders<Patient>.Filter.ElemMatch(
|
|
"doctors",
|
|
Builders<Patient>.Filter.Eq("_id", opt.Id)
|
|
);
|
|
var filterToReturnDoctor = Builders<Patient>.Filter.And(filterUnit, doctorFilterToReturn);
|
|
var updatedDoctorDocuments = await Collection.Find(filterToReturnDoctor).ToListAsync();
|
|
|
|
// Filtro para encontrar el elemento en el array `doctors` que coincida con el nombre
|
|
var doctorFilter = Builders<Patient>.Filter.ElemMatch(
|
|
"doctors",
|
|
Builders<Patient>.Filter.Eq("_id", opt.Id)
|
|
);
|
|
var filterUpdateDoctor = Builders<Patient>.Filter.And(filterUnit, doctorFilter);
|
|
|
|
// Actualización del campo `name` en el array `doctors`
|
|
var updateDoctor = Builders<Patient>.Update.PullFilter(
|
|
"doctors", Builders<BsonDocument>.Filter.And(
|
|
Builders<BsonDocument>.Filter.Eq("_id", opt.Id)
|
|
));
|
|
|
|
// Ejecutar la actualización
|
|
await Collection.UpdateManyAsync(filterUpdateDoctor, updateDoctor);
|
|
|
|
return updatedDoctorDocuments;
|
|
case MasterListType.InsulationList:
|
|
break;
|
|
case MasterListType.OriginList:
|
|
// Filtro para devolver los documentos actualizados (OR entre diagnosis.name y diagnosisAux)
|
|
var originFilterToReturn = Builders<Patient>.Filter.Eq("origin.name", opt.Name);
|
|
var originAuxFilterToReturn = Builders<Patient>.Filter.Eq("originAux", opt.Name);
|
|
var filterOriginToReturn = Builders<Patient>.Filter.And(
|
|
filterUnit,
|
|
Builders<Patient>.Filter.Or(originFilterToReturn, originAuxFilterToReturn)
|
|
);
|
|
|
|
// Devolver los documentos actualizados
|
|
var updatedDocumentsOrigin = await Collection.Find(filterOriginToReturn).ToListAsync();
|
|
|
|
var originFilter = Builders<Patient>.Filter.Eq(
|
|
"origin.name", opt.Name
|
|
);
|
|
var filterUpdateOrigin = Builders<Patient>.Filter.And(filterUnit, originFilter);
|
|
var updateOrigin = Builders<Patient>.Update
|
|
.Set(x => x.Diagnosis, null);
|
|
await Collection.UpdateManyAsync(filterUpdateOrigin, updateOrigin);
|
|
|
|
await Collection.UpdateManyAsync(Builders<Patient>.Filter.And(filterUnit,
|
|
Builders<Patient>.Filter.Eq(
|
|
"originAux", opt.Name
|
|
)), Builders<Patient>.Update
|
|
.Set(x => x.OriginAux, null));
|
|
|
|
|
|
return updatedDocumentsOrigin;
|
|
|
|
case MasterListType.DoctorTypeList:
|
|
case MasterListType.AllergyList:
|
|
case MasterListType.DestinationList:
|
|
case MasterListType.ProcedureList:
|
|
case MasterListType.ServiceList:
|
|
case MasterListType.TreatmentList:
|
|
case MasterListType.AltableOptionList:
|
|
case MasterListType.DischargeStatusList:
|
|
case MasterListType.InternalDestinationList:
|
|
case MasterListType.LanguageBarrierList:
|
|
case MasterListType.MobilityOptionList:
|
|
case MasterListType.PassiveSittingList:
|
|
case MasterListType.PatientStatusList:
|
|
case MasterListType.TherapeuticCeilingList:
|
|
case MasterListType.VisitOptionList:
|
|
case MasterListType.AccessControlList:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return new List<Patient>();
|
|
}
|
|
|
|
public async Task<Patient?> FindByPatientId(string patientId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(patientId)) return null;
|
|
|
|
//Último paciente admitido
|
|
var patient = await Collection.Find(p => !p.DisTime.HasValue && patientId == p.PatientId)
|
|
.Sort(Builders<Patient>.Sort.Descending(p => p.AdmTime)).Limit(1).FirstOrDefaultAsync();
|
|
|
|
//Último con fecha de alta más reciente
|
|
patient ??= await Collection.Find(p => p.DisTime.HasValue && patientId == p.PatientId)
|
|
.Sort(Builders<Patient>.Sort.Descending(p => p.DisTime)).Limit(1).FirstOrDefaultAsync();
|
|
|
|
return patient;
|
|
}
|
|
|
|
public async Task<Patient?> FindByPatientId(ObjectId patientId)
|
|
{
|
|
//Último paciente admitido
|
|
var patient = await Collection.Find(p => !p.DisTime.HasValue && patientId == p.Id)
|
|
.Sort(Builders<Patient>.Sort.Descending(p => p.AdmTime)).Limit(1).FirstOrDefaultAsync();
|
|
|
|
//Último con fecha de alta más reciente
|
|
patient ??= await Collection.Find(p => p.DisTime.HasValue && patientId == p.Id)
|
|
.Sort(Builders<Patient>.Sort.Descending(p => p.DisTime)).Limit(1).FirstOrDefaultAsync();
|
|
|
|
return patient;
|
|
}
|
|
|
|
public async Task<List<Patient>> FindAll()
|
|
{
|
|
return (await Collection.FindAsync(Builders<Patient>.Filter.Empty)).ToList();
|
|
//return (await Collection.FindAsync(_ => true)).ToList();
|
|
}
|
|
|
|
public async Task<List<Patient>> FindByPointOfCare(string pointOfCare)
|
|
{
|
|
var filterBuilder = Builders<Patient>.Filter;
|
|
var filter = filterBuilder.Eq(pa => pa.UnitString, pointOfCare);
|
|
|
|
var result = await Collection.FindAsync(filter);
|
|
|
|
return await result.ToListAsync();
|
|
}
|
|
|
|
public async Task<List<Patient>> FindByPointOfCare(ObjectId pointOfCare)
|
|
{
|
|
var filterBuilder = Builders<Patient>.Filter;
|
|
var filter = filterBuilder.Eq(pa => pa.PointOfCareId, pointOfCare);
|
|
|
|
var result = await Collection.FindAsync(filter);
|
|
|
|
return await result.ToListAsync();
|
|
}
|
|
|
|
public async Task<List<Patient>> FindInActivePoC()
|
|
{
|
|
var virtualPointOfCareValues = Enum.GetValues(typeof(VirtualPointOfCare)).Cast<VirtualPointOfCare>()
|
|
.Select(p => p.ToString())
|
|
.ToList();
|
|
|
|
//var filterBuilder = Builders<Patient>.Filter;
|
|
|
|
// Define el pipeline de agregación
|
|
var pipeline = new[]
|
|
{
|
|
new BsonDocument("$lookup", new BsonDocument
|
|
{
|
|
{ "from", "pointOfCares" }, // Colección de PointOfCare
|
|
{ "localField", "pointOfCareId" },
|
|
{ "foreignField", "_id" },
|
|
{ "as", "pointOfCareInfo" }
|
|
}),
|
|
new BsonDocument("$unwind", "$pointOfCareInfo"),
|
|
new BsonDocument("$match", new BsonDocument
|
|
{
|
|
{ "pointOfCareInfo.bed", new BsonDocument("$nin", new BsonArray(virtualPointOfCareValues)) }
|
|
})
|
|
};
|
|
|
|
var result = await Collection.Aggregate<Patient>(pipeline).ToListAsync();
|
|
return result;
|
|
}
|
|
|
|
|
|
public async Task<List<Patient>> FindInInactivePoC()
|
|
{
|
|
var virtualPointOfCareValues = Enum.GetValues(typeof(VirtualPointOfCare)).Cast<VirtualPointOfCare>()
|
|
.Select(p => p.ToString())
|
|
.ToList();
|
|
|
|
//var filterBuilder = Builders<Patient>.Filter;
|
|
|
|
// Define el pipeline de agregación
|
|
var pipeline = new[]
|
|
{
|
|
new BsonDocument("$lookup", new BsonDocument
|
|
{
|
|
{ "from", "pointOfCares" }, // Colección de PointOfCare
|
|
{ "localField", "pointOfCareId" },
|
|
{ "foreignField", "_id" },
|
|
{ "as", "pointOfCareInfo" }
|
|
}),
|
|
new BsonDocument("$unwind", "$pointOfCareInfo"),
|
|
new BsonDocument("$match", new BsonDocument
|
|
{
|
|
{ "pointOfCareInfo.bed", new BsonDocument("$in", new BsonArray(virtualPointOfCareValues)) }
|
|
})
|
|
};
|
|
|
|
var result = await Collection.Aggregate<Patient>(pipeline).ToListAsync();
|
|
return result;
|
|
}
|
|
|
|
public IFindFluent<Patient, Patient> GetPaginatedPatients(PaginationFilter filter)
|
|
{
|
|
var filterBuilder = Builders<Patient>.Filter;
|
|
var sort = Builders<Patient>.Sort.Descending("admTime");
|
|
var filters = new List<FilterDefinition<Patient>>();
|
|
|
|
if (filter.FilteredRequest != null)
|
|
{
|
|
AddDefaultFilters(filters, filter, filterBuilder);
|
|
// return CreateFindFluent(filters, sort);
|
|
}
|
|
|
|
var requestFilter = filter.FilteredRequest;
|
|
|
|
if(requestFilter == null) return CreateFindFluent(filters, sort);
|
|
|
|
AddTimeFilters(requestFilter, filters, filterBuilder);
|
|
|
|
if (!string.IsNullOrEmpty(filter.FilteredRequest?.Text))
|
|
{
|
|
var textFilter = filter.FilteredRequest.Text;
|
|
var textFilterEscaped = Regex.Escape(textFilter);
|
|
|
|
var orFilters = new List<FilterDefinition<Patient>>
|
|
{
|
|
filterBuilder.Regex(p => p.Person!.FirstName, new BsonRegularExpression(textFilterEscaped, "i")),
|
|
filterBuilder.Regex(p => p.Person!.SecondName, new BsonRegularExpression(textFilterEscaped, "i")),
|
|
filterBuilder.Regex(p => p.Person!.LastName, new BsonRegularExpression(textFilterEscaped, "i")),
|
|
filterBuilder.Regex(p => p.PatientNumber, new BsonRegularExpression(textFilterEscaped, "i"))
|
|
};
|
|
|
|
if (ObjectId.TryParse(textFilter, out var id)) orFilters.Add(filterBuilder.Eq("_id", id));
|
|
|
|
filters.Add(filterBuilder.Or(orFilters));
|
|
}
|
|
|
|
if (filter.FilteredRequest?.UnitId != null && ObjectId.TryParse(filter.FilteredRequest.UnitId, out var unitId))
|
|
filters.Add(filterBuilder.Eq("unitId", unitId));
|
|
if (filter.FilteredRequest?.PocId != null && ObjectId.TryParse(filter.FilteredRequest.PocId, out var pocId))
|
|
filters.Add(filterBuilder.Eq("pointOfCareId", pocId));
|
|
return CreateFindFluent(filters, sort);
|
|
}
|
|
|
|
|
|
public async Task<List<Patient>> FindPatientsNotUpdatedSince(DateTime date)
|
|
{
|
|
var filter = Builders<Patient>.Filter.Or(
|
|
Builders<Patient>.Filter.Lt(p => p.UpdateDate, date),
|
|
Builders<Patient>.Filter.Eq(p => p.UpdateDate, null)
|
|
);
|
|
return await Collection.Find(filter).ToListAsync();
|
|
|
|
//return await Collection.Find(p => p.UpdateDate < date).ToListAsync();
|
|
}
|
|
|
|
public async Task<List<Patient>> FindDischargedPatients()
|
|
{
|
|
try
|
|
{
|
|
var filter = Builders<Patient>.Filter.Ne(p => p.DisTime, null);
|
|
var cursor = await Collection.FindAsync(filter);
|
|
var patients = await cursor.ToListAsync();
|
|
|
|
return patients;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error("Error finding discharged patients {exMessage}", ex.Message);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public async Task<Patient?> UpdateOne(Patient updatedPatient)
|
|
{
|
|
var filter = Builders<Patient>.Filter.Eq("_id", updatedPatient.Id);
|
|
|
|
var update = Builders<Patient>.Update
|
|
.Set(p => p.PatientNumber, updatedPatient.PatientNumber)
|
|
.Set(p => p.Person, updatedPatient.Person)
|
|
.Set(p => p.UpdateDate, DateTime.UtcNow);
|
|
|
|
return await Collection.FindOneAndUpdateAsync(filter, update,
|
|
new FindOneAndUpdateOptions<Patient, Patient> { ReturnDocument = ReturnDocument.After });
|
|
}
|
|
|
|
public async Task<Patient?> UpdatePatientIncomingData(ObjectId patientId, Patient person)
|
|
{
|
|
var filter = Builders<Patient>.Filter.Eq("_id", patientId);
|
|
var update = Builders<Patient>.Update
|
|
.Set(p => p.OriginAux, person.OriginAux)
|
|
.Set(p => p.DiagnosisAux, person.DiagnosisAux)
|
|
.Set(p => p.Diagnosis, person.Diagnosis)
|
|
.Set(p => p.UpdateDate, DateTime.UtcNow)
|
|
.Set(p => p.AdmTime, person.AdmTime)
|
|
.Set(p => p.Origin, person.Origin);
|
|
|
|
return await Collection.FindOneAndUpdateAsync(filter, update,
|
|
new FindOneAndUpdateOptions<Patient, Patient> { ReturnDocument = ReturnDocument.After });
|
|
}
|
|
|
|
public async Task<Patient?> UpdatePatientDemographicData(ObjectId patientId, Patient person)
|
|
{
|
|
var filter = Builders<Patient>.Filter.Eq("_id", patientId);
|
|
var update = Builders<Patient>.Update
|
|
.Set(p => p.UpdateDate, DateTime.UtcNow)
|
|
.Set(p => p.Allergies, person.Allergies)
|
|
.Set(p => p.LanguageBarrier, person.LanguageBarrier)
|
|
.Set(p => p.DiagnosisAux, person.DiagnosisAux)
|
|
.Set(p => p.Diagnosis, person.Diagnosis)
|
|
.Set(p => p.Person, person.Person);
|
|
|
|
|
|
return await Collection.FindOneAndUpdateAsync(filter, update,
|
|
new FindOneAndUpdateOptions<Patient, Patient> { ReturnDocument = ReturnDocument.After });
|
|
}
|
|
|
|
|
|
public override async Task CreateIndexes()
|
|
{
|
|
var options = new CreateIndexOptions<Patient> { Background = true, Unique = false };
|
|
var optionsUq = new CreateIndexOptions<Patient>
|
|
{
|
|
Background = true,
|
|
Unique = true
|
|
//PartialFilterExpression = Builders<Patient>.Filter.Exists(p => p.PointOfCareId) &
|
|
// Builders<Patient>.Filter.Exists(p => p.UnitId)
|
|
};
|
|
var indexes = new List<CreateIndexModel<Patient>>
|
|
{
|
|
new("{ patientNumber: 1 }", optionsUq),
|
|
new("{ admTime: 1 }", options)
|
|
//new("{ pointOfCareId: 1, unitId: 1 }", optionsUq),
|
|
//new("{ pointOfCareId: 1 }", optionsUq)
|
|
};
|
|
|
|
await MongoUtils.EnsureIndexes(Collection, indexes);
|
|
}
|
|
|
|
private static void AddTimeFilters(FilteredRequest requestFilter, List<FilterDefinition<Patient>> filters,
|
|
FilterDefinitionBuilder<Patient> filterBuilder)
|
|
{
|
|
// Filter by admission time
|
|
if (requestFilter.StartAdmTime.HasValue)
|
|
filters.Add(filterBuilder.Gte(p => p.AdmTime, requestFilter.StartAdmTime.Value));
|
|
if (requestFilter.EndAdmTime.HasValue)
|
|
filters.Add(filterBuilder.Lte(p => p.AdmTime, requestFilter.EndAdmTime.Value));
|
|
|
|
// Filter by discharge time
|
|
if (requestFilter.StartDischargeTime.HasValue)
|
|
filters.Add(filterBuilder.Gte(p => p.DisTime, requestFilter.StartDischargeTime.Value));
|
|
if (requestFilter.EndDischargeTime.HasValue)
|
|
filters.Add(filterBuilder.Lte(p => p.DisTime, requestFilter.EndDischargeTime.Value));
|
|
|
|
// Filter by last observation time
|
|
if (requestFilter.StartLastObsTime.HasValue)
|
|
filters.Add(filterBuilder.Gte(p => p.LastObservationDate, requestFilter.StartLastObsTime.Value));
|
|
if (requestFilter.EndLastObsTime.HasValue)
|
|
filters.Add(filterBuilder.Lte(p => p.LastObservationDate, requestFilter.EndLastObsTime.Value));
|
|
|
|
// Filter by creation date
|
|
if (requestFilter.StartCreationDateTime.HasValue)
|
|
filters.Add(filterBuilder.Gte(p => p.CreationDate, requestFilter.StartCreationDateTime.Value));
|
|
if (requestFilter.EndCreationDateTime.HasValue)
|
|
filters.Add(filterBuilder.Lte(p => p.CreationDate, requestFilter.EndCreationDateTime.Value));
|
|
|
|
// Filter by update date
|
|
if (requestFilter.StartUpDateTime.HasValue)
|
|
filters.Add(filterBuilder.Gte(p => p.UpdateDate, requestFilter.StartUpDateTime.Value));
|
|
if (requestFilter.EndUpDateTime.HasValue)
|
|
filters.Add(filterBuilder.Lte(p => p.UpdateDate, requestFilter.EndUpDateTime.Value));
|
|
|
|
// Filter by birth date (with Person null check)
|
|
if (requestFilter.StartBirthDate.HasValue || requestFilter.EndBirthDate.HasValue)
|
|
{
|
|
// Check if Person is not null
|
|
filters.Add(filterBuilder.Exists(p => p.Person));
|
|
|
|
// Apply birth date filters only if Person is not null
|
|
if (requestFilter.StartBirthDate.HasValue)
|
|
filters.Add(filterBuilder.Gte(p => p.Person!.BirthDate, requestFilter.StartBirthDate.Value));
|
|
if (requestFilter.EndBirthDate.HasValue)
|
|
filters.Add(filterBuilder.Lte(p => p.Person!.BirthDate, requestFilter.EndBirthDate.Value));
|
|
}
|
|
}
|
|
|
|
|
|
public async Task<List<Patient>> FindAllByPointOfCareId(ObjectId pointOfCare)
|
|
{
|
|
var filterBuilder = Builders<Patient>.Filter;
|
|
var filter = filterBuilder.Eq(pa => pa.PointOfCareId, pointOfCare);
|
|
|
|
var result = await Collection.FindAsync(filter);
|
|
|
|
return await result.ToListAsync();
|
|
}
|
|
|
|
private void AddDefaultFilters(List<FilterDefinition<Patient>> filters, PaginationFilter filter,
|
|
FilterDefinitionBuilder<Patient> filterBuilder)
|
|
{
|
|
// Verifica si PatientId tiene valor
|
|
if (filter.FilteredRequest?.PatientId != null)
|
|
{
|
|
if(ObjectId.TryParse(filter.FilteredRequest.PatientId, out var patientId))
|
|
filters.Add(
|
|
filterBuilder.Eq(p => p.Id, patientId)
|
|
);
|
|
}
|
|
|
|
// Verifica si PatientNumber tiene valor
|
|
else if (filter.FilteredRequest?.PatientNumber != null)
|
|
filters.Add(
|
|
filterBuilder.Eq(p => p.PatientNumber, filter.FilteredRequest.PatientNumber)
|
|
);
|
|
}
|
|
|
|
private IFindFluent<Patient, Patient> CreateFindFluent(List<FilterDefinition<Patient>> filters,
|
|
SortDefinition<Patient> sort)
|
|
{
|
|
var combinedFilter = filters.Any()
|
|
? Builders<Patient>.Filter.And(filters)
|
|
: Builders<Patient>.Filter.Empty;
|
|
|
|
return Collection.Find(combinedFilter).Sort(sort);
|
|
}
|
|
} |