Files
adas-core/adas-core.Infrastructure/Repositories/PatientRepository.cs
T

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);
}
}