rama creada apartir de master en j
This commit is contained in:
@@ -32,132 +32,178 @@ public class AdminPanelService(
|
||||
|
||||
#region Patient
|
||||
|
||||
/// <summary>
|
||||
/// Archives the specified patient via the patient service and logs the operation. Returns <c>true</c> on success; any exception thrown by the underlying service is written to the console and rethrown.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to archive.</param>
|
||||
/// <returns><c>true</c> if the patient was archived successfully.</returns>
|
||||
/// <exception cref="System.Exception">Rethrows any exception thrown by the underlying patient service after logging it to the console.</exception>
|
||||
public async Task<bool> ArchivePatient(Patient patient)
|
||||
{
|
||||
try
|
||||
{
|
||||
await patientService.ArchivePatient(patient);
|
||||
try
|
||||
{
|
||||
await patientService.ArchivePatient(patient);
|
||||
|
||||
logger.LogDebug("archived patientid {patientid} from ADMPanel", patient.Id);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogDebug("archived patientid {patientid} from ADMPanel", patient.Id);
|
||||
/// <summary>
|
||||
/// Creates a new patient from an ADMPanel request, generating a new identifier and persisting it through the patient service.
|
||||
/// </summary>
|
||||
/// <param name="admRequest">The ADMPanel request containing the data used to populate the new patient.</param>
|
||||
/// <returns>The newly created <see cref="Patient"/> with its generated identifier.</returns>
|
||||
public async Task<Patient?> CreatePatient(AdmPanelRequest admRequest)
|
||||
{
|
||||
var patient = new Patient
|
||||
{
|
||||
Id = ObjectId.GenerateNewId()
|
||||
};
|
||||
await UpdateNewPatient(patient, admRequest);
|
||||
await patientService.Insert(patient);
|
||||
|
||||
logger.LogDebug("Inserted {patientid} from ADMPanel", patient.Id);
|
||||
return patient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing <see cref="Patient"/> with data from an <see cref="AdmPanelRequest"/>, applying patient fields,
|
||||
/// person data, default identifier records, and unit/point-of-care (location) assignment, including conflict and not-found validations.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient entity to be updated in place.</param>
|
||||
/// <param name="admRequest">The admission panel request containing the new values to apply to the patient.</param>
|
||||
/// <exception cref="Exception">Thrown when the target <c>PointOfCareId</c> is already occupied by another patient.</exception>
|
||||
/// <exception cref="NotFoundException">Thrown when the requested <c>PointOfCareId</c> does not exist.</exception>
|
||||
/// <returns>The asynchronous <see cref="Task"/> representing the update operation.</returns>
|
||||
private async Task UpdateNewPatient(Patient patient, AdmPanelRequest admRequest)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(admRequest.PatientNumber)) patient.PatientNumber = admRequest.PatientNumber;
|
||||
if (admRequest.AdmTime.HasValue) patient.AdmTime = admRequest.AdmTime;
|
||||
if (admRequest.Patient != null && !admRequest.Patient.IsEmptyDontCheckIds())
|
||||
patient.Person = admRequest.Patient;
|
||||
if (admRequest.Patient != null && (admRequest.Patient.Ids == null || admRequest.Patient.Ids.Count == 0))
|
||||
{
|
||||
if (patient.Person is { Ids: null }) patient.Person.Ids = new Dictionary<string, string>();
|
||||
foreach (var item in _defaultIdRecord) patient.Person?.Ids?.Add(item, admRequest?.PatientNumber ?? "null");
|
||||
}
|
||||
|
||||
patient.Person?.SetIds(patient.Person?.Ids ?? new Dictionary<string, string>());
|
||||
|
||||
if (admRequest is { UnitId: not null })
|
||||
{
|
||||
var unit = await unitService.FindById(admRequest.UnitId);
|
||||
patient.UnitId = unit?.Id ?? admRequest.UnitId;
|
||||
patient.UnitString = unit?.Name;
|
||||
|
||||
|
||||
if (admRequest is { PointOfCareId: not null })
|
||||
{
|
||||
//El paciente existe en la localizacion no te dejo insertarlo
|
||||
var patientInLocation = await patientService.FindByPointOfCareId(admRequest.PointOfCareId.Value);
|
||||
if (patientInLocation != null)
|
||||
{
|
||||
logger.LogError(
|
||||
"trying to insert patientid {patientid} in to location already in use. PointOfcareId: {poc}",
|
||||
patient.Id, patient.PointOfCareId);
|
||||
throw new Exception($"patient already in location: {patient.Location}");
|
||||
}
|
||||
|
||||
var poc = await pocService.FindById(admRequest.PointOfCareId.Value);
|
||||
if (poc == null)
|
||||
{
|
||||
logger.LogError(
|
||||
"trying to insert patientid {patientid} in to location not found. PointOfcareId: {poc}",
|
||||
patient.Id, admRequest.PointOfCareId);
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
await pocService.SetPointOfCareStatus(poc.Id, StatusEnum.PointOfCare.InUse);
|
||||
|
||||
patient.Location = new PatientLocation
|
||||
(
|
||||
bed: poc.Bed,
|
||||
room: poc.Room,
|
||||
unitName: unit?.Name
|
||||
);
|
||||
patient.PointOfCareId = poc.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
var pocUnknown =
|
||||
await pocService.FindByBedAndUnitId(VirtualPointOfCare.Unknown.ToString(), patient.UnitId);
|
||||
|
||||
patient.PointOfCareId = pocUnknown?.Id;
|
||||
}
|
||||
|
||||
patient.UpdateDate = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a patient by their unique identifier from the patient service.
|
||||
/// Throws a not-found exception when no matching patient exists for the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient to look up.</param>
|
||||
/// <returns>The patient matching the provided identifier.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no patient is found for the specified identifier.</exception>
|
||||
public async Task<Patient?> FindPatientById(ObjectId id)
|
||||
{
|
||||
return await patientService.FindById(id) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a patient at the specified location by delegating to the patient service.
|
||||
/// Returns null when no patient is found at the given location.
|
||||
/// </summary>
|
||||
/// <param name="location">The location to search for a patient at.</param>
|
||||
/// <returns>A <see cref="Patient"/> if one is found at the specified location; otherwise, <c>null</c>.</returns>
|
||||
public async Task<Patient?> FindPatientByLocation(PatientLocation location)
|
||||
{
|
||||
return await patientService.FindByLocation(location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a patient by their unique patient number by delegating to the patient service.
|
||||
/// Returns <c>null</c> when no matching patient is found.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The unique patient number used to look up the patient.</param>
|
||||
/// <returns>A <see cref="Patient"/> if a match is found; otherwise, <c>null</c>.</returns>
|
||||
public async Task<Patient?> FindPatientByPatientNumber(string patientNumber)
|
||||
{
|
||||
return await patientService.FindByPatientNumber(patientNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the patient information based on the provided administrative panel request, persisting only the patient-related fields and detecting whether the patient number has changed.
|
||||
/// </summary>
|
||||
/// <param name="request">The administrative panel request containing the new patient number and the updated patient data to apply.</param>
|
||||
/// <param name="oldPatient">The existing patient record currently stored in the database that will be updated.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the patient data is successfully updated.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the request is missing the patient number, the patient payload is null, or the patient payload is empty.</exception>
|
||||
public async Task<bool> UpdatePatientData(AdmPanelRequest request, Patient oldPatient)
|
||||
{
|
||||
//traer el paciente que se quiere actualizar con los valores que tenga en la base de datos a una variable
|
||||
//actualizar unicamente los campos que tengan que ver con los datos del paciente
|
||||
if (request.PatientNumber == null || request.Patient == null || request.Patient.IsEmptyDontCheckIds())
|
||||
{
|
||||
logger.LogError("Patient Data not updated. Old Patient:{oldPatient}. Api Request {request}", oldPatient,
|
||||
request);
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
}
|
||||
|
||||
var patientNumberChanged = oldPatient.PatientNumber != request.PatientNumber;
|
||||
await patientService.UpdatePatientData(oldPatient.Id, request.PatientNumber, request.Patient,
|
||||
patientNumberChanged);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Patient?> CreatePatient(AdmPanelRequest admRequest)
|
||||
{
|
||||
var patient = new Patient
|
||||
{
|
||||
Id = ObjectId.GenerateNewId()
|
||||
};
|
||||
await UpdateNewPatient(patient, admRequest);
|
||||
await patientService.Insert(patient);
|
||||
|
||||
logger.LogDebug("Inserted {patientid} from ADMPanel", patient.Id);
|
||||
return patient;
|
||||
}
|
||||
|
||||
private async Task UpdateNewPatient(Patient patient, AdmPanelRequest admRequest)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(admRequest.PatientNumber)) patient.PatientNumber = admRequest.PatientNumber;
|
||||
if (admRequest.AdmTime.HasValue) patient.AdmTime = admRequest.AdmTime;
|
||||
if (admRequest.Patient != null && !admRequest.Patient.IsEmptyDontCheckIds())
|
||||
patient.Person = admRequest.Patient;
|
||||
if (admRequest.Patient != null && (admRequest.Patient.Ids == null || admRequest.Patient.Ids.Count == 0))
|
||||
{
|
||||
if (patient.Person is { Ids: null }) patient.Person.Ids = new Dictionary<string, string>();
|
||||
foreach (var item in _defaultIdRecord) patient.Person?.Ids?.Add(item, admRequest?.PatientNumber ?? "null");
|
||||
}
|
||||
|
||||
patient.Person?.SetIds(patient.Person?.Ids ?? new Dictionary<string, string>());
|
||||
|
||||
if (admRequest is { UnitId: not null })
|
||||
{
|
||||
var unit = await unitService.FindById(admRequest.UnitId);
|
||||
patient.UnitId = unit?.Id ?? admRequest.UnitId;
|
||||
patient.UnitString = unit?.Name;
|
||||
|
||||
|
||||
if (admRequest is { PointOfCareId: not null })
|
||||
{
|
||||
//El paciente existe en la localizacion no te dejo insertarlo
|
||||
var patientInLocation = await patientService.FindByPointOfCareId(admRequest.PointOfCareId.Value);
|
||||
if (patientInLocation != null)
|
||||
{
|
||||
logger.LogError(
|
||||
"trying to insert patientid {patientid} in to location already in use. PointOfcareId: {poc}",
|
||||
patient.Id, patient.PointOfCareId);
|
||||
throw new Exception($"patient already in location: {patient.Location}");
|
||||
}
|
||||
|
||||
var poc = await pocService.FindById(admRequest.PointOfCareId.Value);
|
||||
if (poc == null)
|
||||
{
|
||||
logger.LogError(
|
||||
"trying to insert patientid {patientid} in to location not found. PointOfcareId: {poc}",
|
||||
patient.Id, admRequest.PointOfCareId);
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
await pocService.SetPointOfCareStatus(poc.Id, StatusEnum.PointOfCare.InUse);
|
||||
|
||||
patient.Location = new PatientLocation
|
||||
(
|
||||
bed: poc.Bed,
|
||||
room: poc.Room,
|
||||
unitName: unit?.Name
|
||||
);
|
||||
patient.PointOfCareId = poc.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
var pocUnknown =
|
||||
await pocService.FindByBedAndUnitId(VirtualPointOfCare.Unknown.ToString(), patient.UnitId);
|
||||
|
||||
patient.PointOfCareId = pocUnknown?.Id;
|
||||
}
|
||||
|
||||
patient.UpdateDate = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Patient?> FindPatientById(ObjectId id)
|
||||
{
|
||||
return await patientService.FindById(id) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
public async Task<Patient?> FindPatientByLocation(PatientLocation location)
|
||||
{
|
||||
return await patientService.FindByLocation(location);
|
||||
}
|
||||
|
||||
public async Task<Patient?> FindPatientByPatientNumber(string patientNumber)
|
||||
{
|
||||
return await patientService.FindByPatientNumber(patientNumber);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdatePatientData(AdmPanelRequest request, Patient oldPatient)
|
||||
{
|
||||
//traer el paciente que se quiere actualizar con los valores que tenga en la base de datos a una variable
|
||||
//actualizar unicamente los campos que tengan que ver con los datos del paciente
|
||||
if (request.PatientNumber == null || request.Patient == null || request.Patient.IsEmptyDontCheckIds())
|
||||
{
|
||||
logger.LogError("Patient Data not updated. Old Patient:{oldPatient}. Api Request {request}", oldPatient,
|
||||
request);
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
}
|
||||
|
||||
var patientNumberChanged = oldPatient.PatientNumber != request.PatientNumber;
|
||||
await patientService.UpdatePatientData(oldPatient.Id, request.PatientNumber, request.Patient,
|
||||
patientNumberChanged);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdatePatientLocation(AdmPanelRequest request)
|
||||
{
|
||||
@@ -198,102 +244,154 @@ public class AdminPanelService(
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="Patient"/> based on the provided admission panel request, using location-aware lookup when the location is not fully empty.
|
||||
/// When a location is present, the search is performed including location criteria; otherwise the location parameter is ignored.
|
||||
/// </summary>
|
||||
/// <param name="request">The admission panel request containing the patient identification data and optional location used to locate the patient.</param>
|
||||
/// <returns>The matching <see cref="Patient"/> if found.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no patient matches the provided request criteria.</exception>
|
||||
public async Task<Patient?> FindPatient(AdmPanelRequest request)
|
||||
{
|
||||
var findByLocation = !request.Location?.IsFullEmpty();
|
||||
|
||||
return await patientService.FindPatient(request.PatientId, request.PatientNumber, request.Location,
|
||||
findByLocation ?? false) ?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
{
|
||||
var findByLocation = !request.Location?.IsFullEmpty();
|
||||
|
||||
return await patientService.FindPatient(request.PatientId, request.PatientNumber, request.Location,
|
||||
findByLocation ?? false) ?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a patient associated with the specified location, throwing a not-found exception if no patient is found.
|
||||
/// </summary>
|
||||
/// <param name="location">The location used to look up the patient.</param>
|
||||
/// <returns>The patient found at the specified location.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no patient is found for the given location.</exception>
|
||||
public async Task<Patient?> FindByLocation(PatientLocation location)
|
||||
{
|
||||
return await patientService.FindByLocation(location) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
{
|
||||
return await patientService.FindByLocation(location) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ConfigObservations
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new configuration based on the provided observation data.
|
||||
/// Throws a conflict exception when the underlying creation operation fails (returns null), otherwise returns true.
|
||||
/// </summary>
|
||||
/// <param name="configObservation">The configuration observation containing the data to persist.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the configuration is created successfully.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the configuration creation fails, indicated by a null result from the service call.</exception>
|
||||
public async Task<bool> CreateConfig(ConfigObservation configObservation)
|
||||
{
|
||||
_ = await configObservationService.CreateConfig(configObservation) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
|
||||
return true;
|
||||
}
|
||||
{
|
||||
_ = await configObservationService.CreateConfig(configObservation) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a configuration observation by delegating to the configuration service. If the service returns a null result, indicating a failure to update, a conflict exception is thrown.
|
||||
/// </summary>
|
||||
/// <param name="configObservation">The configuration observation to update.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the configuration is successfully updated.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the underlying update operation fails, as indicated by a null result from the service.</exception>
|
||||
public async Task<bool> UpdateConfig(ConfigObservation configObservation)
|
||||
{
|
||||
_ = await configObservationService.UpdateConfig(configObservation) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
return true;
|
||||
}
|
||||
{
|
||||
_ = await configObservationService.UpdateConfig(configObservation) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a config observation item by its identifier. Throws a conflict exception if the underlying removal service returns a null result, indicating the delete could not be completed.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the config observation item to delete.</param>
|
||||
/// <returns><c>true</c> when the config observation item is successfully removed.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the removal operation returns a null result, signaling a conflict with the delete request.</exception>
|
||||
public async Task<bool> DeleteConfigObservationItem(ObjectId id)
|
||||
{
|
||||
_ = await configObservationService.RemoveConfigItem(id) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
|
||||
return true;
|
||||
}
|
||||
{
|
||||
_ = await configObservationService.RemoveConfigItem(id) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unit
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new unit and automatically creates the default Points of Care associated with it, one for each value of the <see cref="VirtualPointOfCare"/> enum.
|
||||
/// Each created Point of Care is set to Available status, using the enum value name for both Room and Bed, and its identifier is added to the unit's PointOfCareIds before updating the unit. Returns null if the initial unit insertion fails.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit to be inserted.</param>
|
||||
/// <returns>The inserted unit with its associated Point of Care identifiers, or null if the unit could not be inserted.</returns>
|
||||
public async Task<Unit?> InsertUnit(Unit unit)
|
||||
{
|
||||
//Insertamos la unidad y creamos los PoCs por defecto para esa unidad
|
||||
var result = await unitService.InsertOne(unit);
|
||||
if (result != null)
|
||||
{
|
||||
// Por cada valor del enum VirtualPointOfCare, creamos un PointOfCare asociado a la unidad
|
||||
foreach (var pocEnum in Enum.GetValues(typeof(VirtualPointOfCare)).Cast<VirtualPointOfCare>())
|
||||
//Insertamos la unidad y creamos los PoCs por defecto para esa unidad
|
||||
var result = await unitService.InsertOne(unit);
|
||||
if (result != null)
|
||||
{
|
||||
var poc = new PointOfCare
|
||||
// Por cada valor del enum VirtualPointOfCare, creamos un PointOfCare asociado a la unidad
|
||||
foreach (var pocEnum in Enum.GetValues(typeof(VirtualPointOfCare)).Cast<VirtualPointOfCare>())
|
||||
{
|
||||
UnitId = result.Id,
|
||||
Status = StatusEnum.PointOfCare.Available,
|
||||
Room = pocEnum.ToString(),
|
||||
Bed = pocEnum.ToString()
|
||||
};
|
||||
var insertedPoc = await pocService.InsertPointOfCare(poc);
|
||||
if (insertedPoc != null)
|
||||
{
|
||||
result.PointOfCareIds ??= [];
|
||||
result.PointOfCareIds.Add(insertedPoc.Id);
|
||||
var poc = new PointOfCare
|
||||
{
|
||||
UnitId = result.Id,
|
||||
Status = StatusEnum.PointOfCare.Available,
|
||||
Room = pocEnum.ToString(),
|
||||
Bed = pocEnum.ToString()
|
||||
};
|
||||
var insertedPoc = await pocService.InsertPointOfCare(poc);
|
||||
if (insertedPoc != null)
|
||||
{
|
||||
result.PointOfCareIds ??= [];
|
||||
result.PointOfCareIds.Add(insertedPoc.Id);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizamos la unidad con los nuevos PointOfCareIds
|
||||
await unitService.UpdateUnit(result);
|
||||
}
|
||||
|
||||
// Actualizamos la unidad con los nuevos PointOfCareIds
|
||||
await unitService.UpdateUnit(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously creates a <see cref="UnitInfoDto"/> for the specified unit, populated with counts of related entities such as admissions, discharges, displays, patients, points of care, and virtual points of care. Returns <c>null</c> if any of the underlying count operations fail, logging the error.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit for which to build the dependency information DTO.</param>
|
||||
/// <returns>A task that resolves to a <see cref="UnitInfoDto"/> containing the unit's dependency counts, or <c>null</c> if an error occurs while retrieving the counts.</returns>
|
||||
public async Task<UnitInfoDto?> GetUnitDependencyDto(Unit unit)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new UnitInfoDto(unit)
|
||||
try
|
||||
{
|
||||
Admissions = await admissionService.CountAdmissionsByUnitId(unit.Id),
|
||||
Discharges = await dischargeService.CountDischargesByUnitId(unit.Id),
|
||||
Displays = await displayService.CountDisplaysByUnitId(unit.Id),
|
||||
Patients = await patientService.CountPatientsByUnitId(unit.Id),
|
||||
PointOfCares = await pocService.CountPoCsByUnitId(unit.Id),
|
||||
VirtualPointOfCares = await pocService.CountVirtualPoCsByUnitId(unit.Id)
|
||||
};
|
||||
return new UnitInfoDto(unit)
|
||||
{
|
||||
Admissions = await admissionService.CountAdmissionsByUnitId(unit.Id),
|
||||
Discharges = await dischargeService.CountDischargesByUnitId(unit.Id),
|
||||
Displays = await displayService.CountDisplaysByUnitId(unit.Id),
|
||||
Patients = await patientService.CountPatientsByUnitId(unit.Id),
|
||||
PointOfCares = await pocService.CountPoCsByUnitId(unit.Id),
|
||||
VirtualPointOfCares = await pocService.CountVirtualPoCsByUnitId(unit.Id)
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a unit identified by its identifier, cascading the removal to all associated resources
|
||||
/// (admissions, discharges, authorizations, displays, and points of care). Throws a <see cref="NotFoundException"/>
|
||||
/// if the unit does not exist, and a <see cref="ConflictException"/> if the unit still has patients assigned to it.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit to delete.</param>
|
||||
/// <returns><c>true</c> if the unit and its related resources were successfully deleted; otherwise, <c>false</c> when an error is caught and logged.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no unit is found for the specified <paramref name="unitId"/>.</exception>
|
||||
/// <exception cref="ConflictException">Thrown when the unit has patients assigned to it, preventing deletion.</exception>
|
||||
public async Task<bool> DeleteUnitById(ObjectId unitId)
|
||||
{
|
||||
try
|
||||
@@ -338,35 +436,61 @@ public class AdminPanelService(
|
||||
|
||||
#region Medicine
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a medicine by its unique identifier, or throws an exception if the medicine cannot be found.
|
||||
/// </summary>
|
||||
/// <param name="medicineId">The unique identifier of the medicine to retrieve.</param>
|
||||
/// <returns>The medicine matching the specified identifier.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no medicine is found with the specified identifier.</exception>
|
||||
public async Task<Medicine?> GetMedicineById(ObjectId medicineId)
|
||||
{
|
||||
return await medicineService.GetMedicineById(medicineId) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
{
|
||||
return await medicineService.GetMedicineById(medicineId) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new medicine by forwarding the request to the medicine service. Throws a conflict exception if the service is unable to create the medicine.
|
||||
/// </summary>
|
||||
/// <param name="medicine">The medicine to be created.</param>
|
||||
/// <returns>The created <see cref="Medicine"/>.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the medicine service fails to create the medicine.</exception>
|
||||
public async Task<Medicine?> PostMedicine(Medicine medicine)
|
||||
{
|
||||
var newMedicine = await medicineService.PostMedicine(medicine) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
|
||||
return newMedicine;
|
||||
}
|
||||
{
|
||||
var newMedicine = await medicineService.PostMedicine(medicine) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
|
||||
return newMedicine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing <see cref="Medicine"/> by delegating to the medicine service.
|
||||
/// Throws a <see cref="ConflictException"/> when the service returns a null result, indicating the update could not be applied.
|
||||
/// </summary>
|
||||
/// <param name="medicine">The medicine entity containing the updated information to persist.</param>
|
||||
/// <returns>The updated <see cref="Medicine"/> returned by the service.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the update operation fails and the service returns a null result.</exception>
|
||||
public async Task<Medicine?> UpdateMedicine(Medicine medicine)
|
||||
{
|
||||
var updatedMedicine = await medicineService.UpdateMedicine(medicine) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
return updatedMedicine;
|
||||
}
|
||||
{
|
||||
var updatedMedicine = await medicineService.UpdateMedicine(medicine) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
return updatedMedicine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a medicine record by its identifier, validating the identifier format and confirming successful removal.
|
||||
/// </summary>
|
||||
/// <param name="medicineId">The string representation of the medicine's ObjectId to delete.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the medicine has been successfully deleted.</returns>
|
||||
/// <exception cref="BadRequestException">Thrown when <paramref name="medicineId"/> is not a valid ObjectId format.</exception>
|
||||
/// <exception cref="ConflictException">Thrown when the medicine still exists after the delete operation, indicating the deletion failed.</exception>
|
||||
public async Task<bool> DeleteMedicineById(string medicineId)
|
||||
{
|
||||
if (!ObjectId.TryParse(medicineId, out var objectId))
|
||||
throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
|
||||
await medicineService.DeleteMedicineById(objectId);
|
||||
_ = await medicineService.GetMedicineById(ObjectId.Parse(medicineId)) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
|
||||
return true;
|
||||
}
|
||||
{
|
||||
if (!ObjectId.TryParse(medicineId, out var objectId))
|
||||
throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
|
||||
await medicineService.DeleteMedicineById(objectId);
|
||||
_ = await medicineService.GetMedicineById(ObjectId.Parse(medicineId)) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -33,11 +33,19 @@ public class AdmissionService(
|
||||
// Auditory logs
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified admission by delegating to the delete operation using the admission's identifier.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission entity to delete, identified by its <see cref="Admission.Id"/>.</param>
|
||||
public async Task DeleteAdmissionAsync(Admission admission)
|
||||
{
|
||||
await DeleteAdmissionByIdAsync(admission.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an admission identified by the given id. If the admission is not found, the operation is skipped and logged; otherwise the admission is removed, any associated point of care is detached (clearing its <c>AdmissionId</c> and <c>Admission</c>) and set to <c>Available</c> when not currently <c>Locked</c> or <c>InUse</c>, a delete broadcast is sent, and an audit log entry is created.
|
||||
/// </summary>
|
||||
/// <param name="admissionId">The identifier of the admission to delete.</param>
|
||||
public async Task DeleteAdmissionByIdAsync(ObjectId admissionId)
|
||||
{
|
||||
var admissionAux = await admissionRepository.FindById(admissionId);
|
||||
@@ -71,24 +79,38 @@ public class AdmissionService(
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, admissionAux, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deletes all admissions associated with the specified unit identifier by delegating the operation to the admission repository.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose admissions should be removed.</param>
|
||||
public async Task DeleteAdmissionsByUnitId(ObjectId unitId)
|
||||
{
|
||||
_ = await admissionRepository.DeleteAdmissionsByUnitId(unitId);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an admission by its identifier and, when a point of care is associated, enriches the result with the patient's location (unit, bed, and room) obtained from the point of care service. Returns null if the admission cannot be found.
|
||||
/// </summary>
|
||||
/// <param name="admissionId">The unique identifier of the admission to retrieve.</param>
|
||||
/// <returns>The matching <see cref="Admission"/> with its <see cref="Admission.PatientLocation"/> populated when applicable, or null if no admission is found.</returns>
|
||||
public async Task<Admission?> GetAdmissionByIdAsync(ObjectId admissionId)
|
||||
{
|
||||
var result = await admissionRepository.FindById(admissionId);
|
||||
if (result?.PointOfCareId != null)
|
||||
{
|
||||
var poc = await pointOfCareService.GetInfo(result.PointOfCareId.Value, null,false);
|
||||
var poc = await pointOfCareService.GetInfo(result.PointOfCareId.Value, null, false);
|
||||
result.PatientLocation = new PatientLocation(poc?.UnitName, poc?.Bed, poc?.Room);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all admissions and enriches each one with its associated point of care information (unit, bed, and room) when available.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a collection of <see cref="Admission"/> objects with patient location details populated for those linked to a point of care.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the admission repository returns no results.</exception>
|
||||
public async Task<IEnumerable<Admission>> GetAdmissionsAsync()
|
||||
{
|
||||
var resultList = await admissionRepository.FindAll() ??
|
||||
@@ -97,13 +119,21 @@ public class AdmissionService(
|
||||
foreach (var admission in admissionsAsync)
|
||||
if (admission.PointOfCareId != null)
|
||||
{
|
||||
var poc = await pointOfCareService.GetInfo(admission.PointOfCareId.Value, null,false);
|
||||
var poc = await pointOfCareService.GetInfo(admission.PointOfCareId.Value, null, false);
|
||||
admission.PatientLocation = new PatientLocation(poc?.UnitName, poc?.Bed, poc?.Room);
|
||||
}
|
||||
|
||||
return admissionsAsync;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new admission, preventing duplicates by NHC and optionally linking it to a Point of Care.
|
||||
/// When a Point of Care is assigned, its information is used to populate the patient location and, if free, it is reserved and associated with the newly created admission.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission to insert, optionally including a PointOfCareId to associate with a care location.</param>
|
||||
/// <returns>The newly inserted <see cref="Admission"/>, or <c>null</c> if no result is produced.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when an admission with the same NHC already exists, or when the insertion fails to return a result.</exception>
|
||||
/// <exception cref="NotFoundException">Thrown when the specified Point of Care does not exist.</exception>
|
||||
public async Task<Admission?> InsertAdmission(Admission admission)
|
||||
{
|
||||
var admissionAux = await admissionRepository.FindByNhc(admission.Nhc);
|
||||
@@ -141,20 +171,25 @@ public class AdmissionService(
|
||||
return insertedAdmission;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing admission record, enriching its patient location details from the linked point of care on both the prior and incoming states, and records the change via audit log and broadcast.
|
||||
/// If the admission is not found, the method returns without making changes; point of care lookups are only applied when a <c>PointOfCareId</c> is present and yields a result.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission entity containing the updated information to persist.</param>
|
||||
public async Task UpdateAdmissionAsync(Admission admission)
|
||||
{
|
||||
var oldAdmission = await admissionRepository.FindById(admission.Id);
|
||||
if (oldAdmission == null) return;
|
||||
if (oldAdmission.PointOfCareId.HasValue)
|
||||
{
|
||||
var pocOld = await pointOfCareService.GetInfo(oldAdmission.PointOfCareId.Value, null,false);
|
||||
var pocOld = await pointOfCareService.GetInfo(oldAdmission.PointOfCareId.Value, null, false);
|
||||
if (pocOld != null)
|
||||
oldAdmission.PatientLocation = new PatientLocation(pocOld.UnitName, pocOld.Bed, pocOld.Room);
|
||||
}
|
||||
|
||||
if (admission.PointOfCareId.HasValue)
|
||||
{
|
||||
var poc = await pointOfCareService.GetInfo(admission.PointOfCareId.Value, null,false);
|
||||
var poc = await pointOfCareService.GetInfo(admission.PointOfCareId.Value, null, false);
|
||||
if (poc != null) admission.PatientLocation = new PatientLocation(poc.UnitName, poc.Bed, poc.Room);
|
||||
}
|
||||
|
||||
@@ -165,6 +200,11 @@ public class AdmissionService(
|
||||
SendAdmissionBroadcast(admission, OperationType.UpdateAdmission);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Admits a patient based on the provided <see cref="Admission"/>, creating a new <see cref="Patient"/> assigned to the specified point of care, marking the point of care as in use, and updating related master lists (insulation, allergies, diagnosis, origin, language barrier, passive sitting) when present. If the point of care id, unit, or point of care cannot be resolved, the operation is skipped after logging an error. When <paramref name="isNew"/> is <c>false</c>, the originating admission record is deleted after the patient is inserted.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission data used to create the patient and populate location, diagnosis, allergies, and other attributes.</param>
|
||||
/// <param name="isNew">When <c>false</c>, the admission record is deleted after a successful patient insertion; when <c>true</c>, the admission is retained.</param>
|
||||
public async Task AdmitPatient(Admission admission, bool isNew = false)
|
||||
{
|
||||
if (admission.PointOfCareId == null)
|
||||
@@ -238,7 +278,7 @@ public class AdmissionService(
|
||||
|
||||
if (!isNew)
|
||||
await DeleteAdmissionAsync(admission);
|
||||
|
||||
|
||||
if (admission.Insulation != null)
|
||||
await patientService.UpdatePatientMasterList(
|
||||
patient.Id,
|
||||
@@ -278,6 +318,10 @@ public class AdmissionService(
|
||||
null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a patient to the admissions workflow by creating a new admission record, removing any existing discharge, and archiving the patient. Validates that the patient and its associated unit exist before proceeding, and only builds the admission when a point of care is assigned.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient to be returned to admissions.</param>
|
||||
public async Task ReturnPatientToAdmissions(ObjectId patientId)
|
||||
{
|
||||
var patient = await patientService.FindById(patientId);
|
||||
@@ -327,6 +371,11 @@ public class AdmissionService(
|
||||
}
|
||||
|
||||
// Used for temporal beds like PUSHED
|
||||
/// <summary>
|
||||
/// Returns a patient to the admissions flow by creating a new admission record from the patient's existing data, removing any prior discharge, and archiving the patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient to be returned to admissions.</param>
|
||||
/// <param name="adm">The admission context used to resolve the unit and point of care for the new admission record.</param>
|
||||
public async Task ReturnPatientToAdmissions(ObjectId patientId, Admission adm)
|
||||
{
|
||||
var patient = await patientService.FindById(patientId);
|
||||
@@ -376,6 +425,11 @@ public class AdmissionService(
|
||||
pointOfCareService.CheckNextAdmission(patient.PointOfCareId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of admissions for the specified patient location. If an error occurs during retrieval, the error is logged and an empty list is returned.
|
||||
/// </summary>
|
||||
/// <param name="location">The patient location used to filter admissions.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of admissions matching the specified location, or an empty list if an error occurs.</returns>
|
||||
public async Task<List<Admission>> GetAdmissionByLocation(PatientLocation location)
|
||||
{
|
||||
try
|
||||
@@ -390,6 +444,11 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of admissions associated with the specified point of care identifier, enriching each admission with its patient location information when available.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of care whose admissions should be retrieved.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the list of admissions for the given point of care, or an empty list if an error occurs.</returns>
|
||||
public async Task<List<Admission>> GetAdmissionByPointOfCareId(ObjectId pocId)
|
||||
{
|
||||
try
|
||||
@@ -397,7 +456,7 @@ public class AdmissionService(
|
||||
var result = await admissionRepository.FindByPointOfCareId(pocId);
|
||||
foreach (var admission in result)
|
||||
{
|
||||
var poc = await pointOfCareService.GetInfo(pocId, null,false);
|
||||
var poc = await pointOfCareService.GetInfo(pocId, null, false);
|
||||
if (poc != null) admission.PatientLocation = new PatientLocation(poc.UnitName, poc.Bed, poc.Room);
|
||||
}
|
||||
|
||||
@@ -410,6 +469,12 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all admissions associated with the specified point of care and applies translations according to the given locale in parallel.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of care whose admissions will be retrieved.</param>
|
||||
/// <param name="locale">The locale used to translate the admission fields.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of admissions with their fields translated to the specified locale.</returns>
|
||||
public async Task<List<Admission>> GetAdmissionByPointOfCareIdAndLocale(ObjectId pocId, LocaleEnum locale)
|
||||
{
|
||||
var admissions = await GetAdmissionByPointOfCareId(pocId);
|
||||
@@ -422,6 +487,12 @@ public class AdmissionService(
|
||||
return translatedAdmissions.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves admissions associated with the specified unit, excluding those linked to a Point of Care (PoC).
|
||||
/// If an error occurs during retrieval, the exception is logged and an empty list is returned as a fallback.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose admissions (without PoC) are being requested.</param>
|
||||
/// <returns>A task that returns a list of <see cref="Admission"/> objects for the given unit, or an empty list if an error occurs.</returns>
|
||||
public async Task<List<Admission>> GetAdmissionByUnitIdWithOutPoC(ObjectId unitId)
|
||||
{
|
||||
try
|
||||
@@ -436,6 +507,12 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of admissions associated with the specified unit identifier by delegating to the admission repository.
|
||||
/// Returns 0 and logs the error if the repository operation fails, ensuring the method does not propagate exceptions to the caller.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose admissions should be counted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the number of admissions for the given unit, or 0 if an error occurs.</returns>
|
||||
public async Task<long> CountAdmissionsByUnitId(ObjectId unitId)
|
||||
{
|
||||
try
|
||||
@@ -450,6 +527,12 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a patient by patient number, enriching the current patient record with its point of care and unit name when available, and combines it with archived patient and admission lookups scoped to the specified unit.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The unique patient number used as the primary search key.</param>
|
||||
/// <param name="unitId">The identifier of the unit used to filter the archived patient and admission searches.</param>
|
||||
/// <returns>A <see cref="PatientSearch"/> aggregating the current patient, archived patient, and admission data, including flags indicating whether the patient exists only in the archive and whether any of the three sources returned a result.</returns>
|
||||
public async Task<PatientSearch?> SearchByPatientNumberAndDistinctUnit(string patientNumber, ObjectId unitId)
|
||||
{
|
||||
var patient = await patientService.FindByPatientNumber(patientNumber);
|
||||
@@ -475,13 +558,24 @@ public class AdmissionService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the admission record associated with the specified patient clinical record number (NHC) from the admission repository.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The patient's clinical record number (NHC) used to look up the admission.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="Admission"/> if found, or <c>null</c> when no admission exists for the given patient number.</returns>
|
||||
public Task<Admission?> GetAdmissionByPatientNumber(string patientNumber)
|
||||
{
|
||||
return admissionRepository.FindByNhc(patientNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the master list option for admissions associated with the specified units, records an audit log entry for each modified admission, and broadcasts the updates.
|
||||
/// </summary>
|
||||
/// <param name="opt">The master list update options to apply to the matching admissions.</param>
|
||||
/// <param name="unitList">The collection of units whose admissions are affected by the update.</param>
|
||||
/// <param name="typeName">The name of the master list type being modified.</param>
|
||||
public async Task UpdatePatientMasterListItemChange(UpdateOptionMasterListDto opt, IEnumerable<Unit> unitList,
|
||||
string typeName)
|
||||
string typeName)
|
||||
{
|
||||
var unitIds = unitList.Select(x => x.Id).ToList();
|
||||
var oldAdmissionList = await admissionRepository.FindByUnitIds(unitIds);
|
||||
@@ -494,6 +588,12 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a master list option from patient admissions associated with the specified units and type, records an audit log entry for each affected admission, and broadcasts the admission update when the updated admission is found.
|
||||
/// </summary>
|
||||
/// <param name="opt">The master list option to remove from the admissions.</param>
|
||||
/// <param name="unitList">The collection of units whose admissions will be processed for the deletion.</param>
|
||||
/// <param name="typeName">The name of the option type being deleted.</param>
|
||||
public async Task DeletePatientMasterListItem(OptionList opt, IEnumerable<Unit> unitList, string typeName)
|
||||
{
|
||||
var unitIds = unitList.Select(x => x.Id).ToList();
|
||||
@@ -510,6 +610,11 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes an admission API request by performing the appropriate action based on the request type: inserts a new admission, updates an existing one, or deletes it.
|
||||
/// Required fields (Nhc, Origin, and Diagnosis) are validated before insert and update operations, and the method exits early when the admission or any required value is missing.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the admission payload and the operation type to execute.</param>
|
||||
public async Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
try
|
||||
@@ -520,40 +625,40 @@ public class AdmissionService(
|
||||
switch (apiRequest.Type)
|
||||
{
|
||||
case "NewAdmission":
|
||||
{
|
||||
if (string.IsNullOrEmpty(apiRequest.Admission.Nhc) ||
|
||||
apiRequest.Admission.Origin == null ||
|
||||
apiRequest.Admission.Diagnosis == null)
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Error saving admission api request. Some values are required. Admission: {Admission}",
|
||||
apiRequest.Admission);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(apiRequest.Admission.Nhc) ||
|
||||
apiRequest.Admission.Origin == null ||
|
||||
apiRequest.Admission.Diagnosis == null)
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Error saving admission api request. Some values are required. Admission: {Admission}",
|
||||
apiRequest.Admission);
|
||||
return;
|
||||
}
|
||||
|
||||
await InsertAdmission(apiRequest.Admission);
|
||||
break;
|
||||
}
|
||||
await InsertAdmission(apiRequest.Admission);
|
||||
break;
|
||||
}
|
||||
case "UpdateAdmission":
|
||||
{
|
||||
if (string.IsNullOrEmpty(apiRequest.Admission.Nhc) ||
|
||||
apiRequest.Admission.Origin == null ||
|
||||
apiRequest.Admission.Diagnosis == null)
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Error updating admission api request. Some values are required. Admission: {Admission}",
|
||||
apiRequest.Admission);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(apiRequest.Admission.Nhc) ||
|
||||
apiRequest.Admission.Origin == null ||
|
||||
apiRequest.Admission.Diagnosis == null)
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Error updating admission api request. Some values are required. Admission: {Admission}",
|
||||
apiRequest.Admission);
|
||||
return;
|
||||
}
|
||||
|
||||
await UpdateAdmissionAsync(apiRequest.Admission);
|
||||
break;
|
||||
}
|
||||
await UpdateAdmissionAsync(apiRequest.Admission);
|
||||
break;
|
||||
}
|
||||
case "DeleteAdmission":
|
||||
{
|
||||
await DeleteAdmissionAsync(apiRequest.Admission);
|
||||
break;
|
||||
}
|
||||
{
|
||||
await DeleteAdmissionAsync(apiRequest.Admission);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -564,11 +669,21 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves the specified API request by scheduling the underlying save operation on a background task.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to persist.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the PointOfCare change when an admission is updated, transferring the assignment from the old PointOfCare to the new one. Updates the status of both PointOfCares (e.g., Reserved, InUse, Available) based on patient occupancy, locks, and admission association, and checks for the next pending admission whenever a PointOfCare becomes available.
|
||||
/// </summary>
|
||||
/// <param name="admission">The current admission containing the updated PointOfCare identifier.</param>
|
||||
/// <param name="oldAdmission">The previous admission state used to identify the original PointOfCare to release.</param>
|
||||
private async Task HandlePointOfCareChange(Admission admission, Admission oldAdmission)
|
||||
{
|
||||
// Check if PointOfCare has changed.
|
||||
@@ -620,6 +735,11 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the status of the specified point of care, performing a lookup by identifier first. If no point of care is found, the method returns without applying any change.
|
||||
/// </summary>
|
||||
/// <param name="pointOfCareId">The identifier of the point of care whose status will be updated.</param>
|
||||
/// <param name="status">The new status to assign to the point of care.</param>
|
||||
private async Task SetPointOfCareStatus(ObjectId pointOfCareId, StatusEnum.PointOfCare status)
|
||||
{
|
||||
var pointOfCare = await pointOfCareService.FindById(pointOfCareId);
|
||||
@@ -628,6 +748,12 @@ public class AdmissionService(
|
||||
await pointOfCareService.SetPointOfCareStatus(pointOfCareId, status);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an admission broadcast message by routing to the appropriate sender based on whether a point-of-care identifier is set.
|
||||
/// Falls back to unit-based delivery when no point-of-care is available; logs and swallows any errors encountered during dispatch.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission record to broadcast.</param>
|
||||
/// <param name="operation">The operation type associated with the broadcast.</param>
|
||||
private async void SendAdmissionBroadcast(Admission admission, OperationType operation)
|
||||
{
|
||||
try
|
||||
@@ -644,6 +770,12 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an admission broadcast to subscribers associated with the admission's Point of Care, grouped and translated by locale.
|
||||
/// Logs an error and returns early if the admission has no Point of Care id or the Point of Care cannot be found.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission whose broadcast is being sent; its Point of Care is used to select subscribers and locale-specific content.</param>
|
||||
/// <param name="operation">The type of operation to send to the subscribers.</param>
|
||||
private async Task SendAdmissionBroadcastByPoC(Admission admission, OperationType operation)
|
||||
{
|
||||
if (!admission.PointOfCareId.HasValue)
|
||||
@@ -677,6 +809,11 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an admission broadcast to all WebSocket subscribers associated with displays in the admission's unit, grouped by locale so each subscriber receives a localized copy. If the admission has an empty unit id, the broadcast is skipped and an error is logged.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission to broadcast, which supplies the target unit identifier.</param>
|
||||
/// <param name="operation">The operation type associated with the broadcast message.</param>
|
||||
private async Task SendAdmissionByUnitId(Admission admission, OperationType operation)
|
||||
{
|
||||
if (admission.UnitId == ObjectId.Empty)
|
||||
@@ -702,6 +839,15 @@ public class AdmissionService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Localizes the <see cref="Admission"/>'s Origin, Diagnosis, and Insulation names by resolving them
|
||||
/// against locale-specific master lists associated with the admission's unit. If the unit is not found,
|
||||
/// or any of the referenced master list lookups fail or contain no matching option, the original
|
||||
/// admission values are preserved as a fallback.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission whose reference names will be updated with localized values.</param>
|
||||
/// <param name="locale">The locale used to retrieve the appropriate master list translations.</param>
|
||||
/// <returns>The same <see cref="Admission"/> instance with its localized reference names applied when available.</returns>
|
||||
private async Task<Admission> GetAdmissionWithLocale(Admission admission, LocaleEnum locale)
|
||||
{
|
||||
var unit = await unitService.FindById(admission.UnitId);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,11 +7,22 @@ using MongoDB.Bson;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an implementation of the IAlertValuesService interface for managing alert values
|
||||
/// using a configuration observation repository.
|
||||
/// </summary>
|
||||
public class AlertValuesService(IConfigObservationRepository alertValueRepository) : IAlertValuesService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="ConfigObservation"/> by its unique identifier from the alert value repository.
|
||||
/// Throws a <see cref="NotFoundException"/> when no matching resource is found.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique identifier of the configuration observation to retrieve.</param>
|
||||
/// <returns>The matching <see cref="ConfigObservation"/> if found.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no configuration observation exists for the specified <paramref name="key"/>.</exception>
|
||||
public async Task<ConfigObservation?> FindByKey(ObjectId key)
|
||||
{
|
||||
return await alertValueRepository.FindById(key) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
{
|
||||
return await alertValueRepository.FindById(key) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,13 @@ public class AppointmentService(
|
||||
: IAppointmentService
|
||||
{
|
||||
private readonly bool _createPatientWithSiu = apiSettings.Value.CreatePatientWithSiu;
|
||||
private readonly CacheSettings? _cacheSettings = cacheSettings.Value ;
|
||||
private readonly CacheSettings? _cacheSettings = cacheSettings.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Saves an API request by validating the patient, resolving or creating the patient record, processing the request, and handling associated observations and diagnoses.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the patient number, location, type, observations, diagnosis, and related data to be processed.</param>
|
||||
/// <exception cref="ApiRequestException">Thrown when the <paramref name="apiRequest"/> has a null or empty patient number.</exception>
|
||||
public async Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
if (string.IsNullOrEmpty(apiRequest.PatientNumber))
|
||||
@@ -73,6 +78,12 @@ public class AppointmentService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes an incoming API request for a patient, handling HL7 SIU message types to create, update, or cancel appointments.
|
||||
/// Validates the patient and auto-ADT configuration, then routes the request to the appropriate handler based on message type: SIU_S12-S14 and SIU_S18-S22 (booking/rescheduling/modification), SIU_S15-S17 (cancellation), and other types (blocked slots / no-show), persisting changes, refreshing cache, creating audit logs, and emitting broadcasts.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the HL7 message type, timestamp, and appointment payload to process.</param>
|
||||
/// <param name="patient">The patient associated with the request; if null, the method returns without processing.</param>
|
||||
public async Task ProcessApiRequest(ApiRequest apiRequest, Patient? patient)
|
||||
{
|
||||
if (patient == null)
|
||||
@@ -209,16 +220,29 @@ public class AppointmentService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves the provided API request by running the save operation on a background task.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to be saved.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives the specified patient by delegating the operation to <see cref="ArchiveByPatientId"/> using the patient's identifier.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to be archived.</param>
|
||||
public async Task Archive(Patient patient)
|
||||
{
|
||||
await ArchiveByPatientId(patient.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives all appointments associated with the specified patient by copying them to the appointment archive repository and then deleting them from the source collection.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose appointments should be archived.</param>
|
||||
public async Task ArchiveByPatientId(ObjectId id)
|
||||
{
|
||||
logger.LogDebug("Archive Appointments by patientId {id}", id);
|
||||
@@ -232,14 +256,26 @@ public class AppointmentService(
|
||||
await DeleteByPatientId(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of patient appointments associated with the specified patient identifier by delegating to the appointment repository.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose appointments are being requested.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientAppointment"/> records for the given patient.</returns>
|
||||
public async Task<List<PatientAppointment>> GetByPatient(ObjectId patientId)
|
||||
{
|
||||
return await appointmentRepository.GetByPatient(patientId);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of appointments scheduled for today (UTC) for the specified patient, using a cache-aside pattern to avoid repeated database queries.
|
||||
/// The full list of patient appointments is fetched from cache (or loaded from the repository on a cache miss) and then filtered locally to include only those whose start time falls on the current UTC date.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose appointments are being queried.</param>
|
||||
/// <param name="ct">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that resolves to a list of <see cref="PatientAppointment"/> instances scheduled for today; an empty list is returned when no appointments match.</returns>
|
||||
public async Task<List<PatientAppointment>> GetTodayByPatient(
|
||||
ObjectId patientId,
|
||||
CancellationToken ct = default)
|
||||
ObjectId patientId,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
// Obtener clave + TTL según CacheSettings
|
||||
var (key, ttl) = CacheKeys.PatientAppointmentsTodayKeyWithTtl(_cacheSettings, patientId);
|
||||
@@ -270,14 +306,20 @@ public class AppointmentService(
|
||||
return todayAppointments;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the patient appointments scheduled for today at the specified point of care. Uses a cache to store the full appointment list for the point of care and filters it by today's date; returns an empty list if the point of care is not found.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of care whose appointments should be retrieved.</param>
|
||||
/// <param name="ct">A cancellation token to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of patient appointments scheduled for today at the specified point of care.</returns>
|
||||
public async Task<List<PatientAppointment>> GetTodayByPoc(
|
||||
ObjectId pocId,
|
||||
CancellationToken ct = default)
|
||||
ObjectId pocId,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var poc = await pointOfCareService.FindById(pocId);
|
||||
if(poc == null) return [];
|
||||
|
||||
if (poc == null) return [];
|
||||
|
||||
// Obtener clave + TTL según CacheSettings
|
||||
var (key, ttl) = CacheKeys.PocAppointmentsTodayKeyWithTtl(_cacheSettings, pocId);
|
||||
|
||||
@@ -307,34 +349,59 @@ public class AppointmentService(
|
||||
return todayAppointments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all patient appointments associated with the specified patient identifier by delegating to the appointment repository.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose appointments are to be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an async cursor over the matching <see cref="PatientAppointment"/> documents.</returns>
|
||||
public Task<IAsyncCursor<PatientAppointment>> FindByPatientIdAsync(ObjectId patientId)
|
||||
{
|
||||
return appointmentRepository.FindByPatientIdAsync(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all patient appointments associated with the specified location.
|
||||
/// </summary>
|
||||
/// <param name="location">The location used to filter the patient appointments.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of patient appointments for the specified location.</returns>
|
||||
public async Task<List<PatientAppointment>> FindByLocation(PatientLocation location)
|
||||
{
|
||||
return await appointmentRepository.FindByLocation(location);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all appointments associated with the specified patient identifier, invalidates the appointments cache, and records the action in the audit log.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose appointments will be deleted.</param>
|
||||
public async Task DeleteByPatientId(ObjectId id)
|
||||
{
|
||||
var patientApp = await FindByPatientIdAsync(id);
|
||||
logger.LogDebug("Delete Appointments by Patient Id {id}", id);
|
||||
await appointmentRepository.DeleteByPatientId(id);
|
||||
|
||||
|
||||
// Invalidar CACHE (colección completa)
|
||||
await cacheService.DeleteByPatternAsync(CacheKeyPatterns.ForEntity(CacheEnum.EntityType.Appointments));
|
||||
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, patientApp, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates multiple appointment records by replacing the specified <paramref name="oldId"/> with the new <paramref name="id"/>, scoped by the given <paramref name="nameId"/>. Delegates the operation to the underlying appointment repository.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The identifier used to scope which appointment records are affected by the update.</param>
|
||||
/// <param name="id">The new ObjectId that will replace the existing one in the matching records.</param>
|
||||
/// <param name="oldId">The current ObjectId to be replaced in the matching records.</param>
|
||||
public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
|
||||
{
|
||||
await appointmentRepository.UpdateManyObjectId(nameId, id, oldId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a patient appointment operation to all subscribers associated with the appointment's locations. Iterates through each resource group and location, resolving the unit and point of care, and dispatches a fire-and-forget message to every matching subscriber. Skips locations with missing unit/bed data and silently ignores unresolved unit or point-of-care lookups.
|
||||
/// </summary>
|
||||
/// <param name="appointment">The patient appointment whose resource groups and locations will be broadcast to subscribers.</param>
|
||||
/// <param name="operationType">The optional operation type describing the change performed on the appointment; passed along to the subscriber message.</param>
|
||||
private async Task SendBroadcast(PatientAppointment appointment, OperationType? operationType)
|
||||
{
|
||||
//RECORRE LOS DIFERENTES LOCATIONS DE LA CITA
|
||||
|
||||
@@ -16,6 +16,11 @@ public class ArchivePatientCarePlanService(
|
||||
{
|
||||
#region Create
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a patient care plan into the archived patient repository, logs the operation, and creates an audit log entry capturing the current user context.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient care plan to be archived and inserted.</param>
|
||||
/// <returns>The inserted patient care plan, or <see langword="null"/> if no plan was provided.</returns>
|
||||
public async Task<PatientCarePlan?> InsertOneAsync(PatientCarePlan patient)
|
||||
{
|
||||
await archivedPatientRepository.InsertOneAsync(patient);
|
||||
@@ -26,6 +31,10 @@ public class ArchivePatientCarePlanService(
|
||||
return patient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a batch of archived patient care plans into the repository and creates an audit log entry for each one using the current HTTP context user.
|
||||
/// </summary>
|
||||
/// <param name="patientCarePla">The list of patient care plans to insert and audit.</param>
|
||||
public async Task InsertManyAsync(List<PatientCarePlan> patientCarePla)
|
||||
{
|
||||
await archivedPatientRepository.InsertManyAsync(patientCarePla);
|
||||
@@ -38,21 +47,43 @@ public class ArchivePatientCarePlanService(
|
||||
|
||||
#region Read
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of archived patient care plans associated with the specified patient identifier by delegating to the underlying repository.
|
||||
/// Returns a nullable list, which may be null when no archived care plans exist for the patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose archived care plans are being searched.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="PatientCarePlan"/> for the patient, or null if no records are found.</returns>
|
||||
public async Task<List<PatientCarePlan>?> FindByPatientId(ObjectId patientId)
|
||||
{
|
||||
return await archivedPatientRepository.FindByPatientId(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of archived patient care plans associated with the specified patient identifier.
|
||||
/// Returns null when no archived care plans are found for the given patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose archived care plans are being retrieved.</param>
|
||||
/// <returns>A list of <see cref="PatientCarePlan"/> entries for the patient, or <c>null</c> if no records exist.</returns>
|
||||
public async Task<List<PatientCarePlan>?> FindByPatientId(string patientId)
|
||||
{
|
||||
return await archivedPatientRepository.FindByPatientId(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of archived patient care plans associated with the specified patient number.
|
||||
/// Returns null if no archived care plans are found for the given patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose archived care plans are being searched.</param>
|
||||
/// <returns>A list of <see cref="PatientCarePlan"/> objects for the specified patient, or null if no records are found.</returns>
|
||||
public async Task<List<PatientCarePlan>?> FindByPatientNumber(string patientId)
|
||||
{
|
||||
return await archivedPatientRepository.FindByPatientNumber(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all archived patient care plans by delegating to the archived patient repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that resolves to a list of <see cref="PatientCarePlan"/> objects representing all archived patient care plans.</returns>
|
||||
public Task<List<PatientCarePlan>> FindAll()
|
||||
{
|
||||
return archivedPatientRepository.FindAll();
|
||||
|
||||
@@ -8,6 +8,11 @@ namespace adas_core.Application.Services;
|
||||
public class ArchivePatientObservationsService(IObservationArchiveRepository archivedPatientObservationService)
|
||||
: IArchivedPatientObservationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all archived patient observations associated with the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose archived observations are being requested.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of archived <see cref="PatientObservation"/> records for the patient.</returns>
|
||||
public async Task<List<PatientObservation>> FindArchivedPatientObservationsFromPatient(ObjectId patientId)
|
||||
{
|
||||
return await archivedPatientObservationService.FindAllFromPatient(patientId);
|
||||
|
||||
@@ -4,8 +4,18 @@ using adas_core.Domain.Models.MongoModels;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides services for managing archived patient data, implementing the <see cref="IArchivedPatientService"/> interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class uses an <see cref="IPatientArchiveRepository"/> to perform operations on archived patient records, following the repository pattern.
|
||||
/// </remarks>
|
||||
public class ArchivedPatientService(IPatientArchiveRepository archivedPatientRepository) : IArchivedPatientService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all archived patients by delegating to the archived patient repository.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of all archived <see cref="Patient"/> entities.</returns>
|
||||
public async Task<List<Patient>> FindAllPatients()
|
||||
{
|
||||
return await archivedPatientRepository.FindAll();
|
||||
|
||||
@@ -8,6 +8,11 @@ namespace adas_core.Application.Services;
|
||||
public class ArchivedPatientTreatmentService(ITreatmentArchiveRepository archivedPatientTreatmentService)
|
||||
: IArchivedPatientTreatmentService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all archived patient treatments associated with the specified patient by delegating to the archived patient treatment service.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose archived treatments are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientTreatment"/> records for the specified patient.</returns>
|
||||
public async Task<List<PatientTreatment>> FindAllPatientTreatmentsByPatient(ObjectId patientId)
|
||||
{
|
||||
return await archivedPatientTreatmentService.FindAllFromPatient(patientId);
|
||||
|
||||
@@ -12,6 +12,10 @@ using Newtonsoft.Json;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an implementation of the <see cref="IAuthService"/> interface, offering
|
||||
/// authentication-related services to consuming components.
|
||||
/// </summary>
|
||||
public class AuthService : IAuthService
|
||||
{
|
||||
private readonly IAuthorityRepository _authorityRepository;
|
||||
@@ -33,6 +37,10 @@ public class AuthService : IAuthService
|
||||
_ = InstanceAuthUtils();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously obtains a login token from the recording API using the configured client credentials and caches it for reuse via <see cref="AuthUtils"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="LoginResponse"/> containing the authentication token when the request succeeds and the response is valid; otherwise, <c>null</c> if the API URL is not configured, the request fails, the returned token is empty, or an exception is caught and logged.</returns>
|
||||
public async Task<LoginResponse?> GetLoginResponse()
|
||||
{
|
||||
try
|
||||
@@ -79,6 +87,11 @@ public class AuthService : IAuthService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an authentication token, returning the cached token if it is not empty or expired.
|
||||
/// Otherwise, attempts a fresh login and returns the new token, falling back to an empty string when no token is obtained.
|
||||
/// </summary>
|
||||
/// <returns>A task that resolves to the authentication token, or an empty string if the token could not be obtained.</returns>
|
||||
public async Task<string> GetToken()
|
||||
{
|
||||
var loginResponse = AuthUtils.Instance.GetLoginResponse();
|
||||
@@ -89,26 +102,49 @@ public class AuthService : IAuthService
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of authorizations associated with the specified unit identifier by delegating to the authority repository.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose authorizations are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Authorization"/> objects for the specified unit.</returns>
|
||||
public async Task<List<Authorization>> GetByUnitId(ObjectId unitId)
|
||||
{
|
||||
return await _authorityRepository.GetByUnitId(unitId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of authorizations associated with the specified user identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the user whose authorities are being requested.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Authorization"/> entries for the user.</returns>
|
||||
public async Task<List<Authorization>> GetUserAuthorities(ObjectId id)
|
||||
{
|
||||
return await _authorityRepository.GetUserAuthorities(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all authorities associated with the specified unit identifier by delegating to the authority repository.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose authorities are to be removed.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the deletion was successful; otherwise, <c>false</c>.</returns>
|
||||
public async Task<bool> DeleteByUnitId(ObjectId unitId)
|
||||
{
|
||||
return await _authorityRepository.DeleteAllAuthoritiesByUnit(unitId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all authorities associated with the specified display identifier by delegating to the authority repository.
|
||||
/// </summary>
|
||||
/// <param name="displayId">The unique identifier of the display whose related authorities should be removed.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if authorities were successfully deleted; otherwise, <c>false</c>.</returns>
|
||||
public async Task<bool> DeleteByDisplayId(ObjectId displayId)
|
||||
{
|
||||
return await _authorityRepository.DeleteAllAuthoritiesByDisplay(displayId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the authentication utilities are initialized with a valid login token. Returns early if the recording API URL is not configured; otherwise, requests a new token when the current one is missing or expired, and updates the shared <see cref="AuthUtils"/> instance with the refreshed response when successful.
|
||||
/// </summary>
|
||||
private async Task InstanceAuthUtils()
|
||||
{
|
||||
if (_recordingSettings.RecordingApiUrl.IsEmpty())
|
||||
|
||||
@@ -21,91 +21,189 @@ namespace adas_core.Application.Services.Caching
|
||||
{
|
||||
|
||||
// Selección de backend
|
||||
/// <summary>
|
||||
/// Selects the appropriate cache backend (Redis, in-memory, or no-op) for the given key by classifying the key into an entity type and resolving its configured cache mode.
|
||||
/// Unknown entity types default to in-memory caching, and unrecognized modes fall back to the no-op cache service.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to determine the entity type and the corresponding cache backend.</param>
|
||||
/// <returns>The <see cref="ICacheService"/> instance that should handle caching for the supplied key.</returns>
|
||||
private ICacheService SelectBackend(string key)
|
||||
{
|
||||
var entity = CacheKeyClassifier.Classify(key);
|
||||
var mode = entity switch
|
||||
{
|
||||
CacheEnum.EntityType.Patients => cacheSettings.Patients,
|
||||
CacheEnum.EntityType.Displays => cacheSettings.Displays,
|
||||
CacheEnum.EntityType.PumpObservations => cacheSettings.PumpObservations,
|
||||
CacheEnum.EntityType.Appointments => cacheSettings.Appointments,
|
||||
CacheEnum.EntityType.GroupedObservations => cacheSettings.GroupedObservations,
|
||||
_ => CacheEnum.Mode.Cache
|
||||
};
|
||||
|
||||
return mode switch
|
||||
{
|
||||
CacheEnum.Mode.Redis => redis,
|
||||
CacheEnum.Mode.Cache => memory,
|
||||
_ => noop
|
||||
};
|
||||
}
|
||||
{
|
||||
var entity = CacheKeyClassifier.Classify(key);
|
||||
var mode = entity switch
|
||||
{
|
||||
CacheEnum.EntityType.Patients => cacheSettings.Patients,
|
||||
CacheEnum.EntityType.Displays => cacheSettings.Displays,
|
||||
CacheEnum.EntityType.PumpObservations => cacheSettings.PumpObservations,
|
||||
CacheEnum.EntityType.Appointments => cacheSettings.Appointments,
|
||||
CacheEnum.EntityType.GroupedObservations => cacheSettings.GroupedObservations,
|
||||
_ => CacheEnum.Mode.Cache
|
||||
};
|
||||
|
||||
return mode switch
|
||||
{
|
||||
CacheEnum.Mode.Redis => redis,
|
||||
CacheEnum.Mode.Cache => memory,
|
||||
_ => noop
|
||||
};
|
||||
}
|
||||
|
||||
// Para GroupedObservations generamos la misma clave compuesta que el resto de servicios,
|
||||
// de modo que el clasificador y la política de TTL funcionen igual.
|
||||
/// <summary>
|
||||
/// Builds a composite key used to identify grouped observations for a specific patient.
|
||||
/// </summary>
|
||||
/// <param name="gf">The grouped field whose name contributes to the key.</param>
|
||||
/// <param name="patientId">The identifier of the patient associated with the grouped observation.</param>
|
||||
/// <returns>A formatted key string in the form <c>GroupedObs:{patientId}:{gf.Name}</c>.</returns>
|
||||
private static string BuildGroupedKey(GroupedField gf, ObjectId patientId)
|
||||
=> $"GroupedObs:{patientId}:{gf.Name}";
|
||||
=> $"GroupedObs:{patientId}:{gf.Name}";
|
||||
|
||||
/// <summary>
|
||||
/// Selects the appropriate cache backend for the given grouped field and patient identifier by building a grouped key and resolving the backend through the key-based overload.
|
||||
/// </summary>
|
||||
/// <param name="groupedField">The grouped field used to derive the cache key.</param>
|
||||
/// <param name="patientId">The patient identifier used to derive the cache key.</param>
|
||||
/// <returns>The <see cref="ICacheService"/> backend associated with the built grouped key.</returns>
|
||||
private ICacheService SelectBackend(GroupedField groupedField, ObjectId patientId)
|
||||
=> SelectBackend(BuildGroupedKey(groupedField, patientId));
|
||||
=> SelectBackend(BuildGroupedKey(groupedField, patientId));
|
||||
|
||||
|
||||
// GetOrSet (KEY string)
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the object associated with the specified key from the selected backend, or sets it using the provided factory if it is not already cached.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to identify the object and to select the appropriate backend.</param>
|
||||
/// <param name="factory">A delegate that asynchronously produces the value to store when the key is not present in the selected backend.</param>
|
||||
/// <param name="ttl">An optional time-to-live duration for the cached object. If null, the backend's default expiration is applied.</param>
|
||||
/// <param name="cancellationToken">A token to observe while waiting for the operation to complete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the retrieved or newly created object of type <typeparamref name="T"/>.</returns>
|
||||
public Task<T> GetOrSetObjectAsync<T>(
|
||||
string key,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> SelectBackend(key).GetOrSetObjectAsync(key, factory, ttl, cancellationToken);
|
||||
string key,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> SelectBackend(key).GetOrSetObjectAsync(key, factory, ttl, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the value associated with the specified key from the backend selected for that key,
|
||||
/// or loads and stores it using the provided loader function if it is not already present.
|
||||
/// Supports an optional time-to-live (TTL) for the cached entry, and the returned value may be null.
|
||||
/// </summary>
|
||||
/// <param name="key">The key used to identify the cached value and to select the appropriate backend.</param>
|
||||
/// <param name="loader">An asynchronous function that produces the value to cache when no existing entry is found.</param>
|
||||
/// <param name="ttl">An optional time-to-live duration after which the cached entry expires. If null, the backend's default TTL is used.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the cached or loaded string value, or null if no value could be obtained.</returns>
|
||||
public Task<string?> GetOrSetValueAsync(
|
||||
string key,
|
||||
Func<Task<string>> loader,
|
||||
TimeSpan? ttl = null)
|
||||
=> SelectBackend(key).GetOrSetValueAsync(key, loader, ttl);
|
||||
string key,
|
||||
Func<Task<string>> loader,
|
||||
TimeSpan? ttl = null)
|
||||
=> SelectBackend(key).GetOrSetValueAsync(key, loader, ttl);
|
||||
|
||||
|
||||
// GetOrSet (GroupedField + PatientId)
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the object associated with the specified grouped field and patient, or creates and stores it using the provided factory if it does not exist.
|
||||
/// The appropriate backend is selected based on the grouped field and patient identifier before the underlying get-or-set operation is performed.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the object to retrieve or create.</typeparam>
|
||||
/// <param name="groupedField">The grouped field that determines the target backend and identifies the cached object.</param>
|
||||
/// <param name="patientId">The identifier of the patient whose object is being retrieved or created.</param>
|
||||
/// <param name="factory">The asynchronous factory used to create the object when no cached value is available.</param>
|
||||
/// <param name="ttl">An optional time-to-live applied to the cached object. When <c>null</c>, the backend's default expiration is used.</param>
|
||||
/// <param name="cancellationToken">The token to observe for canceling the asynchronous operation.</param>
|
||||
/// <returns>A task that represents the asynchronous get-or-set operation, containing the retrieved or newly created object.</returns>
|
||||
public Task<T> GetOrSetObjectAsync<T>(
|
||||
GroupedField groupedField,
|
||||
ObjectId patientId,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> SelectBackend(groupedField, patientId)
|
||||
.GetOrSetObjectAsync(groupedField, patientId, factory, ttl, cancellationToken);
|
||||
GroupedField groupedField,
|
||||
ObjectId patientId,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> SelectBackend(groupedField, patientId)
|
||||
.GetOrSetObjectAsync(groupedField, patientId, factory, ttl, cancellationToken);
|
||||
|
||||
|
||||
// Set/Get básicos
|
||||
/// <summary>
|
||||
/// Sets the value associated with the specified key by selecting the appropriate backend for that key and delegating the assignment to it.
|
||||
/// </summary>
|
||||
/// <param name="key">The key used to select the backend and identify the value to set.</param>
|
||||
/// <param name="value">The value to associate with the specified key.</param>
|
||||
public void SetValue(string key, string value)
|
||||
=> SelectBackend(key).SetValue(key, value);
|
||||
=> SelectBackend(key).SetValue(key, value);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the value associated with the specified key by delegating the lookup to a backend selected for that key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key used to select the backend and retrieve the associated value.</param>
|
||||
/// <returns>The value associated with the key, or <c>null</c> if the selected backend returns no value.</returns>
|
||||
public string? GetValue(string key)
|
||||
=> SelectBackend(key).GetValue(key);
|
||||
=> SelectBackend(key).GetValue(key);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an object of type <typeparamref name="T"/> from the backend selected by the given key, with an option to trigger an update.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier used to select the appropriate backend and to look up the object.</param>
|
||||
/// <param name="upd">Indicates whether the underlying backend should perform an update during retrieval. Defaults to <c>true</c>.</param>
|
||||
/// <returns>A task that represents the asynchronous retrieval operation, containing the object of type <typeparamref name="T"/> or <c>null</c> if not found.</returns>
|
||||
public Task<T?> GetObjectAsync<T>(string key, bool upd = true)
|
||||
=> SelectBackend(key).GetObjectAsync<T>(key, upd);
|
||||
=> SelectBackend(key).GetObjectAsync<T>(key, upd);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously stores an object in the backend selected by the specified key.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the object to store.</typeparam>
|
||||
/// <param name="key">The key used to select the backend and identify the stored object.</param>
|
||||
/// <param name="obj">The object to store in the selected backend.</param>
|
||||
/// <param name="upd">Indicates whether an update operation should be performed. Defaults to <c>true</c>.</param>
|
||||
/// <returns>A task that represents the asynchronous store operation.</returns>
|
||||
public Task SetObjectAsync<T>(string key, T obj, bool upd = true)
|
||||
=> SelectBackend(key).SetObjectAsync(key, obj, upd);
|
||||
=> SelectBackend(key).SetObjectAsync(key, obj, upd);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an object of the specified type from the backend selected by the given key, optionally applying a time-to-live and update behavior.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier used to select the backend and locate the stored object.</param>
|
||||
/// <param name="ttl">An optional time-to-live applied to the object; if null, the backend's default is used.</param>
|
||||
/// <param name="upd">A flag indicating whether the retrieval should update the object's state (e.g., refresh expiration).</param>
|
||||
/// <returns>A task containing the deserialized object, or null if the object is not found in the selected backend.</returns>
|
||||
public Task<T?> GetObjectAsync<T>(string key, TimeSpan? ttl, bool upd)
|
||||
=> SelectBackend(key).GetObjectAsync<T>(key, ttl, upd);
|
||||
=> SelectBackend(key).GetObjectAsync<T>(key, ttl, upd);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously stores an object in the backend selected for the specified key, with an optional time-to-live and update flag.
|
||||
/// </summary>
|
||||
/// <param name="key">The key used to select the target backend and identify the object.</param>
|
||||
/// <param name="obj">The object to store.</param>
|
||||
/// <param name="ttl">The optional time-to-live duration for the stored object.</param>
|
||||
/// <param name="upd">Indicates whether to update an existing entry or create a new one.</param>
|
||||
/// <returns>A task that represents the asynchronous set operation.</returns>
|
||||
public Task SetObjectAsync<T>(string key, T obj, TimeSpan? ttl, bool upd)
|
||||
=> SelectBackend(key).SetObjectAsync(key, obj, ttl, upd);
|
||||
=> SelectBackend(key).SetObjectAsync(key, obj, ttl, upd);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the object identified by the specified key by delegating the operation to the backend selected for that key.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier of the object to delete, also used to resolve the responsible backend.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation.</returns>
|
||||
public Task DeleteObjectAsync(string key)
|
||||
=> SelectBackend(key).DeleteObjectAsync(key);
|
||||
=> SelectBackend(key).DeleteObjectAsync(key);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes entries matching the specified pattern from both Redis and in-memory storage, returning the total count of deleted entries.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern used to match entries for deletion in both storage backends.</param>
|
||||
/// <returns>The combined total number of entries deleted from Redis and in-memory storage.</returns>
|
||||
public async Task<long> DeleteByPatternAsync(string pattern)
|
||||
=> await redis.DeleteByPatternAsync(pattern) + await memory.DeleteByPatternAsync(pattern);
|
||||
=> await redis.DeleteByPatternAsync(pattern) + await memory.DeleteByPatternAsync(pattern);
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cached data from both the in-memory cache and the Redis cache, ensuring that stale entries are removed across all configured cache providers.
|
||||
/// </summary>
|
||||
public void CleanCache()
|
||||
{
|
||||
memory.CleanCache();
|
||||
redis.CleanCache();
|
||||
}
|
||||
{
|
||||
memory.CleanCache();
|
||||
redis.CleanCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,131 +15,214 @@ namespace adas_core.Application.Services.Caching
|
||||
private readonly ConcurrentDictionary<string, object> _mem = new();
|
||||
|
||||
// HELPERS
|
||||
/// <summary>
|
||||
/// Builds a composite key for a grouped observation field, scoped to a specific patient.
|
||||
/// </summary>
|
||||
/// <param name="gf">The grouped field whose name is included in the key.</param>
|
||||
/// <param name="patientId">The identifier of the patient the key is scoped to.</param>
|
||||
/// <returns>A formatted key string in the form <c>GroupedObs:{patientId}:{gf.Name}</c>.</returns>
|
||||
private static string BuildGroupedKey(GroupedField gf, ObjectId patientId)
|
||||
=> $"GroupedObs:{patientId}:{gf.Name}";
|
||||
=> $"GroupedObs:{patientId}:{gf.Name}";
|
||||
|
||||
|
||||
// GET OR SET (string key)
|
||||
/// <summary>
|
||||
/// Retrieves an object from the in-memory cache by key, or creates and stores it using the provided factory if absent. Uses a fast path for cache hits and a lock-based path with a double-check to prevent duplicate creation across concurrent callers.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to look up and store the object.</param>
|
||||
/// <param name="factory">The asynchronous factory function invoked to produce the object when it is not found in the cache.</param>
|
||||
/// <param name="ttl">Optional time-to-live associated with the cached object.</param>
|
||||
/// <param name="cancellationToken">A token to observe for cancellation requests.</param>
|
||||
/// <returns>The cached or newly created object of type <typeparamref name="T"/>.</returns>
|
||||
public async Task<T> GetOrSetObjectAsync<T>(
|
||||
string key,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// FAST PATH
|
||||
if (_mem.TryGetValue(key, out var existing))
|
||||
return (T)existing;
|
||||
|
||||
// LOCKED PATH
|
||||
return await lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
string key,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_mem.TryGetValue(key, out var again))
|
||||
return (T)again;
|
||||
|
||||
var created = await factory();
|
||||
|
||||
if (created != null!)
|
||||
_mem[key] = created;
|
||||
|
||||
return created!;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
// FAST PATH
|
||||
if (_mem.TryGetValue(key, out var existing))
|
||||
return (T)existing;
|
||||
|
||||
// LOCKED PATH
|
||||
return await lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
{
|
||||
if (_mem.TryGetValue(key, out var again))
|
||||
return (T)again;
|
||||
|
||||
var created = await factory();
|
||||
|
||||
if (created != null!)
|
||||
_mem[key] = created;
|
||||
|
||||
return created!;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the cached string value associated with the specified key, or loads and stores it using the provided loader function if absent.
|
||||
/// Delegates to <see cref="GetOrSetObjectAsync"/> to handle caching, honoring the optional TTL override for the cache entry.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to identify the stored string value.</param>
|
||||
/// <param name="loader">The asynchronous function invoked to load the value when no cached entry exists for the key.</param>
|
||||
/// <param name="ttlOverride">An optional time span that overrides the default time-to-live for the cached value.</param>
|
||||
/// <returns>The cached or newly loaded string value, or <c>null</c> when the underlying cache entry is absent or cannot be cast to a string.</returns>
|
||||
public async Task<string?> GetOrSetValueAsync(
|
||||
string key,
|
||||
Func<Task<string>> loader,
|
||||
TimeSpan? ttlOverride = null)
|
||||
{
|
||||
|
||||
var result = await GetOrSetObjectAsync(key, loader, ttlOverride);
|
||||
return (string?)result;
|
||||
|
||||
}
|
||||
string key,
|
||||
Func<Task<string>> loader,
|
||||
TimeSpan? ttlOverride = null)
|
||||
{
|
||||
|
||||
var result = await GetOrSetObjectAsync(key, loader, ttlOverride);
|
||||
return (string?)result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// GET OR SET (GroupedField + patientId)
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a cached object associated with the given grouped field and patient, or creates and caches a new one using the supplied factory when no cached value exists.
|
||||
/// The factory is only invoked when the cache does not contain a value for the key, and the produced value is stored in the cache only when it is not null.
|
||||
/// </summary>
|
||||
/// <param name="groupedField">The grouped field that contributes to the cache key.</param>
|
||||
/// <param name="patientId">The patient identifier that contributes to the cache key.</param>
|
||||
/// <param name="factory">The asynchronous factory used to build the object when no cached value is available.</param>
|
||||
/// <param name="ttl">Optional time-to-live for the cached entry.</param>
|
||||
/// <param name="cancellationToken">Token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task containing the cached or newly created object.</returns>
|
||||
public async Task<T> GetOrSetObjectAsync<T>(
|
||||
GroupedField groupedField,
|
||||
ObjectId patientId,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var key = BuildGroupedKey(groupedField, patientId);
|
||||
|
||||
if (_mem.TryGetValue(key, out var existing))
|
||||
return (T)existing;
|
||||
|
||||
return await lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
GroupedField groupedField,
|
||||
ObjectId patientId,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_mem.TryGetValue(key, out var again))
|
||||
return (T)again;
|
||||
|
||||
var created = await factory();
|
||||
|
||||
if (created != null!)
|
||||
_mem[key] = created;
|
||||
|
||||
return created!;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
var key = BuildGroupedKey(groupedField, patientId);
|
||||
|
||||
if (_mem.TryGetValue(key, out var existing))
|
||||
return (T)existing;
|
||||
|
||||
return await lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
{
|
||||
if (_mem.TryGetValue(key, out var again))
|
||||
return (T)again;
|
||||
|
||||
var created = await factory();
|
||||
|
||||
if (created != null!)
|
||||
_mem[key] = created;
|
||||
|
||||
return created!;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
// GET / SET
|
||||
/// <summary>
|
||||
/// Stores the specified value in the in-memory collection under the given key, overwriting any existing entry.
|
||||
/// </summary>
|
||||
/// <param name="key">The key that identifies where the value will be stored.</param>
|
||||
/// <param name="value">The value to associate with the specified key.</param>
|
||||
public void SetValue(string key, string value)
|
||||
=> _mem[key] = value;
|
||||
=> _mem[key] = value;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the string representation of the value associated with the specified key from the in-memory store.
|
||||
/// Returns the value converted via <see cref="object.ToString"/> when the key is found, or <c>null</c> when the key is not present.
|
||||
/// </summary>
|
||||
/// <param name="key">The key used to look up the value in the underlying store.</param>
|
||||
/// <returns>The string representation of the stored value if the key exists; otherwise, <c>null</c>.</returns>
|
||||
public string? GetValue(string key)
|
||||
=> _mem.TryGetValue(key, out var v) ? v.ToString() : null;
|
||||
=> _mem.TryGetValue(key, out var v) ? v.ToString() : null;
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an object of type <typeparamref name="T"/> from the in-memory cache using the specified key.
|
||||
/// Returns the stored value cast to <typeparamref name="T"/> if the key exists, or <c>default</c> (null for reference types) if the key is not found.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to look up the stored object.</param>
|
||||
/// <param name="updateExpiration">Indicates whether the entry's expiration should be refreshed on access. Not currently used by this implementation.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> containing the cached value cast to <typeparamref name="T"/>, or <c>null</c> if no entry exists for the given key.</returns>
|
||||
public Task<T?> GetObjectAsync<T>(string key, bool updateExpiration = true)
|
||||
{
|
||||
return Task.FromResult(
|
||||
_mem.TryGetValue(key, out var v) ? (T?)v : default
|
||||
);
|
||||
}
|
||||
{
|
||||
return Task.FromResult(
|
||||
_mem.TryGetValue(key, out var v) ? (T?)v : default
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously stores the specified object in the in-memory cache using the given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key under which the object will be stored.</param>
|
||||
/// <param name="obj">The object to store in the cache.</param>
|
||||
/// <param name="updateExpiration">Indicates whether the cache entry's expiration should be refreshed.</param>
|
||||
public Task SetObjectAsync<T>(string key, T obj, bool updateExpiration = true)
|
||||
{
|
||||
_mem[key] = obj!;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
{
|
||||
_mem[key] = obj!;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an object of type <typeparamref name="T"/> associated with the specified key by delegating to an overload that supports an update flag. The <paramref name="ttlOverride"/> parameter is accepted by this overload but is not forwarded to the underlying call.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier of the object to retrieve.</param>
|
||||
/// <param name="ttlOverride">An optional time-to-live override accepted by this overload but ignored when delegating to the underlying retrieval call.</param>
|
||||
/// <param name="upd">A flag indicating whether the retrieval should trigger an update on the stored object.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the retrieved object of type <typeparamref name="T"/> or <c>null</c> if no object is found for the given key.</returns>
|
||||
public Task<T?> GetObjectAsync<T>(string key, TimeSpan? ttlOverride, bool upd)
|
||||
=> GetObjectAsync<T>(key, upd);
|
||||
=> GetObjectAsync<T>(key, upd);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously stores an object associated with the specified key, with an option to override its time-to-live. The <paramref name="ttlOverride"/> parameter is accepted but is not forwarded to the underlying storage call, so the effective time-to-live is determined elsewhere.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier used to store and later retrieve the object.</param>
|
||||
/// <param name="obj">The object to store in the underlying store.</param>
|
||||
/// <param name="ttlOverride">An optional time-to-live override for the stored entry; not applied by this overload.</param>
|
||||
/// <param name="upd">A flag indicating whether the operation should update an existing entry.</param>
|
||||
/// <returns>A task that represents the asynchronous set operation.</returns>
|
||||
public Task SetObjectAsync<T>(string key, T obj, TimeSpan? ttlOverride, bool upd)
|
||||
=> SetObjectAsync(key, obj, upd);
|
||||
=> SetObjectAsync(key, obj, upd);
|
||||
|
||||
|
||||
// DELETE / CLEAN
|
||||
/// <summary>
|
||||
/// Asynchronously removes the object associated with the specified key from the in-memory store. The operation succeeds silently whether or not the key exists.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier of the object to delete.</param>
|
||||
public Task DeleteObjectAsync(string key)
|
||||
{
|
||||
_mem.TryRemove(key, out _);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
{
|
||||
_mem.TryRemove(key, out _);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deletes cache entries whose keys contain the specified pattern, where asterisk (*) characters in the pattern are treated as wildcards (stripped and matched as substrings). Returns the number of entries successfully removed.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern to match against cache keys. Asterisk (*) characters are removed and the remaining text is used as a substring match.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the count of entries that were removed.</returns>
|
||||
public Task<long> DeleteByPatternAsync(string pattern)
|
||||
{
|
||||
var p = pattern.Replace("*", "");
|
||||
var keys = _mem.Keys.Where(k => k.Contains(p)).ToList();
|
||||
|
||||
long removed = 0;
|
||||
foreach (var k in keys)
|
||||
if (_mem.TryRemove(k, out _))
|
||||
removed++;
|
||||
|
||||
return Task.FromResult(removed);
|
||||
}
|
||||
{
|
||||
var p = pattern.Replace("*", "");
|
||||
var keys = _mem.Keys.Where(k => k.Contains(p)).ToList();
|
||||
|
||||
long removed = 0;
|
||||
foreach (var k in keys)
|
||||
if (_mem.TryRemove(k, out _))
|
||||
removed++;
|
||||
|
||||
return Task.FromResult(removed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all entries from the in-memory cache, removing any previously stored data.
|
||||
/// </summary>
|
||||
public void CleanCache() => _mem.Clear();
|
||||
}
|
||||
}
|
||||
@@ -11,50 +11,105 @@ namespace adas_core.Application.Services.Caching
|
||||
/// </summary>
|
||||
public class NoCacheService : ICacheService
|
||||
{
|
||||
/// <summary>
|
||||
/// Stub implementation that performs no action. Intended as a placeholder for storing a value associated with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier used to reference the value.</param>
|
||||
/// <param name="value">The value intended to be associated with the key.</param>
|
||||
public void SetValue(string key, string value)
|
||||
{
|
||||
// No hacer nada
|
||||
}
|
||||
{
|
||||
// No hacer nada
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the string value associated with the specified key.
|
||||
/// Returns <c>null</c> when no value is found for the given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key used to look up the associated value.</param>
|
||||
/// <returns>The value associated with <paramref name="key"/>, or <c>null</c> if no value is found.</returns>
|
||||
public string? GetValue(string key)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an object of type <typeparamref name="T"/> associated with the specified <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier of the object to retrieve.</param>
|
||||
/// <param name="updateExpiration">Indicates whether the expiration of the entry should be updated upon retrieval.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that represents the asynchronous retrieval, containing the object associated with the key or the default value of <typeparamref name="T"/> if not found.</returns>
|
||||
public Task<T?> GetObjectAsync<T>(string key, bool updateExpiration = true)
|
||||
{
|
||||
return Task.FromResult<T?>(default);
|
||||
}
|
||||
{
|
||||
return Task.FromResult<T?>(default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously stores the specified object associated with the given key in the underlying data store.
|
||||
/// When <paramref name="updateExpiration"/> is true, the expiration of the entry is refreshed; otherwise the existing expiration is preserved.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique identifier used to store and later retrieve the object.</param>
|
||||
/// <param name="obj">The object to store in the data store.</param>
|
||||
/// <param name="updateExpiration">Indicates whether the expiration time of the cached entry should be updated. Defaults to true.</param>
|
||||
public Task SetObjectAsync<T>(string key, T obj, bool updateExpiration = true)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an object of type <typeparamref name="T"/> associated with the specified <paramref name="key"/>.
|
||||
/// Supports an optional time-to-live override and an option to update the expiration of the stored entry.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique identifier used to look up the stored object.</param>
|
||||
/// <param name="ttlOverride">An optional time-to-live value that, when provided, overrides the default expiration for the entry.</param>
|
||||
/// <param name="updateExpiration">Indicates whether the expiration of the entry should be refreshed upon a successful retrieval.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that represents the asynchronous operation, containing the retrieved object or <c>null</c> if no value is found.</returns>
|
||||
public Task<T?> GetObjectAsync<T>(string key, TimeSpan? ttlOverride, bool updateExpiration)
|
||||
{
|
||||
return Task.FromResult<T?>(default);
|
||||
}
|
||||
{
|
||||
return Task.FromResult<T?>(default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously stores an object associated with the specified key, optionally overriding the time-to-live and updating the expiration.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier used to store and retrieve the object.</param>
|
||||
/// <param name="obj">The object to be stored.</param>
|
||||
/// <param name="ttlOverride">An optional time-to-live value that overrides the default expiration period; <c>null</c> uses the default.</param>
|
||||
/// <param name="updateExpiration">A value indicating whether the expiration time should be updated.</param>
|
||||
/// <returns>A task that represents the asynchronous set operation.</returns>
|
||||
public Task SetObjectAsync<T>(string key, T obj, TimeSpan? ttlOverride, bool updateExpiration)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deletes items matching the specified pattern and returns the number of items removed.
|
||||
/// This implementation is a stub that always returns 0, performing no actual deletion regardless of the provided pattern.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern used to identify the items to delete.</param>
|
||||
/// <returns>A <see cref="Task{Int64}"/> representing the asynchronous operation, with a result of 0 indicating that no items were deleted.</returns>
|
||||
public Task<long> DeleteByPatternAsync(string pattern)
|
||||
{
|
||||
return Task.FromResult(0L);
|
||||
}
|
||||
{
|
||||
return Task.FromResult(0L);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the object identified by the specified key.
|
||||
/// The operation completes immediately without performing an actual deletion.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier of the object to delete.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous delete operation.</returns>
|
||||
public Task DeleteObjectAsync(string key)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a cleanup operation on the cache. Currently, this method has no implementation and does not perform any cleanup actions.
|
||||
/// </summary>
|
||||
public void CleanCache()
|
||||
{
|
||||
// Nada que limpiar
|
||||
}
|
||||
{
|
||||
// Nada que limpiar
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// GET OR SET - STRING KEY
|
||||
@@ -70,14 +125,21 @@ namespace adas_core.Application.Services.Caching
|
||||
return await factory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously loads a value using the provided loader function.
|
||||
/// </summary>
|
||||
/// <param name="key">The key associated with the value to retrieve or set.</param>
|
||||
/// <param name="loader">The asynchronous function used to load the value.</param>
|
||||
/// <param name="ttl">An optional time-to-live duration for the value.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the loaded value as a nullable string.</returns>
|
||||
public async Task<string?> GetOrSetValueAsync(
|
||||
string key,
|
||||
Func<Task<string>> loader,
|
||||
TimeSpan? ttl = null)
|
||||
{
|
||||
var result = await loader();
|
||||
return (string?)result;
|
||||
}
|
||||
string key,
|
||||
Func<Task<string>> loader,
|
||||
TimeSpan? ttl = null)
|
||||
{
|
||||
var result = await loader();
|
||||
return (string?)result;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// GET OR SET - GroupedField + patientId
|
||||
|
||||
@@ -24,44 +24,55 @@ namespace adas_core.Application.Services.Caching
|
||||
private readonly ConcurrentDictionary<string, string> _tokens =
|
||||
new(StringComparer.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to acquire a distributed lock for the specified key using Redis, retrying until the timeout expires.
|
||||
/// Returns <c>false</c> if the Redis database is unavailable or if the lock cannot be acquired within the given timeout.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier of the resource to lock.</param>
|
||||
/// <param name="timeout">The maximum duration to keep retrying before giving up.</param>
|
||||
/// <returns><c>true</c> if the lock was successfully acquired; otherwise, <c>false</c>.</returns>
|
||||
public async Task<bool> AcquireAsync(string key, TimeSpan timeout)
|
||||
{
|
||||
var redis = getDatabase();
|
||||
if (redis is null) return false;
|
||||
|
||||
var redisKey = (RedisKey)(_prefix + key);
|
||||
var token = Guid.NewGuid().ToString("N");
|
||||
var end = DateTime.UtcNow.Add(timeout);
|
||||
|
||||
while (DateTime.UtcNow < end)
|
||||
{
|
||||
if (await redis.StringSetAsync(redisKey, token, _ttl, When.NotExists))
|
||||
{
|
||||
_tokens[key] = token;
|
||||
return true;
|
||||
var redis = getDatabase();
|
||||
if (redis is null) return false;
|
||||
|
||||
var redisKey = (RedisKey)(_prefix + key);
|
||||
var token = Guid.NewGuid().ToString("N");
|
||||
var end = DateTime.UtcNow.Add(timeout);
|
||||
|
||||
while (DateTime.UtcNow < end)
|
||||
{
|
||||
if (await redis.StringSetAsync(redisKey, token, _ttl, When.NotExists))
|
||||
{
|
||||
_tokens[key] = token;
|
||||
return true;
|
||||
}
|
||||
|
||||
await Task.Delay(_retryDelay);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
await Task.Delay(_retryDelay);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously releases the token associated with the specified key by removing it from the in-memory token store and executing a Lua release script against Redis. If the key is not found in the local store, or the Redis database is unavailable, the method returns without performing any further action.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier of the token to release.</param>
|
||||
public async Task ReleaseAsync(string key)
|
||||
{
|
||||
if (!_tokens.TryRemove(key, out var token))
|
||||
return;
|
||||
|
||||
var redis = getDatabase();
|
||||
if (redis is null) return;
|
||||
|
||||
var redisKey = (RedisKey)(_prefix + key);
|
||||
|
||||
await redis.ScriptEvaluateAsync(
|
||||
LuaReleaseScript,
|
||||
[redisKey],
|
||||
[token]
|
||||
);
|
||||
}
|
||||
{
|
||||
if (!_tokens.TryRemove(key, out var token))
|
||||
return;
|
||||
|
||||
var redis = getDatabase();
|
||||
if (redis is null) return;
|
||||
|
||||
var redisKey = (RedisKey)(_prefix + key);
|
||||
|
||||
await redis.ScriptEvaluateAsync(
|
||||
LuaReleaseScript,
|
||||
[redisKey],
|
||||
[token]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ using StackExchange.Redis;
|
||||
|
||||
namespace adas_core.Application.Services.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Redis-based implementation of the <see cref="ICacheService"/> interface for caching operations.
|
||||
/// </summary>
|
||||
public class RedisService : ICacheService
|
||||
{
|
||||
private readonly ILogger<RedisService> _logger;
|
||||
@@ -40,236 +43,334 @@ namespace adas_core.Application.Services.Caching
|
||||
|
||||
|
||||
// GET OR SET (string key)
|
||||
/// <summary>
|
||||
/// Retrieves an object of type T from the cache using the specified key, or creates and caches a new instance using the provided factory if no cached value exists.
|
||||
/// Uses a distributed lock to prevent concurrent cache misses from creating duplicate objects, and falls back to calling the factory directly when Redis is unavailable.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to identify the stored object.</param>
|
||||
/// <param name="factory">The asynchronous factory function invoked to create a new instance when the object is not present in the cache.</param>
|
||||
/// <param name="ttl">Optional time-to-live duration for the cached object. If null, the cache default is used.</param>
|
||||
/// <param name="cancellationToken">The token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the cached or newly created object.</returns>
|
||||
public async Task<T> GetOrSetObjectAsync<T>(
|
||||
string key,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_isRedisAvailable)
|
||||
return await factory();
|
||||
|
||||
var direct = await GetObjectAsync<T>(key);
|
||||
|
||||
if (direct is not null)
|
||||
return direct;
|
||||
|
||||
return await _lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
string key,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var again = await GetObjectAsync<T>(key);
|
||||
if (again is not null)
|
||||
return again;
|
||||
|
||||
var created = await factory();
|
||||
|
||||
if (created != null)
|
||||
await SetObjectAsync(key, created, ttl, true);
|
||||
|
||||
return created!;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
if (!_isRedisAvailable)
|
||||
return await factory();
|
||||
|
||||
var direct = await GetObjectAsync<T>(key);
|
||||
|
||||
if (direct is not null)
|
||||
return direct;
|
||||
|
||||
return await _lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
{
|
||||
var again = await GetObjectAsync<T>(key);
|
||||
if (again is not null)
|
||||
return again;
|
||||
|
||||
var created = await factory();
|
||||
|
||||
if (created != null)
|
||||
await SetObjectAsync(key, created, ttl, true);
|
||||
|
||||
return created!;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a cached value for the specified key, or loads, caches, and returns it via the supplied loader if absent.
|
||||
/// Falls back to invoking the loader directly when Redis is unavailable, and uses a distributed lock to prevent duplicate loads under concurrent access.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to identify the stored value.</param>
|
||||
/// <param name="loader">The asynchronous function invoked to produce the value when it is not present in the cache.</param>
|
||||
/// <param name="ttl">Optional time-to-live applied to the cached value; if not provided, the default caching policy is used.</param>
|
||||
/// <returns>The cached value when available, or the value produced by the loader when the cache is empty or Redis is unavailable.</returns>
|
||||
public async Task<string?> GetOrSetValueAsync(
|
||||
string key,
|
||||
Func<Task<string>> loader,
|
||||
TimeSpan? ttl = null)
|
||||
{
|
||||
if (!_isRedisAvailable)
|
||||
return await loader();
|
||||
|
||||
var direct = GetValue(key);
|
||||
if (direct is not null)
|
||||
return direct;
|
||||
|
||||
return await _lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
string key,
|
||||
Func<Task<string>> loader,
|
||||
TimeSpan? ttl = null)
|
||||
{
|
||||
var again = GetValue(key);
|
||||
if (again is not null)
|
||||
return again;
|
||||
|
||||
var created = await loader();
|
||||
SetValue(key, created);
|
||||
|
||||
return created;
|
||||
});
|
||||
}
|
||||
if (!_isRedisAvailable)
|
||||
return await loader();
|
||||
|
||||
var direct = GetValue(key);
|
||||
if (direct is not null)
|
||||
return direct;
|
||||
|
||||
return await _lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
{
|
||||
var again = GetValue(key);
|
||||
if (again is not null)
|
||||
return again;
|
||||
|
||||
var created = await loader();
|
||||
SetValue(key, created);
|
||||
|
||||
return created;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// GET OR SET (GroupedField + patientId)
|
||||
/// <summary>
|
||||
/// Builds a unique cache key for a grouped observation associated with a specific patient.
|
||||
/// </summary>
|
||||
/// <param name="gf">The grouped field whose name is used to identify the observation group.</param>
|
||||
/// <param name="patientId">The identifier of the patient the observation belongs to.</param>
|
||||
/// <returns>A formatted string key combining the <c>GroupedObs</c> prefix, the patient identifier, and the grouped field name.</returns>
|
||||
private static string BuildGroupedKey(GroupedField gf, ObjectId patientId)
|
||||
=> $"GroupedObs:{patientId}:{gf.Name}";
|
||||
=> $"GroupedObs:{patientId}:{gf.Name}";
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a cached object associated with the specified grouped field and patient identifier, or creates and stores it using the provided factory if absent. Uses a distributed lock to prevent duplicate creation under cache misses and falls back to invoking the factory directly when Redis is unavailable.
|
||||
/// </summary>
|
||||
/// <param name="groupedField">The grouped field used, together with the patient identifier, to build the cache key.</param>
|
||||
/// <param name="patientId">The patient identifier used to build the cache key.</param>
|
||||
/// <param name="factory">Asynchronous factory invoked to produce the object when no cached value exists.</param>
|
||||
/// <param name="ttl">Optional time-to-live applied to the stored cache entry. If null, no expiration is set.</param>
|
||||
/// <param name="cancellationToken">Token used to cancel the distributed lock operation.</param>
|
||||
/// <returns>The cached object if present, otherwise the object produced by <paramref name="factory"/>.</returns>
|
||||
public async Task<T> GetOrSetObjectAsync<T>(
|
||||
GroupedField groupedField,
|
||||
ObjectId patientId,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var key = BuildGroupedKey(groupedField, patientId);
|
||||
|
||||
if (!_isRedisAvailable)
|
||||
return await factory();
|
||||
|
||||
var direct = await GetObjectAsync<T>(key);
|
||||
if (direct is not null)
|
||||
return direct;
|
||||
|
||||
return await _lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
GroupedField groupedField,
|
||||
ObjectId patientId,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var again = await GetObjectAsync<T>(key);
|
||||
if (again is not null)
|
||||
return again;
|
||||
|
||||
var created = await factory();
|
||||
|
||||
if (created != null)
|
||||
await SetObjectAsync(key, created, ttl, true);
|
||||
|
||||
return created!;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
var key = BuildGroupedKey(groupedField, patientId);
|
||||
|
||||
if (!_isRedisAvailable)
|
||||
return await factory();
|
||||
|
||||
var direct = await GetObjectAsync<T>(key);
|
||||
if (direct is not null)
|
||||
return direct;
|
||||
|
||||
return await _lockManager.WithLockAsync(
|
||||
$"getorset:{key}",
|
||||
TimeSpan.FromSeconds(5),
|
||||
async () =>
|
||||
{
|
||||
var again = await GetObjectAsync<T>(key);
|
||||
if (again is not null)
|
||||
return again;
|
||||
|
||||
var created = await factory();
|
||||
|
||||
if (created != null)
|
||||
await SetObjectAsync(key, created, ttl, true);
|
||||
|
||||
return created!;
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
// BASIC OPERATIONS
|
||||
/// <summary>
|
||||
/// Stores a string value in the database under the specified key, applying a TTL resolved from <c>GetEntityTtl</c> and preserving any existing TTL on overwrite. If the underlying database is not initialized, the operation is skipped.
|
||||
/// </summary>
|
||||
/// <param name="key">The key under which the value will be stored.</param>
|
||||
/// <param name="value">The string value to persist.</param>
|
||||
public void SetValue(string key, string value)
|
||||
=> _database?.StringSet(key, value, GetEntityTtl(key), true);
|
||||
=> _database?.StringSet(key, value, GetEntityTtl(key), true);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a string value from the underlying data store by its key, and conditionally renews the entity's time-to-live when the key is found and renewal is permitted by policy.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier of the value to look up in the data store.</param>
|
||||
/// <returns>The stored string value, or <c>null</c> if the key does not exist or the data store is unavailable.</returns>
|
||||
public string? GetValue(string key)
|
||||
{
|
||||
var val = _database?.StringGet(key);
|
||||
if (val.HasValue && ShouldRenewTtl(key))
|
||||
_database?.KeyExpire(key, GetEntityTtl(key));
|
||||
return val;
|
||||
}
|
||||
{
|
||||
var val = _database?.StringGet(key);
|
||||
if (val.HasValue && ShouldRenewTtl(key))
|
||||
_database?.KeyExpire(key, GetEntityTtl(key));
|
||||
return val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves and deserializes an object of type <typeparamref name="T"/> from Redis using the specified key.
|
||||
/// Returns <c>default</c> when Redis is unavailable or when the key is not found or holds an empty value, and optionally refreshes the key's expiration time on a successful hit.
|
||||
/// </summary>
|
||||
/// <param name="key">The Redis key identifying the stored object to retrieve.</param>
|
||||
/// <param name="updateExpiration">When <c>true</c> (the default), resets the key's time-to-live to the configured entity TTL on a successful read, implementing sliding expiration.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> containing the deserialized object, or <c>default</c> if Redis is unavailable or the key is missing/empty.</returns>
|
||||
/// <exception cref="Exception">Thrown when the stored JSON payload cannot be deserialized into <typeparamref name="T"/>; the original exception is wrapped and rethrown.</exception>
|
||||
public async Task<T?> GetObjectAsync<T>(string key, bool updateExpiration = true)
|
||||
{
|
||||
if (!_isRedisAvailable)
|
||||
return default;
|
||||
|
||||
var json = await _database!.StringGetAsync(key);
|
||||
if (json.IsNullOrEmpty)
|
||||
return default;
|
||||
|
||||
if (updateExpiration)
|
||||
_database!.KeyExpire(key, GetEntityTtl(key));
|
||||
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
Converters = new List<JsonConverter> { new StringEnumConverter(), new ObjectIdConverter() }
|
||||
};
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json!, settings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error deserializing object in RedisService {e}",e.ToString());
|
||||
throw new Exception($"Error deserializing object in RedisService {e}", e);
|
||||
}
|
||||
}
|
||||
{
|
||||
if (!_isRedisAvailable)
|
||||
return default;
|
||||
|
||||
var json = await _database!.StringGetAsync(key);
|
||||
if (json.IsNullOrEmpty)
|
||||
return default;
|
||||
|
||||
if (updateExpiration)
|
||||
_database!.KeyExpire(key, GetEntityTtl(key));
|
||||
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
Converters = new List<JsonConverter> { new StringEnumConverter(), new ObjectIdConverter() }
|
||||
};
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json!, settings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error deserializing object in RedisService {e}",e.ToString());
|
||||
throw new Exception($"Error deserializing object in RedisService {e}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously stores an object associated with the specified key, optionally refreshing its expiration time.
|
||||
/// </summary>
|
||||
/// <param name="key">The key under which the object will be stored.</param>
|
||||
/// <param name="obj">The object to store.</param>
|
||||
/// <param name="updateExpiration">Indicates whether the expiration time of the entry should be updated.</param>
|
||||
public async Task SetObjectAsync<T>(
|
||||
string key,
|
||||
T obj,
|
||||
bool updateExpiration = true)
|
||||
=> await SetObjectAsync(key, obj, null, updateExpiration);
|
||||
string key,
|
||||
T obj,
|
||||
bool updateExpiration = true)
|
||||
=> await SetObjectAsync(key, obj, null, updateExpiration);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously serializes the specified object to JSON and stores it in Redis under the given key, using the provided TTL override or the default entity TTL when not specified. The operation is skipped when Redis is unavailable, and the object is serialized using camelCase property names with string enum and ObjectId converters.
|
||||
/// </summary>
|
||||
/// <param name="key">The Redis key under which the serialized object will be stored.</param>
|
||||
/// <param name="obj">The object to serialize and persist to Redis.</param>
|
||||
/// <param name="ttlOverride">An optional time-to-live override; when null, the entity's default TTL is applied.</param>
|
||||
/// <param name="updateExpiration">Flag indicating whether the expiration should be updated.</param>
|
||||
public async Task SetObjectAsync<T>(
|
||||
string key,
|
||||
T obj,
|
||||
TimeSpan? ttlOverride,
|
||||
bool updateExpiration)
|
||||
{
|
||||
if (!_isRedisAvailable)
|
||||
return;
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
Converters = new List<JsonConverter> { new StringEnumConverter(), new ObjectIdConverter() }
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(obj, settings);
|
||||
await _database!.StringSetAsync(key, json, ttlOverride ?? GetEntityTtl(key), true);
|
||||
}
|
||||
string key,
|
||||
T obj,
|
||||
TimeSpan? ttlOverride,
|
||||
bool updateExpiration)
|
||||
{
|
||||
if (!_isRedisAvailable)
|
||||
return;
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
Converters = new List<JsonConverter> { new StringEnumConverter(), new ObjectIdConverter() }
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(obj, settings);
|
||||
await _database!.StringSetAsync(key, json, ttlOverride ?? GetEntityTtl(key), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deletes an object from the Redis cache using the specified key.
|
||||
/// When the Redis backend is unavailable, the call is skipped silently as a no-op fallback.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique identifier of the cached object to remove.</param>
|
||||
public async Task DeleteObjectAsync(string key)
|
||||
{
|
||||
if (_isRedisAvailable)
|
||||
await _database!.KeyDeleteAsync(key);
|
||||
}
|
||||
{
|
||||
if (_isRedisAvailable)
|
||||
await _database!.KeyDeleteAsync(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deletes all Redis keys matching the specified pattern.
|
||||
/// Returns 0 if Redis is unavailable or the server is not initialized.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern used to match Redis keys to be deleted.</param>
|
||||
/// <returns>The number of keys that were deleted.</returns>
|
||||
public async Task<long> DeleteByPatternAsync(string pattern)
|
||||
{
|
||||
if (!_isRedisAvailable || _server == null)
|
||||
return 0;
|
||||
|
||||
var keys = _server.Keys(pattern: pattern).ToArray();
|
||||
|
||||
foreach (var key in keys)
|
||||
await _database!.KeyDeleteAsync(key);
|
||||
|
||||
return keys.Length;
|
||||
}
|
||||
{
|
||||
if (!_isRedisAvailable || _server == null)
|
||||
return 0;
|
||||
|
||||
var keys = _server.Keys(pattern: pattern).ToArray();
|
||||
|
||||
foreach (var key in keys)
|
||||
await _database!.KeyDeleteAsync(key);
|
||||
|
||||
return keys.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cached data by flushing the underlying server database. If the server instance is <see langword="null"/>, the call is safely skipped as a no-op.
|
||||
/// </summary>
|
||||
public void CleanCache()
|
||||
=> _server?.FlushDatabase();
|
||||
=> _server?.FlushDatabase();
|
||||
|
||||
|
||||
// TTL
|
||||
/// <summary>
|
||||
/// Resolves the time-to-live (TTL) for a cache entity based on the entity type inferred from the cache key, returning entity-specific TTL values for Patients and Displays while falling back to the global TTL for any other entity. Returns <c>null</c> when the resolved TTL in seconds is zero or negative, indicating that the entity should not be cached.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to classify the entity type and determine the applicable TTL.</param>
|
||||
/// <returns>A <see cref="TimeSpan"/> representing the configured TTL, or <c>null</c> if the resolved seconds value is not positive.</returns>
|
||||
private TimeSpan? GetEntityTtl(string key)
|
||||
{
|
||||
var entity = CacheKeyClassifier.Classify(key);
|
||||
var ttl = _cacheSettings.Redis.Ttl;
|
||||
|
||||
int? seconds = entity switch
|
||||
{
|
||||
CacheEnum.EntityType.Patients => ttl.PatientsSeconds,
|
||||
CacheEnum.EntityType.Displays => ttl.DisplaysSeconds,
|
||||
_ => ttl.GlobalSeconds
|
||||
};
|
||||
|
||||
return seconds > 0 ? TimeSpan.FromSeconds(seconds.Value) : null;
|
||||
}
|
||||
{
|
||||
var entity = CacheKeyClassifier.Classify(key);
|
||||
var ttl = _cacheSettings.Redis.Ttl;
|
||||
|
||||
int? seconds = entity switch
|
||||
{
|
||||
CacheEnum.EntityType.Patients => ttl.PatientsSeconds,
|
||||
CacheEnum.EntityType.Displays => ttl.DisplaysSeconds,
|
||||
_ => ttl.GlobalSeconds
|
||||
};
|
||||
|
||||
return seconds > 0 ? TimeSpan.FromSeconds(seconds.Value) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the time-to-live (TTL) for the entity associated with the specified key should be renewed, based on whether an existing TTL value is found.
|
||||
/// </summary>
|
||||
/// <param name="key">The key identifying the entity whose TTL presence is being checked.</param>
|
||||
/// <returns><c>true</c> if a TTL value is found for the specified key; otherwise, <c>false</c>.</returns>
|
||||
private bool ShouldRenewTtl(string key)
|
||||
=> GetEntityTtl(key) != null;
|
||||
=> GetEntityTtl(key) != null;
|
||||
|
||||
|
||||
// INITIALIZATION
|
||||
/// <summary>
|
||||
/// Initializes the Redis connection for caching by connecting asynchronously, obtaining the database and server, and marking the connection as available on success. Returns early without establishing a connection when the configured Redis connection string is null, and logs any exception that occurs during initialization without rethrowing, leaving the connection marked as unavailable.
|
||||
/// </summary>
|
||||
private async Task InitializeRedisConnectionAsync()
|
||||
{
|
||||
_isRedisAvailable = false;
|
||||
try
|
||||
{
|
||||
if (_cacheSettings.Redis.ConnectionString == null)
|
||||
return;
|
||||
|
||||
_connection = await ConnectionMultiplexer.ConnectAsync(_cacheSettings.Redis.ConnectionString);
|
||||
_database = _connection.GetDatabase();
|
||||
_server = _connection.GetServer(_cacheSettings.Redis.ConnectionString);
|
||||
|
||||
_isRedisAvailable = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError( "Error Initializing Redis Connection. Exception:{ex}", ex.Message);
|
||||
}
|
||||
}
|
||||
{
|
||||
_isRedisAvailable = false;
|
||||
try
|
||||
{
|
||||
if (_cacheSettings.Redis.ConnectionString == null)
|
||||
return;
|
||||
|
||||
_connection = await ConnectionMultiplexer.ConnectAsync(_cacheSettings.Redis.ConnectionString);
|
||||
_database = _connection.GetDatabase();
|
||||
_server = _connection.GetServer(_cacheSettings.Redis.ConnectionString);
|
||||
|
||||
_isRedisAvailable = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError( "Error Initializing Redis Connection. Exception:{ex}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an object asynchronously from the cache, optionally updating its expiration time.
|
||||
/// The optional TTL override is ignored by this overload and is not passed to the underlying call.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key identifying the object to retrieve.</param>
|
||||
/// <param name="ttlOverride">An optional time-to-live override; not applied by this overload.</param>
|
||||
/// <param name="updateExpiration">When true, the expiration of the cached entry is refreshed on retrieval.</param>
|
||||
/// <returns>A task that resolves to the cached object, or null if no entry exists for the specified key.</returns>
|
||||
public Task<T?> GetObjectAsync<T>(string key, TimeSpan? ttlOverride, bool updateExpiration)
|
||||
=> GetObjectAsync<T>(key, updateExpiration);
|
||||
=> GetObjectAsync<T>(key, updateExpiration);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@ using MongoDB.Bson;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the <see cref="ICalculatedObservationsService"/> contract, providing a service for working with calculated observations.
|
||||
/// </summary>
|
||||
public class CalculatedObservationsService : ICalculatedObservationsService
|
||||
{
|
||||
private static ICalculatedObservations? _service;
|
||||
@@ -61,6 +64,13 @@ public class CalculatedObservationsService : ICalculatedObservationsService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientObservation"/> by delegating to the configured mapping service.
|
||||
/// If no service is available, the original observation is returned unchanged; if the service produces no mapping, a debug message is logged and <c>null</c> is returned.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation to map.</param>
|
||||
/// <param name="onlyByName">When <c>true</c>, restricts the mapping to lookups by name only.</param>
|
||||
/// <returns>The mapped <see cref="PatientObservation"/>, or <c>null</c> when the service yields no result; the input <paramref name="obs"/> is returned unchanged when no mapping service is configured.</returns>
|
||||
public virtual async Task<PatientObservation?> Map(PatientObservation obs, bool onlyByName = false)
|
||||
{
|
||||
if (_service == null) return obs;
|
||||
@@ -71,6 +81,12 @@ public class CalculatedObservationsService : ICalculatedObservationsService
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientObservationAlarm"/> using the configured mapping service. Falls back to returning the original alarm unchanged when no service is available, and logs a debug entry when the service produces a null result (i.e., the alarm is ignored).
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation alarm to be mapped.</param>
|
||||
/// <param name="onlyByName">When true, restricts the mapping to a name-based lookup only.</param>
|
||||
/// <returns>The mapped <see cref="PatientObservationAlarm"/>, or <c>null</c> if the service mapped it to null, or the original <paramref name="obs"/> when no service is configured.</returns>
|
||||
public virtual async Task<PatientObservationAlarm?> Map(PatientObservationAlarm obs, bool onlyByName = false)
|
||||
{
|
||||
if (_service == null) return obs;
|
||||
@@ -81,6 +97,12 @@ public class CalculatedObservationsService : ICalculatedObservationsService
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the supplied <see cref="PumpObservation"/> by delegating to the configured mapping service.
|
||||
/// Returns <see langword="null"/> when the underlying service has not been initialized.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to be mapped.</param>
|
||||
/// <returns>A task that yields the mapped <see cref="PumpObservation"/>, or <see langword="null"/> if no mapping service is available.</returns>
|
||||
public virtual async Task<PumpObservation?> Map(PumpObservation obs)
|
||||
{
|
||||
if (_service == null) return null;
|
||||
@@ -88,6 +110,11 @@ public class CalculatedObservationsService : ICalculatedObservationsService
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientRecordingAlert"/> using the configured mapping service. Falls back to returning the original alert when no service is configured, and logs a debug entry when the service produces no mapping.
|
||||
/// </summary>
|
||||
/// <param name="obs">The <see cref="PatientRecordingAlert"/> instance to be mapped.</param>
|
||||
/// <returns>The mapped <see cref="PatientRecordingAlert"/>, the original <paramref name="obs"/> if no service is available, or <c>null</c> if the service returned no result.</returns>
|
||||
public async Task<PatientRecordingAlert?> Map(PatientRecordingAlert obs)
|
||||
{
|
||||
if (_service == null) return obs;
|
||||
@@ -98,6 +125,11 @@ public class CalculatedObservationsService : ICalculatedObservationsService
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously maps a <see cref="PatientTreatment"/> using the underlying service. If the service is unavailable (null), the original treatment is returned unchanged as a fallback.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The patient treatment instance to be mapped or transformed.</param>
|
||||
/// <returns>A task that represents the asynchronous mapping operation, containing the mapped <see cref="PatientTreatment"/> or <c>null</c> if the service returns no result.</returns>
|
||||
public async Task<PatientTreatment?> Map(PatientTreatment treatment)
|
||||
{
|
||||
if (_service == null) return treatment;
|
||||
@@ -105,6 +137,11 @@ public class CalculatedObservationsService : ICalculatedObservationsService
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientDiagnosis"/> by delegating to the configured mapping service. If no service is available, the original <paramref name="diagnosis"/> is returned as a fallback.
|
||||
/// </summary>
|
||||
/// <param name="diagnosis">The patient diagnosis to be mapped.</param>
|
||||
/// <returns>A task that yields the mapped <see cref="PatientDiagnosis"/>, or <c>null</c> if the underlying service returns no result.</returns>
|
||||
public virtual async Task<PatientDiagnosis?> Map(PatientDiagnosis diagnosis)
|
||||
{
|
||||
if (_service == null) return diagnosis;
|
||||
@@ -112,30 +149,58 @@ public class CalculatedObservationsService : ICalculatedObservationsService
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously calculates medicine observations for a patient based on their active medicines.
|
||||
/// Delegates the calculation to the underlying service if it has been initialized; otherwise, the call is silently skipped.
|
||||
/// </summary>
|
||||
/// <param name="activeMedicines">The list of medicines currently active for the patient.</param>
|
||||
/// <param name="patientId">The unique identifier of the patient whose medicine observations are being calculated.</param>
|
||||
public virtual async Task CalculateMedicineObservation(List<Medicine> activeMedicines, ObjectId patientId)
|
||||
{
|
||||
if (_service != null) await _service.CalculateMedicineObservation(activeMedicines, patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously calculates the active bolus dosage of opiates for the specified patient by delegating to the underlying service.
|
||||
/// If the service dependency is not initialized, the call is skipped silently as a no-op fallback.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient for whom the active bolus opiates calculation is performed.</param>
|
||||
public async Task CalculateBolusOpiates(ObjectId patientId)
|
||||
{
|
||||
if (_service != null) await _service.CalculateActiveBolus(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the active treatments associated with the specified patient.
|
||||
/// If the underlying service is not available, returns an empty list as a fallback instead of throwing.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose active treatments should be retrieved.</param>
|
||||
/// <returns>A task that yields the collection of active <see cref="PatientTreatment"/> entries for the patient, or an empty list when the service is unavailable.</returns>
|
||||
public virtual async Task<IEnumerable<PatientTreatment?>> GetActiveTreatmentsByPatient(ObjectId id)
|
||||
{
|
||||
if (_service != null) return await _service.GetActiveTreatmentsByPatient(id);
|
||||
return new List<PatientTreatment?>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a list of patient observations by delegating to the configured service's pre-mapping logic when available; otherwise, returns an empty list.
|
||||
/// </summary>
|
||||
/// <param name="listToInsert">The list of patient observations to be mapped.</param>
|
||||
/// <returns>A task containing the mapped list of patient observations, or an empty list if no service is configured.</returns>
|
||||
public virtual async Task<List<PatientObservation>> MapList(List<PatientObservation> listToInsert)
|
||||
{
|
||||
if (_service != null) return await _service.PreMapList(listToInsert);
|
||||
return [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a source alarm onto the given patient observation by delegating to the configured service when available; if no service is configured, returns the original observation unchanged.
|
||||
/// </summary>
|
||||
/// <param name="observation">The patient observation onto which the source alarm will be mapped.</param>
|
||||
/// <param name="observationAlarm">The source alarm to be applied to the observation.</param>
|
||||
/// <returns>The <see cref="PatientObservation"/> produced by the service mapping, or the original <paramref name="observation"/> when no service is configured.</returns>
|
||||
public virtual async Task<PatientObservation> MapSourceAlarm(PatientObservation observation,
|
||||
PatientObservationAlarm observationAlarm)
|
||||
PatientObservationAlarm observationAlarm)
|
||||
{
|
||||
if (_service != null) return await _service.MapSourceAlarm(observation, observationAlarm);
|
||||
return observation;
|
||||
|
||||
@@ -9,18 +9,40 @@ using MongoDB.Driver;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides camera-related operations by coordinating the camera repository and point-of-care service, and logging diagnostic information.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This service implements <see cref="ICameraService"/> and serves as the application-layer entry point for camera functionality.
|
||||
/// </remarks>
|
||||
public class CameraService(ILogger<CameraService> logger, ICameraRepository cameraRepository, IPointOfCareService pointOfCareService) : ICameraService
|
||||
{
|
||||
private ICameraRepository _cameraRepository = cameraRepository;
|
||||
/// <summary>
|
||||
/// Retrieves a camera by its associated relay identifier from the camera repository.
|
||||
/// Returns null when no camera is found for the specified relay identifier.
|
||||
/// </summary>
|
||||
/// <param name="relayId">The unique identifier of the relay used to look up the camera.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="Camera"/>, or null if no camera is found.</returns>
|
||||
public Task<Camera?> GetById(ObjectId relayId)
|
||||
{
|
||||
return _cameraRepository.GetById(relayId);
|
||||
}
|
||||
/// <summary>
|
||||
/// Retrieves the list of cameras associated with the specified configuration relay identifiers.
|
||||
/// </summary>
|
||||
/// <param name="configurationRelayList">The list of configuration relay identifiers used to look up the corresponding cameras.</param>
|
||||
/// <returns>A <see cref="List{Camera}"/> containing the cameras linked to the provided configuration relay identifiers.</returns>
|
||||
public List<Camera> GetCameraInList(List<ObjectId> configurationRelayList)
|
||||
{
|
||||
return _cameraRepository.GetCameraInList(configurationRelayList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of cameras, optionally filtered by whether they are currently in use, and resolves the in-use status for each returned camera using the point-of-care service.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that controls page number, page size, and optional filtering criteria such as the in-use flag.</param>
|
||||
/// <returns>A paginated response containing the requested cameras, the current page metadata, and the total document count; if no data is found, an empty paginated response is returned.</returns>
|
||||
public async Task<PaginationResponse<Camera>> GetPaginatedCameras(PaginationFilter filter)
|
||||
{
|
||||
var usedCameraIds = await pointOfCareService.FindAllIdCamerasInUse();
|
||||
@@ -31,8 +53,8 @@ public class CameraService(ILogger<CameraService> logger, ICameraRepository came
|
||||
{
|
||||
bool filterInUse = filter.FilteredRequest.InUse.Value;
|
||||
var filterBuilder = Builders<Camera>.Filter;
|
||||
|
||||
var idFilter = filterInUse
|
||||
|
||||
var idFilter = filterInUse
|
||||
? filterBuilder.In(c => c.Id, usedCameraIds)
|
||||
: filterBuilder.Not(filterBuilder.In(c => c.Id, usedCameraIds));
|
||||
|
||||
@@ -45,42 +67,61 @@ public class CameraService(ILogger<CameraService> logger, ICameraRepository came
|
||||
.Limit(filter.PageSize)
|
||||
.ToListAsync();
|
||||
|
||||
if(data == null) return new PaginationResponse<Camera>([], filter.PageNumber, filter.PageSize, count);
|
||||
if (data == null) return new PaginationResponse<Camera>([], filter.PageNumber, filter.PageSize, count);
|
||||
|
||||
foreach (var camera in data)
|
||||
{
|
||||
if (camera == null) continue;
|
||||
|
||||
bool isInUse = usedCameraIds.Contains(camera.Id);
|
||||
|
||||
|
||||
// Asignación mediante reflexión para el private set
|
||||
camera.GetType().GetProperty(nameof(Camera.InUse))
|
||||
?.SetValue(camera, isInUse);
|
||||
}
|
||||
|
||||
|
||||
return new PaginationResponse<Camera>(data, filter.PageNumber, filter.PageSize, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new camera into the system after validating its name and ensuring no duplicate exists.
|
||||
/// Throws an exception if the camera name is null or if another camera with the same name already exists.
|
||||
/// </summary>
|
||||
/// <param name="camera">The camera entity to insert.</param>
|
||||
/// <returns>The inserted camera, or null if the insertion did not return a result.</returns>
|
||||
/// <exception cref="System.Exception">Thrown when the camera name is null.</exception>
|
||||
/// <exception cref="System.Exception">Thrown when a camera with the same name already exists.</exception>
|
||||
public async Task<Camera?> InsertCamera(Camera camera)
|
||||
{
|
||||
if (camera.Name == null) throw new Exception("Camera name cannot be null");
|
||||
var cameraFound = await _cameraRepository.GetByName(camera.Name);
|
||||
if(cameraFound != null) throw new Exception($"Camera with name {camera.Name} already exists");
|
||||
if (cameraFound != null) throw new Exception($"Camera with name {camera.Name} already exists");
|
||||
return await _cameraRepository.InsertOneCamera(camera);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing camera identified by its unique identifier, returning the updated entity if the operation succeeds.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The unique identifier of the camera to update.</param>
|
||||
/// <param name="camera">The camera data containing the updated values.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the updated <see cref="Camera"/>, or <c>null</c> if no camera with the specified identifier was found.</returns>
|
||||
public async Task<Camera?> UpdateCameraById(ObjectId objectId, Camera camera)
|
||||
{
|
||||
return await _cameraRepository.UpdateCameraAsync(objectId, camera);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a camera identified by the specified object identifier. Returns <c>false</c> when the camera is not found, and logs and returns <c>false</c> if an error occurs during the operation.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The unique identifier of the camera to delete.</param>
|
||||
/// <returns><c>true</c> if the camera was successfully deleted; otherwise, <c>false</c>.</returns>
|
||||
public async Task<bool> DeleteCamera(ObjectId objectId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cameraToDelete = await _cameraRepository.GetById(objectId);
|
||||
if(cameraToDelete == null) return false;
|
||||
|
||||
if (cameraToDelete == null) return false;
|
||||
|
||||
await _cameraRepository.DeleteAsync(cameraToDelete.Id);
|
||||
return true;
|
||||
}
|
||||
@@ -89,9 +130,14 @@ public class CameraService(ILogger<CameraService> logger, ICameraRepository came
|
||||
logger.LogError(e, e.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of cameras matching the specified search text by delegating to the camera repository.
|
||||
/// </summary>
|
||||
/// <param name="textToSearch">The search text used to find cameras by name.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Camera"/> objects that match the search criteria.</returns>
|
||||
public async Task<List<Camera>> GetSearchByNameCameras(string textToSearch)
|
||||
{
|
||||
return await _cameraRepository.GetSearchByNameCameras(textToSearch);
|
||||
|
||||
@@ -21,6 +21,12 @@ using MongoDB.Bson;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a concrete implementation of the <see cref="IConfigObservationService"/> contract for observing configuration state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Acts as the default service type that fulfills the configuration observation interface.
|
||||
/// </remarks>
|
||||
public class ConfigObservationService : IConfigObservationService
|
||||
{
|
||||
private static readonly ConcurrentDictionary<ObjectId, ConfigObservationCached> CachedConfigObservations = new();
|
||||
@@ -39,8 +45,8 @@ public class ConfigObservationService : IConfigObservationService
|
||||
private readonly int? _refreshTimeout;
|
||||
private readonly IUnitService _unitService;
|
||||
private readonly ICacheService _cacheService;
|
||||
private readonly CacheSettings? _cacheSettings;
|
||||
|
||||
private readonly CacheSettings? _cacheSettings;
|
||||
|
||||
private bool IgnoreUnknownObservation =>
|
||||
_apiSettings.Value.ConfigObservation?.IgnoreUnknownObservation ?? false;
|
||||
|
||||
@@ -73,9 +79,14 @@ public class ConfigObservationService : IConfigObservationService
|
||||
_auditService = auditService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all configuration observations using a cache-aside strategy. If no cached value exists, the data is fetched from the repository and cached using the key and TTL determined by the cache settings.
|
||||
/// </summary>
|
||||
/// <param name="ct">A token to monitor for cancellation requests.</param>
|
||||
/// <returns>A collection of all <see cref="ConfigObservation"/> entries, sourced from cache when available or from the repository otherwise.</returns>
|
||||
public async Task<ICollection<ConfigObservation>> GetAllConfigs(CancellationToken ct = default)
|
||||
{
|
||||
|
||||
|
||||
var (key, ttl) = CacheKeys.ConfigObservationsAllKeyWithTtl(_cacheSettings);
|
||||
|
||||
var result = await _cacheService.GetOrSetObjectAsync(
|
||||
@@ -88,12 +99,21 @@ public class ConfigObservationService : IConfigObservationService
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a compact representation of all configuration observations by returning the total item count.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ConfigObservationDto"/> containing the total number of configuration observations.</returns>
|
||||
public async Task<ConfigObservationDto> GetAllCompact()
|
||||
{
|
||||
var count = await _configObservationRepository.Count();
|
||||
return new ConfigObservationDto { ItemCount = count };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of <see cref="ConfigObservation"/> items from the repository along with the total count, used to build pagination metadata for the response.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter containing the requested page number and page size used to retrieve the items and populate the response metadata.</param>
|
||||
/// <returns>A <see cref="PaginationResponse{ConfigObservation}"/> containing the items for the requested page and the total count of all available items.</returns>
|
||||
public async Task<PaginationResponse<ConfigObservation>> GetPaginatedItems(PaginationFilter filter)
|
||||
{
|
||||
var result = await _configObservationRepository.GetPaginatedItems(filter);
|
||||
@@ -103,35 +123,66 @@ public class ConfigObservationService : IConfigObservationService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a configuration observation by its unique identifier from the repository.
|
||||
/// Returns null if no matching configuration observation is found.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the configuration observation to retrieve.</param>
|
||||
/// <returns>The configuration observation matching the specified identifier, or null if not found.</returns>
|
||||
public async Task<ConfigObservation?> GetConfigById(ObjectId id)
|
||||
{
|
||||
return await _configObservationRepository.FindById(id) ?? null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of configuration names associated with the specified identifier by delegating to the configuration observation repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier used to look up the related configuration names.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of configuration names matching the given identifier.</returns>
|
||||
public async Task<List<string>> GetConfigNames(string id)
|
||||
{
|
||||
return await _configObservationRepository.GetConfigNames(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of configuration names from the configuration observation repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of configuration names.</returns>
|
||||
public async Task<List<string>> GetConfigNames()
|
||||
{
|
||||
return await _configObservationRepository.GetConfigNames();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines the retention action to apply for a patient observation by resolving its configured retention policy. Falls back to a "NoDelete" retention policy with no value when the observation has no associated configuration or its retention policy is null.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The patient observation type, constrained to <see cref="BasePatientObservation"/>.</typeparam>
|
||||
/// <param name="obs">The patient observation for which the retention action is being evaluated.</param>
|
||||
/// <returns>A task containing the resolved <see cref="ObservatitonRetentionResult"/>, or null when no configuration is available.</returns>
|
||||
public async Task<ObservatitonRetentionResult?> RetentionActions<T>(T obs) where T : BasePatientObservation
|
||||
{
|
||||
var conf = await Get(obs);
|
||||
return conf is { RetentionPolicy: not null } ?
|
||||
new ObservatitonRetentionResult(conf.RetentionPolicy.Value, conf.RetentionPolicyValue) :
|
||||
return conf is { RetentionPolicy: not null } ?
|
||||
new ObservatitonRetentionResult(conf.RetentionPolicy.Value, conf.RetentionPolicyValue) :
|
||||
new ObservatitonRetentionResult(RetentionPolicy.NoDelete, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the status of a grouped patient observation by retrieving the configuration for the given name and mapping the observation through group-specific, result-specific, or default configuration. Returns <see cref="StatusEnum.Type.Ok"/> when no configuration exists for the name or when the mapping does not produce a status.
|
||||
/// </summary>
|
||||
/// <param name="groupedField">The grouped field whose <c>Group</c> key is used to look up group-specific configuration.</param>
|
||||
/// <param name="result">The grouped observation result whose name is used to look up result-specific configuration.</param>
|
||||
/// <param name="name">The observation name used to retrieve the configuration.</param>
|
||||
/// <param name="value">The observation value included in the mapping.</param>
|
||||
/// <param name="min">The optional minimum reference value included in the mapping.</param>
|
||||
/// <param name="max">The optional maximum reference value included in the mapping.</param>
|
||||
/// <returns>A task that resolves to the <see cref="StatusEnum.Type"/> computed from the mapped observation, or <see cref="StatusEnum.Type.Ok"/> when no applicable configuration or mapping status is found.</returns>
|
||||
public async Task<StatusEnum.Type> GroupedObservationStatus(GroupedField groupedField,
|
||||
GroupedObservationEnum.Result result,
|
||||
string name, object value, double? min, double? max)
|
||||
GroupedObservationEnum.Result result,
|
||||
string name, object value, double? min, double? max)
|
||||
{
|
||||
var conf = await Get(name);
|
||||
if (conf == null) return StatusEnum.Type.Ok;
|
||||
@@ -156,6 +207,15 @@ public class ConfigObservationService : IConfigObservationService
|
||||
return StatusEnum.Type.Ok;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a patient observation to a configured representation by looking up its corresponding configuration
|
||||
/// and applying the mapping. When no matching configuration is found, returns <c>null</c> if unknown
|
||||
/// observations should be ignored, or the original observation otherwise. If the resolved configuration
|
||||
/// has an empty name, the mapping is aborted and <c>null</c> is returned.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation to be mapped.</param>
|
||||
/// <param name="onlyByName">If <c>true</c>, the configuration lookup is performed by name only; otherwise the full lookup is used.</param>
|
||||
/// <returns>The mapped observation, the original observation when unknown observations are allowed, or <c>null</c> when mapping is ignored or the configuration is invalid.</returns>
|
||||
public async Task<T?> Map<T>(T obs, bool onlyByName = false) where T : BasePatientObservation
|
||||
{
|
||||
var conf = onlyByName ? await Get(obs, onlyByName) : await Get(obs);
|
||||
@@ -175,11 +235,17 @@ public class ConfigObservationService : IConfigObservationService
|
||||
return await MapConf(obs, conf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a configuration observation item by its identifier. Returns <c>null</c> when the item does not exist,
|
||||
/// otherwise deletes it from the repository and invalidates the cached collection of configuration observations.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the configuration observation to remove.</param>
|
||||
/// <returns>The removed <see cref="ConfigObservation"/> if it was found and deleted; otherwise, <c>null</c>.</returns>
|
||||
public async Task<ConfigObservation?> RemoveConfigItem(ObjectId id)
|
||||
{
|
||||
var item = await _configObservationRepository.FindById(id);
|
||||
if (item == null) return null;
|
||||
|
||||
|
||||
var deleted = await _configObservationRepository.Delete(id);
|
||||
|
||||
// Invalidar CACHE (colección completa)
|
||||
@@ -189,6 +255,11 @@ public class ConfigObservationService : IConfigObservationService
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientTreatment"/> by resolving the configuration observation for each of its requested give codes. Looks up the configuration by text when both the coding system and identifier are empty, otherwise by coding system and identifier. Returns <c>null</c> when the configuration is unknown and unknown treatments are ignored, or when the resolved configuration has no name; otherwise returns the original treatment.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The patient treatment whose requested give codes are resolved against the configuration store.</param>
|
||||
/// <returns>The original <see cref="PatientTreatment"/> if a valid configuration is found, or <c>null</c> when the treatment should be discarded.</returns>
|
||||
public async Task<PatientTreatment?> Map(PatientTreatment treatment)
|
||||
{
|
||||
ConfigObservation? conf = null;
|
||||
@@ -203,8 +274,14 @@ public class ConfigObservationService : IConfigObservationService
|
||||
return conf.Name == null ? null : treatment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="ConfigObservation"/> that matches the supplied patient observation, either by name only or by a combination of code, coding system, and parent data fields.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation whose matching configuration should be resolved.</param>
|
||||
/// <param name="onlyByName">When <c>true</c>, the lookup is restricted to matching by <see cref="BasePatientObservation.Name"/> only; otherwise matching also considers code, coding system, and parent observation data.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing the matched <see cref="ConfigObservation"/> processed via <c>Process</c>, or <c>null</c> if no configuration items are available or no match is found.</returns>
|
||||
public async Task<ConfigObservation?> Get<T>(T obs, bool onlyByName = false)
|
||||
where T : BasePatientObservation
|
||||
where T : BasePatientObservation
|
||||
{
|
||||
var items = await GetAllConfigs();
|
||||
if (items.Count == 0) return null;
|
||||
@@ -273,6 +350,13 @@ public class ConfigObservationService : IConfigObservationService
|
||||
// .ToList() ?? [];
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="ConfigObservation"/> that matches the specified coding system and code, processing it before returning.
|
||||
/// If no matching observation is found, a warning is logged and <see langword="null"/> is returned.
|
||||
/// </summary>
|
||||
/// <param name="codingSystem">The coding system identifier used to filter the observation. May be <see langword="null"/>.</param>
|
||||
/// <param name="code">The code value used to filter the observation. May be <see langword="null"/>.</param>
|
||||
/// <returns>A processed <see cref="ConfigObservation"/> if a match is found; otherwise, <see langword="null"/>.</returns>
|
||||
public async Task<ConfigObservation?> GetByCodeSysAndCode(string? codingSystem, string? code)
|
||||
{
|
||||
var filteredResult = await _configObservationRepository.GetByCodeSysAndCode(codingSystem, code);
|
||||
@@ -283,6 +367,11 @@ public class ConfigObservationService : IConfigObservationService
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="ConfigObservation"/> by its name, returning <c>null</c> when the name is not provided or the configuration is not found.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the configuration to look up; if null or empty, the method returns <c>null</c>.</param>
|
||||
/// <returns>A <see cref="ConfigObservation"/> when a matching configuration is found and successfully processed; otherwise, <c>null</c>.</returns>
|
||||
public async Task<ConfigObservation?> Get(string? name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name)) return null;
|
||||
@@ -298,6 +387,13 @@ public class ConfigObservationService : IConfigObservationService
|
||||
return await Process(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing <see cref="ConfigObservation"/> identified by its id and returns the updated entity.
|
||||
/// Throws a not found exception when the configuration observation does not exist, invalidates the related cache entries, and records an audit log of the change.
|
||||
/// </summary>
|
||||
/// <param name="configObservationItem">The configuration observation payload containing the identifier of the record to update.</param>
|
||||
/// <returns>The updated <see cref="ConfigObservation"/>.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no configuration observation exists for the supplied id.</exception>
|
||||
public async Task<ConfigObservation?> UpdateConfig(ConfigObservation configObservationItem)
|
||||
{
|
||||
// if (!configObservationItem.Id.HasValue)
|
||||
@@ -306,15 +402,21 @@ public class ConfigObservationService : IConfigObservationService
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
|
||||
var updatedConfig = await _configObservationRepository.Update(configObservation);
|
||||
|
||||
|
||||
// Invalidar CACHE (colección completa)
|
||||
await _cacheService.DeleteByPatternAsync(CacheKeyPatterns.ForEntity(CacheEnum.EntityType.ConfigObservations));
|
||||
|
||||
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, configObservation,
|
||||
updatedConfig!);
|
||||
return updatedConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves configuration observation items matching the specified name. Returns the matching items if any are found; otherwise logs a warning and throws a <see cref="NotFoundException"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The name used to look up the configuration observation items.</param>
|
||||
/// <returns>A collection of <see cref="ConfigObservation"/> items matching the specified name.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no configuration observation items are found for the given name.</exception>
|
||||
public async Task<IEnumerable<ConfigObservation>?> GetConfigObservationItem(string name)
|
||||
{
|
||||
var configObservationItems = await _configObservationRepository.FindAllByName(name);
|
||||
@@ -324,8 +426,18 @@ public class ConfigObservationService : IConfigObservationService
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a single <see cref="ConfigObservation"/> item that matches the specified code, coding system, name, and original name.
|
||||
/// Throws a not-found exception when no matching item exists in the repository.
|
||||
/// </summary>
|
||||
/// <param name="code">The code used to identify the configuration observation item.</param>
|
||||
/// <param name="codingSystem">The coding system associated with the item.</param>
|
||||
/// <param name="name">The name of the configuration observation item.</param>
|
||||
/// <param name="originalName">The original name of the configuration observation item.</param>
|
||||
/// <returns>The matching <see cref="ConfigObservation"/> item.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no matching configuration observation item is found.</exception>
|
||||
public async Task<ConfigObservation?> GetSingleConfigObservationItem(string? code, string? codingSystem,
|
||||
string? name, string? originalName)
|
||||
string? name, string? originalName)
|
||||
{
|
||||
var matchingItem =
|
||||
await _configObservationRepository.GetSingleConfigObservationItem(code, codingSystem, name, originalName);
|
||||
@@ -335,6 +447,13 @@ public class ConfigObservationService : IConfigObservationService
|
||||
return matchingItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a single configuration observation item, invalidating the related cache entries and recording an audit log of the operation.
|
||||
/// Throws a conflict exception if the item does not exist or the delete operation cannot be completed.
|
||||
/// </summary>
|
||||
/// <param name="configObservationItem">The configuration observation item to delete; its identifier is used to locate the existing record.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the item is successfully deleted.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when no configuration observation is found with the specified identifier, or when the underlying delete operation fails.</exception>
|
||||
public async Task<bool> DeleteSingleConfigObservationItem(ConfigObservation configObservationItem)
|
||||
{
|
||||
var configObservation = await _configObservationRepository.FindById(configObservationItem.Id) ??
|
||||
@@ -344,23 +463,34 @@ public class ConfigObservationService : IConfigObservationService
|
||||
|
||||
_ = await _configObservationRepository.DeleteAsync(configObservation.Id) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
|
||||
|
||||
|
||||
// Invalidar CACHE (colección completa)
|
||||
await _cacheService.DeleteByPatternAsync(CacheKeyPatterns.ForEntity(CacheEnum.EntityType.ConfigObservations));
|
||||
|
||||
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, auxConfigObservation,
|
||||
configObservation);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves configuration observation items that match the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name used to filter configuration observation items.</param>
|
||||
/// <returns>A collection of <see cref="ConfigObservation"/> items matching the specified name.</returns>
|
||||
public async Task<IEnumerable<ConfigObservation>> GetConfigObservationItemsByName(string name)
|
||||
{
|
||||
return await _configObservationRepository.FindAllByName(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new configuration observation after verifying that no existing record shares the same identifier.
|
||||
/// </summary>
|
||||
/// <param name="configObservation">The configuration observation entity to persist.</param>
|
||||
/// <returns>The created <see cref="ConfigObservation"/> if the operation succeeds.</returns>
|
||||
/// <exception cref="BadRequestException">Thrown when a configuration observation with the same identifier already exists.</exception>
|
||||
public async Task<ConfigObservation?> CreateConfig(ConfigObservation configObservation)
|
||||
{
|
||||
|
||||
|
||||
var existing = await _configObservationRepository.FindById(configObservation.Id);
|
||||
if (existing != null)
|
||||
throw new BadRequestException(HttpEnum.ErrorMessage.ConflictCreationFailed);
|
||||
@@ -371,6 +501,13 @@ public class ConfigObservationService : IConfigObservationService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes a configuration item identified by its name, creating an audit log entry prior to deletion and invalidating the related cache.
|
||||
/// Throws a conflict exception when no configuration item with the specified name is found.
|
||||
/// </summary>
|
||||
/// <param name="itemName">The name of the configuration item to remove.</param>
|
||||
/// <returns>The removed <see cref="ConfigObservation"/>, or <c>null</c> if the repository did not return a result.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when no configuration item is found with the specified name.</exception>
|
||||
public async Task<ConfigObservation?> RemoveConfigItem(string itemName)
|
||||
{
|
||||
var configObservation = await GetConfigByName(itemName) ??
|
||||
@@ -380,14 +517,20 @@ public class ConfigObservationService : IConfigObservationService
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, auxConfigObservation,
|
||||
configObservation);
|
||||
|
||||
var result = await _configObservationRepository.Delete(configObservation.Id!);
|
||||
|
||||
var result = await _configObservationRepository.Delete(configObservation.Id!);
|
||||
|
||||
// Invalidar CACHE (colección completa)
|
||||
await _cacheService.DeleteByPatternAsync(CacheKeyPatterns.ForEntity(CacheEnum.EntityType.ConfigObservations));
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a configuration observation by its identifier, using an in-memory cache with a configurable refresh timeout to reduce repository calls.
|
||||
/// Returns the cached value when available and not yet expired; otherwise fetches from the repository and caches the result, falling back to a new empty <see cref="ConfigObservation"/> when the repository does not find a matching record.
|
||||
/// </summary>
|
||||
/// <param name="configObservationId">The unique identifier of the configuration observation to retrieve.</param>
|
||||
/// <returns>The configuration observation obtained from cache or repository, or a new empty instance when no matching record exists.</returns>
|
||||
public async Task<ConfigObservation?> GetConfig(ObjectId configObservationId)
|
||||
{
|
||||
RefreshCachedConfigObservations();
|
||||
@@ -408,6 +551,13 @@ public class ConfigObservationService : IConfigObservationService
|
||||
return cached.ConfigObservation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a configuration observation by its name, using a time-limited in-memory cache before falling back to the repository.
|
||||
/// Returns <c>null</c> if <paramref name="name"/> is null, empty, or whitespace, or if no matching configuration exists in the cache or repository.
|
||||
/// Cache hits require a non-expired <c>NextRefresh</c> and use a case-insensitive name comparison.
|
||||
/// </summary>
|
||||
/// <param name="name">The case-insensitive name of the configuration observation to look up.</param>
|
||||
/// <returns>The matching <see cref="ConfigObservation"/>, or <c>null</c> if not found or the name is invalid.</returns>
|
||||
public async Task<ConfigObservation?> GetConfigByName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) return null;
|
||||
@@ -432,10 +582,14 @@ public class ConfigObservationService : IConfigObservationService
|
||||
};
|
||||
|
||||
CachedConfigObservations[config.Id!] = newCachedItem;
|
||||
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes expired entries from the cached configuration observations when a refresh timeout is configured,
|
||||
/// performing an early return if no refresh timeout is set.
|
||||
/// </summary>
|
||||
private void RefreshCachedConfigObservations()
|
||||
{
|
||||
if (!_refreshTimeout.HasValue) return;
|
||||
@@ -449,6 +603,11 @@ public class ConfigObservationService : IConfigObservationService
|
||||
foreach (var key in keysToRemove) CachedConfigObservations.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a configuration observation by applying the default retention policy when none is set, and normalizing the associated retention policy value based on the selected policy.
|
||||
/// </summary>
|
||||
/// <param name="confItem">The configuration observation to process. Its retention policy and retention policy value are updated in place.</param>
|
||||
/// <returns>A task containing the processed <see cref="ConfigObservation"/>.</returns>
|
||||
private Task<ConfigObservation> Process(ConfigObservation confItem)
|
||||
{
|
||||
confItem.RetentionPolicy ??= _defaultRetentionPolicy;
|
||||
@@ -465,6 +624,11 @@ public class ConfigObservationService : IConfigObservationService
|
||||
return Task.FromResult(confItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes expired entries from the cached configuration observation keys based on the configured refresh timeout.
|
||||
/// If no refresh timeout is set, the method returns without performing any cleanup.
|
||||
/// Any exceptions encountered during the cleanup are caught and logged.
|
||||
/// </summary>
|
||||
private void RefreshCachedConfigObservationKeys()
|
||||
{
|
||||
try
|
||||
@@ -485,6 +649,15 @@ public class ConfigObservationService : IConfigObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps configuration settings from a <see cref="ConfigObservation"/> onto a <see cref="BasePatientObservation"/>,
|
||||
/// applying alert and warning thresholds, evaluating dynamic level conditions, and computing the observation status
|
||||
/// for numeric and string values. Handles <see cref="PatientObservation"/> and <see cref="PatientObservationAlarm"/>
|
||||
/// subtypes with their respective properties, including expiration, units, colors, and UI configuration.
|
||||
/// </summary>
|
||||
/// <param name="obs">The observation instance to enrich with configuration values. Modified in place.</param>
|
||||
/// <param name="conf">The configuration observation providing thresholds, colors, expiration, and other settings to apply.</param>
|
||||
/// <returns>The mapped observation, returned as-is when the observation's coding system is configured to skip status calculation.</returns>
|
||||
private async Task<T?> MapConf<T>(T obs, ConfigObservation conf) where T : BasePatientObservation
|
||||
{
|
||||
_logger.LogTrace("Mapping observation: {obs}", obs);
|
||||
@@ -628,12 +801,21 @@ public class ConfigObservationService : IConfigObservationService
|
||||
return obs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a private cached container for configuration observation data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This type is intended to be used internally to store and reuse configuration observation results.
|
||||
/// </remarks>
|
||||
private class ConfigObservationCached
|
||||
{
|
||||
public DateTime NextRefresh { get; set; }
|
||||
public ConfigObservation? ConfigObservation { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a private cache entry for configuration observation keys, used to store and retrieve previously computed key values associated with configuration observations.
|
||||
/// </summary>
|
||||
private class ConfigObservationKeyCached
|
||||
{
|
||||
public DateTime NextRefresh { get; set; }
|
||||
|
||||
@@ -27,6 +27,11 @@ public class ConfigPumpsService(
|
||||
private readonly string _key = apiSettings.Value.ConfigPumpsKey ?? "PV1";
|
||||
private readonly int? _refreshTimeout = apiSettings.Value.ConfigObservation?.Refresh;
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PumpObservation"/> using its alarm type configuration. Returns the original observation unchanged when configuration-based pump mapping is not required or when no matching configuration is found.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to map, containing the alarm type used for configuration lookup.</param>
|
||||
/// <returns>The mapped pump observation, or the original observation when mapping is skipped or the configuration lookup yields no result.</returns>
|
||||
public async Task<PumpObservation> Map(PumpObservation obs)
|
||||
{
|
||||
if (!_configPumpsRequired) return obs;
|
||||
@@ -40,22 +45,44 @@ public class ConfigPumpsService(
|
||||
return await MapConf(obs, conf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all available pump configurations from the underlying repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="ConfigPumps"/> if any are available, or <c>null</c> when no configurations exist.</returns>
|
||||
public async Task<List<ConfigPumps>?> GetAllPumpConfigs()
|
||||
{
|
||||
return await configPumpsRepository.GetAllConfigs();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the configuration for a pump identified by its unique identifier from the configuration repository.
|
||||
/// Returns <c>null</c> when no matching pump configuration is found.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the pump configuration to retrieve.</param>
|
||||
/// <returns>A <see cref="ConfigPumps"/> instance if a matching configuration is found; otherwise, <c>null</c>.</returns>
|
||||
public async Task<ConfigPumps?> GetPumpConfigById(string id)
|
||||
{
|
||||
return await configPumpsRepository.FindById(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of configuration items associated with the config pump identified by the given identifier. Returns <c>null</c> when no matching config pump is found in the repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the config pump to look up.</param>
|
||||
/// <returns>A task that resolves to the list of <see cref="ConfigPumpItem"/> entries, or <c>null</c> if the config pump does not exist.</returns>
|
||||
public async Task<List<ConfigPumpItem>?> GetConfigItems(string id)
|
||||
{
|
||||
var result = await configPumpsRepository.FindById(id);
|
||||
return result?.Items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the pump configuration in the repository and records an audit log of the change.
|
||||
/// Throws a <see cref="ConflictException"/> if the update operation returns null.
|
||||
/// </summary>
|
||||
/// <param name="pumpConfig">The pump configuration to update, identified by its <see cref="ConfigPumps.Id"/>.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the updated <see cref="ConfigPumps"/>.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the update operation fails.</exception>
|
||||
public async Task<ConfigPumps?> UpdatePumpConfig(ConfigPumps pumpConfig)
|
||||
{
|
||||
var oldPumpConfig = configPumpsRepository.FindById(pumpConfig.Id);
|
||||
@@ -65,6 +92,12 @@ public class ConfigPumpsService(
|
||||
return newPumpConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new pump configuration into the repository, records an audit log entry for the operation, and returns the inserted configuration. If the configuration cannot be retrieved after insertion or any exception occurs, the error is logged and the method returns <c>null</c>.
|
||||
/// </summary>
|
||||
/// <param name="pumpConfig">The pump configuration to insert.</param>
|
||||
/// <returns>The inserted <see cref="ConfigPumps"/> on success; otherwise, <c>null</c> when an error occurs during the operation.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the inserted configuration cannot be found in the repository after insertion.</exception>
|
||||
public async Task<ConfigPumps?> InsertPumpConfig(ConfigPumps pumpConfig)
|
||||
{
|
||||
try
|
||||
@@ -82,6 +115,11 @@ public class ConfigPumpsService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a pump configuration asynchronously, auditing the change on success and returning <c>false</c> if the repository operation reports an error or an exception is thrown.
|
||||
/// </summary>
|
||||
/// <param name="config">The <see cref="ConfigPumps"/> instance representing the pump configuration to delete.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the configuration is deleted and the audit log is recorded; <c>false</c> when the delete operation fails or an exception occurs.</returns>
|
||||
public async Task<bool> DeletePumpConfig(ConfigPumps config)
|
||||
{
|
||||
try
|
||||
@@ -104,23 +142,40 @@ public class ConfigPumpsService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the retention actions to apply for a pump observation based on its configuration.
|
||||
/// When a retention policy is configured, returns the configured policy and its value; otherwise, falls back to <see cref="RetentionPolicy.NoDelete"/> with a null value.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation for which to evaluate the retention policy.</param>
|
||||
/// <returns>A task containing the retention result with the applicable policy and associated value, or a default NoDelete result when no policy is configured.</returns>
|
||||
public async Task<ObservatitonRetentionResult?> RetentionActions(PumpObservation obs)
|
||||
{
|
||||
//TODO sacarlo de la configuración específica de Bombas
|
||||
var conf = await Get(obs);
|
||||
return conf is { RetentionPolicy: not null } ?
|
||||
new ObservatitonRetentionResult(conf.RetentionPolicy.Value, conf.RetentionPolicyValue) :
|
||||
return conf is { RetentionPolicy: not null } ?
|
||||
new ObservatitonRetentionResult(conf.RetentionPolicy.Value, conf.RetentionPolicyValue) :
|
||||
new ObservatitonRetentionResult(RetentionPolicy.NoDelete, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the UI configuration from a <see cref="ConfigPumpItem"/> onto a <see cref="PumpObservation"/>, assigning the configuration only when it is provided and non-empty.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation that will receive the UI configuration.</param>
|
||||
/// <param name="conf">The configuration source whose UI configuration is applied to <paramref name="obs"/> when present.</param>
|
||||
/// <returns>A completed <see cref="Task{PumpObservation}"/> containing the updated observation.</returns>
|
||||
private static Task<PumpObservation> MapConf(PumpObservation obs, ConfigPumpItem conf)
|
||||
{
|
||||
if (conf.UiConfiguration != null && conf.UiConfiguration.Count != 0)
|
||||
if (conf.UiConfiguration != null && conf.UiConfiguration.Count != 0)
|
||||
obs.UiConfiguration = conf.UiConfiguration;
|
||||
|
||||
return Task.FromResult(obs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="ConfigPumpItem"/> matching the specified alarm type by parsing the input string into a <see cref="PumpEnum.AlarmType"/> and searching the configuration items.
|
||||
/// </summary>
|
||||
/// <param name="alarmType">The string representation of the alarm type to look up; if it cannot be parsed into a valid <see cref="PumpEnum.AlarmType"/>, the method returns <c>null</c>.</param>
|
||||
/// <returns>A <see cref="ConfigPumpItem"/> whose <c>AlarmType</c> matches the parsed value, or <c>null</c> if parsing fails or no matching item is found.</returns>
|
||||
private async Task<ConfigPumpItem?> Get(string alarmType)
|
||||
{
|
||||
if (!Enum.TryParse(alarmType, out PumpEnum.AlarmType alarmTypeParsed))
|
||||
@@ -129,11 +184,22 @@ public class ConfigPumpsService(
|
||||
return result?.Items?.FirstOrDefault(i => i.AlarmType == alarmTypeParsed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the configuration item associated with the specified pump observation by matching its message type against the loaded configuration entries.
|
||||
/// Returns null when the configuration, its items collection, or a matching entry is not found.
|
||||
/// </summary>
|
||||
/// <param name="pobs">The pump observation whose <c>MessageType</c> is used to locate the corresponding configuration entry.</param>
|
||||
/// <returns>A <see cref="ConfigPumpItem"/> matching the observation's message type, or <c>null</c> if the configuration is unavailable or no matching item exists.</returns>
|
||||
private async Task<ConfigPumpItem?> Get(PumpObservation pobs)
|
||||
{
|
||||
var config = await GetConfig();
|
||||
var config = await GetConfig();
|
||||
return config?.Items?.FirstOrDefault(i => i.Type == pobs.MessageType);
|
||||
}
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the list of configuration pump items by fetching the current configuration.
|
||||
/// Returns a null list if the underlying configuration is not available.
|
||||
/// </summary>
|
||||
/// <returns>A task containing a list of <see cref="ConfigPumpItem"/> objects, or <c>null</c> if the configuration could not be retrieved.</returns>
|
||||
public async Task<List<ConfigPumpItem>?> Get()
|
||||
{
|
||||
var result = await GetConfig();
|
||||
@@ -141,11 +207,16 @@ public class ConfigPumpsService(
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the configuration associated with the current key, returning a cached value when it has not yet expired.
|
||||
/// Falls back to fetching from the repository when the cache is missing or stale, and returns <c>null</c> if an error occurs during retrieval.
|
||||
/// </summary>
|
||||
/// <returns>A task containing the <see cref="ConfigPumps"/> instance if available; otherwise, <c>null</c> when the repository lookup fails.</returns>
|
||||
private async Task<ConfigPumps?> GetConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_config != null && DateTime.Now <= _nextRefresh)
|
||||
if (_config != null && DateTime.Now <= _nextRefresh)
|
||||
return _config;
|
||||
_config = await configPumpsRepository.FindById(_key);
|
||||
_nextRefresh = _refreshTimeout.HasValue
|
||||
|
||||
@@ -24,15 +24,27 @@ public class ConfigUnitsService(
|
||||
private readonly string _key = apiSettings.Value.ConfigUnitsKey ?? "PV1";
|
||||
private readonly int? _refreshTimeout = apiSettings.Value.ConfigObservation?.Refresh;
|
||||
|
||||
/// <summary>
|
||||
/// Maps a patient observation using the unit configuration when configuration units are required.
|
||||
/// If configuration units are not required, the observation's units are not specified, or no matching configuration is found, the original observation is returned unchanged.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation to be mapped.</param>
|
||||
/// <returns>The mapped observation when a matching unit configuration is resolved; otherwise, the original observation.</returns>
|
||||
public async Task<T> Map<T>(T obs) where T : BasePatientObservation
|
||||
{
|
||||
if (!_configUnitsRequired) return obs;
|
||||
|
||||
|
||||
var conf = !string.IsNullOrEmpty(obs.Units) ? await Get(obs.Units) : null;
|
||||
|
||||
return conf == null ? obs : MapConf(obs, conf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PumpObservation"/> by processing its pump value properties.
|
||||
/// If configuration units are not required, the observation is returned unchanged; otherwise, the pump values are mapped via <c>MapPumpValues</c> and the observation is returned.
|
||||
/// </summary>
|
||||
/// <param name="obs">The <see cref="PumpObservation"/> to be mapped.</param>
|
||||
/// <returns>The mapped <see cref="PumpObservation"/>, returned as-is when units configuration is not required or after pump value mapping otherwise.</returns>
|
||||
public async Task<PumpObservation> Map(PumpObservation obs)
|
||||
{
|
||||
if (!_configUnitsRequired) return obs;
|
||||
@@ -42,7 +54,7 @@ public class ConfigUnitsService(
|
||||
|
||||
// var conf = !string.IsNullOrEmpty(obs.Units) ? await Get(obs.Units) : null;
|
||||
// return conf == null ? obs : MapConf(obs, conf);
|
||||
|
||||
|
||||
return obs;
|
||||
}
|
||||
|
||||
@@ -81,6 +93,12 @@ public class ConfigUnitsService(
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Maps a configuration unit value onto a patient observation. If the observation is not a <see cref="PatientObservation"/>, the original observation is returned unchanged; otherwise, its <c>Units</c> property is assigned from the configuration unit item's value.
|
||||
/// </summary>
|
||||
/// <param name="obs">The base patient observation to which the configured unit value will be applied.</param>
|
||||
/// <param name="conf">The configuration unit item whose <c>Value</c> is used as the unit to assign.</param>
|
||||
/// <returns>The input observation, with <c>Units</c> set from <paramref name="conf"/> when applicable, or the unchanged observation when it is not a <see cref="PatientObservation"/>.</returns>
|
||||
private T MapConf<T>(T obs, ConfigUnitItem conf) where T : BasePatientObservation
|
||||
{
|
||||
if (obs is not PatientObservation pobs) return obs;
|
||||
@@ -91,6 +109,12 @@ public class ConfigUnitsService(
|
||||
return obs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a configuration unit item by its unique code from the configuration store.
|
||||
/// Returns null when the configuration cannot be loaded or when no item matches the specified code.
|
||||
/// </summary>
|
||||
/// <param name="code">The unique code identifier of the configuration unit item to look up.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="ConfigUnitItem"/>, or null if the configuration is unavailable or no item is found.</returns>
|
||||
public async Task<ConfigUnitItem?> Get(string code)
|
||||
{
|
||||
var result = await GetConfig();
|
||||
@@ -98,6 +122,15 @@ public class ConfigUnitsService(
|
||||
return result?.Items?.FirstOrDefault(i => i.Code == code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="ConfigUnits"/> configuration associated with the current key, using a time-based cache
|
||||
/// to avoid repeated repository calls before the configured refresh timeout elapses. On any failure during the
|
||||
/// repository lookup, the error is logged and <c>null</c> is returned.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="Task{TResult}"/> containing the cached or freshly fetched <see cref="ConfigUnits"/>, or
|
||||
/// <c>null</c> if the repository lookup fails.
|
||||
/// </returns>
|
||||
private async Task<ConfigUnits?> GetConfig()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -6,60 +6,124 @@ using MongoDB.Bson;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the default implementation of the <see cref="ICalculatedObservations"/> interface.
|
||||
/// </summary>
|
||||
public class DefaultCalculatedObservations : ICalculatedObservations
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously calculates medicine observations for a patient based on their currently active medicines.
|
||||
/// </summary>
|
||||
/// <param name="activeMedicines">The list of medicines currently active for the patient, used as the basis for the observation calculation.</param>
|
||||
/// <param name="patientId">The identifier of the patient whose medicine observations are being calculated.</param>
|
||||
public Task CalculateMedicineObservation(List<Medicine> activeMedicines, ObjectId patientId)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the active bolus for the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose active bolus is being calculated.</param>
|
||||
public Task CalculateActiveBolus(ObjectId patientId)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the provided patient observation as a completed task, preserving the original instance for further processing.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation to map.</param>
|
||||
/// <param name="onlyByName">A flag indicating whether mapping should be performed by name only.</param>
|
||||
/// <returns>A completed <see cref="Task{T}"/> containing the provided observation.</returns>
|
||||
public Task<T?> Map<T>(T obs, bool onlyByName) where T : BasePatientObservation
|
||||
{
|
||||
return Task.FromResult(obs)!;
|
||||
}
|
||||
{
|
||||
return Task.FromResult(obs)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientTreatment"/> instance to a completed task, returning the provided treatment unchanged.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The patient treatment to map.</param>
|
||||
/// <returns>A <see cref="Task{PatientTreatment}"/> that completes with the supplied <paramref name="treatment"/>.</returns>
|
||||
public Task<PatientTreatment> Map(PatientTreatment treatment)
|
||||
{
|
||||
return Task.FromResult(treatment);
|
||||
}
|
||||
{
|
||||
return Task.FromResult(treatment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the active treatments associated with the specified patient identifier.
|
||||
/// Returns an empty collection, typically serving as a stub or fallback when no treatments are available.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose active treatments are being retrieved.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing an enumerable collection of the patient's active treatments, or an empty collection if none are found.</returns>
|
||||
public Task<IEnumerable<PatientTreatment?>> GetActiveTreatmentsByPatient(ObjectId id)
|
||||
{
|
||||
return Task.FromResult<IEnumerable<PatientTreatment?>>(new List<PatientTreatment?>());
|
||||
}
|
||||
{
|
||||
return Task.FromResult<IEnumerable<PatientTreatment?>>(new List<PatientTreatment?>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the provided patient diagnosis as-is, wrapped in a completed task for asynchronous compatibility.
|
||||
/// </summary>
|
||||
/// <param name="diagnosis">The patient diagnosis to map.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the provided patient diagnosis.</returns>
|
||||
public Task<PatientDiagnosis> Map(PatientDiagnosis diagnosis)
|
||||
{
|
||||
return Task.FromResult(diagnosis);
|
||||
}
|
||||
{
|
||||
return Task.FromResult(diagnosis);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PumpObservation"/> instance to a target <see cref="PumpObservation"/> representation asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="pumpObservation">The source <see cref="PumpObservation"/> to be mapped.</param>
|
||||
/// <returns>A task that represents the asynchronous mapping operation, containing the resulting <see cref="PumpObservation"/>.</returns>
|
||||
/// <exception cref="NotImplementedException">Thrown when the method is invoked, as the mapping logic has not been implemented yet.</exception>
|
||||
public Task<PumpObservation> Map(PumpObservation pumpObservation)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a new patient observation to address potential time inconsistency with the previous observation, returning the observation unchanged.
|
||||
/// </summary>
|
||||
/// <param name="newObservation">The new patient observation to evaluate for time consistency with the prior observation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the provided patient observation.</returns>
|
||||
public Task<PatientObservation?> FixTimeInconsistencyWithLast(PatientObservation newObservation)
|
||||
{
|
||||
return Task.FromResult<PatientObservation?>(newObservation);
|
||||
}
|
||||
{
|
||||
return Task.FromResult<PatientObservation?>(newObservation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a pre-mapping step on a list of patient observations before further processing, returning the list unchanged.
|
||||
/// </summary>
|
||||
/// <param name="listToInsert">The list of patient observations to be pre-mapped.</param>
|
||||
/// <returns>A completed task containing the provided list of patient observations.</returns>
|
||||
public Task<List<PatientObservation>> PreMapList(List<PatientObservation> listToInsert)
|
||||
{
|
||||
return Task.FromResult(listToInsert);
|
||||
}
|
||||
{
|
||||
return Task.FromResult(listToInsert);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a source alarm onto a <see cref="PatientObservation"/> and returns the observation wrapped in a completed task.
|
||||
/// In the current implementation, the observation is returned as-is without applying the supplied alarm.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation to be returned by the mapping.</param>
|
||||
/// <param name="alarmToInsert">The patient observation alarm intended to be associated with the observation.</param>
|
||||
/// <returns>A completed <see cref="Task{TResult}"/> containing the <see cref="PatientObservation"/>.</returns>
|
||||
public Task<PatientObservation> MapSourceAlarm(PatientObservation obs, PatientObservationAlarm alarmToInsert)
|
||||
{
|
||||
return Task.FromResult(obs);
|
||||
}
|
||||
{
|
||||
return Task.FromResult(obs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an alarm notification based on the provided patient observation, associated name, and optional alarm code.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation that triggers the alarm.</param>
|
||||
/// <param name="name">The name associated with the alarm being sent.</param>
|
||||
/// <param name="code">The optional alarm code that categorizes the type of alarm; may be null when no specific code applies.</param>
|
||||
/// <exception cref="NotImplementedException">Thrown in all cases, as the method has not been implemented yet.</exception>
|
||||
public Task SendAlarm(PatientObservation obs, string name, AlarmEnum.Name? code)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,10 @@ using MongoDB.Bson;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the concrete implementation of the <see cref="IDeviceService"/> contract,
|
||||
/// handling device-related service operations defined by the interface.
|
||||
/// </summary>
|
||||
public class DeviceService : IDeviceService
|
||||
{
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
@@ -34,213 +38,263 @@ public class DeviceService : IDeviceService
|
||||
_alarmService = alarmService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="DeviceDto"/> into a <see cref="Device"/> entity by mapping its properties. Applies a fallback to an empty list when <c>PointOfCareIds</c> is null, and to a new <see cref="DeviceSettings"/> instance when <c>Settings</c> is null.
|
||||
/// </summary>
|
||||
/// <param name="dto">The data transfer object containing the device information to convert.</param>
|
||||
/// <returns>A new <see cref="Device"/> entity populated with the values from the supplied DTO.</returns>
|
||||
public Device ToEntity(DeviceDto dto)
|
||||
{
|
||||
return new Device()
|
||||
{
|
||||
DeviceType = dto.DeviceType,
|
||||
MacAddr = dto.MacAddr,
|
||||
SerialNumber = dto.SerialNumber,
|
||||
Name = dto.Name,
|
||||
Battery = dto.Battery,
|
||||
Color = dto.Color,
|
||||
Connected = dto.Connected,
|
||||
Ready = dto.Ready,
|
||||
Uuid = dto.Uuid,
|
||||
Key = dto.Key,
|
||||
CreatedAt = dto.CreatedAt,
|
||||
UpdatedAt = dto.UpdatedAt,
|
||||
PointOfCareIds = dto.PointOfCareIds ?? new List<ObjectId>(),
|
||||
Settings = dto.Settings ?? new DeviceSettings()
|
||||
};
|
||||
}
|
||||
return new Device()
|
||||
{
|
||||
DeviceType = dto.DeviceType,
|
||||
MacAddr = dto.MacAddr,
|
||||
SerialNumber = dto.SerialNumber,
|
||||
Name = dto.Name,
|
||||
Battery = dto.Battery,
|
||||
Color = dto.Color,
|
||||
Connected = dto.Connected,
|
||||
Ready = dto.Ready,
|
||||
Uuid = dto.Uuid,
|
||||
Key = dto.Key,
|
||||
CreatedAt = dto.CreatedAt,
|
||||
UpdatedAt = dto.UpdatedAt,
|
||||
PointOfCareIds = dto.PointOfCareIds ?? new List<ObjectId>(),
|
||||
Settings = dto.Settings ?? new DeviceSettings()
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new device entity from the provided DTO, sets the creation and update timestamps to the current UTC time, and persists it via the device repository.
|
||||
/// </summary>
|
||||
/// <param name="deviceDto">The data transfer object containing the device information to be mapped and stored.</param>
|
||||
/// <returns>The created <see cref="Device"/> entity after successful insertion, or <see langword="null"/> if the device could not be created.</returns>
|
||||
public async Task<Device?> Create(DeviceDto deviceDto)
|
||||
{
|
||||
var device = ToEntity(deviceDto);
|
||||
device.CreatedAt = DateTime.UtcNow;
|
||||
device.UpdatedAt = DateTime.UtcNow;
|
||||
await _deviceRepository.InsertOneAsync(device);
|
||||
return device;
|
||||
}
|
||||
{
|
||||
var device = ToEntity(deviceDto);
|
||||
device.CreatedAt = DateTime.UtcNow;
|
||||
device.UpdatedAt = DateTime.UtcNow;
|
||||
await _deviceRepository.InsertOneAsync(device);
|
||||
return device;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an object identified by the specified <paramref name="objectId"/> by delegating to the device repository.
|
||||
/// Returns <c>true</c> when the repository's delete operation yields a non-null result, and <c>false</c> when the result is <c>null</c> (e.g., the object was not found or could not be deleted).
|
||||
/// </summary>
|
||||
/// <param name="objectId">The unique identifier of the object to delete.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the object was deleted; otherwise, <c>false</c>.</returns>
|
||||
public async Task<bool> Delete(ObjectId objectId)
|
||||
{
|
||||
return await _deviceRepository.DeleteAsync(objectId) != null;
|
||||
}
|
||||
{
|
||||
return await _deviceRepository.DeleteAsync(objectId) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing device by mapping the provided DTO to a device entity, setting the update timestamp, and persisting the changes through the repository.
|
||||
/// </summary>
|
||||
/// <param name="deviceDto">The data transfer object containing the updated device information.</param>
|
||||
/// <returns>The updated <see cref="Device"/> entity, or <c>null</c> if no device is returned.</returns>
|
||||
public async Task<Device?> Update(DeviceDto deviceDto)
|
||||
{
|
||||
var device = ToEntity(deviceDto);
|
||||
device.UpdatedAt = DateTime.UtcNow;
|
||||
await _deviceRepository.UpdateOneAsync(device.Id, device);
|
||||
return device;
|
||||
}
|
||||
{
|
||||
var device = ToEntity(deviceDto);
|
||||
device.UpdatedAt = DateTime.UtcNow;
|
||||
await _deviceRepository.UpdateOneAsync(device.Id, device);
|
||||
return device;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes an incoming device event by locating an existing device or creating a new one, then handling device-type-specific logic. Looks up the device using the MAC address, serial number, UUID, or key in that order, falling back to creating a new record when no match is found. When the device is a button, additional button management logic is invoked.
|
||||
/// </summary>
|
||||
/// <param name="deviceDto">The data transfer object containing the device information from the event, used for lookup and creation.</param>
|
||||
/// <returns>The existing or newly created <see cref="Device"/> associated with the event.</returns>
|
||||
public async Task<Device?> ReceiveEvent(DeviceDto deviceDto)
|
||||
{
|
||||
Device? deviceExist = null;
|
||||
if (!string.IsNullOrEmpty(deviceDto.MacAddr))
|
||||
{
|
||||
deviceExist = await _deviceRepository.FindByMacAddr(deviceDto.MacAddr);
|
||||
Device? deviceExist = null;
|
||||
if (!string.IsNullOrEmpty(deviceDto.MacAddr))
|
||||
{
|
||||
deviceExist = await _deviceRepository.FindByMacAddr(deviceDto.MacAddr);
|
||||
}
|
||||
|
||||
if (deviceExist == null && deviceDto.SerialNumber != null)
|
||||
{
|
||||
deviceExist = await _deviceRepository.FindBySerialNumber(deviceDto.SerialNumber);
|
||||
}
|
||||
|
||||
if (deviceExist == null && deviceDto.Uuid != null)
|
||||
{
|
||||
deviceExist = await _deviceRepository.FindByUuid(deviceDto.Uuid);
|
||||
}
|
||||
|
||||
if (deviceExist == null && deviceDto.Key != null)
|
||||
{
|
||||
deviceExist = await _deviceRepository.FindByKey(deviceDto.Key);
|
||||
}
|
||||
|
||||
if (deviceExist == null)
|
||||
{
|
||||
deviceExist = ToEntity(deviceDto);
|
||||
deviceExist.CreatedAt = DateTime.UtcNow;
|
||||
deviceExist.UpdatedAt = DateTime.UtcNow;
|
||||
await _deviceRepository.InsertOneAsync(deviceExist);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _deviceRepository.UpdateDeviceStats(deviceExist.Id, deviceDto);
|
||||
}
|
||||
|
||||
switch (deviceDto.DeviceType)
|
||||
{
|
||||
case DeviceType.Unknown:
|
||||
break;
|
||||
case DeviceType.Button:
|
||||
await ManageDeviceButton(deviceExist, deviceDto);
|
||||
break;
|
||||
}
|
||||
|
||||
return deviceExist;
|
||||
}
|
||||
|
||||
if (deviceExist == null && deviceDto.SerialNumber != null)
|
||||
{
|
||||
deviceExist = await _deviceRepository.FindBySerialNumber(deviceDto.SerialNumber);
|
||||
}
|
||||
|
||||
if (deviceExist == null && deviceDto.Uuid != null)
|
||||
{
|
||||
deviceExist = await _deviceRepository.FindByUuid(deviceDto.Uuid);
|
||||
}
|
||||
|
||||
if (deviceExist == null && deviceDto.Key != null)
|
||||
{
|
||||
deviceExist = await _deviceRepository.FindByKey(deviceDto.Key);
|
||||
}
|
||||
|
||||
if (deviceExist == null)
|
||||
{
|
||||
deviceExist = ToEntity(deviceDto);
|
||||
deviceExist.CreatedAt = DateTime.UtcNow;
|
||||
deviceExist.UpdatedAt = DateTime.UtcNow;
|
||||
await _deviceRepository.InsertOneAsync(deviceExist);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _deviceRepository.UpdateDeviceStats(deviceExist.Id, deviceDto);
|
||||
}
|
||||
|
||||
switch (deviceDto.DeviceType)
|
||||
{
|
||||
case DeviceType.Unknown:
|
||||
break;
|
||||
case DeviceType.Button:
|
||||
await ManageDeviceButton(deviceExist, deviceDto);
|
||||
break;
|
||||
}
|
||||
|
||||
return deviceExist;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles device button actions by dispatching the configured action type (sending an observation or alarm) when both the device's configured action and the received click event are present.
|
||||
/// </summary>
|
||||
/// <param name="deviceExist">The existing device whose configured action settings determine which action to execute.</param>
|
||||
/// <param name="deviceDto">The incoming device event payload providing the click type that triggers the action.</param>
|
||||
private async Task ManageDeviceButton(Device deviceExist, DeviceDto deviceDto)
|
||||
{
|
||||
if (deviceExist.Settings?.Action?.Type != null && deviceDto.Event?.ClickType != null)
|
||||
{
|
||||
switch (deviceExist.Settings.Action.Type)
|
||||
if (deviceExist.Settings?.Action?.Type != null && deviceDto.Event?.ClickType != null)
|
||||
{
|
||||
case DeviceActionType.SendObs:
|
||||
await SendObservationOnAction(deviceExist.Settings.Action, deviceDto.Event.ClickType.Value, deviceExist.PointOfCareIds);
|
||||
break;
|
||||
case DeviceActionType.SendAlarm:
|
||||
await SendAlarmOnAction(deviceExist.Settings.Action, deviceDto.Event.ClickType.Value, deviceExist.PointOfCareIds);
|
||||
break;
|
||||
switch (deviceExist.Settings.Action.Type)
|
||||
{
|
||||
case DeviceActionType.SendObs:
|
||||
await SendObservationOnAction(deviceExist.Settings.Action, deviceDto.Event.ClickType.Value, deviceExist.PointOfCareIds);
|
||||
break;
|
||||
case DeviceActionType.SendAlarm:
|
||||
await SendAlarmOnAction(deviceExist.Settings.Action, deviceDto.Event.ClickType.Value, deviceExist.PointOfCareIds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a patient observation alarm based on a device action and the type of click event that triggered it.
|
||||
/// The alarm value is selected from the device action's configuration according to the click type
|
||||
/// (SingleClick, DoubleClick, or Hold) and is dispatched for each point of care that has an associated patient.
|
||||
/// The method exits early when the configuration observation cannot be found or when the action has no value defined for the current click type.
|
||||
/// </summary>
|
||||
/// <param name="settingsAction">The device action containing the configuration observation and the per-click-type alarm values to use.</param>
|
||||
/// <param name="eventClickType">The click event that triggered the action, which determines which value from the device action is sent.</param>
|
||||
/// <param name="deviceExistPointOfCareIds">The list of point of care identifiers whose patients should receive the alarm.</param>
|
||||
private async Task SendAlarmOnAction(
|
||||
DeviceAction settingsAction,
|
||||
ClickType eventClickType,
|
||||
List<ObjectId> deviceExistPointOfCareIds)
|
||||
{
|
||||
var configObs = await _configObservationService.GetConfigById(settingsAction.ConfigObservationId);
|
||||
if(configObs == null) return;
|
||||
var obsData = new ObservationData
|
||||
DeviceAction settingsAction,
|
||||
ClickType eventClickType,
|
||||
List<ObjectId> deviceExistPointOfCareIds)
|
||||
{
|
||||
Code = configObs.Code,
|
||||
CodingSystem = configObs.CodingSystem,
|
||||
Time = DateTime.UtcNow,
|
||||
Text = configObs.Name,
|
||||
};
|
||||
var obs = new PatientObservationAlarm
|
||||
{
|
||||
InactivationState = new InactivationState{ Visual = AlarmEnum.AudioVideoState.Enabled, Audio = AlarmEnum.AudioVideoState.Enabled},
|
||||
MessageTime = DateTime.UtcNow,
|
||||
Persist = true,
|
||||
Code = obsData.Code,
|
||||
CodingSystem = obsData.CodingSystem,
|
||||
Name = configObs.Name,
|
||||
Time = DateTime.UtcNow
|
||||
};
|
||||
foreach (var pocId in deviceExistPointOfCareIds)
|
||||
{
|
||||
var data = await _pointOfCareService.GetInfo(pocId, null, true);
|
||||
if (data != null && data.Patient?.Id != null)
|
||||
var configObs = await _configObservationService.GetConfigById(settingsAction.ConfigObservationId);
|
||||
if(configObs == null) return;
|
||||
var obsData = new ObservationData
|
||||
{
|
||||
obs.PatientId = data.Patient.Id;
|
||||
obs.Patient = data.Patient;
|
||||
switch (eventClickType)
|
||||
Code = configObs.Code,
|
||||
CodingSystem = configObs.CodingSystem,
|
||||
Time = DateTime.UtcNow,
|
||||
Text = configObs.Name,
|
||||
};
|
||||
var obs = new PatientObservationAlarm
|
||||
{
|
||||
InactivationState = new InactivationState{ Visual = AlarmEnum.AudioVideoState.Enabled, Audio = AlarmEnum.AudioVideoState.Enabled},
|
||||
MessageTime = DateTime.UtcNow,
|
||||
Persist = true,
|
||||
Code = obsData.Code,
|
||||
CodingSystem = obsData.CodingSystem,
|
||||
Name = configObs.Name,
|
||||
Time = DateTime.UtcNow
|
||||
};
|
||||
foreach (var pocId in deviceExistPointOfCareIds)
|
||||
{
|
||||
var data = await _pointOfCareService.GetInfo(pocId, null, true);
|
||||
if (data != null && data.Patient?.Id != null)
|
||||
{
|
||||
case ClickType.SingleClick:
|
||||
if(settingsAction.ValueOnSingleClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnSingleClick;
|
||||
break;
|
||||
case ClickType.DoubleClick:
|
||||
if(settingsAction.ValueOnDoubleClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnDoubleClick;
|
||||
break;
|
||||
case ClickType.Hold:
|
||||
if(settingsAction.ValueOnHoldClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnHoldClick;
|
||||
break;
|
||||
obs.PatientId = data.Patient.Id;
|
||||
obs.Patient = data.Patient;
|
||||
switch (eventClickType)
|
||||
{
|
||||
case ClickType.SingleClick:
|
||||
if(settingsAction.ValueOnSingleClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnSingleClick;
|
||||
break;
|
||||
case ClickType.DoubleClick:
|
||||
if(settingsAction.ValueOnDoubleClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnDoubleClick;
|
||||
break;
|
||||
case ClickType.Hold:
|
||||
if(settingsAction.ValueOnHoldClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnHoldClick;
|
||||
break;
|
||||
}
|
||||
// Process Obs on service
|
||||
await _alarmService.ProcessAlarmObservations(new List<PatientObservationAlarm>{obs},new List<PatientObservation>(){new (){Name = configObs.Name, Value = obs.Value, Code = obsData.Code, CodingSystem = obsData.CodingSystem}}, data.Patient, DateTime.UtcNow, obsData);
|
||||
}
|
||||
// Process Obs on service
|
||||
await _alarmService.ProcessAlarmObservations(new List<PatientObservationAlarm>{obs},new List<PatientObservation>(){new (){Name = configObs.Name, Value = obs.Value, Code = obsData.Code, CodingSystem = obsData.CodingSystem}}, data.Patient, DateTime.UtcNow, obsData);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a patient observation derived from the configuration linked to a device action, applying the value
|
||||
/// associated with the specified click type (single, double, or hold) for each point of care patient found.
|
||||
/// The method short-circuits when the configuration observation is missing or when no value is defined for
|
||||
/// the given click type.
|
||||
/// </summary>
|
||||
/// <param name="settingsAction">The device action whose associated configuration observation and per-click values drive the observation payload.</param>
|
||||
/// <param name="eventClickType">The type of click event that triggered the action; selects which value (single, double, or hold) is assigned to the observation.</param>
|
||||
/// <param name="deviceExistPointOfCareIds">The list of point of care identifiers whose resolved patients will receive the generated observation.</param>
|
||||
/// <returns>A task that represents the asynchronous send operation; no meaningful business result is returned.</returns>
|
||||
private async Task SendObservationOnAction(
|
||||
DeviceAction settingsAction,
|
||||
ClickType eventClickType,
|
||||
List<ObjectId> deviceExistPointOfCareIds)
|
||||
{
|
||||
var configObs = await _configObservationService.GetConfigById(settingsAction.ConfigObservationId);
|
||||
if(configObs == null) return;
|
||||
var obsData = new ObservationData
|
||||
DeviceAction settingsAction,
|
||||
ClickType eventClickType,
|
||||
List<ObjectId> deviceExistPointOfCareIds)
|
||||
{
|
||||
Code = configObs.Code,
|
||||
CodingSystem = configObs.CodingSystem,
|
||||
Time = DateTime.UtcNow,
|
||||
Text = configObs.Name,
|
||||
};
|
||||
var obs = new PatientObservation
|
||||
{
|
||||
MessageTime = DateTime.UtcNow,
|
||||
Persist = true,
|
||||
Code = obsData.Code,
|
||||
CodingSystem = obsData.CodingSystem,
|
||||
Name = configObs.Name,
|
||||
Time = DateTime.UtcNow
|
||||
};
|
||||
foreach (var pocId in deviceExistPointOfCareIds)
|
||||
{
|
||||
var data = await _pointOfCareService.GetInfo(pocId, null, true);
|
||||
if (data != null && data.Patient?.Id != null)
|
||||
var configObs = await _configObservationService.GetConfigById(settingsAction.ConfigObservationId);
|
||||
if(configObs == null) return;
|
||||
var obsData = new ObservationData
|
||||
{
|
||||
obs.PatientId = data.Patient.Id;
|
||||
obs.Patient = data.Patient;
|
||||
switch (eventClickType)
|
||||
Code = configObs.Code,
|
||||
CodingSystem = configObs.CodingSystem,
|
||||
Time = DateTime.UtcNow,
|
||||
Text = configObs.Name,
|
||||
};
|
||||
var obs = new PatientObservation
|
||||
{
|
||||
MessageTime = DateTime.UtcNow,
|
||||
Persist = true,
|
||||
Code = obsData.Code,
|
||||
CodingSystem = obsData.CodingSystem,
|
||||
Name = configObs.Name,
|
||||
Time = DateTime.UtcNow
|
||||
};
|
||||
foreach (var pocId in deviceExistPointOfCareIds)
|
||||
{
|
||||
var data = await _pointOfCareService.GetInfo(pocId, null, true);
|
||||
if (data != null && data.Patient?.Id != null)
|
||||
{
|
||||
case ClickType.SingleClick:
|
||||
if(settingsAction.ValueOnSingleClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnSingleClick;
|
||||
break;
|
||||
case ClickType.DoubleClick:
|
||||
if(settingsAction.ValueOnDoubleClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnDoubleClick;
|
||||
break;
|
||||
case ClickType.Hold:
|
||||
if(settingsAction.ValueOnHoldClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnHoldClick;
|
||||
break;
|
||||
obs.PatientId = data.Patient.Id;
|
||||
obs.Patient = data.Patient;
|
||||
switch (eventClickType)
|
||||
{
|
||||
case ClickType.SingleClick:
|
||||
if(settingsAction.ValueOnSingleClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnSingleClick;
|
||||
break;
|
||||
case ClickType.DoubleClick:
|
||||
if(settingsAction.ValueOnDoubleClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnDoubleClick;
|
||||
break;
|
||||
case ClickType.Hold:
|
||||
if(settingsAction.ValueOnHoldClick == null) return;
|
||||
obs.Value = settingsAction.ValueOnHoldClick;
|
||||
break;
|
||||
}
|
||||
// Process Obs on service
|
||||
_observationService.ProcessObservations(new List<PatientObservation>{obs}, data.Patient, DateTime.UtcNow, obsData);
|
||||
}
|
||||
// Process Obs on service
|
||||
_observationService.ProcessObservations(new List<PatientObservation>{obs}, data.Patient, DateTime.UtcNow, obsData);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,10 @@ using MongoDB.Driver;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the implementation of the <see cref="IDiagnosisService"/> contract,
|
||||
/// offering diagnosis-related operations as defined by the interface.
|
||||
/// </summary>
|
||||
public class DiagnosisService : IDiagnosisService
|
||||
{
|
||||
private readonly ILocalAuditService _auditService;
|
||||
@@ -81,283 +85,366 @@ public class DiagnosisService : IDiagnosisService
|
||||
// await _clientMessageService.SendAsync(subscriber.Id, OperationType.Diagnosis, diagnosis);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Archives the specified patient by delegating to the archival routine keyed by the patient's identifier.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to be archived. Its <c>Id</c> is used to locate the record to archive.</param>
|
||||
public async Task Archive(Patient patient)
|
||||
{
|
||||
await ArchiveByPatientId(patient.Id);
|
||||
}
|
||||
{
|
||||
await ArchiveByPatientId(patient.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives all diagnoses associated with the specified patient by copying them to the diagnosis archive repository and then deleting the original records.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the patient whose diagnoses will be archived.</param>
|
||||
public async Task ArchiveByPatientId(ObjectId id)
|
||||
{
|
||||
_logger.LogDebug("Archive Diagnoses by Patient Id {id}", id);
|
||||
using (var cursor = await FindByPatientIdAsync(id))
|
||||
{
|
||||
while (await cursor.MoveNextAsync())
|
||||
foreach (var current in cursor.Current)
|
||||
await _diagnosisArchiveRepository.InsertOneAsync(current);
|
||||
_logger.LogDebug("Archive Diagnoses by Patient Id {id}", id);
|
||||
using (var cursor = await FindByPatientIdAsync(id))
|
||||
{
|
||||
while (await cursor.MoveNextAsync())
|
||||
foreach (var current in cursor.Current)
|
||||
await _diagnosisArchiveRepository.InsertOneAsync(current);
|
||||
}
|
||||
|
||||
await DeleteByPatientId(id);
|
||||
}
|
||||
|
||||
await DeleteByPatientId(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all diagnoses associated with the specified patient identifier and records an audit log entry capturing the previous state of the records.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose diagnoses should be deleted.</param>
|
||||
public async Task DeleteByPatientId(ObjectId id)
|
||||
{
|
||||
_logger.LogDebug("Delete Diagnoses by Patient Id {id}", id);
|
||||
var oldPatient = await _diagnosisRepository.GetByPatient(id);
|
||||
await _diagnosisRepository.DeleteByPatientId(id);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, null);
|
||||
}
|
||||
{
|
||||
_logger.LogDebug("Delete Diagnoses by Patient Id {id}", id);
|
||||
var oldPatient = await _diagnosisRepository.GetByPatient(id);
|
||||
await _diagnosisRepository.DeleteByPatientId(id);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the list of patient diagnoses associated with the specified patient identifier by delegating to the diagnosis repository.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose diagnoses are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientDiagnosis"/> records for the given patient.</returns>
|
||||
public async Task<List<PatientDiagnosis>> GetByPatientId(ObjectId patientId)
|
||||
{
|
||||
var diagnosis = await _diagnosisRepository.GetByPatient(patientId);
|
||||
|
||||
return diagnosis;
|
||||
}
|
||||
{
|
||||
var diagnosis = await _diagnosisRepository.GetByPatient(patientId);
|
||||
|
||||
return diagnosis;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves the specified API request by delegating to an overload that accepts a secondary parameter, which is passed as null.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to save.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest, null); }));
|
||||
}
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest, null); }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves the specified API request by delegating to the underlying save operation with a null secondary parameter.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request instance to persist.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest, null); }));
|
||||
}
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest, null); }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a diagnosis observation from an API request and persists it as a patient diagnosis. Maps SNOMED-coded observation values to diagnosis properties (description, label, code, state, category, start/end time), falling back to the current date when the observation time is missing, and skipping processing if the observations collection is null.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the observation codes, values, message time, and optional observation time used to build the diagnosis.</param>
|
||||
/// <param name="patient">The patient associated with the diagnosis, whose identifier is assigned to the new <see cref="PatientDiagnosis"/>.</param>
|
||||
/// <returns>A task that represents the asynchronous insertion of the resulting <see cref="PatientDiagnosis"/>.</returns>
|
||||
public async Task ProcessDiagnosisObservation(ApiRequest apiRequest, Patient patient)
|
||||
{
|
||||
_logger.LogDebug("INSERT DiagnosisObservation from obsservation");
|
||||
|
||||
var time = apiRequest.ObservationData?.Time;
|
||||
if (time == null)
|
||||
_logger.LogWarning("Processing diagnosis without time. Set Datetime now {DateTimeNow}. ", DateTime.Now);
|
||||
|
||||
var obs = new PatientDiagnosis
|
||||
{
|
||||
CodingSystem = _diagnosisSystem,
|
||||
Time = time ?? DateTime.Now,
|
||||
PatientId = patient.Id,
|
||||
MessageTime = apiRequest.MessageTime
|
||||
};
|
||||
|
||||
if (apiRequest.Observations == null)
|
||||
{
|
||||
_logger.LogError("ApiRequest Observations null. ");
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i <= apiRequest.Observations.Count - 1; i++)
|
||||
{
|
||||
var value = apiRequest.Observations[i].Value;
|
||||
|
||||
var strValue = value.ToString() ?? "null";
|
||||
|
||||
switch (apiRequest.Observations[i].Code)
|
||||
_logger.LogDebug("INSERT DiagnosisObservation from obsservation");
|
||||
|
||||
var time = apiRequest.ObservationData?.Time;
|
||||
if (time == null)
|
||||
_logger.LogWarning("Processing diagnosis without time. Set Datetime now {DateTimeNow}. ", DateTime.Now);
|
||||
|
||||
var obs = new PatientDiagnosis
|
||||
{
|
||||
case "272099008":
|
||||
obs.Description = strValue;
|
||||
break;
|
||||
|
||||
case "1000000013":
|
||||
obs.Label = strValue;
|
||||
break;
|
||||
|
||||
case "1000000014":
|
||||
obs.Code = strValue;
|
||||
break;
|
||||
|
||||
case "394731006":
|
||||
obs.State = strValue;
|
||||
break;
|
||||
|
||||
case "272125009":
|
||||
obs.Category = strValue;
|
||||
break;
|
||||
|
||||
case "398201009":
|
||||
if (DateTime.TryParse(apiRequest.Observations[i].Value.ToString(), out var startTime))
|
||||
obs.StartTime = startTime;
|
||||
break;
|
||||
|
||||
case "397898000":
|
||||
if (DateTime.TryParse(apiRequest.Observations[i].Value.ToString(), out var endTime))
|
||||
obs.EndTime = endTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await InsertDiagnosis(obs);
|
||||
}
|
||||
|
||||
public async Task SaveRequest(ApiRequest apiRequest, Patient? patient)
|
||||
{
|
||||
if (patient == null)
|
||||
{
|
||||
_logger.LogDebug("message:ApiRequest Diagnosis");
|
||||
if (string.IsNullOrEmpty(apiRequest.PatientNumber) &&
|
||||
string.IsNullOrEmpty(apiRequest.Location?.UnitName))
|
||||
CodingSystem = _diagnosisSystem,
|
||||
Time = time ?? DateTime.Now,
|
||||
PatientId = patient.Id,
|
||||
MessageTime = apiRequest.MessageTime
|
||||
};
|
||||
|
||||
if (apiRequest.Observations == null)
|
||||
{
|
||||
_logger.LogDebug("person and PointOfCare are nulls");
|
||||
throw new ApiRequestException("person and PointOfCare are nulls");
|
||||
_logger.LogError("ApiRequest Observations null. ");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug("patientNumber: {apiRequestpatientNumber} location: {apiRequestlocation}",
|
||||
apiRequest.PatientNumber, apiRequest.Location);
|
||||
_logger.LogDebug("RequestType: {apiRequesttype}", apiRequest.Type);
|
||||
|
||||
patient = await _patientService.Value.FindPatientByApiRequest(apiRequest);
|
||||
}
|
||||
|
||||
if (patient == null)
|
||||
{
|
||||
// NO PATIENTS OR LOCATIONS WERE FOUND
|
||||
_logger.LogWarning(
|
||||
"Observation for patient: {apiRequestpatientNumber} with location: {apiRequestlocation} not found, ignoring",
|
||||
apiRequest.PatientNumber, apiRequest.Location);
|
||||
return;
|
||||
}
|
||||
|
||||
var unitConfig = await _unitService.FindById(patient.UnitId);
|
||||
if (!Hl7Utils.ManageAutoAdt(unitConfig, null, _logger, "ORU Diagnosis")) return;
|
||||
|
||||
switch (apiRequest.Type)
|
||||
{
|
||||
//* ORU_R01 - Unsolicited transmission of an observation message
|
||||
//* ORU_R40 - Unsolicited transmission of an alert observation message
|
||||
|
||||
case "ORU_R01":
|
||||
case "ORU_R40":
|
||||
|
||||
// OBSERVATIONS
|
||||
if (apiRequest.Observation != null && apiRequest.Observations?.FirstOrDefault() == null)
|
||||
apiRequest.Observations = [apiRequest.Observation];
|
||||
|
||||
if (apiRequest.Observations != null)
|
||||
|
||||
for (var i = 0; i <= apiRequest.Observations.Count - 1; i++)
|
||||
{
|
||||
var value = apiRequest.Observations[i].Value;
|
||||
|
||||
var strValue = value.ToString() ?? "null";
|
||||
|
||||
switch (apiRequest.Observations[i].Code)
|
||||
{
|
||||
var obrcode = apiRequest.ObservationData?.Code;
|
||||
|
||||
if (apiRequest.ObservationData?.Value != null)
|
||||
apiRequest.Observations.Add(new PatientObservation
|
||||
{ Value = apiRequest.ObservationData.Value });
|
||||
|
||||
if (obrcode != null && _diagnosisCode.Contains(obrcode))
|
||||
_ = ProcessDiagnosisObservation(apiRequest, patient);
|
||||
case "272099008":
|
||||
obs.Description = strValue;
|
||||
break;
|
||||
|
||||
case "1000000013":
|
||||
obs.Label = strValue;
|
||||
break;
|
||||
|
||||
case "1000000014":
|
||||
obs.Code = strValue;
|
||||
break;
|
||||
|
||||
case "394731006":
|
||||
obs.State = strValue;
|
||||
break;
|
||||
|
||||
case "272125009":
|
||||
obs.Category = strValue;
|
||||
break;
|
||||
|
||||
case "398201009":
|
||||
if (DateTime.TryParse(apiRequest.Observations[i].Value.ToString(), out var startTime))
|
||||
obs.StartTime = startTime;
|
||||
break;
|
||||
|
||||
case "397898000":
|
||||
if (DateTime.TryParse(apiRequest.Observations[i].Value.ToString(), out var endTime))
|
||||
obs.EndTime = endTime;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
default:
|
||||
_logger.LogWarning("ApiRequest type {apiRequesttype} sis not valid for Diagnosis",
|
||||
apiRequest.Type);
|
||||
throw new ApiRequestException("ApiRequest type " + apiRequest.Type +
|
||||
" is not valid for Diagnosis");
|
||||
}
|
||||
|
||||
await InsertDiagnosis(obs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes an incoming API request for diagnosis-related observations, resolving the patient from the request when not supplied. Validates that at least one of the patient number or location unit name is provided, handles ORU_R01 and ORU_R40 observation messages, and triggers diagnosis observation processing when the observation code is recognized.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the type, patient identifiers, location, and observation data to be processed.</param>
|
||||
/// <param name="patient">An optional pre-resolved patient; when null, the patient is resolved via the patient service using the request data.</param>
|
||||
/// <exception cref="ApiRequestException">Thrown when both the patient number and the location unit name are missing from the request, or when the request type is not valid for diagnosis processing.</exception>
|
||||
public async Task SaveRequest(ApiRequest apiRequest, Patient? patient)
|
||||
{
|
||||
if (patient == null)
|
||||
{
|
||||
_logger.LogDebug("message:ApiRequest Diagnosis");
|
||||
if (string.IsNullOrEmpty(apiRequest.PatientNumber) &&
|
||||
string.IsNullOrEmpty(apiRequest.Location?.UnitName))
|
||||
{
|
||||
_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);
|
||||
_logger.LogDebug("RequestType: {apiRequesttype}", apiRequest.Type);
|
||||
|
||||
patient = await _patientService.Value.FindPatientByApiRequest(apiRequest);
|
||||
}
|
||||
|
||||
if (patient == null)
|
||||
{
|
||||
// NO PATIENTS OR LOCATIONS WERE FOUND
|
||||
_logger.LogWarning(
|
||||
"Observation for patient: {apiRequestpatientNumber} with location: {apiRequestlocation} not found, ignoring",
|
||||
apiRequest.PatientNumber, apiRequest.Location);
|
||||
return;
|
||||
}
|
||||
|
||||
var unitConfig = await _unitService.FindById(patient.UnitId);
|
||||
if (!Hl7Utils.ManageAutoAdt(unitConfig, null, _logger, "ORU Diagnosis")) return;
|
||||
|
||||
switch (apiRequest.Type)
|
||||
{
|
||||
//* ORU_R01 - Unsolicited transmission of an observation message
|
||||
//* ORU_R40 - Unsolicited transmission of an alert observation message
|
||||
|
||||
case "ORU_R01":
|
||||
case "ORU_R40":
|
||||
|
||||
// OBSERVATIONS
|
||||
if (apiRequest.Observation != null && apiRequest.Observations?.FirstOrDefault() == null)
|
||||
apiRequest.Observations = [apiRequest.Observation];
|
||||
|
||||
if (apiRequest.Observations != null)
|
||||
{
|
||||
var obrcode = apiRequest.ObservationData?.Code;
|
||||
|
||||
if (apiRequest.ObservationData?.Value != null)
|
||||
apiRequest.Observations.Add(new PatientObservation
|
||||
{ Value = apiRequest.ObservationData.Value });
|
||||
|
||||
if (obrcode != null && _diagnosisCode.Contains(obrcode))
|
||||
_ = ProcessDiagnosisObservation(apiRequest, patient);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
default:
|
||||
_logger.LogWarning("ApiRequest type {apiRequesttype} sis not valid for Diagnosis",
|
||||
apiRequest.Type);
|
||||
throw new ApiRequestException("ApiRequest type " + apiRequest.Type +
|
||||
" is not valid for Diagnosis");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a list of patient diagnoses by associating each entry with the specified patient and message time, then inserting them as diagnosis observations.
|
||||
/// </summary>
|
||||
/// <param name="diagnosis">The list of patient diagnoses to be processed and inserted.</param>
|
||||
/// <param name="patient">The patient whose identifier is assigned to each diagnosis entry.</param>
|
||||
/// <param name="messageTime">The timestamp assigned to each diagnosis entry during processing.</param>
|
||||
public async Task ProcessDiagnosis(List<PatientDiagnosis> diagnosis, Patient patient, DateTime messageTime)
|
||||
{
|
||||
_logger.LogDebug("INSERT DiagnosisObservation from diagnosis");
|
||||
|
||||
foreach (var d in diagnosis)
|
||||
{
|
||||
d.PatientId = patient.Id;
|
||||
d.Time = messageTime;
|
||||
await InsertDiagnosis(d);
|
||||
_logger.LogDebug("INSERT DiagnosisObservation from diagnosis");
|
||||
|
||||
foreach (var d in diagnosis)
|
||||
{
|
||||
d.PatientId = patient.Id;
|
||||
d.Time = messageTime;
|
||||
await InsertDiagnosis(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates many diagnosis records, replacing the <paramref name="oldId"/> with the new <paramref name="id"/> for the specified <paramref name="nameId"/> field, by delegating to the diagnosis repository.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name of the identifier field used to locate the records to update.</param>
|
||||
/// <param name="id">The new ObjectId to assign to the matching records.</param>
|
||||
/// <param name="oldId">The existing ObjectId to be replaced in the matching records.</param>
|
||||
public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
|
||||
{
|
||||
await _diagnosisRepository.UpdateManyObjectId(nameId, id, oldId);
|
||||
}
|
||||
{
|
||||
await _diagnosisRepository.UpdateManyObjectId(nameId, id, oldId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all diagnoses associated with the specified patient identifier from the diagnosis repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose diagnoses are being requested.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientDiagnosis"/> records for the given patient.</returns>
|
||||
public async Task<List<PatientDiagnosis>> GetByPatient(ObjectId id)
|
||||
{
|
||||
return await _diagnosisRepository.GetByPatient(id);
|
||||
}
|
||||
{
|
||||
return await _diagnosisRepository.GetByPatient(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a patient diagnosis into the repository after mapping it to the underlying data model, and broadcasts the stored record.
|
||||
/// If the diagnosis cannot be mapped (returns null), the insert and broadcast operations are skipped.
|
||||
/// </summary>
|
||||
/// <param name="diagnosis">The patient diagnosis to insert.</param>
|
||||
public async Task Insert(PatientDiagnosis diagnosis)
|
||||
{
|
||||
_logger.LogDebug("Insert {diagnosis}", diagnosis);
|
||||
var diag = await MapDiagnosis(diagnosis);
|
||||
if (diag != null)
|
||||
{
|
||||
await _diagnosisRepository.InsertOneAsync(diag);
|
||||
await SendBroadcast(diag);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<PatientDiagnosis?> MapDiagnosis(PatientDiagnosis diagnosis)
|
||||
{
|
||||
var diagnosis2 = await _calculatedObservations.Value.Map(diagnosis);
|
||||
if (diagnosis2 == null) _logger.LogDebug("Mapping {diagnosis}: Ignored", diagnosis2);
|
||||
|
||||
return diagnosis2;
|
||||
}
|
||||
|
||||
private async Task SendBroadcast(PatientDiagnosis diagnosis)
|
||||
{
|
||||
var patient = await _patientService.Value.FindById(diagnosis.PatientId);
|
||||
if (patient == null) return;
|
||||
|
||||
var displaySubscribers = _subscribersService.GetSubscribers().Where(s =>
|
||||
!s.Locations.IsNullOrEmpty() && s.Locations.Any(c =>
|
||||
c.UnitName == patient.Location.UnitName &&
|
||||
c.Bed == patient.Location.Bed &&
|
||||
c.Room == patient.Location.Room
|
||||
)).ToList();
|
||||
|
||||
displaySubscribers.ForEach(Action);
|
||||
return;
|
||||
|
||||
void Action(WsSubscriber subscriber)
|
||||
{
|
||||
_clientMessageService.SendAsync(subscriber.Id, OperationType.Diagnosis, diagnosis);
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<PatientDiagnosis>> FindByPatientIdAsync(ObjectId patientId)
|
||||
{
|
||||
return _diagnosisRepository.FindByPatientIdAsync(patientId);
|
||||
}
|
||||
|
||||
public async Task InsertDiagnosis(PatientDiagnosis diagnosis)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Insert {diagnosis}", diagnosis);
|
||||
var diag = await MapDiagnosis(diagnosis);
|
||||
if (diag == null)
|
||||
if (diag != null)
|
||||
{
|
||||
_logger.LogError("Mepped diagnosis is null. Diagnosis: {diagnosis}", diagnosis);
|
||||
await _diagnosisRepository.InsertOneAsync(diag);
|
||||
await SendBroadcast(diag);
|
||||
}
|
||||
else
|
||||
{
|
||||
var dgdb = await _diagnosisRepository.FindByPatientIdAndCode(diag.PatientId, diag.Code,
|
||||
diag.CodingSystem);
|
||||
}
|
||||
|
||||
if (dgdb != null)
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientDiagnosis"/> through the calculated observations mapper to produce a transformed diagnosis instance.
|
||||
/// When the mapper yields a <see langword="null"/> result, indicating the diagnosis is not applicable or cannot be mapped, a debug message is logged and the result is returned as-is.
|
||||
/// </summary>
|
||||
/// <param name="diagnosis">The patient diagnosis to be mapped.</param>
|
||||
/// <returns>The mapped <see cref="PatientDiagnosis"/> produced by the calculated observations mapper, or <see langword="null"/> if the diagnosis was ignored.</returns>
|
||||
private async Task<PatientDiagnosis?> MapDiagnosis(PatientDiagnosis diagnosis)
|
||||
{
|
||||
var diagnosis2 = await _calculatedObservations.Value.Map(diagnosis);
|
||||
if (diagnosis2 == null) _logger.LogDebug("Mapping {diagnosis}: Ignored", diagnosis2);
|
||||
|
||||
return diagnosis2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a patient diagnosis broadcast to all subscribers whose registered location matches the patient's location (unit, room, and bed). Returns early without broadcasting if the patient cannot be found.
|
||||
/// </summary>
|
||||
/// <param name="diagnosis">The patient diagnosis payload to broadcast to the matching subscribers.</param>
|
||||
private async Task SendBroadcast(PatientDiagnosis diagnosis)
|
||||
{
|
||||
var patient = await _patientService.Value.FindById(diagnosis.PatientId);
|
||||
if (patient == null) return;
|
||||
|
||||
var displaySubscribers = _subscribersService.GetSubscribers().Where(s =>
|
||||
!s.Locations.IsNullOrEmpty() && s.Locations.Any(c =>
|
||||
c.UnitName == patient.Location.UnitName &&
|
||||
c.Bed == patient.Location.Bed &&
|
||||
c.Room == patient.Location.Room
|
||||
)).ToList();
|
||||
|
||||
displaySubscribers.ForEach(Action);
|
||||
return;
|
||||
|
||||
void Action(WsSubscriber subscriber)
|
||||
{
|
||||
_clientMessageService.SendAsync(subscriber.Id, OperationType.Diagnosis, diagnosis);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all patient diagnosis records associated with the specified patient identifier by delegating to the diagnosis repository.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose diagnosis records are to be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an asynchronous cursor over the matching <see cref="PatientDiagnosis"/> records.</returns>
|
||||
public Task<IAsyncCursor<PatientDiagnosis>> FindByPatientIdAsync(ObjectId patientId)
|
||||
{
|
||||
return _diagnosisRepository.FindByPatientIdAsync(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts or updates a patient diagnosis, creating an audit log entry. If a diagnosis
|
||||
/// already exists for the same patient, code, and coding system, it is updated while
|
||||
/// preserving its identifier and original timestamp; otherwise a new record is inserted.
|
||||
/// A broadcast is dispatched asynchronously after a successful insert or update.
|
||||
/// </summary>
|
||||
/// <param name="diagnosis">The patient diagnosis to persist.</param>
|
||||
public async Task InsertDiagnosis(PatientDiagnosis diagnosis)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Insert {diagnosis}", diagnosis);
|
||||
var diag = await MapDiagnosis(diagnosis);
|
||||
if (diag == null)
|
||||
{
|
||||
var auxDgdb = dgdb;
|
||||
diag.Id = dgdb.Id;
|
||||
diag.Time = dgdb.Time;
|
||||
diag.UpdateDate = diagnosis.Time;
|
||||
await _diagnosisRepository.UpdateOneAsync(diag.Id, diag);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, auxDgdb, dgdb);
|
||||
_logger.LogError("Mepped diagnosis is null. Diagnosis: {diagnosis}", diagnosis);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _diagnosisRepository.InsertOneAsync(diagnosis);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, diagnosis);
|
||||
var dgdb = await _diagnosisRepository.FindByPatientIdAndCode(diag.PatientId, diag.Code,
|
||||
diag.CodingSystem);
|
||||
|
||||
if (dgdb != null)
|
||||
{
|
||||
var auxDgdb = dgdb;
|
||||
diag.Id = dgdb.Id;
|
||||
diag.Time = dgdb.Time;
|
||||
diag.UpdateDate = diagnosis.Time;
|
||||
await _diagnosisRepository.UpdateOneAsync(diag.Id, diag);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, auxDgdb, dgdb);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _diagnosisRepository.InsertOneAsync(diagnosis);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, diagnosis);
|
||||
}
|
||||
|
||||
_ = SendBroadcast(diagnosis);
|
||||
}
|
||||
|
||||
_ = SendBroadcast(diagnosis);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Error inserting diagnosis. Diagnosis: {diagnosis}. Exception:{ex}", diagnosis, ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Error inserting diagnosis. Diagnosis: {diagnosis}. Exception:{ex}", diagnosis, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,14 @@ using MongoDB.Bson;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the concrete implementation of the <see cref="IDischargeService"/> contract,
|
||||
/// encapsulating the business logic required to process discharge operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This service acts as the default in-memory or infrastructure-backed implementation
|
||||
/// of the discharge operations defined by <see cref="IDischargeService"/>.
|
||||
/// </remarks>
|
||||
public class DischargeService : IDischargeService
|
||||
{
|
||||
private readonly ILocalAuditService _auditService;
|
||||
@@ -52,6 +60,12 @@ public class DischargeService : IDischargeService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a discharge record asynchronously. The method is intended to validate that the patient
|
||||
/// can be discharged (requiring both medical and administrative discharge values and an allowed
|
||||
/// discharge status), and otherwise falls back to deleting the discharge by its identifier.
|
||||
/// </summary>
|
||||
/// <param name="discharge">The discharge entity to be deleted.</param>
|
||||
public async Task DeleteDischargeAsync(Discharge discharge)
|
||||
{
|
||||
//var patient = await _patientServiceLazy.Value.FindById(discharge.PatientId);
|
||||
@@ -65,6 +79,10 @@ public class DischargeService : IDischargeService
|
||||
await DeleteDischargeByIdAsync(discharge.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a discharge record by its identifier. If the discharge is not found, an error is logged and the operation is skipped; otherwise the record is removed, an audit log entry is created, and a delete broadcast is sent.
|
||||
/// </summary>
|
||||
/// <param name="dischargeId">The unique identifier of the discharge to delete.</param>
|
||||
public async Task DeleteDischargeByIdAsync(ObjectId dischargeId)
|
||||
{
|
||||
try
|
||||
@@ -95,11 +113,25 @@ public class DischargeService : IDischargeService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of discharge records associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose discharge records will be counted.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the total number of discharges for the given unit.</returns>
|
||||
public async Task<long> CountDischargesByUnitId(ObjectId unitId)
|
||||
{
|
||||
return await _dischargeRepository.CountByUnitId(unitId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a discharge by its identifier, enriching the result with patient location details
|
||||
/// when an associated point of care is available.
|
||||
/// </summary>
|
||||
/// <param name="dischargeId">The unique identifier of the discharge to retrieve.</param>
|
||||
/// <returns>The matching <see cref="Discharge"/>, populated with <see cref="PatientLocation"/>
|
||||
/// information if a point of care is linked; otherwise the discharge as stored.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no discharge is found for the specified
|
||||
/// <paramref name="dischargeId"/>.</exception>
|
||||
public async Task<Discharge?> GetDischargeByIdAsync(ObjectId dischargeId)
|
||||
{
|
||||
var result = await _dischargeRepository.FindById(dischargeId) ??
|
||||
@@ -107,17 +139,26 @@ public class DischargeService : IDischargeService
|
||||
if (result.PointOfCareId == null)
|
||||
return result;
|
||||
|
||||
var poc = await _pointOfCareService.GetInfo(result.PointOfCareId.Value, null,false);
|
||||
var poc = await _pointOfCareService.GetInfo(result.PointOfCareId.Value, null, false);
|
||||
result.PatientLocation = new PatientLocation(poc?.UnitName, poc?.Bed, poc?.Room);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all discharge records from the repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an enumerable collection of <see cref="Discharge"/> records.</returns>
|
||||
public async Task<IEnumerable<Discharge>> GetDischargesAsync()
|
||||
{
|
||||
return await _dischargeRepository.FindAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the discharge record associated with the specified patient identifier, enriching it with patient location details when a point of care is linked.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose discharge record should be retrieved.</param>
|
||||
/// <returns>A <see cref="Discharge"/> object populated with patient location information when a point of care is associated, or <c>null</c> if no discharge is found or an error occurs.</returns>
|
||||
public async Task<Discharge?> GetDischargeByPatientId(ObjectId patientId)
|
||||
{
|
||||
try
|
||||
@@ -127,7 +168,7 @@ public class DischargeService : IDischargeService
|
||||
_logger.LogError("Discharge not found by patient Id {id}", patientId);
|
||||
if (discharge is { PointOfCareId: not null })
|
||||
{
|
||||
var poc = await _pointOfCareService.GetInfo(discharge.PointOfCareId.Value, null,false);
|
||||
var poc = await _pointOfCareService.GetInfo(discharge.PointOfCareId.Value, null, false);
|
||||
discharge.PatientLocation = new PatientLocation(poc?.UnitName, poc?.Bed, poc?.Room);
|
||||
}
|
||||
|
||||
@@ -140,6 +181,13 @@ public class DischargeService : IDischargeService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new discharge record into the repository, verifies its persistence, logs the operation, and broadcasts a notification.
|
||||
/// Throws a <see cref="ConflictException"/> if the discharge cannot be retrieved after insertion, indicating a creation failure.
|
||||
/// </summary>
|
||||
/// <param name="discharge">The discharge entity to be inserted.</param>
|
||||
/// <returns>The persisted <see cref="Discharge"/> entity retrieved from the repository after insertion.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the inserted discharge cannot be found by its identifier, indicating that the creation failed.</exception>
|
||||
public async Task<Discharge?> InsertDischarge(Discharge discharge)
|
||||
{
|
||||
await _dischargeRepository.InsertOneAsync(discharge);
|
||||
@@ -152,6 +200,12 @@ public class DischargeService : IDischargeService
|
||||
return dischargeAux;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the <see cref="Discharge"/> associated with the specified patient location.
|
||||
/// Returns <c>null</c> if an exception occurs while accessing the underlying repository.
|
||||
/// </summary>
|
||||
/// <param name="location">The patient location used to look up the associated discharge record.</param>
|
||||
/// <returns>A <see cref="Discharge"/> if one is found for the given location; otherwise, <c>null</c> when an error occurs.</returns>
|
||||
public async Task<Discharge?> GetDischargeByLocation(PatientLocation location)
|
||||
{
|
||||
try
|
||||
@@ -165,6 +219,13 @@ public class DischargeService : IDischargeService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a discharge record by the specified point of care identifier and enriches it with patient location details.
|
||||
/// If the discharge is not found, an information message is logged; when a related point of care is available, its unit, bed, and room are mapped to the discharge's <see cref="PatientLocation"/>.
|
||||
/// On failure, the error is logged and <c>null</c> is returned.
|
||||
/// </summary>
|
||||
/// <param name="poc">The point of care identifier used to look up the discharge record.</param>
|
||||
/// <returns>A <see cref="Discharge"/> with the patient location populated when available, or <c>null</c> if the discharge is not found or an error occurs.</returns>
|
||||
public async Task<Discharge?> GetDischargeByPointOfCareId(ObjectId poc)
|
||||
{
|
||||
try
|
||||
@@ -174,7 +235,7 @@ public class DischargeService : IDischargeService
|
||||
_logger.LogInformation("Discharge not found by PointOfCareId {id}", poc);
|
||||
if (discharge is { PointOfCareId: not null })
|
||||
{
|
||||
var pocInfo = await _pointOfCareService.GetInfo(discharge.PointOfCareId.Value, null,false);
|
||||
var pocInfo = await _pointOfCareService.GetInfo(discharge.PointOfCareId.Value, null, false);
|
||||
discharge.PatientLocation = new PatientLocation(pocInfo?.UnitName, pocInfo?.Bed, pocInfo?.Room);
|
||||
}
|
||||
|
||||
@@ -187,6 +248,13 @@ public class DischargeService : IDischargeService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a discharge record by its point of care (location) identifier and applies locale-specific data using the associated unit.
|
||||
/// Returns the discharge as-is if no associated unit is found, or <c>null</c> if no discharge exists for the given location.
|
||||
/// </summary>
|
||||
/// <param name="location">The ObjectId identifying the point of care (location) whose discharge record should be retrieved.</param>
|
||||
/// <param name="dataLocale">The locale used to localize the discharge data when the associated unit is found.</param>
|
||||
/// <returns>A <see cref="Task{Discharge}"/> containing the localized discharge, the unmodified discharge when its unit cannot be found, or <c>null</c> when no discharge exists for the specified location.</returns>
|
||||
public async Task<Discharge?> GetDischargeByPointOfCareIdWithLocale(ObjectId location, LocaleEnum dataLocale)
|
||||
{
|
||||
var discharge = await GetDischargeByPointOfCareId(location);
|
||||
@@ -197,6 +265,11 @@ public class DischargeService : IDischargeService
|
||||
return dischargeWithLocale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing discharge record, auditing the change and broadcasting the update. Throws a conflict exception if the discharge cannot be found by its identifier.
|
||||
/// </summary>
|
||||
/// <param name="discharge">The discharge entity to be updated.</param>
|
||||
/// <exception cref="ConflictException">Thrown when no discharge is found with the specified identifier, preventing the update from proceeding.</exception>
|
||||
public async Task UpdateDischargeAsync(Discharge discharge)
|
||||
{
|
||||
var oldDischarge = await GetDischargeByIdAsync(discharge.Id) ??
|
||||
@@ -228,39 +301,39 @@ public class DischargeService : IDischargeService
|
||||
switch (apiRequest.Type)
|
||||
{
|
||||
case "NewDischarge":
|
||||
{
|
||||
//TODO: ver qué tipos llegan
|
||||
if (!"Altable".Equals(apiRequest.Discharge.Patient.DischargeStatus?.OptionType))
|
||||
{
|
||||
_logger.LogError("Error discharging. Patient not altable: {patient}", patient);
|
||||
return;
|
||||
//TODO: ver qué tipos llegan
|
||||
if (!"Altable".Equals(apiRequest.Discharge.Patient.DischargeStatus?.OptionType))
|
||||
{
|
||||
_logger.LogError("Error discharging. Patient not altable: {patient}", patient);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await _dischargeRepository.InsertOneAsync(apiRequest.Discharge);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null,
|
||||
apiRequest.Discharge);
|
||||
SendDischargeBroadcast(apiRequest.Discharge, OperationType.NewDischarge);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
await _dischargeRepository.InsertOneAsync(apiRequest.Discharge);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null,
|
||||
apiRequest.Discharge);
|
||||
SendDischargeBroadcast(apiRequest.Discharge, OperationType.NewDischarge);
|
||||
|
||||
break;
|
||||
}
|
||||
case "UpdateDischarge":
|
||||
{
|
||||
await GetDischargeByIdAsync(apiRequest.Discharge.Id);
|
||||
await UpdateDischargeAsync(apiRequest.Discharge);
|
||||
break;
|
||||
}
|
||||
case "DeleteDischarge":
|
||||
{
|
||||
if (StatusEnum.Discharge.NoAltable.ToString().Equals(patient.DischargeStatus?.OptionType))
|
||||
{
|
||||
_logger.LogError("Error deleting discharge. Patient altable: {patient}", patient);
|
||||
return;
|
||||
await GetDischargeByIdAsync(apiRequest.Discharge.Id);
|
||||
await UpdateDischargeAsync(apiRequest.Discharge);
|
||||
break;
|
||||
}
|
||||
case "DeleteDischarge":
|
||||
{
|
||||
if (StatusEnum.Discharge.NoAltable.ToString().Equals(patient.DischargeStatus?.OptionType))
|
||||
{
|
||||
_logger.LogError("Error deleting discharge. Patient altable: {patient}", patient);
|
||||
return;
|
||||
}
|
||||
|
||||
await DeleteDischargeAsync(apiRequest.Discharge);
|
||||
break;
|
||||
}
|
||||
await DeleteDischargeAsync(apiRequest.Discharge);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -270,12 +343,22 @@ public class DischargeService : IDischargeService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously persists the specified API request by executing the save operation on a background thread.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to save.</param>
|
||||
/// <returns>A task that completes when the request has been saved.</returns>
|
||||
public Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
|
||||
}
|
||||
|
||||
// Revisar
|
||||
/// <summary>
|
||||
/// Sends a discharge broadcast to all subscribers associated with the discharge's point of care, grouped by their locale. Validates that the point of care identifier is present; logs and returns early if it is null. Exceptions during the broadcast are caught and logged without rethrowing.
|
||||
/// </summary>
|
||||
/// <param name="discharge">The discharge whose point of care is used to locate matching subscribers and whose data is sent in the broadcast.</param>
|
||||
/// <param name="operation">The operation type associated with the outgoing message sent to each subscriber.</param>
|
||||
public async void SendDischargeBroadcast(Discharge discharge, OperationType operation)
|
||||
{
|
||||
try
|
||||
@@ -311,8 +394,14 @@ public class DischargeService : IDischargeService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the master list option for the specified units and type, then records an audit log entry and broadcasts the change for each affected discharge.
|
||||
/// </summary>
|
||||
/// <param name="opt">The master list update options to apply to the discharges.</param>
|
||||
/// <param name="unitList">The collection of units whose associated discharges will be updated.</param>
|
||||
/// <param name="typeName">The name of the master list type used to target the update.</param>
|
||||
public async Task UpdatePatientMasterListItemChange(UpdateOptionMasterListDto opt, IEnumerable<Unit> unitList,
|
||||
string typeName)
|
||||
string typeName)
|
||||
{
|
||||
var unitIds = unitList.Select(x => x.Id).ToList();
|
||||
await _dischargeRepository.GetDischargesByUnitIds(unitIds);
|
||||
@@ -326,6 +415,12 @@ public class DischargeService : IDischargeService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a patient master list option for the specified units and type, then processes the resulting updated discharge records by creating audit log entries and broadcasting update notifications (only when the updated discharge record is found).
|
||||
/// </summary>
|
||||
/// <param name="opt">The option list entry to be removed from the patient master list.</param>
|
||||
/// <param name="unitList">The collection of units whose identifiers are used to scope the deletion.</param>
|
||||
/// <param name="typeName">The name of the master list type/category to which the option belongs.</param>
|
||||
public async Task DeletePatientMasterListItem(OptionList opt, IEnumerable<Unit> unitList, string typeName)
|
||||
{
|
||||
var unitIds = unitList.Select(x => x.Id).ToList();
|
||||
@@ -339,11 +434,24 @@ public class DischargeService : IDischargeService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deletes all discharge records associated with the specified unit identifier by delegating the operation to the discharge repository.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose discharge records should be removed.</param>
|
||||
public async Task DeleteDischargesByUnitId(ObjectId unitId)
|
||||
{
|
||||
await _dischargeRepository.DeleteByUnitId(unitId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the given <paramref name="discharge"/> with its configurable option names translated according to the specified <paramref name="locale"/>.
|
||||
/// When <paramref name="unit"/> is null or the locale is <see cref="LocaleEnum.Default"/>, the discharge is returned unchanged.
|
||||
/// Otherwise, the configured service and destination options are looked up in the locale-specific master lists and their <c>Name</c> values are updated; fields without a matching list, missing properties, null values, or without a translation are left untouched.
|
||||
/// </summary>
|
||||
/// <param name="unit">Source of the master list identifiers used to resolve locale-specific options; when null, no translation is performed.</param>
|
||||
/// <param name="discharge">Discharge instance whose option names may be translated in place.</param>
|
||||
/// <param name="locale">Target locale used to load the appropriate master list; when set to <see cref="LocaleEnum.Default"/>, the discharge is returned without changes.</param>
|
||||
/// <returns>The same <paramref name="discharge"/> instance, with translated option names when a matching locale-specific entry is found.</returns>
|
||||
private async Task<Discharge> GetDischargeWithLocale(Unit? unit, Discharge discharge, LocaleEnum locale)
|
||||
{
|
||||
if (unit == null)
|
||||
|
||||
@@ -31,6 +31,11 @@ public class DisplayConfigService(
|
||||
IDisplayChartConfigRepository displayChartRepository)
|
||||
: IDisplayConfigService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all display configurations from the repository and enriches each one with its minimal display section.
|
||||
/// Configurations for which the minimal display section cannot be resolved are excluded from the result.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of enriched <see cref="DisplayConfig"/> items, omitting any entries that could not be enriched.</returns>
|
||||
public async Task<List<DisplayConfig>> GetAll()
|
||||
{
|
||||
var result = await displayConfigRepository.GetAll();
|
||||
@@ -44,6 +49,12 @@ public class DisplayConfigService(
|
||||
return resultToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of display configurations in a compact form, including a flag indicating whether each configuration is currently in use.
|
||||
/// Applies server-side pagination using the provided filter and maps each result to a <see cref="DisplayConfigMinimalResponse"/> enriched with its usage status.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter containing the page number, page size, and any filtering criteria used to retrieve and paginate the display configurations.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a <see cref="PaginationResponse{T}"/> with the compact display configurations, current page metadata, and total document count.</returns>
|
||||
public async Task<PaginationResponse<DisplayConfigMinimalResponse>> GetAllCompactPaginated(PaginationFilter filter)
|
||||
{
|
||||
var result = displayConfigRepository.GetAllPaginated(filter);
|
||||
@@ -66,6 +77,11 @@ public class DisplayConfigService(
|
||||
count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves display configurations matching the specified display type and enriches each one with its minimal display section. Configurations for which the enrichment returns null are excluded from the result list.
|
||||
/// </summary>
|
||||
/// <param name="type">The display type used to filter the configurations.</param>
|
||||
/// <returns>A list of enriched display configurations; entries whose display section could not be resolved are omitted.</returns>
|
||||
public async Task<List<DisplayConfig>> GetByType(DisplayConfigEnums.DisplayType type)
|
||||
{
|
||||
var result = await displayConfigRepository.GetByType(type);
|
||||
@@ -79,14 +95,28 @@ public class DisplayConfigService(
|
||||
return resultToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="DisplayConfig"/> by its identifier and enriches it with minimal display section data.
|
||||
/// Throws a <see cref="NotFoundException"/> if no configuration is found for the given id.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the display configuration to retrieve.</param>
|
||||
/// <returns>The display configuration with the minimal display section applied.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no display configuration exists for the specified <paramref name="id"/>.</exception>
|
||||
public async Task<DisplayConfig> GetById(ObjectId id)
|
||||
{
|
||||
return await AddDisplaySectionMinimal(await displayConfigRepository.GetById(id)) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="DisplayConfig"/> by its identifier, falling back to the default configuration for the given unit and display type when the identifier is not provided or not found. When both a configuration by id and a default configuration are found, the two are merged and the merged result is returned.
|
||||
/// </summary>
|
||||
/// <param name="configId">The optional configuration identifier. When null, only the default configuration is returned.</param>
|
||||
/// <param name="unitId">The unit identifier used to look up the default configuration.</param>
|
||||
/// <param name="displayType">The display type used to look up the default configuration.</param>
|
||||
/// <returns>The current configuration, the default configuration, the merged configuration, or null when neither is available.</returns>
|
||||
public async Task<DisplayConfig?> GetById(ObjectId? configId, ObjectId unitId,
|
||||
DisplayConfigEnums.DisplayType displayType)
|
||||
DisplayConfigEnums.DisplayType displayType)
|
||||
{
|
||||
if (configId.HasValue)
|
||||
{
|
||||
@@ -118,12 +148,22 @@ public class DisplayConfigService(
|
||||
return await GetDefaultByUnitIdAndType(unitId, displayType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new display configuration into the repository while recording an audit log entry for the operation.
|
||||
/// </summary>
|
||||
/// <param name="config">The display configuration to insert.</param>
|
||||
/// <returns>The inserted display configuration, or null if no entity was returned by the repository.</returns>
|
||||
public async Task<DisplayConfig?> InsertOne(DisplayConfig config)
|
||||
{
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, config);
|
||||
return await displayConfigRepository.InsertOneAsyncAndReturn(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a minimal display configuration, creating a <see cref="DisplayNurse"/> instance when the type is <see cref="DisplayConfigEnums.DisplayType.DisplayNurse"/> and a base <see cref="DisplayConfig"/> otherwise, while recording an audit log for the creation.
|
||||
/// </summary>
|
||||
/// <param name="config">The DTO containing the hospital and display type used to build the new configuration entity.</param>
|
||||
/// <returns>The inserted <see cref="DisplayConfig"/> entity, or <c>null</c> if the repository did not return a result.</returns>
|
||||
public async Task<DisplayConfig?> InsertOneMinimal(CreateDisplayConfigDto config)
|
||||
{
|
||||
DisplayConfig newDisplayConfig;
|
||||
@@ -147,6 +187,10 @@ public class DisplayConfigService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts test records for the DisplayNurse and SmartDisplay display configuration types into the repository and returns the inserted DisplayNurse record.
|
||||
/// </summary>
|
||||
/// <returns>The inserted <see cref="DisplayConfig"/> instance of type DisplayNurse.</returns>
|
||||
public async Task<DisplayConfig> InsertOneTest()
|
||||
{
|
||||
var d = new DisplayNurse
|
||||
@@ -162,6 +206,13 @@ public class DisplayConfigService(
|
||||
return d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a display configuration, dispatching to a type-specific update path for <see cref="DisplayConfigEnums.DisplayType.SmartDisplay"/> or <see cref="DisplayConfigEnums.DisplayType.DisplayNurse"/>, broadcasting the change and writing an audit log when the update succeeds.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The identifier of the display configuration to update.</param>
|
||||
/// <param name="newDisplayConfig">The new configuration payload, deserialized internally into the appropriate DTO based on its <c>Type</c>.</param>
|
||||
/// <returns>The updated <see cref="DisplayConfig"/> when the corresponding update succeeds; otherwise the method throws.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the configuration's <c>Type</c> is not handled, or when the underlying update returns no result.</exception>
|
||||
public async Task<DisplayConfig?> UpdateConfig(ObjectId displayConfigId, object newDisplayConfig)
|
||||
{
|
||||
var baseType = JsonConvert.DeserializeObject<DisplayConfigDto>(newDisplayConfig.ToString()!);
|
||||
@@ -209,11 +260,24 @@ public class DisplayConfigService(
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the list of fields associated with a display configuration identified by the specified object ID.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The unique identifier of the display configuration whose field list is being updated.</param>
|
||||
/// <param name="fields">The collection of fields to be associated with the display configuration.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a boolean value indicating whether the update was successful.</returns>
|
||||
public Task<bool> UpdateFieldList(ObjectId objectIdConfigDisplay, List<Field> fields)
|
||||
{
|
||||
return displayConfigRepository.UpdateFieldList(objectIdConfigDisplay, fields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the color configuration of a display, initializing an empty color configuration when none exists, and records an audit log of the change.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the display configuration to update.</param>
|
||||
/// <param name="colorConfig">The new color configuration to apply to the display.</param>
|
||||
/// <returns>True if the color configuration was updated successfully; otherwise, false.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the display configuration identified by <paramref name="objectIdConfigDisplay"/> cannot be found before or after the update.</exception>
|
||||
public async Task<bool> UpdateConfigColor(ObjectId objectIdConfigDisplay, ColorConfig colorConfig)
|
||||
{
|
||||
var oldDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
|
||||
@@ -232,6 +296,14 @@ public class DisplayConfigService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the header configuration of a display configuration record and creates an audit log entry
|
||||
/// capturing the previous and updated values.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The unique identifier of the display configuration to update.</param>
|
||||
/// <param name="headerConfig">The new header configuration to apply to the display configuration.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the display configuration is not found before the update, or when the updated display configuration cannot be retrieved afterwards.</exception>
|
||||
public async Task<bool> UpdateHeaderConfig(ObjectId objectIdConfigDisplay, HeaderConfig headerConfig)
|
||||
{
|
||||
var oldDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
|
||||
@@ -244,6 +316,13 @@ public class DisplayConfigService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the home banner configuration for the specified display configuration and records an audit log comparing the old and new states.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the display configuration to update.</param>
|
||||
/// <param name="bannerItems">The list of banner items to set for the home banner.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the update succeeds; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the display configuration cannot be found before or after the update.</exception>
|
||||
public async Task<bool> UpdateSetHomeBanner(ObjectId objectIdConfigDisplay, List<BannerItem> bannerItems)
|
||||
{
|
||||
var oldDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
|
||||
@@ -256,6 +335,12 @@ public class DisplayConfigService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the base display configuration and records an audit log comparing the previous and updated configurations.
|
||||
/// </summary>
|
||||
/// <param name="baseConfig">The display configuration containing the updated values, identified by its <see cref="DisplayConfig.Id"/>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The result indicates whether the update was successful.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the display configuration with the specified identifier does not exist before or after the update.</exception>
|
||||
public async Task<bool> UpdateBaseConfig(DisplayConfig baseConfig)
|
||||
{
|
||||
var oldDisplayConfig = await displayConfigRepository.GetById(baseConfig.Id) ??
|
||||
@@ -268,6 +353,13 @@ public class DisplayConfigService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hospital name of an existing display configuration and records an audit log entry comparing the previous and updated values.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the display configuration to update.</param>
|
||||
/// <param name="name">The new hospital name to apply to the display configuration.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the display configuration identified by <paramref name="objectIdConfigDisplay"/> cannot be found before or after the update.</exception>
|
||||
public async Task<bool> UpdateDisplayConfigHospitalName(ObjectId objectIdConfigDisplay, string name)
|
||||
{
|
||||
var oldDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
|
||||
@@ -280,6 +372,12 @@ public class DisplayConfigService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a display configuration by its identifier. When displays are still associated with the configuration, they are reassigned to a default configuration for the same type before deletion, and an audit log entry is recorded on success.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the display configuration to delete.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the configuration was successfully deleted; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when no default configuration is found for the related display type, or when reassigning a display to the default configuration fails.</exception>
|
||||
public async Task<bool> DeleteDisplayConfig(ObjectId objectIdConfigDisplay)
|
||||
{
|
||||
var displays = await displayService.Value.GetByConfigId(objectIdConfigDisplay);
|
||||
@@ -303,18 +401,34 @@ public class DisplayConfigService(
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of display configuration locations associated with the specified configuration display identifier.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the configuration display whose locations are being retrieved.</param>
|
||||
/// <returns>A task that returns a list of <see cref="DisplayConfigLocationDto"/> items for the given configuration display.</returns>
|
||||
public async Task<List<DisplayConfigLocationDto>> GetDisplayConfigLocations(ObjectId objectIdConfigDisplay)
|
||||
{
|
||||
return await displayService.Value.GetDisplayConfigLocations(objectIdConfigDisplay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a compact list of all display configurations from the repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="DisplayConfigMinimalResponse"/> objects representing the compact display configuration data.</returns>
|
||||
public async Task<List<DisplayConfigMinimalResponse>> GetAllCompact()
|
||||
{
|
||||
return await displayConfigRepository.GetAllCompact();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new display configuration by cloning an existing template identified by its object ID, applying the specified hospital and display type, and inserting it into the data store. Returns <c>null</c> when the object ID cannot be parsed, the template cannot be retrieved, the retrieved template does not match the requested display type, or the display type is not one of the handled cases.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The object ID of the existing template used as the base for the new configuration.</param>
|
||||
/// <param name="configType">The display type of the configuration to create; determines which template subtype is expected and produced.</param>
|
||||
/// <param name="configHospital">The hospital to associate with the newly created configuration.</param>
|
||||
/// <returns>The inserted <see cref="DisplayConfig"/> when the template is found and matches the requested type; otherwise, <c>null</c>.</returns>
|
||||
public async Task<DisplayConfig?> InsertOneWithTemplate(string objectId,
|
||||
DisplayConfigEnums.DisplayType configType, string? configHospital)
|
||||
DisplayConfigEnums.DisplayType configType, string? configHospital)
|
||||
{
|
||||
if (ObjectId.TryParse(objectId, out var objectIdConfigDisplay))
|
||||
{
|
||||
@@ -325,7 +439,7 @@ public class DisplayConfigService(
|
||||
if (template is StandarDisplay standardTemplate)
|
||||
{
|
||||
var standarConfigg = new StandarDisplay
|
||||
{ Hospital = configHospital, Type = DisplayConfigEnums.DisplayType.StandarDisplay };
|
||||
{ Hospital = configHospital, Type = DisplayConfigEnums.DisplayType.StandarDisplay };
|
||||
standarConfigg.MergeConfig(standardTemplate);
|
||||
await InsertOne(standarConfigg);
|
||||
return standarConfigg;
|
||||
@@ -337,7 +451,7 @@ public class DisplayConfigService(
|
||||
if (template is DisplayNurse nurseTemplate)
|
||||
{
|
||||
var nurseConfig = new DisplayNurse
|
||||
{ Hospital = configHospital, Type = DisplayConfigEnums.DisplayType.DisplayNurse };
|
||||
{ Hospital = configHospital, Type = DisplayConfigEnums.DisplayType.DisplayNurse };
|
||||
nurseConfig.MergeConfig(nurseTemplate);
|
||||
await InsertOne(nurseConfig);
|
||||
return nurseConfig;
|
||||
@@ -349,7 +463,7 @@ public class DisplayConfigService(
|
||||
if (template is SmartDisplay smartTemplate)
|
||||
{
|
||||
var smartConfig = new SmartDisplay
|
||||
{ Hospital = configHospital, Type = DisplayConfigEnums.DisplayType.DisplayNurse };
|
||||
{ Hospital = configHospital, Type = DisplayConfigEnums.DisplayType.DisplayNurse };
|
||||
smartConfig.MergeConfig(smartTemplate);
|
||||
await InsertOne(smartConfig);
|
||||
return smartConfig;
|
||||
@@ -365,10 +479,15 @@ public class DisplayConfigService(
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing card configuration. When the update is successful, propagates the new configuration to related rotating display configs by refreshing their nurse data, and broadcasts the card display config update to all associated displays.
|
||||
/// </summary>
|
||||
/// <param name="baseConfig">The card configuration to update.</param>
|
||||
/// <returns>True if the update was applied (repository reported changes); otherwise, false.</returns>
|
||||
public async Task<bool> UpdateCardConfig(CardConfig baseConfig)
|
||||
{
|
||||
var result = await displayCardConfigRepository.UpdateOne(baseConfig);
|
||||
|
||||
|
||||
if (result.Changes > 0)
|
||||
{
|
||||
var displayConfigs = await displayConfigRepository.GetAllByCardConfigIdAndRotating(baseConfig.Id);
|
||||
@@ -377,7 +496,7 @@ public class DisplayConfigService(
|
||||
await displayConfigRepository.UpdateDisplayNurse(displayConfig,
|
||||
new DisplayNurseDto() { CardConfig = result.Data }, masterListServiceFactory.StringNurseObs());
|
||||
}
|
||||
|
||||
|
||||
var displays = await displayConfigRepository.GetAllByCardConfigId(baseConfig.Id);
|
||||
foreach (var display in displays)
|
||||
SendDisplayConfigBroadcast(display, OperationType.UpdateCardDisplayConfig, result.Data);
|
||||
@@ -388,6 +507,12 @@ public class DisplayConfigService(
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing card detail configuration and propagates the change by broadcasting an update event to all related displays.
|
||||
/// Returns <c>true</c> if the update modified at least one record, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
/// <param name="baseConfig">The card detail configuration to update, identified by its <c>Id</c>.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the update affected one or more records; <c>false</c> when no changes were made.</returns>
|
||||
public async Task<bool> UpdateDetailConfig(CardDetailsConfig baseConfig)
|
||||
{
|
||||
var result = await displayDetailConfigRepository.UpdateOne(baseConfig);
|
||||
@@ -398,6 +523,11 @@ public class DisplayConfigService(
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing chart configuration in the repository and returns whether the operation modified any records.
|
||||
/// </summary>
|
||||
/// <param name="baseConfig">The chart configuration to be updated.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the update changed at least one record; otherwise, <c>false</c>.</returns>
|
||||
public async Task<bool> UpdateChartConfig(ChartConfig baseConfig)
|
||||
{
|
||||
var result = await displayChartRepository.UpdateOne(baseConfig);
|
||||
@@ -405,6 +535,12 @@ public class DisplayConfigService(
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the chart configuration identified by the given display object ID.
|
||||
/// If the repository deletion succeeds, the deleted chart configuration is updated and the method returns <c>true</c>; otherwise, it returns <c>false</c>.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The object ID of the chart configuration display to delete.</param>
|
||||
/// <returns><c>true</c> if the chart configuration was successfully deleted; <c>false</c> if no matching configuration was found.</returns>
|
||||
public async Task<bool> DeleteChartConfig(ObjectId objectIdConfigDisplay)
|
||||
{
|
||||
var res = await displayChartRepository.DeleteOne(objectIdConfigDisplay);
|
||||
@@ -417,11 +553,21 @@ public class DisplayConfigService(
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a chart configuration by its unique identifier from the display chart repository.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigChart">The unique identifier of the chart configuration to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="ChartConfig"/> if found; otherwise, <c>null</c>.</returns>
|
||||
public async Task<ChartConfig?> GetChartConfig(ObjectId objectIdConfigChart)
|
||||
{
|
||||
return await displayChartRepository.GetById(objectIdConfigChart);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new card detail configuration and optionally links it to an existing display configuration, broadcasting the change when the link is successfully established.
|
||||
/// </summary>
|
||||
/// <param name="updateDisplayConfigNameDto">The DTO containing the detail configuration to insert and, optionally, the ID of the display configuration to associate it with.</param>
|
||||
/// <returns>The newly inserted <see cref="CardDetailsConfig"/>, or <c>null</c> when no detail configuration is provided in the DTO.</returns>
|
||||
public async Task<CardDetailsConfig?> InsertCardDetailConfig(CreateDisplayConfigCardDto updateDisplayConfigNameDto)
|
||||
{
|
||||
if (updateDisplayConfigNameDto.DetailConfig == null) return null;
|
||||
@@ -438,6 +584,11 @@ public class DisplayConfigService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new chart configuration and, when a display configuration identifier is provided, links the inserted chart to that display configuration and broadcasts the update.
|
||||
/// </summary>
|
||||
/// <param name="updateDisplayConfigNameDto">The data transfer object containing the chart configuration to insert and, optionally, the target display configuration identifier.</param>
|
||||
/// <returns>The inserted <see cref="ChartConfig"/>, or <c>null</c> if the supplied chart configuration is <c>null</c>.</returns>
|
||||
public async Task<ChartConfig?> InsertChartConfig(CreateDisplayConfigCardDto updateDisplayConfigNameDto)
|
||||
{
|
||||
if (updateDisplayConfigNameDto.ChartConfig == null) return null;
|
||||
@@ -453,9 +604,14 @@ public class DisplayConfigService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new card configuration and optionally links it to an existing display configuration by updating the card config id and broadcasting the change.
|
||||
/// </summary>
|
||||
/// <param name="updateDisplayConfigNameDto">The DTO containing the card configuration to insert and, optionally, the display configuration id to associate it with.</param>
|
||||
/// <returns>The inserted <see cref="CardConfig"/>, or <c>null</c> when the provided card configuration is null.</returns>
|
||||
public async Task<CardConfig?> InsertCardConfig(CreateDisplayConfigCardDto updateDisplayConfigNameDto)
|
||||
{
|
||||
if(updateDisplayConfigNameDto.CardConfig == null) return null;
|
||||
if (updateDisplayConfigNameDto.CardConfig == null) return null;
|
||||
var result = await displayCardConfigRepository.InsertOneAsyncAndReturn(updateDisplayConfigNameDto.CardConfig);
|
||||
if (updateDisplayConfigNameDto.DisplayConfigId != null && result != null)
|
||||
{
|
||||
@@ -468,29 +624,57 @@ public class DisplayConfigService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all card configurations from the display card config repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of all <see cref="CardConfig"/> entities.</returns>
|
||||
public async Task<List<CardConfig>> GetCardConfigAll()
|
||||
{
|
||||
return await displayCardConfigRepository.GetAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a card configuration by its unique identifier from the display card configuration repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the card configuration to retrieve.</param>
|
||||
/// <returns>The matching <see cref="CardConfig"/> if found; otherwise, <c>null</c>.</returns>
|
||||
public async Task<CardConfig?> GetCardConfigById(ObjectId id)
|
||||
{
|
||||
return await displayCardConfigRepository.GetById(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the default <see cref="DisplayConfig"/> for the specified display type by delegating to the underlying repository.
|
||||
/// Returns <see langword="null"/> when no default configuration exists for the given type.
|
||||
/// </summary>
|
||||
/// <param name="type">The display type used to look up the default configuration.</param>
|
||||
/// <returns>A <see cref="DisplayConfig"/> representing the default configuration for the specified type, or <see langword="null"/> if no default is found.</returns>
|
||||
public async Task<DisplayConfig?> GetDefaultConfig(DisplayConfigEnums.DisplayType type)
|
||||
{
|
||||
return await displayConfigRepository.GetDefault(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the default <see cref="DisplayConfig"/> for the specified unit and display type by delegating to the repository.
|
||||
/// Returns <c>null</c> when no matching default configuration exists, as the not-found exception is currently commented out.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose default display configuration is being requested.</param>
|
||||
/// <param name="displayType">The display type used to filter the default configuration lookup.</param>
|
||||
/// <returns>A <see cref="DisplayConfig"/> instance if a default is found; otherwise, <c>null</c>.</returns>
|
||||
private async Task<DisplayConfig?> GetDefaultByUnitIdAndType(ObjectId unitId,
|
||||
DisplayConfigEnums.DisplayType displayType)
|
||||
DisplayConfigEnums.DisplayType displayType)
|
||||
{
|
||||
return
|
||||
await displayConfigRepository.GetDefaultByUnitIdAndType(unitId,
|
||||
displayType); //?? throw new NotFoundException(ErrorMessage.NotFound_ResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enriches the given <see cref="DisplayConfig"/> with minimal display section information when it is a SmartDisplay and has associated display section IDs, and records an audit log for the change.
|
||||
/// </summary>
|
||||
/// <param name="displayConfig">The display configuration to augment with minimal display section data, or <see langword="null"/>.</param>
|
||||
/// <returns>The updated <see cref="DisplayConfig"/>, or <see langword="null"/> if the input was <see langword="null"/>.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the display configuration cannot be found in the repository by its identifier.</exception>
|
||||
private async Task<DisplayConfig?> AddDisplaySectionMinimal(DisplayConfig? displayConfig)
|
||||
{
|
||||
if (displayConfig == null) return null;
|
||||
@@ -520,31 +704,61 @@ public class DisplayConfigService(
|
||||
return displayConfig;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the chart configuration to mark the specified entry as deleted by delegating to the display configuration repository.
|
||||
/// </summary>
|
||||
/// <param name="deletedId">The identifier of the chart configuration entry to mark as deleted.</param>
|
||||
private async Task UpdateDeletedChartConfig(ObjectId deletedId)
|
||||
{
|
||||
await displayConfigRepository.UpdateDeletedChartConfig(deletedId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a chart ID to the specified display configuration by delegating the operation to the display configuration repository.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The identifier of the display configuration to which the chart ID will be associated. May be null.</param>
|
||||
/// <param name="resultId">The identifier of the chart result to add to the display configuration.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean indicating whether the chart ID was successfully added.</returns>
|
||||
private async Task<bool> AddChartId(ObjectId? displayConfigId, ObjectId resultId)
|
||||
{
|
||||
var result = await displayConfigRepository.AddChartId(displayConfigId, resultId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the card configuration identifier by delegating the operation to the display configuration repository.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The identifier of the display configuration to update, or null to skip.</param>
|
||||
/// <param name="resultId">The identifier of the result to associate with the card configuration, or null to skip.</param>
|
||||
/// <returns>A task that resolves to true if the update was successful; otherwise, false.</returns>
|
||||
private async Task<bool> UpdateCardConfigId(ObjectId? displayConfigId, ObjectId? resultId)
|
||||
{
|
||||
var result = await displayConfigRepository.UpdateCardConfigId(displayConfigId, resultId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the detail configuration identifier for the specified result by delegating the operation to the display configuration repository.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The display configuration identifier to associate with the result, or <c>null</c> if not specified.</param>
|
||||
/// <param name="resultId">The result identifier whose detail configuration should be updated, or <c>null</c> if not specified.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
|
||||
private async Task<bool> UpdateDetailConfigId(ObjectId? displayConfigId, ObjectId? resultId)
|
||||
{
|
||||
var result = await displayConfigRepository.UpdateDetailConfigId(displayConfigId, resultId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a display configuration change to all subscribers whose display is linked to the given configuration.
|
||||
/// Looks up displays by the configuration id, filters subscribers matching those display ids, and sends the operation and new configuration to each subscriber asynchronously.
|
||||
/// Logs and swallows any errors that occur while sending the broadcast.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The identifier of the display configuration whose change should be broadcast to related subscribers.</param>
|
||||
/// <param name="operationType">The type of operation performed on the configuration (e.g., create, update, delete) to convey to subscribers.</param>
|
||||
/// <param name="newDisplayConfig">The new display configuration payload to send to subscribers, or null if not applicable for the operation.</param>
|
||||
private async void SendDisplayConfigBroadcast(ObjectId displayConfigId, OperationType operationType,
|
||||
object? newDisplayConfig)
|
||||
object? newDisplayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -564,6 +778,14 @@ public class DisplayConfigService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a SmartDisplay configuration update to the relevant subscribers and notifies all clients of the latest display configuration.
|
||||
/// When both the new and old configurations are provided, the update is dispatched to the matching subscribers; otherwise, an error is logged.
|
||||
/// Regardless of the outcome, a global update message is sent to all clients so they can refresh the display configuration.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The identifier of the display configuration used to look up the associated displays and to broadcast the global update.</param>
|
||||
/// <param name="newDisplayConfig">The new SmartDisplay configuration to propagate to subscribers, or null when not available.</param>
|
||||
/// <param name="oldDisplayConfig">The previous SmartDisplay configuration used to build the update payload, or null when not available.</param>
|
||||
private async void SendSmartDisplayConfigBroadcast(ObjectId displayConfigId, SmartDisplay? newDisplayConfig,
|
||||
SmartDisplay? oldDisplayConfig)
|
||||
{
|
||||
@@ -592,8 +814,14 @@ public class DisplayConfigService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends updated display configuration values to all WebSocket subscribers for each property that has changed between the old and new configurations. If the old configuration is null, no update messages are sent.
|
||||
/// </summary>
|
||||
/// <param name="subscribers">The list of WebSocket subscribers that will receive the display configuration update messages.</param>
|
||||
/// <param name="oldDisplayDisplayConfig">The previous smart display configuration, or null if there is no prior configuration to compare against.</param>
|
||||
/// <param name="newDisplayDisplayConfig">The new smart display configuration whose values will be sent to subscribers.</param>
|
||||
private void SendSmartDisplayConfigUpdate(List<WsSubscriber> subscribers,
|
||||
SmartDisplay? oldDisplayDisplayConfig, SmartDisplay? newDisplayDisplayConfig)
|
||||
SmartDisplay? oldDisplayDisplayConfig, SmartDisplay? newDisplayDisplayConfig)
|
||||
{
|
||||
// Obtener las propiedades que han cambiado
|
||||
var differentProperties = oldDisplayDisplayConfig?.GetDifferentProperties(newDisplayDisplayConfig);
|
||||
@@ -601,8 +829,8 @@ public class DisplayConfigService(
|
||||
// Enviar un mensaje a los clientes por cada propiedad que haya cambiado
|
||||
if (differentProperties != null)
|
||||
foreach (var property in differentProperties)
|
||||
foreach (var sub in subscribers)
|
||||
_ = clientMessageService.SendAsync(sub.Id, OperationType.UpdateDisplayConfig,
|
||||
newDisplayDisplayConfig?.GetType().GetProperty(property)?.GetValue(newDisplayDisplayConfig));
|
||||
foreach (var sub in subscribers)
|
||||
_ = clientMessageService.SendAsync(sub.Id, OperationType.UpdateDisplayConfig,
|
||||
newDisplayDisplayConfig?.GetType().GetProperty(property)?.GetValue(newDisplayDisplayConfig));
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,20 @@ public class DisplayService(
|
||||
IOptions<CacheSettings> cacheSettings)
|
||||
: IDisplayService
|
||||
{
|
||||
private readonly CacheSettings? _cacheSettings = cacheSettings.Value ;
|
||||
|
||||
private readonly CacheSettings? _cacheSettings = cacheSettings.Value;
|
||||
|
||||
#region Methods
|
||||
|
||||
#region Create
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new <see cref="Display"/> using the default configuration for its type.
|
||||
/// If no default configuration exists for the display type, a <see cref="NotFoundException"/> is thrown.
|
||||
/// An audit log entry is created after the display is persisted.
|
||||
/// </summary>
|
||||
/// <param name="display">The display to insert. Its <c>DisplayConfigId</c> is assigned from the resolved default configuration.</param>
|
||||
/// <returns>The inserted <see cref="Display"/> with its <c>DisplayConfigId</c> populated.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no default configuration is found for the specified display type.</exception>
|
||||
public async Task<Display> InsertOne(Display display)
|
||||
{
|
||||
var defaultConfig = await displayConfigService.GetDefaultConfig(display.Type) ??
|
||||
@@ -52,6 +60,10 @@ public class DisplayService(
|
||||
return display;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a test Display record into the repository and records a corresponding audit log entry using the current HTTP context user.
|
||||
/// </summary>
|
||||
/// <returns>The newly created <see cref="Display"/> entity.</returns>
|
||||
public async Task<Display> InsertOneTest()
|
||||
{
|
||||
var d = new Display
|
||||
@@ -69,6 +81,10 @@ public class DisplayService(
|
||||
|
||||
#region Read
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all displays from the repository and maps them to a compact representation.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="DisplayMinimalDto"/> with the mapped display data.</returns>
|
||||
public async Task<List<DisplayMinimalDto>> GetAllCompact()
|
||||
{
|
||||
var result = await displayRepository.GetAll();
|
||||
@@ -77,11 +93,22 @@ public class DisplayService(
|
||||
return listToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all display items, optionally filtered by the specified user name.
|
||||
/// </summary>
|
||||
/// <param name="userName">The user name used to filter the display items, or <see langword="null"/> to retrieve all items.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Display"/> items.</returns>
|
||||
/// <exception cref="NotImplementedException">The method has not been implemented yet.</exception>
|
||||
public Task<List<Display>> GetAll(string? userName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of <see cref="Display"/> items along with the total document count, applying page number and page size from the provided filter.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter containing the page number and page size used to determine the slice of results to return.</param>
|
||||
/// <returns>A <see cref="Task{PaginationResponse{Display}}"/> containing the requested page of displays, the current page number, the page size, and the total number of documents.</returns>
|
||||
public async Task<PaginationResponse<Display>> GetPaginatedDisplays(PaginationFilter filter)
|
||||
{
|
||||
var result = displayRepository.GetPaginatedDisplays(filter);
|
||||
@@ -97,6 +124,13 @@ public class DisplayService(
|
||||
return new PaginationResponse<Display>(dataList, filter.PageNumber, filter.PageSize, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all displays accessible to the specified user, together with their associated permissions, by resolving the user's authorizations (both unit-scoped and display-scoped).
|
||||
/// Returns an empty list when the username is null, when the user cannot be found, or when no authorizations are available; throws an exception if permissions for a unit-scoped display cannot be resolved.
|
||||
/// </summary>
|
||||
/// <param name="userName">The username whose displays should be retrieved; when null, the method returns an empty list.</param>
|
||||
/// <returns>A task that yields a list of <see cref="DisplayWithPermissionsDto"/> containing the displays the user can access along with their permissions.</returns>
|
||||
/// <exception cref="ForbbidenException">Thrown when permissions for a unit-scoped display cannot be obtained for the user.</exception>
|
||||
public async Task<List<DisplayWithPermissionsDto>> GetAllByUser(string? userName)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
@@ -119,7 +153,7 @@ public class DisplayService(
|
||||
var dis = await displayRepository.GetByUnitId(dId);
|
||||
foreach (var display in dis)
|
||||
{
|
||||
var toAdd = await GetInfo(display.Id, userName, user.Authorization,null, false, false, false, false);
|
||||
var toAdd = await GetInfo(display.Id, userName, user.Authorization, null, false, false, false, false);
|
||||
|
||||
if (toAdd != null)
|
||||
{
|
||||
@@ -160,11 +194,24 @@ public class DisplayService(
|
||||
return listToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a display with the specified identifier exists in the provided collection of display permissions.
|
||||
/// The check safely skips entries whose <c>Display</c> reference is null before comparing the display identifier.
|
||||
/// </summary>
|
||||
/// <param name="displayId">The identifier of the display to look up, compared as a string.</param>
|
||||
/// <param name="perms">The collection of display-with-permissions entries to search through.</param>
|
||||
/// <returns><c>true</c> if a non-null display with a matching identifier is found; otherwise, <c>false</c>.</returns>
|
||||
private static bool FindDisplayInPerms(string displayId, List<DisplayWithPermissionsDto> perms)
|
||||
{
|
||||
return perms.Any(p => p.Display != null && p.Display.Id.ToString() == displayId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all displays associated with the specified display type by resolving the matching display configurations and loading their corresponding displays.
|
||||
/// Each returned display is enriched with its parent configuration, and configurations without associated displays are skipped.
|
||||
/// </summary>
|
||||
/// <param name="type">The display type used to filter the display configurations.</param>
|
||||
/// <returns>A list of displays matching the specified type, each with its related configuration assigned; an empty list is returned when no displays are found.</returns>
|
||||
public async Task<List<Display>> GetByType(DisplayConfigEnums.DisplayType type)
|
||||
{
|
||||
var configs = await displayConfigService.GetByType(type);
|
||||
@@ -179,32 +226,65 @@ public class DisplayService(
|
||||
return listToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of displays associated with the specified point of care.
|
||||
/// </summary>
|
||||
/// <param name="pointOfCare">The point of care used to filter the displays.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the list of displays matching the specified point of care.</returns>
|
||||
public async Task<List<Display>> GetByPointOfCare(PointOfCare pointOfCare)
|
||||
{
|
||||
return await displayRepository.GetByPointOfCare(pointOfCare);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of displays associated with the specified configuration identifier by delegating to the display repository.
|
||||
/// </summary>
|
||||
/// <param name="configId">The configuration identifier used to look up the associated displays.</param>
|
||||
/// <returns>A task that returns the list of <see cref="Display"/> objects matching the given configuration identifier.</returns>
|
||||
public Task<List<Display>> GetByConfigId(ObjectId configId)
|
||||
{
|
||||
return displayRepository.GetByConfigId(configId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of displays associated with the specified card configuration identifier by delegating to the underlying repository.
|
||||
/// </summary>
|
||||
/// <param name="configId">The unique identifier of the card configuration used to look up the associated displays.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Display"/> objects matching the provided card configuration identifier.</returns>
|
||||
public Task<List<Display>> GetByCardConfigId(ObjectId configId)
|
||||
{
|
||||
return displayRepository.GetByCardConfigId(configId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Display"/> by its name. Throws a <see cref="NotFoundException"/> if no matching display is found.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the display to look up.</param>
|
||||
/// <returns>The <see cref="Display"/> that matches the specified name.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no display is found for the given name.</exception>
|
||||
public async Task<Display?> GetByName(string name)
|
||||
{
|
||||
return await displayRepository.GetByName(name) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Display"/> by its unique identifier from the repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the display to retrieve.</param>
|
||||
/// <returns>The matching <see cref="Display"/> if found; otherwise, <c>null</c>.</returns>
|
||||
public async Task<Display?> GetById(ObjectId id)
|
||||
{
|
||||
return await displayRepository.GetById(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a display by its identifier, enriches it with localized point-of-care information, its display configuration, and the permissions available to the current user.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the display to retrieve.</param>
|
||||
/// <param name="localeEnum">The locale used to localize the related point-of-care information.</param>
|
||||
/// <returns>A <see cref="DisplayWithPermissionsDto"/> containing the display and its associated permissions.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the current user cannot be identified from the JWT or when no display is found for the specified <paramref name="id"/>.</exception>
|
||||
public async Task<DisplayWithPermissionsDto> GetByIdWithPermissions(ObjectId id, LocaleEnum localeEnum)
|
||||
{
|
||||
var username = JwtHelper.GetUsernameFromPrincipal(httpContextAccessor.HttpContext?.User!) ??
|
||||
@@ -241,18 +321,38 @@ public class DisplayService(
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of displays associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose displays should be counted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the total number of displays for the given unit.</returns>
|
||||
public async Task<long> CountDisplaysByUnitId(ObjectId unitId)
|
||||
{
|
||||
return await displayRepository.CountByUnitId(unitId);
|
||||
}
|
||||
|
||||
public async Task<Display?> GetInfo(ObjectId id,
|
||||
string? userName,
|
||||
/// <summary>
|
||||
/// Retrieves display information by id, with optional enrichment of point-of-care, patient data, and section list based on the provided flags.
|
||||
/// Uses cached data when display configuration is requested; otherwise fetches the base display and caches the result.
|
||||
/// Fetches user authorizations from the user repository when not supplied, and logs an error if the display list cannot be populated due to a missing configuration.
|
||||
/// </summary>
|
||||
/// <param name="id">Identifier of the display to retrieve.</param>
|
||||
/// <param name="userName">Optional user name used to look up authorizations when none are provided.</param>
|
||||
/// <param name="authorizations">Optional pre-resolved authorizations used to filter the display section list.</param>
|
||||
/// <param name="locale">Optional locale applied when loading point-of-care data.</param>
|
||||
/// <param name="fillPointOfCare">If true, populates the point-of-care entries for the display.</param>
|
||||
/// <param name="fillPatientData">If true, includes patient data when retrieving point-of-care information.</param>
|
||||
/// <param name="fillDisplayList">If true, populates the display section list filtered by the resolved authorizations.</param>
|
||||
/// <param name="fillDisplayConfig">If true, retrieves the full display including its configuration (cached); otherwise retrieves the base display.</param>
|
||||
/// <param name="ct">Cancellation token to cancel the operation.</param>
|
||||
/// <returns>The requested <see cref="Display"/>, or <c>null</c> if no display is found for the given id.</returns>
|
||||
public async Task<Display?> GetInfo(ObjectId id,
|
||||
string? userName,
|
||||
List<Authorization>? authorizations,
|
||||
LocaleEnum? locale,
|
||||
LocaleEnum? locale,
|
||||
bool fillPointOfCare = true,
|
||||
bool fillPatientData = false,
|
||||
bool fillDisplayList = true,
|
||||
bool fillPatientData = false,
|
||||
bool fillDisplayList = true,
|
||||
bool fillDisplayConfig = true,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
@@ -261,7 +361,7 @@ public class DisplayService(
|
||||
Display? display;
|
||||
if (fillDisplayConfig)
|
||||
{
|
||||
|
||||
|
||||
// Clave: display con configuración
|
||||
var (key, ttl) = CacheKeys.DisplayWithConfigKeyWithTtl(_cacheSettings, id);
|
||||
|
||||
@@ -270,7 +370,7 @@ public class DisplayService(
|
||||
async () => await BuildDisplayWithConfig(id, ct),
|
||||
ttl,
|
||||
ct);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -285,7 +385,7 @@ public class DisplayService(
|
||||
|
||||
}
|
||||
if (display == null) return null;
|
||||
|
||||
|
||||
// PointOfCare (cacheado en su propio servicio)
|
||||
if (fillPointOfCare)
|
||||
foreach (var poc in display.PointOfCareIdList)
|
||||
@@ -315,7 +415,13 @@ public class DisplayService(
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Builds a <see cref="Display"/> enriched with its display configuration. Returns <c>null</c> when the display is not found, and for <see cref="SmartDisplay"/> instances that have a <c>CardRotatingLayout</c>, resolves and assigns each card's configuration, falling back to an empty <see cref="CardConfig"/> when a card configuration cannot be retrieved.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the display to load.</param>
|
||||
/// <param name="ct">The cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>The <see cref="Display"/> with its configuration populated, or <c>null</c> if no display exists for the given <paramref name="id"/>.</returns>
|
||||
private async Task<Display?> BuildDisplayWithConfig(ObjectId id, CancellationToken ct)
|
||||
{
|
||||
var display = await displayRepository.GetById(id);
|
||||
@@ -328,10 +434,10 @@ public class DisplayService(
|
||||
await displayConfigService.GetById(display.DisplayConfigId, display.UnitId, type);
|
||||
|
||||
if (type != DisplayConfigEnums.DisplayType.SmartDisplay ||
|
||||
display.DisplayConfig is not SmartDisplay { CardRotatingLayout: not null } smart)
|
||||
display.DisplayConfig is not SmartDisplay { CardRotatingLayout: not null } smart)
|
||||
return display;
|
||||
|
||||
if(smart.CardRotatingLayout== null)
|
||||
|
||||
if (smart.CardRotatingLayout == null)
|
||||
return display;
|
||||
|
||||
foreach (var card in smart.CardRotatingLayout)
|
||||
@@ -339,15 +445,24 @@ public class DisplayService(
|
||||
?? new CardConfig();
|
||||
|
||||
return display;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the display sections accessible to a specific user, filtered by display type, based on the user's authorities (either provided or fetched from the authority service).
|
||||
/// Supports both direct display references and unit-based references, marks the currently selected display, and returns an empty list if the user is not found or no matching sections exist.
|
||||
/// </summary>
|
||||
/// <param name="type">The display type used to filter the returned sections.</param>
|
||||
/// <param name="currentDisplay">The identifier of the currently selected display, which will be flagged as selected in the result; may be null.</param>
|
||||
/// <param name="userName">The username used to look up the user and their authorities; if null, an empty list is returned.</param>
|
||||
/// <param name="authorizations">Optional pre-fetched list of user authorities; when null, authorities are retrieved from the authority service.</param>
|
||||
/// <returns>A task that resolves to a list of <see cref="MinimalDisplaySection"/> items accessible to the user and matching the specified display type.</returns>
|
||||
public async Task<List<MinimalDisplaySection>> GetDisplaySectionByUser(
|
||||
DisplayConfigEnums.DisplayType type,
|
||||
ObjectId? currentDisplay,
|
||||
string? userName,
|
||||
List<Authorization>? authorizations)
|
||||
DisplayConfigEnums.DisplayType type,
|
||||
ObjectId? currentDisplay,
|
||||
string? userName,
|
||||
List<Authorization>? authorizations)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -413,6 +528,10 @@ public class DisplayService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all display configurations of type DisplayNurse and SmartDisplay, mapping them into minimal display sections and grouping them within a display list DTO.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a MinimalDisplayListDto with the populated DisplayNurse and SmartDisplay collections.</returns>
|
||||
public async Task<MinimalDisplayListDto> GetAllDisplaySection()
|
||||
{
|
||||
var minimalDisplayListDto = new MinimalDisplayListDto();
|
||||
@@ -441,11 +560,23 @@ public class DisplayService(
|
||||
return minimalDisplayListDto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of displays associated with the specified unit identifier by delegating to the display repository.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose displays should be returned.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of <see cref="Display"/> objects for the given unit.</returns>
|
||||
public async Task<List<Display>> GetByUnitId(ObjectId unitId)
|
||||
{
|
||||
return await displayRepository.GetByUnitId(unitId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all available points of care (POC) and their associated unit information for the specified display identifiers. Invalid display ID strings are silently skipped, virtual points of care can optionally be excluded, and a <see cref="NotFoundException"/> is thrown when a resolved unit cannot be found; any unexpected error is logged and an empty result is returned.
|
||||
/// </summary>
|
||||
/// <param name="displayIds">A list of display identifier strings used to resolve the related units and their available points of care.</param>
|
||||
/// <param name="excludeVirtual">When set to <c>true</c>, virtual points of care are excluded from the result; otherwise, they are included.</param>
|
||||
/// <returns>A <see cref="PocAndUnitDto"/> containing the available points of care and their associated unit details.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when a unit associated with one of the resolved display identifiers cannot be found.</exception>
|
||||
public async Task<PocAndUnitDto> GetAllAvailablePoc(List<string> displayIds, bool excludeVirtual = false)
|
||||
{
|
||||
try
|
||||
@@ -492,6 +623,11 @@ public class DisplayService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all points of care associated with the specified display. Returns an empty list when the display is not found, when no associated points of care exist, or when an error occurs during retrieval.
|
||||
/// </summary>
|
||||
/// <param name="id">The ObjectId of the display whose points of care should be retrieved.</param>
|
||||
/// <returns>A list of points of care linked to the display, or an empty list if the display cannot be found or if an error is encountered.</returns>
|
||||
public async Task<List<PointOfCare>> GetAllPocsByDisplayId(ObjectId id)
|
||||
{
|
||||
try
|
||||
@@ -516,6 +652,11 @@ public class DisplayService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the display configuration locations associated with the specified display configuration ID, mapping each display to its corresponding unit name. If a unit cannot be found for a display, the resulting location's unit name will be null.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The identifier of the display configuration whose locations are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="DisplayConfigLocationDto"/> objects with display and unit information.</returns>
|
||||
public async Task<List<DisplayConfigLocationDto>> GetDisplayConfigLocations(ObjectId displayConfigId)
|
||||
{
|
||||
var displays = await GetByConfigId(displayConfigId);
|
||||
@@ -534,6 +675,11 @@ public class DisplayService(
|
||||
return locations;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified display configuration is currently in use by checking if it is referenced by any related entity.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The unique identifier of the display configuration to check.</param>
|
||||
/// <returns><c>true</c> if the display configuration is referenced by at least one entity; otherwise, <c>false</c>.</returns>
|
||||
public async Task<bool> IsDisplayConfigInUse(ObjectId displayConfigId)
|
||||
{
|
||||
return await displayRepository.IsDisplayConfigInUse(displayConfigId) > 0;
|
||||
@@ -547,21 +693,35 @@ public class DisplayService(
|
||||
* En esta actualización se espera una resubscipción al id del display ya que actualizar los PoC conlleva actualizar
|
||||
* subscrioptor y locations para las observaciones
|
||||
*/
|
||||
/// <summary>
|
||||
/// Updates the point of care list associated with the specified display, invalidating the related cache entries and broadcasting the change to subscribers.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The identifier of the display whose point of care list is being updated.</param>
|
||||
/// <param name="listPocObId">The list of point of care object identifiers to assign to the display.</param>
|
||||
/// <returns>The updated <see cref="Display"/> instance after the point of care list change.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no display exists for the specified <paramref name="objectId"/>.</exception>
|
||||
/// <exception cref="ConflictException">Thrown when the point of care list update cannot be persisted.</exception>
|
||||
public async Task<Display?> UpdatePointOfCareList(ObjectId objectId, List<ObjectId> listPocObId)
|
||||
{
|
||||
var oldDisplay = await displayRepository.GetById(objectId) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var displayToReturn = await displayRepository.UpdatePointOfCareList(objectId, listPocObId) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
|
||||
|
||||
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(objectId));
|
||||
|
||||
|
||||
SendDisplayBroadcast(displayToReturn, OperationType.UpdateDisplayPoC);
|
||||
|
||||
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplay, displayToReturn);
|
||||
return displayToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the configuration of an existing display by casting the new configuration to its specific type based on <see cref="DisplayConfigEnums.DisplayType"/>, supporting <c>DisplayNurse</c> and <c>SmartDisplay</c>. On a successful update, broadcasts the change, creates an audit log entry, and invalidates the related cache entry. Returns <c>null</c> if the provided configuration type is not supported or the cast results in <c>null</c>.
|
||||
/// </summary>
|
||||
/// <param name="oldDisplay">The existing display whose configuration will be updated.</param>
|
||||
/// <param name="newDisplayConfig">The new configuration to apply, or <c>null</c> if no update is provided.</param>
|
||||
/// <returns>The updated <see cref="Display"/> if the configuration was successfully applied; otherwise, <c>null</c>.</returns>
|
||||
public async Task<Display?> UpdateConfig(Display oldDisplay, DisplayConfig? newDisplayConfig)
|
||||
{
|
||||
var newDisplayConfigCast = new DisplayConfig();
|
||||
@@ -584,15 +744,21 @@ public class DisplayService(
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplay,
|
||||
displayToReturn);
|
||||
}
|
||||
|
||||
|
||||
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(oldDisplay.Id));
|
||||
|
||||
|
||||
return displayToReturn;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the configuration ID associated with the specified display. On a successful update, the related cache entries are invalidated, a display update broadcast is sent, and an audit log entry is created.
|
||||
/// </summary>
|
||||
/// <param name="oldDisplay">The display whose configuration ID is being updated.</param>
|
||||
/// <param name="configId">The new configuration ID to assign to the display.</param>
|
||||
/// <returns>The updated display, or <c>null</c> if the display was not found.</returns>
|
||||
public async Task<Display?> UpdateConfigId(Display oldDisplay, ObjectId configId)
|
||||
{
|
||||
var displayToReturn = await displayRepository.UpdateConfigId(oldDisplay.Id, configId);
|
||||
@@ -606,13 +772,21 @@ public class DisplayService(
|
||||
return displayToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the configuration preset associated with the specified display, invalidates the display cache, records an audit log entry, and broadcasts a notification to subscribers based on the resolved display type (DisplayNurse, SmartDisplay, or Unknown). Throws a not-found exception when the update result or configuration cannot be resolved, and an invalid-format exception when the configuration type is not one of the handled types.
|
||||
/// </summary>
|
||||
/// <param name="objectIdDisplay">The identifier of the display whose configuration preset is being updated.</param>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the new configuration preset to apply to the display.</param>
|
||||
/// <returns>The updated <see cref="Display"/> entity, or <c>null</c> if the update could not be completed.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the update result or the resolved configuration is <c>null</c>.</exception>
|
||||
/// <exception cref="InvalidFormatException">Thrown when the configuration type is not one of the handled display types.</exception>
|
||||
public async Task<Display?> UpdateConfigPreset(ObjectId objectIdDisplay, ObjectId objectIdConfigDisplay)
|
||||
{
|
||||
var oldConfig = await displayConfigService.GetById(objectIdConfigDisplay);
|
||||
var result = await displayRepository.UpdateConfigPreset(objectIdDisplay, objectIdConfigDisplay);
|
||||
|
||||
|
||||
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(objectIdDisplay));
|
||||
|
||||
|
||||
var config = await displayConfigService.GetById(objectIdConfigDisplay);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldConfig, config);
|
||||
if (result == null || config == null)
|
||||
@@ -636,14 +810,21 @@ public class DisplayService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the name of an existing display identified by the given identifier, invalidates the related cache entries, and records an audit log entry for the change.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the display to update.</param>
|
||||
/// <param name="name">The new name to assign to the display.</param>
|
||||
/// <returns>The updated <see cref="Display"/> instance, or <c>null</c> if the update could not be performed.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no display is found for the specified <paramref name="id"/>.</exception>
|
||||
public async Task<Display?> UpdateName(ObjectId id, string name)
|
||||
{
|
||||
var display = await displayRepository.GetById(id) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var newDisplay = await displayRepository.UpdateName(display, name);
|
||||
|
||||
|
||||
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(id));
|
||||
|
||||
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, display, newDisplay);
|
||||
return newDisplay;
|
||||
}
|
||||
@@ -652,27 +833,37 @@ public class DisplayService(
|
||||
|
||||
#region Delete
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a display by its identifier, removing the record, invalidating the related cache, clearing associated authorities, and recording an audit log entry.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the display to delete.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the display has been successfully deleted.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no display is found for the specified <paramref name="id"/>.</exception>
|
||||
public async Task<bool> DeleteDisplay(ObjectId id)
|
||||
{
|
||||
var display = await displayRepository.GetById(id) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
await displayRepository.DeleteAsync(id);
|
||||
|
||||
|
||||
// Invalidar CACHE (colección completa)
|
||||
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(id));
|
||||
|
||||
await authorityService.DeleteByDisplayId(id);
|
||||
|
||||
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, display, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all displays associated with the specified unit identifier, invalidates the displays cache, and removes related authority data for the unit.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose displays and related authority data will be removed.</param>
|
||||
public async Task DeleteDisplaysByUnitId(ObjectId unitId)
|
||||
{
|
||||
await displayRepository.DeleteManyByUnitId(unitId);
|
||||
// Invalidar CACHE (colección completa)
|
||||
await cacheService.DeleteByPatternAsync(CacheKeyPatterns.ForEntity(CacheEnum.EntityType.Displays));
|
||||
|
||||
|
||||
await authorityService.DeleteByUnitId(unitId);
|
||||
}
|
||||
|
||||
@@ -680,18 +871,34 @@ public class DisplayService(
|
||||
|
||||
#region Send Notification
|
||||
|
||||
/// <summary>
|
||||
/// Sends a smart display configuration update broadcast asynchronously to all specified WebSocket subscribers.
|
||||
/// </summary>
|
||||
/// <param name="subscribers">The list of WebSocket subscribers that will receive the smart display configuration update.</param>
|
||||
/// <param name="config">The smart display configuration to broadcast. May be <c>null</c> if no configuration is provided.</param>
|
||||
private void SendSmartDisplayBroadcast(List<WsSubscriber> subscribers, SmartDisplay? config)
|
||||
{
|
||||
foreach (var subscriber in subscribers)
|
||||
_ = clientMessageService.SendAsync(subscriber.Id, OperationType.UpdateSmartDisplayConfig, config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a nurse display configuration update to all specified WebSocket subscribers by sending an asynchronous update message to each one.
|
||||
/// </summary>
|
||||
/// <param name="subscribers">The list of WebSocket subscribers that will receive the nurse display configuration update.</param>
|
||||
/// <param name="config">The nurse display configuration to broadcast, which may be <c>null</c>.</param>
|
||||
private void SendNurseDisplayBroadcast(List<WsSubscriber> subscribers, DisplayNurse? config)
|
||||
{
|
||||
foreach (var subscriber in subscribers)
|
||||
_ = clientMessageService.SendAsync(subscriber.Id, OperationType.UpdateNurseDisplayConfig, config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a broadcast message to all subscribers associated with the specified display.
|
||||
/// Currently only handles the <see cref="OperationType.UpdateDisplayPoC"/> operation, dispatching an asynchronous notification to each subscriber; other operation types are ignored.
|
||||
/// </summary>
|
||||
/// <param name="display">The display whose subscribers will receive the broadcast; used to filter the subscriber list by its identifier.</param>
|
||||
/// <param name="operation">The type of operation being broadcast, which determines the action taken on matching subscribers.</param>
|
||||
private void SendDisplayBroadcast(Display display, OperationType operation)
|
||||
{
|
||||
var subscribers = subscribersService.GetSubscribers().Where(s =>
|
||||
|
||||
@@ -11,6 +11,9 @@ using Serilog;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a concrete implementation of the <see cref="IFileService"/> contract for performing file-related operations.
|
||||
/// </summary>
|
||||
public class FileService : IFileService
|
||||
{
|
||||
private readonly string? _assetsDirectory;
|
||||
@@ -32,6 +35,12 @@ public class FileService : IFileService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Copies the provided uploaded files into the configured update directory, creating each file on disk.
|
||||
/// Returns false if the update directory is not configured (null, empty, or whitespace); otherwise returns true after all files have been copied.
|
||||
/// </summary>
|
||||
/// <param name="files">The collection of uploaded form files to be written to the update directory.</param>
|
||||
/// <returns>A task that resolves to true when every file is successfully copied, or false when the update directory is not configured.</returns>
|
||||
public async Task<bool> CopyUpdateFiles(ICollection<IFormFile> files)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_updateDirectory)) return false;
|
||||
@@ -45,6 +54,12 @@ public class FileService : IFileService
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously uploads a collection of asset files into a directory organized by the specified theme. If the assets directory is not configured, the method returns false; otherwise, it ensures the target directory exists, writes each file to disk, and returns true.
|
||||
/// </summary>
|
||||
/// <param name="files">The collection of uploaded form files to persist to the assets directory.</param>
|
||||
/// <param name="themeParse">The asset theme used to determine the subdirectory in which the files will be stored.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the files are successfully written, or <c>false</c> when the assets directory path is not configured.</returns>
|
||||
public async Task<bool> UploadAssetFiles(ICollection<IFormFile> files, FileEnum.AssetTheme themeParse)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_assetsDirectory)) return false;
|
||||
@@ -60,6 +75,12 @@ public class FileService : IFileService
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all asset files from the subdirectory that matches the specified theme, creating the subdirectory if it does not exist.
|
||||
/// Returns an empty list when the assets directory is not configured or when an error occurs while reading the files.
|
||||
/// </summary>
|
||||
/// <param name="themeParse">The theme used to locate the corresponding subdirectory within the assets directory.</param>
|
||||
/// <returns>A list of <see cref="AssetDto"/> objects containing the name, extension, and full path of each file found; an empty list if the assets directory is not configured or an error occurs.</returns>
|
||||
public List<AssetDto> GetAllAssetFile(FileEnum.AssetTheme themeParse)
|
||||
{
|
||||
try
|
||||
@@ -91,6 +112,13 @@ public class FileService : IFileService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of file paths contained in the specified directory.
|
||||
/// If the directory does not exist, a warning is logged and an empty list is returned;
|
||||
/// if an error occurs during retrieval, it is logged and an empty list is returned.
|
||||
/// </summary>
|
||||
/// <param name="directoryPath">The path of the directory to search for files.</param>
|
||||
/// <returns>A list of file paths found in the directory, or an empty list if the directory does not exist or an error occurs.</returns>
|
||||
public List<string> GetFilesInDirectory(string directoryPath)
|
||||
{
|
||||
List<string> fileList = [];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,10 @@ public class HistoricalConfigChangesService(
|
||||
{
|
||||
private readonly ILogger<HistoricalConfigChangesService> _logger = logger;
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a historical configuration change record by its identifier and creates an audit log entry recording the deletion.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the historical configuration change to delete.</param>
|
||||
public async Task DeleteHistoricalConfigChange(ObjectId id)
|
||||
{
|
||||
var filter = Builders<HistoricalConfigChanges>.Filter.Eq("_id", id);
|
||||
@@ -28,6 +32,11 @@ public class HistoricalConfigChangesService(
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, result, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent historical configuration change entry for the specified configuration type.
|
||||
/// </summary>
|
||||
/// <param name="configType">The configuration type used to look up the last historical change.</param>
|
||||
/// <returns>The most recent <see cref="HistoricalConfigChanges"/> entry, or <c>null</c> if no changes exist for the given type.</returns>
|
||||
public async Task<HistoricalConfigChanges?> FindLastConfigChanges(DisplayConfigEnums.ConfigTypes configType)
|
||||
{
|
||||
var result = await historicalConfigChangesRepository.FindLastHistoricalConfigChangesByType(configType);
|
||||
@@ -35,28 +44,58 @@ public class HistoricalConfigChangesService(
|
||||
return result.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a historical configuration change record by its unique identifier.
|
||||
/// Returns null when no matching record is found in the repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the historical configuration change to retrieve.</param>
|
||||
/// <returns>The matching <see cref="HistoricalConfigChanges"/> record, or null if no record is found.</returns>
|
||||
public async Task<HistoricalConfigChanges?> Get(ObjectId id)
|
||||
{
|
||||
return await historicalConfigChangesRepository.FindById(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all historical configuration changes from the repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of all <see cref="HistoricalConfigChanges"/> records.</returns>
|
||||
public async Task<ICollection<HistoricalConfigChanges>> GetAll()
|
||||
{
|
||||
return await historicalConfigChangesRepository.FindAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent historical configuration changes for the specified configuration type, limited to a given number of entries.
|
||||
/// </summary>
|
||||
/// <param name="type">The configuration type used to filter the historical changes.</param>
|
||||
/// <param name="num">The maximum number of recent changes to return. Defaults to 10.</param>
|
||||
/// <returns>A collection of the latest <see cref="HistoricalConfigChanges"/> entries matching the specified type.</returns>
|
||||
public async Task<ICollection<HistoricalConfigChanges>> GetByType(DisplayConfigEnums.ConfigTypes type, int num = 10)
|
||||
{
|
||||
return await historicalConfigChangesRepository.FindLastHistoricalConfigChangesByType(type, num);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent historical configuration changes for a given user, optionally filtered by configuration type and limited to a specified maximum number of entries.
|
||||
/// </summary>
|
||||
/// <param name="user">The identifier of the user whose historical configuration changes are being retrieved.</param>
|
||||
/// <param name="configTypes">Optional filter for the configuration type; when null, all configuration types are included.</param>
|
||||
/// <param name="num">The maximum number of historical change entries to return. Defaults to 10.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of the user's historical configuration changes.</returns>
|
||||
public async Task<ICollection<HistoricalConfigChanges>> GetByUser(string user,
|
||||
DisplayConfigEnums.ConfigTypes? configTypes = null, int num = 10)
|
||||
DisplayConfigEnums.ConfigTypes? configTypes = null, int num = 10)
|
||||
{
|
||||
return await historicalConfigChangesRepository.FindLastHistoricalConfigChangesByUser(user, configTypes, num);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new historical configuration change record into the repository and creates a corresponding audit log entry.
|
||||
/// If the repository returns null, a conflict exception is thrown; on failure, the error is logged and null is returned.
|
||||
/// </summary>
|
||||
/// <param name="historicalConfigChanges">The historical configuration change entity to insert.</param>
|
||||
/// <returns>The inserted <see cref="HistoricalConfigChanges"/> entity, or null if the operation fails.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the repository returns null after the insert operation.</exception>
|
||||
public async Task<HistoricalConfigChanges?> InsertOne(HistoricalConfigChanges historicalConfigChanges)
|
||||
{
|
||||
try
|
||||
@@ -74,8 +113,15 @@ public class HistoricalConfigChangesService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing historical configuration change record, creating an audit log entry for the change.
|
||||
/// If the record is not found, a <see cref="ConflictException"/> is thrown; any other exception is logged and the method returns <c>null</c>.
|
||||
/// </summary>
|
||||
/// <param name="historicalConfigChanges">The historical configuration change entity containing the updated values to persist.</param>
|
||||
/// <returns>The updated <see cref="HistoricalConfigChanges"/> entity on success, or <c>null</c> if an error occurs during the operation.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when no existing historical configuration change is found with the specified <c>Id</c>.</exception>
|
||||
public async Task<HistoricalConfigChanges?> UpdateHistoricalConfigChange(
|
||||
HistoricalConfigChanges historicalConfigChanges)
|
||||
HistoricalConfigChanges historicalConfigChanges)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -93,8 +139,17 @@ public class HistoricalConfigChangesService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously logs a change to a display configuration, recording the user who made the change,
|
||||
/// the configuration type, the previous value, and the new value. Logs an error if the insertion fails,
|
||||
/// or a debug message if it succeeds.
|
||||
/// </summary>
|
||||
/// <param name="user">The username of the user who made the configuration change.</param>
|
||||
/// <param name="configType">The type of configuration that was changed.</param>
|
||||
/// <param name="newConfig">The new configuration value after the change.</param>
|
||||
/// <param name="oldConfig">The previous configuration value before the change.</param>
|
||||
public async Task LogConfigChanged(string user, DisplayConfigEnums.ConfigTypes configType, string newConfig,
|
||||
string oldConfig)
|
||||
string oldConfig)
|
||||
{
|
||||
HistoricalConfigChanges historicalConfigChanges = new()
|
||||
{
|
||||
|
||||
@@ -4,6 +4,14 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously persists the specified <see cref="ApiRequest"/>.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to be saved.</param>
|
||||
Task SaveRequestAsync(ApiRequest apiRequest);
|
||||
/// <summary>
|
||||
/// Asynchronously saves the specified API request.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to persist.</param>
|
||||
Task SaveRequest(ApiRequest apiRequest);
|
||||
}
|
||||
@@ -7,39 +7,135 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IAdminPanelService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously deletes a unit identified by its unique identifier from the data store.
|
||||
/// Returns a result indicating whether the deletion was successful (e.g., true if the unit was found and removed, false otherwise).
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the unit was successfully deleted; otherwise, <c>false</c> if the unit was not found.</returns>
|
||||
Task<bool> DeleteUnitById(ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Inserts a new <see cref="Unit"/> into the data store and returns the inserted entity.
|
||||
/// </summary>
|
||||
/// <param name="unit">The <see cref="Unit"/> to insert.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that resolves to the inserted <see cref="Unit"/>, or <see langword="null"/> if the insert could not be completed.</returns>
|
||||
Task<Unit?> InsertUnit(Unit unit);
|
||||
|
||||
#region Patient
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new patient based on the provided admin panel request.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The admin panel request containing the data needed to create the patient.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the created <see cref="Patient"/>, or <c>null</c> if no patient was created.</returns>
|
||||
Task<Patient?> CreatePatient(AdmPanelRequest apiRequest);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the patient associated with the specified location.
|
||||
/// </summary>
|
||||
/// <param name="location">The location used to look up the associated patient.</param>
|
||||
/// <returns>A task that resolves to the <see cref="Patient"/> found at the given location, or <c>null</c> if no patient is associated with that location.</returns>
|
||||
Task<Patient?> FindPatientByLocation(PatientLocation location);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="Patient"/> by their unique patient number.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The unique identifier of the patient to look up.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that yields the matching <see cref="Patient"/>, or <c>null</c> if no patient is found with the specified number.</returns>
|
||||
Task<Patient?> FindPatientByPatientNumber(string patientNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a patient from the data store using the specified unique identifier.
|
||||
/// Returns null when no patient matches the provided identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the patient if found; otherwise, null.</returns>
|
||||
Task<Patient?> FindPatientById(ObjectId id);
|
||||
|
||||
//List<person> FindAllPatient();
|
||||
/// <summary>
|
||||
/// Asynchronously finds and returns a <see cref="Patient"/> based on the criteria provided in the admission panel request.
|
||||
/// Returns <c>null</c> when no matching patient is found.
|
||||
/// </summary>
|
||||
/// <param name="request">The admission panel request containing the search criteria used to locate the patient.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that yields the matching <see cref="Patient"/> if found, or <c>null</c> otherwise.</returns>
|
||||
Task<Patient?> FindPatient(AdmPanelRequest request);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the dependency information DTO for the specified <paramref name="unit"/>.
|
||||
/// Returns <c>null</c> when no dependency information is available for the unit.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit for which to retrieve dependency information.</param>
|
||||
/// <returns>A <see cref="Task{UnitInfoDto}"/> that yields the unit dependency DTO, or <c>null</c> if none is found.</returns>
|
||||
Task<UnitInfoDto?> GetUnitDependencyDto(Unit unit);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the patient location based on the provided admission panel request.
|
||||
/// </summary>
|
||||
/// <param name="request">The admission panel request containing the patient location details to update.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdatePatientLocation(AdmPanelRequest request);
|
||||
/// <summary>
|
||||
/// Updates the patient data based on the provided admission panel request, using the existing patient record as a reference.
|
||||
/// </summary>
|
||||
/// <param name="request">The admission panel request containing the updated patient data.</param>
|
||||
/// <param name="oldPatient">The existing patient record to be updated.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the patient data was successfully updated; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdatePatientData(AdmPanelRequest request, Patient oldPatient);
|
||||
/// <summary>
|
||||
/// Archives the specified patient, marking them as inactive while preserving their record.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to be archived.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains <c>true</c> if the patient was successfully archived; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> ArchivePatient(Patient patient);
|
||||
|
||||
#endregion
|
||||
|
||||
#region ConfigObservations
|
||||
/// <summary>
|
||||
/// Asynchronously creates a configuration based on the specified observation.
|
||||
/// </summary>
|
||||
/// <param name="configObservation">The observation data used to create the configuration.</param>
|
||||
/// <returns>A task that represents the asynchronous create operation, containing a value indicating whether the configuration was created successfully.</returns>
|
||||
Task<bool> CreateConfig(ConfigObservation configObservation);
|
||||
/// <summary>
|
||||
/// Asynchronously updates the configuration based on the provided <see cref="ConfigObservation"/>, applying any required changes derived from the observation data.
|
||||
/// </summary>
|
||||
/// <param name="configObservation">The observation data used to determine and apply configuration updates.</param>
|
||||
/// <returns>A <see cref="Task{Boolean}"/> that represents the asynchronous update operation, containing a value indicating whether the configuration was successfully updated.</returns>
|
||||
Task<bool> UpdateConfig(ConfigObservation configObservation);
|
||||
/// <summary>
|
||||
/// Deletes the configuration observation item identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the configuration observation item to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the item was successfully deleted; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> DeleteConfigObservationItem(ObjectId id);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Medicienes
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Medicine"/> entity by its unique identifier from the data store.
|
||||
/// Returns <c>null</c> when no medicine matches the provided identifier.
|
||||
/// </summary>
|
||||
/// <param name="medicineId">The unique <see cref="ObjectId"/> of the medicine to look up.</param>
|
||||
/// <returns>A <see cref="Task{Medicine}"/> that resolves to the matching <see cref="Medicine"/>, or <c>null</c> if not found.</returns>
|
||||
Task<Medicine?> GetMedicineById(ObjectId medicineId);
|
||||
/// <summary>
|
||||
/// Asynchronously creates and persists a new medicine record.
|
||||
/// </summary>
|
||||
/// <param name="medicine">The medicine entity to be created and posted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the newly created <see cref="Medicine"/>, or <c>null</c> if the operation fails.</returns>
|
||||
Task<Medicine?> PostMedicine(Medicine medicine);
|
||||
/// <summary>
|
||||
/// Updates an existing medicine record in the system.
|
||||
/// </summary>
|
||||
/// <param name="medicine">The medicine entity containing the updated information to be persisted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the updated <see cref="Medicine"/> if found, or <c>null</c> if the medicine does not exist.</returns>
|
||||
Task<Medicine?> UpdateMedicine(Medicine medicine);
|
||||
/// <summary>
|
||||
/// Deletes a medicine identified by its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="medicineId">The unique identifier of the medicine to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains <c>true</c> if the medicine was successfully deleted; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> DeleteMedicineById(string medicineId);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -9,26 +9,121 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IAdmissionService : IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an admission by its unique identifier, returning <c>null</c> when no matching admission is found.
|
||||
/// </summary>
|
||||
/// <param name="admissionId">The unique identifier of the admission to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="Admission"/> if found, or <c>null</c> if no admission matches the specified identifier.</returns>
|
||||
Task<Admission?> GetAdmissionByIdAsync(ObjectId admissionId);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes an admission record identified by the specified admission ID.
|
||||
/// </summary>
|
||||
/// <param name="admissionId">The unique identifier of the admission to delete.</param>
|
||||
Task DeleteAdmissionByIdAsync(ObjectId admissionId);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the specified admission record.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission entity to remove.</param>
|
||||
Task DeleteAdmissionAsync(Admission admission);
|
||||
/// <summary>
|
||||
/// Deletes all admissions associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose admissions will be deleted.</param>
|
||||
Task DeleteAdmissionsByUnitId(ObjectId unitId);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously updates an existing admission record with the provided information.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission entity containing the updated data to be persisted.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation.</returns>
|
||||
Task UpdateAdmissionAsync(Admission admission);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a collection of admissions.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an enumerable collection of <see cref="Admission"/> objects.</returns>
|
||||
Task<IEnumerable<Admission>> GetAdmissionsAsync();
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a new admission record and returns the created entry, or <see langword="null"/> if the insertion was not performed.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission entity to be inserted into the data store.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the inserted <see cref="Admission"/>, or <see langword="null"/> when no record is produced.</returns>
|
||||
Task<Admission?> InsertAdmission(Admission admission);
|
||||
/// <summary>
|
||||
/// Admits a patient based on the provided admission details, optionally registering the patient as new.
|
||||
/// </summary>
|
||||
/// <param name="admission">The admission information used to process the patient admission.</param>
|
||||
/// <param name="isNew">Indicates whether the patient is being admitted for the first time. Defaults to <c>false</c>.</param>
|
||||
Task AdmitPatient(Admission admission, bool isNew = false);
|
||||
/// <summary>
|
||||
/// Processes the return of a patient to the admissions workflow, typically used when a patient needs to be re-queued or reinstated for admission processing.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient to be returned to admissions.</param>
|
||||
Task ReturnPatientToAdmissions(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Asynchronously returns a patient to the admissions workflow using the specified admission record.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient being returned to admissions.</param>
|
||||
/// <param name="adm">The admission record associated with the patient being returned.</param>
|
||||
Task ReturnPatientToAdmissions(ObjectId patientId, Admission adm);
|
||||
/// <summary>
|
||||
/// Retrieves a list of admissions associated with the specified patient location.
|
||||
/// </summary>
|
||||
/// <param name="location">The patient location used to filter the admissions.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of admissions for the given location.</returns>
|
||||
Task<List<Admission>> GetAdmissionByLocation(PatientLocation location);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the list of admissions associated with the specified point of care identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the point of care used to filter the admissions.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Admission"/> objects matching the specified point of care id, or an empty list if no admissions are found.</returns>
|
||||
Task<List<Admission>> GetAdmissionByPointOfCareId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Retrieves the list of admissions associated with the specified point of care, localized for the given locale.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of care whose admissions are being queried.</param>
|
||||
/// <param name="locale">The locale used to localize the returned admission data.</param>
|
||||
/// <returns>A task that resolves to the list of admissions matching the point of care and locale; an empty list when no matches are found.</returns>
|
||||
Task<List<Admission>> GetAdmissionByPointOfCareIdAndLocale(ObjectId pocId, LocaleEnum locale);
|
||||
/// <summary>
|
||||
/// Retrieves a list of admissions associated with the specified unit identifier, excluding any Point of Care (PoC) related admissions.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose admissions should be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of admissions for the specified unit, excluding PoC admissions.</returns>
|
||||
Task<List<Admission>> GetAdmissionByUnitIdWithOutPoC(ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of admissions associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The MongoDB ObjectId of the unit whose admissions should be counted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the total count of admissions for the given unit.</returns>
|
||||
Task<long> CountAdmissionsByUnitId(ObjectId unitId);
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a patient by their patient number within the specified unit, returning the matching patient search result or null if no match is found.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The patient number used to identify the patient.</param>
|
||||
/// <param name="unitId">The identifier of the unit in which the patient is being searched.</param>
|
||||
/// <returns>A task that returns the matching <see cref="PatientSearch"/> if found; otherwise, null.</returns>
|
||||
Task<PatientSearch?> SearchByPatientNumberAndDistinctUnit(string patientNumber, ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Retrieves the admission record associated with the specified patient number, returning <c>null</c> when no matching admission is found.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The unique patient number used to look up the admission.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="Admission"/>, or <c>null</c> if no admission exists for the given patient number.</returns>
|
||||
Task<Admission?> GetAdmissionByPatientNumber(string patientNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Updates a patient master list item based on the provided option change and related unit and type information.
|
||||
/// </summary>
|
||||
/// <param name="opt">The update option master list data transfer object containing the change details.</param>
|
||||
/// <param name="unitList">The collection of units associated with the master list item change.</param>
|
||||
/// <param name="typeName">The name of the type used to identify the master list item category.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation.</returns>
|
||||
Task UpdatePatientMasterListItemChange(UpdateOptionMasterListDto opt, IEnumerable<Unit> unitList, string typeName);
|
||||
/// <summary>
|
||||
/// Deletes a patient master list item identified by the specified options, units, and type name.
|
||||
/// </summary>
|
||||
/// <param name="opt">The option list used to identify the master list item to delete.</param>
|
||||
/// <param name="unitList">The collection of units associated with the master list item.</param>
|
||||
/// <param name="typeName">The name of the type associated with the master list item.</param>
|
||||
Task DeletePatientMasterListItem(OptionList opt, IEnumerable<Unit> unitList, string typeName);
|
||||
}
|
||||
@@ -8,23 +8,73 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IAlarmService : IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the most recent patient observations for the specified patient, optionally filtered by a set of fields.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose last observations should be retrieved.</param>
|
||||
/// <param name="filterObservations">An optional list of fields to restrict the returned observations; when null, no field filter is applied.</param>
|
||||
/// <returns>A task that resolves to the list of the patient's most recent <see cref="PatientObservationAlarm"/> records.</returns>
|
||||
public Task<List<PatientObservationAlarm>> FindLastObservationsByField(ObjectId patientId,
|
||||
List<Field>? filterObservations = null);
|
||||
List<Field>? filterObservations = null);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent non-expired patient observation alarms for a specific patient, based on the provided alarm fields and configuration.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose alarms are being queried.</param>
|
||||
/// <param name="dataAlarmfields">The list of fields used to identify and filter the patient observation alarm data.</param>
|
||||
/// <param name="configAlarm">The list of configuration observations that define the alarm criteria, including expiration rules.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of the latest non-expired <see cref="PatientObservationAlarm"/> entries for the patient.</returns>
|
||||
Task<List<PatientObservationAlarm>> FindLastValuesNotExpired(ObjectId patientId, List<Field> dataAlarmfields,
|
||||
List<ConfigObservation> configAlarm);
|
||||
List<ConfigObservation> configAlarm);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a patient observation alarm, optionally restricting the lookup to name-based matching only. Returns a null result when no matching alarm is found.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation alarm to be mapped.</param>
|
||||
/// <param name="onlyByName">When true, limits the mapping to lookups performed by name only.</param>
|
||||
/// <returns>A task that resolves to the mapped patient observation alarm, or null if no corresponding alarm is found.</returns>
|
||||
public Task<PatientObservationAlarm?> MapObservation(PatientObservationAlarm obs, bool onlyByName = false);
|
||||
/// <summary>
|
||||
/// Maps the given patient observation alarm to a corresponding record by name, returning a null result when no matching observation is found.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation alarm to be mapped by name.</param>
|
||||
/// <returns>A task containing the mapped <see cref="PatientObservationAlarm"/>, or <c>null</c> if no matching observation exists.</returns>
|
||||
public Task<PatientObservationAlarm?> MapObservationsByName(PatientObservationAlarm obs);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously calculates an alarm test result based on the provided patient observation value.
|
||||
/// </summary>
|
||||
/// <param name="source">The base patient observation value used as input for the alarm evaluation.</param>
|
||||
/// <param name="name">The name that identifies the alarm test to be calculated.</param>
|
||||
Task CalculateAlarmTest(BasePatientObservationValue source, string name);
|
||||
|
||||
/// <summary>
|
||||
/// Sends an alarm notification associated with the specified patient observation, using the provided name, optional code, severity, and type.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation that triggers the alarm.</param>
|
||||
/// <param name="name">The display name of the alarm.</param>
|
||||
/// <param name="code">The optional alarm code identifier, or null when not applicable.</param>
|
||||
/// <param name="severity">The severity level assigned to the alarm.</param>
|
||||
/// <param name="type">The type category of the alarm.</param>
|
||||
/// <returns>A task that represents the asynchronous alarm sending operation.</returns>
|
||||
Task SendAlarm(PatientObservation obs, string name, AlarmEnum.Name? code, AlarmEnum.Severity severity,
|
||||
AlarmEnum.Type type);
|
||||
AlarmEnum.Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously checks the observation alarm for the specified patient observation.
|
||||
/// </summary>
|
||||
/// <param name="obs4">The patient observation to evaluate for alarm conditions.</param>
|
||||
Task CheckObservationAlarm(PatientObservation obs4);
|
||||
|
||||
/// <summary>
|
||||
/// Processes a collection of patient observation alarms, associating them with the corresponding observations and patient, and recording the processing time.
|
||||
/// </summary>
|
||||
/// <param name="alarmObservations">The list of patient observation alarms to be processed.</param>
|
||||
/// <param name="observations">The list of patient observations related to the alarms.</param>
|
||||
/// <param name="patient">The patient to whom the observations and alarms belong.</param>
|
||||
/// <param name="messageTime">The timestamp associated with the message being processed.</param>
|
||||
/// <param name="observationData">Optional additional observation data used during processing.</param>
|
||||
Task ProcessAlarmObservations(List<PatientObservationAlarm> alarmObservations,
|
||||
List<PatientObservation> observations, Patient patient,
|
||||
DateTime messageTime, ObservationData? observationData = null);
|
||||
List<PatientObservation> observations, Patient patient,
|
||||
DateTime messageTime, ObservationData? observationData = null);
|
||||
}
|
||||
@@ -5,5 +5,11 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IAlertValuesService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously finds a <see cref="ConfigObservation"/> identified by the specified key.
|
||||
/// Returns <c>null</c> when no matching configuration observation exists.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier of the configuration observation to look up.</param>
|
||||
/// <returns>A task that yields the matching <see cref="ConfigObservation"/>, or <c>null</c> if no record is found.</returns>
|
||||
Task<ConfigObservation?> FindByKey(ObjectId key);
|
||||
}
|
||||
@@ -7,14 +7,64 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IAppointmentService : IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the list of patient appointments associated with the specified location.
|
||||
/// </summary>
|
||||
/// <param name="location">The patient location used to filter and locate the relevant appointments.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientAppointment"/> items matching the given location.</returns>
|
||||
Task<List<PatientAppointment>> FindByLocation(PatientLocation location);
|
||||
/// <summary>
|
||||
/// Retrieves all appointments associated with the specified patient asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose appointments are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientAppointment"/> entries for the patient.</returns>
|
||||
Task<List<PatientAppointment>> GetByPatient(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Retrieves the list of patient appointments scheduled for today for the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose appointments are being queried.</param>
|
||||
/// <param name="ct">A cancellation token to observe while waiting for the task to complete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientAppointment"/> entries for the given patient for the current day.</returns>
|
||||
Task<List<PatientAppointment>> GetTodayByPatient(ObjectId patientId, CancellationToken ct = default);
|
||||
/// <summary>
|
||||
/// Retrieves the list of patient appointments scheduled for today at the specified point of care.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of care whose appointments will be returned.</param>
|
||||
/// <param name="ct">The cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of today's patient appointments for the specified point of care.</returns>
|
||||
Task<List<PatientAppointment>> GetTodayByPoc(ObjectId pocId, CancellationToken ct = default);
|
||||
/// <summary>
|
||||
/// Asynchronously finds all <see cref="PatientAppointment"/> records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose appointments are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an <see cref="IAsyncCursor{TDocument}"/> for iterating over the matching <see cref="PatientAppointment"/> documents.</returns>
|
||||
Task<IAsyncCursor<PatientAppointment>> FindByPatientIdAsync(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Asynchronously archives the specified patient, marking the record as archived in the system.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose record should be archived.</param>
|
||||
Task Archive(Patient patient);
|
||||
/// <summary>
|
||||
/// Asynchronously archives records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient whose related records should be archived.</param>
|
||||
Task ArchiveByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Deletes a record associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient whose related record should be removed.</param>
|
||||
Task DeleteByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Processes an incoming API request in the context of the specified patient, handling the required business logic and returning when the processing completes.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to be processed.</param>
|
||||
/// <param name="patient">The patient associated with the API request.</param>
|
||||
Task ProcessApiRequest(ApiRequest apiRequest, Patient patient);
|
||||
/// <summary>
|
||||
/// Updates multiple records by replacing the old <see cref="ObjectId"/> with the new one for the field identified by <paramref name="nameId"/>.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name of the field whose <see cref="ObjectId"/> value should be updated.</param>
|
||||
/// <param name="id">The new <see cref="ObjectId"/> value to set on matching records.</param>
|
||||
/// <param name="oldId">The existing <see cref="ObjectId"/> value to be replaced.</param>
|
||||
Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId);
|
||||
}
|
||||
@@ -5,15 +5,43 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IArchivePatientCarePlanService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a list of patient care plans associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose care plans are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="PatientCarePlan"/> objects if found, or <c>null</c> if no care plans exist for the patient.</returns>
|
||||
Task<List<PatientCarePlan>?> FindByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Retrieves the list of care plans associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose care plans should be retrieved.</param>
|
||||
/// <returns>A task that returns a list of <see cref="PatientCarePlan"/> records for the patient, or <c>null</c> if no care plans are found.</returns>
|
||||
Task<List<PatientCarePlan>?> FindByPatientId(string id);
|
||||
/// <summary>
|
||||
/// Retrieves a list of patient care plans associated with the specified patient number.
|
||||
/// </summary>
|
||||
/// <param name="id">The patient number used to locate the associated care plans.</param>
|
||||
/// <returns>A task that returns a list of <see cref="PatientCarePlan"/> for the given patient number, or <c>null</c> if no care plans are found.</returns>
|
||||
Task<List<PatientCarePlan>?> FindByPatientNumber(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all patient care plans.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientCarePlan"/> objects.</returns>
|
||||
Task<List<PatientCarePlan>> FindAll();
|
||||
|
||||
// Task<PatientCarePlan?> UpdateTreatment(PatientCarePlan patientCarePla, List<OptionList> options);
|
||||
// Task<PatientCarePlan?> UpdateProcedure(PatientCarePlan patientCarePla, List<OptionList> options);
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a single patient care plan and returns the inserted entity, or null if the insert did not produce a result.
|
||||
/// </summary>
|
||||
/// <param name="patientCarePla">The patient care plan to insert.</param>
|
||||
/// <returns>A <see cref="Task{PatientCarePlan}"/> that represents the asynchronous insert operation, containing the inserted <see cref="PatientCarePlan"/> or null if no entity was returned.</returns>
|
||||
Task<PatientCarePlan?> InsertOneAsync(PatientCarePlan patientCarePla);
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a collection of patient care plans into the data store in a single bulk operation.
|
||||
/// </summary>
|
||||
/// <param name="patientCarePla">The list of <see cref="PatientCarePlan"/> entities to be inserted.</param>
|
||||
Task InsertManyAsync(List<PatientCarePlan> patientCarePla);
|
||||
|
||||
// Task Update(PatientCarePlan oldPatientCarePla, PatientCarePlan newPatientCarePla);
|
||||
|
||||
@@ -5,5 +5,10 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IArchivedPatientObservationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the list of archived patient observations associated with the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose archived observations are to be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientObservation"/> objects representing the archived observations for the patient.</returns>
|
||||
public Task<List<PatientObservation>> FindArchivedPatientObservationsFromPatient(ObjectId patientId);
|
||||
}
|
||||
@@ -4,5 +4,9 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IArchivedPatientService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all patients from the data store.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of all patients.</returns>
|
||||
public Task<List<Patient>> FindAllPatients();
|
||||
}
|
||||
@@ -5,5 +5,10 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IArchivedPatientTreatmentService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all patient treatment records associated with the specified patient asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose treatments are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientTreatment"/> records for the given patient.</returns>
|
||||
Task<List<PatientTreatment>> FindAllPatientTreatmentsByPatient(ObjectId patientId);
|
||||
}
|
||||
@@ -6,10 +6,38 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IAuthService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the current login response, returning <c>null</c> when no login state is available.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that resolves to the current <see cref="LoginResponse"/>, or <c>null</c> if no login response exists.</returns>
|
||||
Task<LoginResponse?> GetLoginResponse();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a token.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the retrieved token string.</returns>
|
||||
Task<string> GetToken();
|
||||
/// <summary>
|
||||
/// Retrieves a list of authorizations associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose authorizations are being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Authorization"/> objects matching the provided unit identifier.</returns>
|
||||
Task<List<Authorization>> GetByUnitId(ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the record identified by the specified display identifier.
|
||||
/// </summary>
|
||||
/// <param name="displayId">The display identifier of the record to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a value indicating whether the record was successfully deleted.</returns>
|
||||
Task<bool> DeleteByDisplayId(ObjectId displayId);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the entity associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose associated entity should be deleted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a value indicating whether the deletion was successful.</returns>
|
||||
Task<bool> DeleteByUnitId(ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Retrieves the list of authorizations associated with the specified user identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the user whose authorizations are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of authorizations for the user.</returns>
|
||||
Task<List<Authorization>> GetUserAuthorities(ObjectId id);
|
||||
}
|
||||
@@ -6,34 +6,111 @@ namespace adas_core.Application.Services.Interfaces
|
||||
public interface ICacheService
|
||||
{
|
||||
//Métodos básicos
|
||||
/// <summary>
|
||||
/// Stores the specified value associated with the given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier used to associate the value.</param>
|
||||
/// <param name="value">The value to store for the specified key.</param>
|
||||
void SetValue(string key, string value);
|
||||
/// <summary>
|
||||
/// Retrieves the string value associated with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key used to look up the value.</param>
|
||||
/// <returns>The value associated with the key, or <c>null</c> if the key is not found.</returns>
|
||||
string? GetValue(string key);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an object associated with the specified <paramref name="key"/>, returning null if no entry is found.
|
||||
/// When <paramref name="updateExpiration"/> is true, the expiration of the retrieved entry is extended upon access.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique identifier of the object to retrieve.</param>
|
||||
/// <param name="updateExpiration">Indicates whether the expiration of the entry should be extended when it is successfully retrieved. Defaults to true.</param>
|
||||
/// <returns>A task that represents the asynchronous retrieval operation. The task result contains the object of type <typeparamref name="T"/> associated with the key, or null if no matching entry exists.</returns>
|
||||
Task<T?> GetObjectAsync<T>(string key, bool updateExpiration = true);
|
||||
/// <summary>
|
||||
/// Asynchronously stores an object of type <typeparamref name="T"/> using the specified key, optionally updating its expiration time.
|
||||
/// When <paramref name="updateExpiration"/> is true, the entry's expiration is refreshed; otherwise the existing expiration is preserved.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier under which the object will be stored.</param>
|
||||
/// <param name="obj">The object to be stored.</param>
|
||||
/// <param name="updateExpiration">Specifies whether the expiration time of the entry should be refreshed. Defaults to true.</param>
|
||||
Task SetObjectAsync<T>(string key, T obj, bool updateExpiration = true);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an object of type <typeparamref name="T"/> associated with the specified <paramref name="key"/>.
|
||||
/// Supports an optional <paramref name="ttlOverride"/> to apply a custom time-to-live and an <paramref name="updateExpiration"/> flag
|
||||
/// to control whether the entry's expiration is refreshed on access.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique identifier of the object to retrieve.</param>
|
||||
/// <param name="ttlOverride">An optional time-to-live value that overrides the default expiration period; if <see langword="null"/>, the default TTL is used.</param>
|
||||
/// <param name="updateExpiration">A value indicating whether the object's expiration should be extended when it is successfully retrieved.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that represents the asynchronous operation, containing the retrieved object of type <typeparamref name="T"/>, or <see langword="null"/> if the object is not found.</returns>
|
||||
Task<T?> GetObjectAsync<T>(string key, TimeSpan? ttlOverride, bool updateExpiration);
|
||||
/// <summary>
|
||||
/// Asynchronously stores the specified object associated with the given key, using an optional time-to-live override and expiration update behavior.
|
||||
/// </summary>
|
||||
/// <param name="key">The identifier under which the object will be stored.</param>
|
||||
/// <param name="obj">The object to store.</param>
|
||||
/// <param name="ttlOverride">An optional <see cref="TimeSpan"/> that overrides the default time-to-live for the stored object.</param>
|
||||
/// <param name="updateExpiration">A value indicating whether the expiration of the stored object should be updated based on the provided TTL.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous storage operation.</returns>
|
||||
Task SetObjectAsync<T>(string key, T obj, TimeSpan? ttlOverride, bool updateExpiration);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes an object identified by the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique identifier of the object to delete.</param>
|
||||
Task DeleteObjectAsync(string key);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes entries matching the specified pattern and returns the number of deleted items.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern used to match the entries to be deleted.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation. The task result contains the total number of entries that were deleted.</returns>
|
||||
Task<long> DeleteByPatternAsync(string pattern);
|
||||
/// <summary>
|
||||
/// Clears the application cache, removing all cached entries.
|
||||
/// </summary>
|
||||
void CleanCache();
|
||||
|
||||
|
||||
// Métodos para transparencia y gestión de locks
|
||||
|
||||
// GetOrSet (string key)
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a cached value for the specified key, or loads and stores it using the provided loader function when the key is not present.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to identify the stored value.</param>
|
||||
/// <param name="loader">The asynchronous function invoked to produce the value when no cached entry exists for the specified key.</param>
|
||||
/// <param name="ttlOverride">An optional time-to-live duration that overrides the default expiration for the cached entry; when <c>null</c>, the default TTL is used.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The result is the cached or freshly loaded string value, or <c>null</c> when no value is available.</returns>
|
||||
Task<string?> GetOrSetValueAsync(string key, Func<Task<string>> loader, TimeSpan? ttlOverride = null);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an object associated with the specified key from the cache, or invokes the factory to create and cache a new one when the key is not found.
|
||||
/// </summary>
|
||||
/// <param name="key">The cache key used to identify the stored object.</param>
|
||||
/// <param name="factory">The asynchronous function executed to produce the object when no cached value exists for the given key.</param>
|
||||
/// <param name="ttl">The optional expiration period for the cached entry; if null, the default cache lifetime is applied.</param>
|
||||
/// <param name="cancellationToken">The token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that resolves to the cached or newly created object of type <typeparamref name="T"/>.</returns>
|
||||
Task<T> GetOrSetObjectAsync<T>(
|
||||
string key,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
string key,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
// GetOrSet especializado para GroupedObservations
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a cached object identified by the patient and grouped field, or creates and stores it via the supplied factory when no cached value exists.
|
||||
/// </summary>
|
||||
/// <param name="groupedField">The grouped field used to categorize and identify the cached object.</param>
|
||||
/// <param name="patientId">The identifier of the patient the object is associated with.</param>
|
||||
/// <param name="factory">The asynchronous factory delegate invoked to produce the object when it is not found in the cache.</param>
|
||||
/// <param name="ttl">The optional time-to-live duration applied to the cached entry.</param>
|
||||
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||
/// <returns>A task containing the cached or newly created object of type <typeparamref name="T"/>.</returns>
|
||||
Task<T> GetOrSetObjectAsync<T>(
|
||||
GroupedField groupedField,
|
||||
ObjectId patientId,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
GroupedField groupedField,
|
||||
ObjectId patientId,
|
||||
Func<Task<T>> factory,
|
||||
TimeSpan? ttl = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -7,21 +7,78 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface ICalculatedObservations
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously maps a patient observation to a corresponding target type, optionally restricting the mapping to name-based matching only.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source patient observation to be mapped.</param>
|
||||
/// <param name="onlyByName">When set to <c>true</c>, the mapping is performed considering only the observation name; otherwise, additional mapping criteria are applied. Defaults to <c>false</c>.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that represents the asynchronous mapping operation. The result is the mapped observation of type <typeparamref name="T"/>, or <c>null</c> when no matching mapping is found.</returns>
|
||||
Task<T?> Map<T>(T obs, bool onlyByName = false) where T : BasePatientObservation;
|
||||
/// <summary>
|
||||
/// Asynchronously maps the specified <paramref name="treatment"/> to a <see cref="PatientTreatment"/> result.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The patient treatment instance to map.</param>
|
||||
/// <returns>A task that represents the asynchronous mapping operation. The task result contains the mapped <see cref="PatientTreatment"/>.</returns>
|
||||
Task<PatientTreatment> Map(PatientTreatment treatment);
|
||||
/// <summary>
|
||||
/// Asynchronously maps a <see cref="PatientDiagnosis"/> to a <see cref="PatientDiagnosis"/> representation.
|
||||
/// </summary>
|
||||
/// <param name="diagnosis">The patient diagnosis to be mapped.</param>
|
||||
/// <returns>A task that represents the asynchronous mapping operation, containing the mapped <see cref="PatientDiagnosis"/>.</returns>
|
||||
Task<PatientDiagnosis> Map(PatientDiagnosis diagnosis);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously maps the specified <see cref="PumpObservation"/> to a <see cref="PumpObservation"/> result.
|
||||
/// </summary>
|
||||
/// <param name="pumpObservation">The <see cref="PumpObservation"/> to be mapped.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> representing the asynchronous operation, containing the mapped <see cref="PumpObservation"/>.</returns>
|
||||
Task<PumpObservation> Map(PumpObservation pumpObservation);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously calculates a medicine observation for a patient based on their active medicines.
|
||||
/// </summary>
|
||||
/// <param name="activeMedicines">The list of medicines currently active for the patient.</param>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observation is being calculated.</param>
|
||||
Task CalculateMedicineObservation(List<Medicine> activeMedicines, ObjectId patientId);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the active bolus for the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose active bolus is to be calculated.</param>
|
||||
Task CalculateActiveBolus(ObjectId patientId);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the active treatments associated with a specific patient by their unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose active treatments are being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of nullable <see cref="PatientTreatment"/> entries that represent the active treatments for the specified patient.</returns>
|
||||
Task<IEnumerable<PatientTreatment?>> GetActiveTreatmentsByPatient(ObjectId id);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves time inconsistencies between the new observation and the last stored observation, returning a corrected version when applicable.
|
||||
/// </summary>
|
||||
/// <param name="newObservation">The new patient observation to validate and reconcile against the previous observation's time information.</param>
|
||||
/// <returns>A task that yields the fixed <see cref="PatientObservation"/>, or <c>null</c> when no time inconsistency is detected or no correction is required.</returns>
|
||||
Task<PatientObservation?> FixTimeInconsistencyWithLast(PatientObservation newObservation);
|
||||
/// <summary>
|
||||
/// Performs pre-insertion mapping on a list of patient observations, transforming or preparing the data before it is persisted.
|
||||
/// </summary>
|
||||
/// <param name="listToInsert">The list of patient observations to be pre-mapped prior to insertion.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the mapped list of patient observations.</returns>
|
||||
Task<List<PatientObservation>> PreMapList(List<PatientObservation> listToInsert);
|
||||
/// <summary>
|
||||
/// Maps the source alarm onto the specified patient observation and returns the resulting observation.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation to which the source alarm will be mapped.</param>
|
||||
/// <param name="alarmToInsert">The patient observation alarm to insert and map as the source alarm.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the patient observation with the source alarm mapped.</returns>
|
||||
Task<PatientObservation> MapSourceAlarm(PatientObservation obs, PatientObservationAlarm alarmToInsert);
|
||||
|
||||
/// <summary>
|
||||
/// Sends an alarm associated with the specified patient observation, optionally classified by an alarm code.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation that triggered the alarm.</param>
|
||||
/// <param name="name">The name associated with the alarm.</param>
|
||||
/// <param name="code">An optional alarm code categorizing the type of alarm to send.</param>
|
||||
Task SendAlarm(PatientObservation obs, string name, AlarmEnum.Name? code);
|
||||
}
|
||||
@@ -6,15 +6,77 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface ICalculatedObservationsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientObservation"/> to a corresponding <see cref="PatientObservation"/>, typically resolved through a lookup or translation process.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source <see cref="PatientObservation"/> to be mapped.</param>
|
||||
/// <param name="onlyByName">When <c>true</c>, restricts the mapping to be performed by name only; otherwise, additional criteria are used.</param>
|
||||
/// <returns>A <see cref="Task{PatientObservation}"/> that resolves to the mapped <see cref="PatientObservation"/>, or <c>null</c> if no match is found.</returns>
|
||||
Task<PatientObservation?> Map(PatientObservation obs, bool onlyByName = false);
|
||||
/// <summary>
|
||||
/// Asynchronously maps a <see cref="PatientObservationAlarm"/> instance, optionally performing the mapping
|
||||
/// by name only when <paramref name="onlyByName"/> is <c>true</c>. Returns <c>null</c> when no matching
|
||||
/// alarm can be resolved based on the selected lookup strategy.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source patient observation alarm to map.</param>
|
||||
/// <param name="onlyByName">When <c>true</c>, restricts the mapping to match by name only; otherwise the
|
||||
/// full mapping logic is applied. Defaults to <c>false</c>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the mapped
|
||||
/// <see cref="PatientObservationAlarm"/>, or <c>null</c> if no mapping is found.</returns>
|
||||
Task<PatientObservationAlarm?> Map(PatientObservationAlarm obs, bool onlyByName = false);
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientTreatment"/> instance to a projected <see cref="PatientTreatment"/> representation asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The source <see cref="PatientTreatment"/> to be mapped.</param>
|
||||
/// <returns>A <see cref="Task{PatientTreatment}"/> that represents the asynchronous mapping operation. The result is the mapped <see cref="PatientTreatment"/>, or <see langword="null"/> if no mapping could be produced.</returns>
|
||||
Task<PatientTreatment?> Map(PatientTreatment treatment);
|
||||
/// <summary>
|
||||
/// Maps the provided <see cref="PumpObservation"/> to a <see cref="PumpObservation"/> result, returning <see langword="null"/> when no mapping is produced.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source <see cref="PumpObservation"/> to be mapped.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing the mapped <see cref="PumpObservation"/>, or <see langword="null"/> if the mapping yields no result.</returns>
|
||||
Task<PumpObservation?> Map(PumpObservation obs);
|
||||
/// <summary>
|
||||
/// Asynchronously maps the specified <see cref="PatientRecordingAlert"/> to a resulting <see cref="PatientRecordingAlert"/>, returning <see langword="null"/> when no mapping is produced.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source <see cref="PatientRecordingAlert"/> instance to map.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the mapped <see cref="PatientRecordingAlert"/>, or <see langword="null"/> if the source cannot be mapped.</returns>
|
||||
Task<PatientRecordingAlert?> Map(PatientRecordingAlert obs);
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientDiagnosis"/> instance to its corresponding representation, returning <see langword="null"/> when no mapping is available.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source <see cref="PatientDiagnosis"/> to map.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> containing the mapped <see cref="PatientDiagnosis"/>, or <see langword="null"/> if the source cannot be mapped.</returns>
|
||||
Task<PatientDiagnosis?> Map(PatientDiagnosis obs);
|
||||
/// <summary>
|
||||
/// Maps a list of patient observations for insertion, returning the transformed list asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="listToInsert">The list of patient observations to be mapped for insertion.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the mapped list of patient observations.</returns>
|
||||
Task<List<PatientObservation>> MapList(List<PatientObservation> listToInsert);
|
||||
/// <summary>
|
||||
/// Asynchronously calculates the bolus dose of opiates for the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient for whom the bolus opiates calculation is performed.</param>
|
||||
Task CalculateBolusOpiates(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Asynchronously calculates medicine observations for a patient based on their active medicines.
|
||||
/// </summary>
|
||||
/// <param name="activeMedicines">The list of medicines currently active for the patient.</param>
|
||||
/// <param name="patientId">The unique identifier of the patient.</param>
|
||||
/// <returns>A task that represents the asynchronous calculation operation.</returns>
|
||||
Task CalculateMedicineObservation(List<Medicine> activeMedicines, ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Retrieves the active patient treatments associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose active treatments are being requested.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an enumerable collection of active PatientTreatment entries for the patient.</returns>
|
||||
Task<IEnumerable<PatientTreatment?>> GetActiveTreatmentsByPatient(ObjectId id);
|
||||
/// <summary>
|
||||
/// Maps a source alarm from a <see cref="PatientObservationAlarm"/> onto a <see cref="PatientObservation"/>, producing an observation enriched with the corresponding alarm information.
|
||||
/// </summary>
|
||||
/// <param name="observation">The patient observation that serves as the base for the mapping.</param>
|
||||
/// <param name="observationAlarm">The source alarm whose data is mapped onto the observation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="PatientObservation"/> populated with the mapped alarm data.</returns>
|
||||
Task<PatientObservation> MapSourceAlarm(PatientObservation observation, PatientObservationAlarm observationAlarm);
|
||||
}
|
||||
@@ -7,11 +7,48 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface ICameraService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the camera associated with the specified relay identifier.
|
||||
/// </summary>
|
||||
/// <param name="relayId">The unique identifier of the relay used to look up the camera.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="Camera"/> if a matching record is found; otherwise, <c>null</c>.</returns>
|
||||
Task<Camera?> GetById(ObjectId relayId);
|
||||
/// <summary>
|
||||
/// Retrieves the list of cameras associated with the specified configuration relay identifiers.
|
||||
/// </summary>
|
||||
/// <param name="configurationRelayList">The list of configuration relay object identifiers used to look up the associated cameras.</param>
|
||||
/// <returns>A list of cameras that correspond to the provided configuration relay identifiers.</returns>
|
||||
List<Camera> GetCameraInList(List<ObjectId> configurationRelayList);
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of cameras based on the provided pagination filter.
|
||||
/// </summary>
|
||||
/// <param name="request">The pagination filter containing the page size, page number, and optional search criteria used to query cameras.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="PaginationResponse{Camera}"/> with the requested page of cameras and pagination metadata.</returns>
|
||||
Task<PaginationResponse<Camera>> GetPaginatedCameras(PaginationFilter request);
|
||||
/// <summary>
|
||||
/// Inserts a new camera into the system asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="camera">The camera entity to insert.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the inserted camera with its generated identifier, or <c>null</c> if the camera could not be inserted.</returns>
|
||||
Task<Camera?> InsertCamera(Camera camera);
|
||||
/// <summary>
|
||||
/// Updates an existing camera identified by the specified object identifier with the provided camera data.
|
||||
/// Returns <see langword="null"/> when no camera with the given identifier exists.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The unique identifier of the camera to update.</param>
|
||||
/// <param name="camera">The camera data containing the updated values to apply.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the updated camera, or <see langword="null"/> if the camera was not found.</returns>
|
||||
Task<Camera?> UpdateCameraById(ObjectId objectId, Camera camera);
|
||||
/// <summary>
|
||||
/// Deletes a camera identified by the specified object identifier.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The unique identifier of the camera to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation. The result is <c>true</c> if the camera was successfully deleted; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> DeleteCamera(ObjectId objectId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a list of cameras whose names match the specified search text.
|
||||
/// </summary>
|
||||
/// <param name="textToSearch">The text used to search camera names.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Camera"/> objects matching the search criteria; an empty list is returned if no matches are found.</returns>
|
||||
Task<List<Camera>> GetSearchByNameCameras(string textToSearch);
|
||||
}
|
||||
@@ -6,8 +6,30 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IClientMessageService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously sends a message to the specified receiver, optionally categorized by an operation type.
|
||||
/// </summary>
|
||||
/// <param name="receiverId">The identifier of the intended message receiver.</param>
|
||||
/// <param name="type">The optional operation type used to classify the message.</param>
|
||||
/// <param name="msg">The optional message payload to be sent.</param>
|
||||
/// <returns>A task that represents the asynchronous send operation.</returns>
|
||||
Task SendAsync(string receiverId, OperationType? type, object? msg);
|
||||
/// <summary>
|
||||
/// Asynchronously broadcasts a message of the specified operation type to all connected recipients.
|
||||
/// </summary>
|
||||
/// <param name="type">The operation type that classifies the broadcast message.</param>
|
||||
/// <param name="msg">The message payload to send, or <c>null</c> when no payload is required.</param>
|
||||
/// <returns>A task that represents the asynchronous broadcast operation.</returns>
|
||||
Task SendToAllAsync(OperationType type, object? msg);
|
||||
/// <summary>
|
||||
/// Processes an incoming message within the context of the specified connection.
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to be processed.</param>
|
||||
/// <param name="contextConnectionId">The identifier of the connection context associated with the message.</param>
|
||||
Task ProcessMessage(Message msg, string contextConnectionId);
|
||||
/// <summary>
|
||||
/// Sends an update message to the specified list of patient location boxes.
|
||||
/// </summary>
|
||||
/// <param name="boxes">The list of patient locations that will receive the update message.</param>
|
||||
void SendUpdateMessageToBoxes(List<PatientLocation> boxes);
|
||||
}
|
||||
@@ -12,33 +12,149 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IConfigObservationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="ConfigObservation"/> identified by the specified coding system and code.
|
||||
/// Returns <c>null</c> when no matching configuration observation is found.
|
||||
/// </summary>
|
||||
/// <param name="codingSystem">The coding system used to identify the configuration observation (e.g., ICD, SNOMED).</param>
|
||||
/// <param name="code">The code within the given coding system that uniquely identifies the configuration observation.</param>
|
||||
/// <returns>A <see cref="ConfigObservation"/> if a match is found; otherwise, <c>null</c>.</returns>
|
||||
Task<ConfigObservation?> GetByCodeSysAndCode(string codingSystem, string code);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="ConfigObservation"/> identified by the given name.
|
||||
/// Returns <see langword="null"/> when no matching configuration observation is found.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the configuration observation to retrieve.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that yields the matching <see cref="ConfigObservation"/>, or <see langword="null"/> if no observation is found.</returns>
|
||||
Task<ConfigObservation?> Get(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="ConfigObservation"/> associated with the specified patient observation, optionally restricting the lookup to a match performed by name only.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation whose corresponding configuration observation is being requested.</param>
|
||||
/// <param name="onlyByName">When <c>true</c>, limits the lookup to a name-based match; otherwise, other matching criteria may be applied.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="ConfigObservation"/>, or <c>null</c> if no matching observation is found.</returns>
|
||||
Task<ConfigObservation?> Get<T>(T obs, bool onlyByName = false) where T : BasePatientObservation;
|
||||
|
||||
/// <summary>
|
||||
/// Performs retention actions for the specified patient observation and returns the resulting retention outcome.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation of type <typeparamref name="T"/> on which retention actions will be executed.</param>
|
||||
/// <returns>A task that represents the asynchronous retention operation. The task result contains the <see cref="ObservatitonRetentionResult"/> produced by the retention actions, or <c>null</c> when no retention result is produced.</returns>
|
||||
Task<ObservatitonRetentionResult?> RetentionActions<T>(T obs) where T : BasePatientObservation;
|
||||
/// <summary>
|
||||
/// Maps a patient observation to a corresponding target observation of the same type, optionally restricting the lookup to name-based matching only.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source patient observation to be mapped.</param>
|
||||
/// <param name="onlyByName">When set to <c>true</c>, the mapping is performed using the observation's name only; otherwise, additional matching criteria are considered. Defaults to <c>false</c>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the mapped patient observation of type <typeparamref name="T"/>, or <c>null</c> if no matching observation is found.</returns>
|
||||
Task<T?> Map<T>(T obs, bool onlyByName = false) where T : BasePatientObservation;
|
||||
/// <summary>
|
||||
/// Asynchronously maps the specified <see cref="PatientTreatment"/> to a populated <see cref="PatientTreatment"/> instance, returning <see langword="null"/> when the treatment cannot be resolved.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The <see cref="PatientTreatment"/> to be mapped.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that yields the mapped <see cref="PatientTreatment"/>, or <see langword="null"/> if no mapping result is available.</returns>
|
||||
Task<PatientTreatment?> Map(PatientTreatment treatment);
|
||||
|
||||
/// <summary>
|
||||
/// Determines the status of a grouped observation field based on the provided result, value, and optional threshold range.
|
||||
/// </summary>
|
||||
/// <param name="groupedField">The grouped field associated with the observation.</param>
|
||||
/// <param name="result">The result of the grouped observation used to evaluate the status.</param>
|
||||
/// <param name="name">The name of the field or value being evaluated.</param>
|
||||
/// <param name="value">The value associated with the grouped observation.</param>
|
||||
/// <param name="min">The optional minimum threshold for the value.</param>
|
||||
/// <param name="max">The optional maximum threshold for the value.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the evaluated <see cref="StatusEnum.Type"/> for the grouped observation.</returns>
|
||||
Task<StatusEnum.Type> GroupedObservationStatus(GroupedField groupedField, GroupedObservationEnum.Result result,
|
||||
string name, object value, double? min, double? max);
|
||||
string name, object value, double? min, double? max);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all configuration observations asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="ct">The cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a collection of <see cref="ConfigObservation"/> objects representing all available configurations.</returns>
|
||||
Task<ICollection<ConfigObservation>> GetAllConfigs(CancellationToken ct = default);
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of <see cref="ConfigObservation"/> items based on the supplied filter criteria.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that defines the page size, page number, and query criteria used to retrieve the configuration observations.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a <see cref="PaginationResponse{ConfigObservation}"/> with the requested items and pagination metadata.</returns>
|
||||
Task<PaginationResponse<ConfigObservation>> GetPaginatedItems(PaginationFilter filter);
|
||||
/// <summary>
|
||||
/// Retrieves all configuration observations in a compact format.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation that returns a <see cref="ConfigObservationDto"/> containing the compact representation of all configuration observations.</returns>
|
||||
Task<ConfigObservationDto> GetAllCompact();
|
||||
/// <summary>
|
||||
/// Retrieves a configuration observation by its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the configuration observation to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="ConfigObservation"/> if a matching record is found, or <c>null</c> if no configuration exists for the specified id.</returns>
|
||||
Task<ConfigObservation?> GetConfigById(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a list of configuration names associated with the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier used to look up the associated configuration names.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of configuration names.</returns>
|
||||
Task<List<string>> GetConfigNames(string id);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the list of available configuration names.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of configuration names.</returns>
|
||||
Task<List<string>> GetConfigNames();
|
||||
/// <summary>
|
||||
/// Updates an existing configuration observation and returns the updated result.
|
||||
/// </summary>
|
||||
/// <param name="configObservation">The configuration observation containing the data to update.</param>
|
||||
/// <returns>A task that resolves to the updated <see cref="ConfigObservation"/>, or <c>null</c> when no matching configuration is found.</returns>
|
||||
Task<ConfigObservation?> UpdateConfig(ConfigObservation configObservation);
|
||||
/// <summary>
|
||||
/// Asynchronously creates a new configuration based on the provided observation data.
|
||||
/// </summary>
|
||||
/// <param name="configObservation">The observation data used to create the configuration.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the created <see cref="ConfigObservation"/>, or <c>null</c> if the configuration could not be created.</returns>
|
||||
Task<ConfigObservation?> CreateConfig(ConfigObservation configObservation);
|
||||
/// <summary>
|
||||
/// Asynchronously removes the configuration item with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="itemName">The name of the configuration item to remove.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="ConfigObservation"/> describing the removed item, or <c>null</c> if no matching item was found.</returns>
|
||||
Task<ConfigObservation?> RemoveConfigItem(string itemName);
|
||||
/// <summary>
|
||||
/// Asynchronously removes the configuration item identified by the specified identifier and returns the resulting observation.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the configuration item to remove.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="ConfigObservation"/> describing the removed configuration item, or <c>null</c> if no matching item was found.</returns>
|
||||
Task<ConfigObservation?> RemoveConfigItem(ObjectId id);
|
||||
/// <summary>
|
||||
/// Retrieves the configuration observation items associated with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name used to look up the configuration observation items.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The result contains a collection of <see cref="ConfigObservation"/> items matching the provided name, or <c>null</c> if no matching items are found.</returns>
|
||||
Task<IEnumerable<ConfigObservation>?> GetConfigObservationItem(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a single configuration observation item based on the provided code, coding system, name, and original name.
|
||||
/// </summary>
|
||||
/// <param name="code">The code used to identify the configuration observation item.</param>
|
||||
/// <param name="codingSystem">The coding system associated with the code.</param>
|
||||
/// <param name="name">The name of the configuration observation item.</param>
|
||||
/// <param name="originalName">The original name of the configuration observation item.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="ConfigObservation"/>, or <c>null</c> if no item is found.</returns>
|
||||
Task<ConfigObservation?> GetSingleConfigObservationItem(string? code, string? codingSystem, string? name,
|
||||
string? originalName);
|
||||
string? originalName);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deletes a single configuration observation item from the underlying store.
|
||||
/// </summary>
|
||||
/// <param name="configObservationItem">The configuration observation item to be deleted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean value indicating whether the deletion was successful.</returns>
|
||||
Task<bool> DeleteSingleConfigObservationItem(ConfigObservation configObservationItem);
|
||||
/// <summary>
|
||||
/// Retrieves configuration observation items that match the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name used to filter the configuration observation items.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the collection of <see cref="ConfigObservation"/> items matching the specified name.</returns>
|
||||
Task<IEnumerable<ConfigObservation>> GetConfigObservationItemsByName(string name);
|
||||
}
|
||||
@@ -6,13 +6,53 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IConfigPumpsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously maps the source PumpObservation to a new PumpObservation instance, transforming its data into the target representation.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source PumpObservation to be mapped.</param>
|
||||
/// <returns>A task that represents the asynchronous mapping operation, containing the resulting PumpObservation.</returns>
|
||||
Task<PumpObservation> Map(PumpObservation obs);
|
||||
/// <summary>
|
||||
/// Retrieves all available pump configurations from the configuration store.
|
||||
/// </summary>
|
||||
/// <returns>A task containing a list of <see cref="ConfigPumps"/> with all pump configurations, or <c>null</c> if no configurations are available.</returns>
|
||||
Task<List<ConfigPumps>?> GetAllPumpConfigs();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the configuration pump items associated with the specified identifier.
|
||||
/// Returns null if no configuration items are found for the given id.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier used to look up the configuration items.</param>
|
||||
/// <returns>A task containing a list of ConfigPumpItem objects if found, or null if no items exist for the specified id.</returns>
|
||||
Task<List<ConfigPumpItem>?> GetConfigItems(string id);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the pump configuration that matches the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the pump configuration to look up.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="ConfigPumps"/> configuration if found; otherwise, <c>null</c> when no configuration exists for the given identifier.</returns>
|
||||
Task<ConfigPumps?> GetPumpConfigById(string id);
|
||||
/// <summary>
|
||||
/// Updates the pump configuration asynchronously and returns the updated configuration.
|
||||
/// </summary>
|
||||
/// <param name="pumpConfig">The pump configuration to update.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the updated <see cref="ConfigPumps"/>, or <c>null</c> if the configuration was not found.</returns>
|
||||
Task<ConfigPumps?> UpdatePumpConfig(ConfigPumps pumpConfig);
|
||||
/// <summary>
|
||||
/// Inserts a new pump configuration into the data store.
|
||||
/// </summary>
|
||||
/// <param name="pumpConfig">The pump configuration to insert.</param>
|
||||
/// <returns>The inserted <see cref="ConfigPumps"/> entity, or <c>null</c> if the insertion was not performed.</returns>
|
||||
Task<ConfigPumps?> InsertPumpConfig(ConfigPumps pumpConfig);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the specified pump configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">The pump configuration to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean indicating whether the deletion was successful.</returns>
|
||||
Task<bool> DeletePumpConfig(ConfigPumps config);
|
||||
/// <summary>
|
||||
/// Asynchronously evaluates and applies retention actions for a pump observation, returning the resulting retention outcome.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to process for retention actions.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the retention result, or null if no retention action applies.</returns>
|
||||
Task<ObservatitonRetentionResult?> RetentionActions(PumpObservation obs);
|
||||
|
||||
}
|
||||
@@ -5,6 +5,17 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IConfigUnitsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a patient observation of type <typeparamref name="T"/> to a corresponding output representation.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The specific patient observation type, constrained to <see cref="BasePatientObservation"/>.</typeparam>
|
||||
/// <param name="obs">The patient observation instance to be mapped.</param>
|
||||
/// <returns>A task that represents the asynchronous mapping operation, yielding the mapped patient observation.</returns>
|
||||
Task<T> Map<T>(T obs) where T : BasePatientObservation;
|
||||
/// <summary>
|
||||
/// Asynchronously maps the specified <see cref="PumpObservation"/> to a resulting <see cref="PumpObservation"/>.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to be mapped.</param>
|
||||
/// <returns>A task that represents the asynchronous mapping operation, containing the resulting <see cref="PumpObservation"/>.</returns>
|
||||
Task<PumpObservation> Map(PumpObservation obs);
|
||||
}
|
||||
@@ -6,8 +6,29 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IDeviceService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Device"/> from the provided <see cref="DeviceDto"/>.
|
||||
/// </summary>
|
||||
/// <param name="device">The data transfer object containing the information used to create the device.</param>
|
||||
/// <returns>A task that represents the asynchronous create operation. The task result contains the created <see cref="Device"/>, or <see langword="null"/> if the device could not be created.</returns>
|
||||
Task<Device?> Create(DeviceDto device);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the object identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The unique identifier of the object to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation. The result is <c>true</c> if the object was deleted; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> Delete(ObjectId objectId);
|
||||
/// <summary>
|
||||
/// Updates an existing device using the provided data transfer object.
|
||||
/// </summary>
|
||||
/// <param name="device">The data transfer object containing the updated device information.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the updated device, or <c>null</c> if the device was not found.</returns>
|
||||
Task<Device?> Update(DeviceDto device);
|
||||
/// <summary>
|
||||
/// Processes an incoming event for the specified device and returns the associated <see cref="Device"/>.
|
||||
/// Returns <c>null</c> when the device referenced by the event cannot be found.
|
||||
/// </summary>
|
||||
/// <param name="device">The device data transfer object carrying the event information to be processed.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the resolved <see cref="Device"/> or <c>null</c> if no matching device was found.</returns>
|
||||
Task<Device?> ReceiveEvent(DeviceDto device);
|
||||
}
|
||||
@@ -6,12 +6,51 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IDiagnosisService : IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all diagnoses associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose diagnoses are being retrieved.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of <see cref="PatientDiagnosis"/> records for the specified patient; an empty list is returned if no diagnoses are found.</returns>
|
||||
Task<List<PatientDiagnosis>> GetByPatientId(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Deletes records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose related records should be removed.</param>
|
||||
Task DeleteByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously archives the specified patient, marking the patient record as archived rather than permanently deleting it.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to archive.</param>
|
||||
Task Archive(Patient patient);
|
||||
/// <summary>
|
||||
/// Archives the records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose records will be archived.</param>
|
||||
Task ArchiveByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Processes a diagnosis observation for the specified patient based on the supplied API request data.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the diagnosis observation payload and contextual information to process.</param>
|
||||
/// <param name="patient">The patient to whom the diagnosis observation pertains.</param>
|
||||
Task ProcessDiagnosisObservation(ApiRequest apiRequest, Patient patient);
|
||||
/// <summary>
|
||||
/// Persists the specified API request along with its associated patient data.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to be saved.</param>
|
||||
/// <param name="patient">The patient associated with the API request.</param>
|
||||
Task SaveRequest(ApiRequest apiRequest, Patient patient);
|
||||
/// <summary>
|
||||
/// Processes the provided list of patient diagnoses for the specified patient at the given message time.
|
||||
/// </summary>
|
||||
/// <param name="diagnosis">The list of patient diagnoses to be processed.</param>
|
||||
/// <param name="patient">The patient associated with the diagnoses.</param>
|
||||
/// <param name="messageTime">The timestamp of the message triggering the diagnosis processing.</param>
|
||||
Task ProcessDiagnosis(List<PatientDiagnosis> diagnosis, Patient patient, DateTime messageTime);
|
||||
/// <summary>
|
||||
/// Updates multiple records by replacing the <paramref name="oldId"/> with the new <paramref name="id"/> in the field identified by <paramref name="nameId"/>.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The identifier of the field or property whose ObjectId values will be updated.</param>
|
||||
/// <param name="id">The new ObjectId value to assign to the matching records.</param>
|
||||
/// <param name="oldId">The existing ObjectId value to be replaced in the matching records.</param>
|
||||
Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId);
|
||||
}
|
||||
@@ -9,19 +9,94 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IDischargeService : IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a discharge record by its unique identifier, returning null if no matching discharge is found.
|
||||
/// </summary>
|
||||
/// <param name="dischargeId">The unique identifier of the discharge to look up.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="Discharge"/> if found, or null when no record matches the provided identifier.</returns>
|
||||
Task<Discharge?> GetDischargeByIdAsync(ObjectId dischargeId);
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of discharge records associated with the specified unit.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose discharges should be counted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the total number of discharges for the specified unit.</returns>
|
||||
Task<long> CountDischargesByUnitId(ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes a discharge record identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="dischargeId">The unique identifier of the discharge to delete.</param>
|
||||
Task DeleteDischargeByIdAsync(ObjectId dischargeId);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the specified discharge record.
|
||||
/// </summary>
|
||||
/// <param name="discharge">The discharge entity to be removed.</param>
|
||||
Task DeleteDischargeAsync(Discharge discharge);
|
||||
/// <summary>
|
||||
/// Asynchronously updates an existing discharge record in the data store.
|
||||
/// </summary>
|
||||
/// <param name="discharge">The <see cref="Discharge"/> entity containing the updated information to persist.</param>
|
||||
Task UpdateDischargeAsync(Discharge discharge);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a collection of discharge records.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an enumerable collection of <see cref="Discharge"/> objects.</returns>
|
||||
Task<IEnumerable<Discharge>> GetDischargesAsync();
|
||||
/// <summary>
|
||||
/// Inserts a new discharge record into the data store. Returns the inserted <see cref="Discharge"/> entity, or <see langword="null"/> if the record could not be created.
|
||||
/// </summary>
|
||||
/// <param name="discharge">The <see cref="Discharge"/> entity containing the data to be inserted.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that resolves to the inserted <see cref="Discharge"/>, or <see langword="null"/> when the insertion does not produce a result.</returns>
|
||||
Task<Discharge?> InsertDischarge(Discharge discharge);
|
||||
/// <summary>
|
||||
/// Retrieves the discharge record associated with the specified patient location, returning null if no matching discharge is found.
|
||||
/// </summary>
|
||||
/// <param name="location">The patient location used to look up the discharge record.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="Discharge"/> if found, or null if no discharge is associated with the specified location.</returns>
|
||||
Task<Discharge?> GetDischargeByLocation(PatientLocation location);
|
||||
/// <summary>
|
||||
/// Retrieves the discharge record associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose discharge record is being requested.</param>
|
||||
/// <returns>A <see cref="Task{Discharge}"/> that resolves to the matching <see cref="Discharge"/> if found; otherwise, <c>null</c>.</returns>
|
||||
Task<Discharge?> GetDischargeByPatientId(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Retrieves a discharge record associated with the specified point of care location identifier.
|
||||
/// </summary>
|
||||
/// <param name="location">The ObjectId of the point of care location used to look up the discharge.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="Discharge"/> if found, or <c>null</c> if no discharge exists for the specified point of care location.</returns>
|
||||
Task<Discharge?> GetDischargeByPointOfCareId(ObjectId location);
|
||||
/// <summary>
|
||||
/// Retrieves the discharge associated with the specified point of care location, returning localized data for the requested locale.
|
||||
/// Returns null when no matching discharge is found.
|
||||
/// </summary>
|
||||
/// <param name="location">The identifier of the point of care location whose discharge should be retrieved.</param>
|
||||
/// <param name="dataLocale">The locale used to determine the language of the returned discharge data.</param>
|
||||
/// <returns>A task containing the matching <see cref="Discharge"/>, or null if no discharge exists for the given location.</returns>
|
||||
Task<Discharge?> GetDischargeByPointOfCareIdWithLocale(ObjectId location, LocaleEnum dataLocale);
|
||||
/// <summary>
|
||||
/// Sends a broadcast notification for the specified discharge based on the given operation type.
|
||||
/// </summary>
|
||||
/// <param name="discharge">The discharge entity to be included in the broadcast.</param>
|
||||
/// <param name="operation">The type of operation (e.g., create, update, delete) that determines the broadcast context.</param>
|
||||
void SendDischargeBroadcast(Discharge discharge, OperationType operation);
|
||||
/// <summary>
|
||||
/// Updates a patient master list item change using the provided update options, applying the change across the specified unit list and master list type.
|
||||
/// </summary>
|
||||
/// <param name="opt">The update options describing the change to apply to the patient master list item.</param>
|
||||
/// <param name="unitList">The collection of units to which the master list item change should be applied.</param>
|
||||
/// <param name="typeName">The name of the master list type that identifies which list the item belongs to.</param>
|
||||
Task UpdatePatientMasterListItemChange(UpdateOptionMasterListDto opt, IEnumerable<Unit> unitList, string typeName);
|
||||
/// <summary>
|
||||
/// Deletes the specified patient master list item associated with the given option list and units.
|
||||
/// </summary>
|
||||
/// <param name="opt">The option list containing the patient master list item to delete.</param>
|
||||
/// <param name="unitList">The collection of units associated with the item to be deleted.</param>
|
||||
/// <param name="typeName">The name of the type used to identify the patient master list item.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation.</returns>
|
||||
Task DeletePatientMasterListItem(OptionList opt, IEnumerable<Unit> unitList, string typeName);
|
||||
/// <summary>
|
||||
/// Deletes discharge records associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose discharge records should be removed.</param>
|
||||
Task DeleteDischargesByUnitId(ObjectId unitId);
|
||||
}
|
||||
@@ -10,37 +10,194 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IDisplayConfigService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all display configurations asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of all <see cref="DisplayConfig"/> entries.</returns>
|
||||
Task<List<DisplayConfig>> GetAll();
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of display configurations in a compact (minimal) representation.
|
||||
/// </summary>
|
||||
/// <param name="request">The pagination filter that controls page size, page number, and sorting criteria.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a <see cref="PaginationResponse{T}"/> with the compact display configuration entries.</returns>
|
||||
Task<PaginationResponse<DisplayConfigMinimalResponse>> GetAllCompactPaginated(PaginationFilter request);
|
||||
/// <summary>
|
||||
/// Retrieves a list of display configurations filtered by the specified display type.
|
||||
/// </summary>
|
||||
/// <param name="type">The display type used to filter the configurations.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="DisplayConfig"/> objects matching the specified type.</returns>
|
||||
Task<List<DisplayConfig>> GetByType(DisplayConfigEnums.DisplayType type);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="DisplayConfig"/> by its identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the display configuration to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="DisplayConfig"/> matching the specified identifier.</returns>
|
||||
Task<DisplayConfig> GetById(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="DisplayConfig"/> identified by the specified configuration identifier, unit identifier, and display type.
|
||||
/// </summary>
|
||||
/// <param name="configId">The optional identifier of the display configuration to look up; may be <c>null</c> when searching without a specific configuration.</param>
|
||||
/// <param name="unitId">The identifier of the unit the display configuration belongs to.</param>
|
||||
/// <param name="displayType">The display type used to filter or scope the lookup.</param>
|
||||
/// <returns>A <see cref="Task{DisplayConfig}"/> that resolves to the matching <see cref="DisplayConfig"/>, or <c>null</c> if no configuration is found.</returns>
|
||||
Task<DisplayConfig?> GetById(ObjectId? configId, ObjectId unitId, DisplayConfigEnums.DisplayType displayType);
|
||||
/// <summary>
|
||||
/// Inserts a single display configuration asynchronously and returns the resulting configuration, or null when no record is produced.
|
||||
/// </summary>
|
||||
/// <param name="config">The display configuration to insert.</param>
|
||||
/// <returns>A task that represents the asynchronous insert operation, containing the inserted <see cref="DisplayConfig"/> or null.</returns>
|
||||
Task<DisplayConfig?> InsertOne(DisplayConfig config);
|
||||
/// <summary>
|
||||
/// Inserts a new display configuration in a minimal fashion and returns the created <see cref="DisplayConfig"/>, or <c>null</c> when no configuration could be produced.
|
||||
/// </summary>
|
||||
/// <param name="config">The data transfer object containing the values used to create the new display configuration.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that yields the created <see cref="DisplayConfig"/> when successful, or <c>null</c> when no result is available.</returns>
|
||||
Task<DisplayConfig?> InsertOneMinimal(CreateDisplayConfigDto config);
|
||||
/// <summary>
|
||||
/// Performs a test insertion operation that returns a <see cref="DisplayConfig"/> instance, used to validate insertion behavior.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task{DisplayConfig}"/> representing the asynchronous test insertion result.</returns>
|
||||
Task<DisplayConfig> InsertOneTest();
|
||||
/// <summary>
|
||||
/// Updates the display configuration identified by the specified identifier with the provided new configuration data.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The unique identifier of the display configuration to update.</param>
|
||||
/// <param name="newDisplayConfig">The new display configuration data to apply to the existing configuration.</param>
|
||||
/// <returns>The updated <see cref="DisplayConfig"/>, or <c>null</c> if no display configuration with the specified identifier is found.</returns>
|
||||
Task<DisplayConfig?> UpdateConfig(ObjectId displayConfigId, object newDisplayConfig);
|
||||
/// <summary>
|
||||
/// Updates the list of fields associated with the specified configuration display.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the configuration display whose field list will be updated.</param>
|
||||
/// <param name="fields">The list of fields to be applied to the configuration display.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a value indicating whether the update was successful.</returns>
|
||||
Task<bool> UpdateFieldList(ObjectId objectIdConfigDisplay, List<Field> fields);
|
||||
/// <summary>
|
||||
/// Updates the color configuration for the specified config display.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the config display whose color configuration will be updated.</param>
|
||||
/// <param name="colorConfigDto">The color configuration data to apply to the config display.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdateConfigColor(ObjectId objectIdConfigDisplay, ColorConfig colorConfigDto);
|
||||
/// <summary>
|
||||
/// Asynchronously updates the header configuration associated with the specified config display identifier.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the config display whose header configuration will be updated.</param>
|
||||
/// <param name="headerConfig">The new header configuration to apply.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdateHeaderConfig(ObjectId objectIdConfigDisplay, HeaderConfig headerConfig);
|
||||
/// <summary>
|
||||
/// Updates the home banner configuration for the specified config display with the provided list of banner items.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the config display whose home banner will be updated.</param>
|
||||
/// <param name="bannerItems">The list of banner items to set as the home banner configuration.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains <c>true</c> if the home banner was updated successfully; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdateSetHomeBanner(ObjectId objectIdConfigDisplay, List<BannerItem> bannerItems);
|
||||
/// <summary>
|
||||
/// Updates the base display configuration with the specified settings.
|
||||
/// </summary>
|
||||
/// <param name="baseConfig">The display configuration to apply as the new base configuration.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation. The task result contains a boolean value indicating whether the update was successful.</returns>
|
||||
Task<bool> UpdateBaseConfig(DisplayConfig baseConfig);
|
||||
/// <summary>
|
||||
/// Asynchronously updates the hospital name associated with the specified display configuration.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The identifier of the display configuration whose hospital name will be updated.</param>
|
||||
/// <param name="name">The new hospital name to apply to the display configuration.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the update was applied successfully; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdateDisplayConfigHospitalName(ObjectId objectIdConfigDisplay, string name);
|
||||
/// <summary>
|
||||
/// Deletes a display configuration identified by the specified object identifier.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The unique identifier of the display configuration to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation. The task result contains a boolean value indicating whether the display configuration was successfully deleted.</returns>
|
||||
Task<bool> DeleteDisplayConfig(ObjectId objectIdConfigDisplay);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the default display configuration for the specified display type.
|
||||
/// </summary>
|
||||
/// <param name="type">The display type used to look up the default configuration.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the default <see cref="DisplayConfig"/> for the given display type, or <c>null</c> if no default configuration is available.</returns>
|
||||
Task<DisplayConfig?> GetDefaultConfig(DisplayConfigEnums.DisplayType type);
|
||||
/// <summary>
|
||||
/// Retrieves the list of display configuration locations associated with the specified configuration display identifier.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The ObjectId of the configuration display whose locations are to be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of DisplayConfigLocationDto objects for the specified configuration display.</returns>
|
||||
Task<List<DisplayConfigLocationDto>> GetDisplayConfigLocations(ObjectId objectIdConfigDisplay);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all display configurations in a compact (minimal) representation.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="DisplayConfigMinimalResponse"/> objects representing the compact display configurations.</returns>
|
||||
Task<List<DisplayConfigMinimalResponse>> GetAllCompact();
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new display configuration record using a predefined template, optionally scoped to a specific hospital context.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The identifier of the object the display configuration is associated with.</param>
|
||||
/// <param name="configType">The type of display configuration template to use for the insertion.</param>
|
||||
/// <param name="configHospital">The optional hospital identifier used to scope the configuration; may be null when the configuration is not hospital-specific.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the inserted <see cref="DisplayConfig"/>, or null if the configuration could not be created.</returns>
|
||||
Task<DisplayConfig?> InsertOneWithTemplate(string objectId,
|
||||
DisplayConfigEnums.DisplayType configType, string? configHospital);
|
||||
DisplayConfigEnums.DisplayType configType, string? configHospital);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the card configuration based on the provided settings.
|
||||
/// </summary>
|
||||
/// <param name="baseConfig">The card configuration to be updated.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean value indicating whether the update was successful.</returns>
|
||||
Task<bool> UpdateCardConfig(CardConfig baseConfig);
|
||||
/// <summary>
|
||||
/// Inserts a new card configuration using the supplied data and returns the resulting <see cref="CardConfig"/>, or null if no card config is created.
|
||||
/// </summary>
|
||||
/// <param name="updateDisplayConfigNameDto">The DTO containing the data used to create the display config card.</param>
|
||||
/// <returns>A task that resolves to the inserted <see cref="CardConfig"/>, or null if the insert did not produce a card config.</returns>
|
||||
Task<CardConfig?> InsertCardConfig(CreateDisplayConfigCardDto updateDisplayConfigNameDto);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all card configurations.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of all <see cref="CardConfig"/> items.</returns>
|
||||
Task<List<CardConfig>> GetCardConfigAll();
|
||||
/// <summary>
|
||||
/// Retrieves the card configuration associated with the specified identifier.
|
||||
/// Returns <c>null</c> when no matching card configuration is found.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the card configuration to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="CardConfig"/> if found; otherwise, <c>null</c>.</returns>
|
||||
Task<CardConfig?> GetCardConfigById(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously updates the card details configuration based on the provided base configuration.
|
||||
/// </summary>
|
||||
/// <param name="baseConfig">The base card details configuration to apply during the update.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a boolean value indicating whether the update was successful.</returns>
|
||||
Task<bool> UpdateDetailConfig(CardDetailsConfig baseConfig);
|
||||
/// <summary>
|
||||
/// Inserts a new card detail configuration based on the provided display config data.
|
||||
/// </summary>
|
||||
/// <param name="updateDisplayConfigNameDto">The DTO containing the data required to create the card detail configuration.</param>
|
||||
/// <returns>A task that resolves to the created <see cref="CardDetailsConfig"/>, or <c>null</c> if the configuration could not be inserted.</returns>
|
||||
Task<CardDetailsConfig?> InsertCardDetailConfig(CreateDisplayConfigCardDto updateDisplayConfigNameDto);
|
||||
/// <summary>
|
||||
/// Inserts a new chart configuration based on the provided display config card data.
|
||||
/// </summary>
|
||||
/// <param name="updateDisplayConfigNameDto">The data transfer object containing the details of the chart configuration to create.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the newly inserted <see cref="ChartConfig"/>, or <c>null</c> if the insertion was not successful.</returns>
|
||||
Task<ChartConfig?> InsertChartConfig(CreateDisplayConfigCardDto updateDisplayConfigNameDto);
|
||||
/// <summary>
|
||||
/// Asynchronously updates the chart configuration based on the provided base configuration and returns a value indicating whether the update was successful.
|
||||
/// </summary>
|
||||
/// <param name="baseConfig">The base chart configuration to apply during the update.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The result is <c>true</c> if the chart configuration was updated successfully; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdateChartConfig(ChartConfig baseConfig);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes a chart configuration identified by the specified configuration display identifier.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigDisplay">The <see cref="ObjectId"/> of the chart configuration display to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation, containing a value indicating whether the deletion was successful.</returns>
|
||||
Task<bool> DeleteChartConfig(ObjectId objectIdConfigDisplay);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the chart configuration associated with the specified chart identifier.
|
||||
/// </summary>
|
||||
/// <param name="objectIdConfigChart">The unique identifier of the chart whose configuration should be fetched.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="ChartConfig"/> associated with the provided identifier, or <c>null</c> if no configuration is found.</returns>
|
||||
Task<ChartConfig?> GetChartConfig(ObjectId objectIdConfigChart);
|
||||
}
|
||||
@@ -12,52 +12,208 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
public interface IDisplayService
|
||||
{
|
||||
//Task<List<DisplayWithPermissionsDto>> GetAll(string? userName, List<Authorization> displayIdByAuthorities);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all displays in a compact format containing only minimal identifying information.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="DisplayMinimalDto"/> objects representing all available displays in a compact projection.</returns>
|
||||
Task<List<DisplayMinimalDto>> GetAllCompact();
|
||||
/// <summary>
|
||||
/// Retrieves all displays along with their associated permissions for the specified user.
|
||||
/// When <paramref name="userName"/> is null, the behavior is determined by the underlying implementation (e.g., returning all displays or an empty result).
|
||||
/// </summary>
|
||||
/// <param name="userName">The username used to look up the associated displays and permissions. May be null.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="DisplayWithPermissionsDto"/> entries for the user.</returns>
|
||||
Task<List<DisplayWithPermissionsDto>> GetAllByUser(string? userName);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a list of <see cref="Display"/> entries filtered by the specified display type.
|
||||
/// </summary>
|
||||
/// <param name="type">The display type used to filter the returned collection.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of <see cref="Display"/> items matching the specified type.</returns>
|
||||
Task<List<Display>> GetByType(DisplayConfigEnums.DisplayType type);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the list of displays associated with the specified point of care.
|
||||
/// </summary>
|
||||
/// <param name="pointOfCare">The point of care used to filter the displays to be returned.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of <see cref="Display"/> items linked to the given point of care.</returns>
|
||||
Task<List<Display>> GetByPointOfCare(PointOfCare pointOfCare);
|
||||
/// <summary>
|
||||
/// Retrieves a list of displays associated with the specified configuration identifier.
|
||||
/// </summary>
|
||||
/// <param name="configId">The unique identifier of the configuration used to filter the displays.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of displays matching the specified configuration identifier.</returns>
|
||||
Task<List<Display>> GetByConfigId(ObjectId configId);
|
||||
/// <summary>
|
||||
/// Retrieves a list of <see cref="Display"/> entities associated with the specified card configuration identifier.
|
||||
/// </summary>
|
||||
/// <param name="configId">The unique identifier of the card configuration whose associated displays are to be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Display"/> objects linked to the given card configuration.</returns>
|
||||
Task<List<Display>> GetByCardConfigId(ObjectId configId);
|
||||
|
||||
// Task<List<Display>> GetByUser();
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Display"/> entity by its name asynchronously.
|
||||
/// Returns <c>null</c> when no matching display is found.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the display to look up.</param>
|
||||
/// <returns>A <see cref="Task{Display}"/> that resolves to the matching <see cref="Display"/>, or <c>null</c> if none is found.</returns>
|
||||
Task<Display?> GetByName(string name);
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Display"/> entity by its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the display to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="Display"/> if found; otherwise, <c>null</c>.</returns>
|
||||
Task<Display?> GetById(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a display representation of an entity along with its associated permissions, localized for the specified locale.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the entity to retrieve.</param>
|
||||
/// <param name="localeEnum">The locale used to localize the returned display data.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the localized display data with permissions.</returns>
|
||||
Task<DisplayWithPermissionsDto> GetByIdWithPermissions(ObjectId id, LocaleEnum localeEnum);
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of displays associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose displays should be counted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the count of displays linked to the specified unit.</returns>
|
||||
Task<long> CountDisplaysByUnitId(ObjectId unitId);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the display identified by <paramref name="id"/>, optionally populating related data such as point-of-care, patient, display list, and display configuration based on the corresponding fill flags.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the display to retrieve.</param>
|
||||
/// <param name="userName">The name of the user requesting the display, used for authorization checks.</param>
|
||||
/// <param name="authorizations">Optional collection of authorizations used to control access to the display and its related data.</param>
|
||||
/// <param name="locale">Optional locale used to localize the returned display information.</param>
|
||||
/// <param name="fillPointOfCare">When <c>true</c>, includes the associated point-of-care data in the result.</param>
|
||||
/// <param name="fillPatientData">When <c>true</c>, includes the associated patient data in the result.</param>
|
||||
/// <param name="fillDisplayList">When <c>true</c>, includes the display list in the result.</param>
|
||||
/// <param name="fillDisplayConfig">When <c>true</c>, includes the display configuration in the result.</param>
|
||||
/// <param name="ct">Token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that resolves to the <see cref="Display"/> if found, or <c>null</c> if no display matches the specified <paramref name="id"/>.</returns>
|
||||
Task<Display?> GetInfo(
|
||||
ObjectId id,
|
||||
string? userName,
|
||||
List<Authorization>? authorizations,
|
||||
LocaleEnum? locale,
|
||||
bool fillPointOfCare = true,
|
||||
bool fillPatientData = false,
|
||||
bool fillDisplayList = true,
|
||||
bool fillDisplayConfig = true,
|
||||
CancellationToken ct = default
|
||||
);
|
||||
ObjectId id,
|
||||
string? userName,
|
||||
List<Authorization>? authorizations,
|
||||
LocaleEnum? locale,
|
||||
bool fillPointOfCare = true,
|
||||
bool fillPatientData = false,
|
||||
bool fillDisplayList = true,
|
||||
bool fillDisplayConfig = true,
|
||||
CancellationToken ct = default
|
||||
);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of minimal display sections filtered by display type, current display context, user name, and the provided authorizations.
|
||||
/// </summary>
|
||||
/// <param name="type">The display type used to filter the available sections.</param>
|
||||
/// <param name="currentDisplay">The identifier of the current display, or <c>null</c> when no display is selected.</param>
|
||||
/// <param name="userName">The user name used to resolve user-specific sections, or <c>null</c> if not applicable.</param>
|
||||
/// <param name="authorizations">The list of authorizations used to authorize and filter the returned sections, or <c>null</c> if no authorization filtering is required.</param>
|
||||
/// <returns>A task that yields the list of <see cref="MinimalDisplaySection"/> instances matching the supplied criteria.</returns>
|
||||
Task<List<MinimalDisplaySection>> GetDisplaySectionByUser(
|
||||
DisplayConfigEnums.DisplayType type,
|
||||
ObjectId? currentDisplay,
|
||||
string? userName,
|
||||
List<Authorization>? authorizations);
|
||||
DisplayConfigEnums.DisplayType type,
|
||||
ObjectId? currentDisplay,
|
||||
string? userName,
|
||||
List<Authorization>? authorizations);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all display sections as a minimal display list.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="MinimalDisplayListDto"/> with the display section data.</returns>
|
||||
Task<MinimalDisplayListDto> GetAllDisplaySection();
|
||||
/// <summary>
|
||||
/// Retrieves a list of <see cref="Display"/> records associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> that identifies the unit whose displays are being requested.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a <see cref="List{Display}"/> of displays linked to the given unit.</returns>
|
||||
Task<List<Display>> GetByUnitId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Retrieves all available Points of Contact (POCs) along with their associated unit information, optionally filtered by the provided display identifiers and allowing exclusion of virtual entries.
|
||||
/// </summary>
|
||||
/// <param name="displayIds">The list of display identifiers used to filter the available POCs.</param>
|
||||
/// <param name="excludeVirtual">When set to <c>true</c>, excludes virtual POCs from the results; otherwise, virtual POCs are included.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="PocAndUnitDto"/> with the matching POCs and their unit details.</returns>
|
||||
Task<PocAndUnitDto> GetAllAvailablePoc(List<string> displayIds, bool excludeVirtual = false);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all Points of Care (POCs) associated with the specified display identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The display identifier used to look up the associated Points of Care.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of Points of Care matching the specified display identifier.</returns>
|
||||
Task<List<PointOfCare>> GetAllPocsByDisplayId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Inserts a new display record into the data store and returns the persisted entity.
|
||||
/// </summary>
|
||||
/// <param name="display">The display entity to insert.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the inserted <see cref="Display"/>.</returns>
|
||||
Task<Display> InsertOne(Display display);
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a single test <see cref="Display"/> record and returns the persisted result.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task{Display}"/> that represents the asynchronous insert operation, containing the inserted <see cref="Display"/>.</returns>
|
||||
Task<Display> InsertOneTest();
|
||||
/// <summary>
|
||||
/// Updates the configuration of an existing display using the provided new configuration.
|
||||
/// </summary>
|
||||
/// <param name="oldDisplay">The current display whose configuration will be updated.</param>
|
||||
/// <param name="newDisplayConfig">The new display configuration to apply, or null to leave the configuration unchanged.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the updated <see cref="Display"/>, or null if the update was not performed.</returns>
|
||||
Task<Display?> UpdateConfig(Display oldDisplay, DisplayConfig? newDisplayConfig);
|
||||
/// <summary>
|
||||
/// Updates the configuration identifier associated with the specified display.
|
||||
/// </summary>
|
||||
/// <param name="oldDisplay">The existing display whose configuration identifier will be updated.</param>
|
||||
/// <param name="configId">The new configuration identifier to associate with the display.</param>
|
||||
/// <returns>A task that resolves to the updated <see cref="Display"/>, or <c>null</c> if no result is available.</returns>
|
||||
Task<Display?> UpdateConfigId(Display oldDisplay, ObjectId configId);
|
||||
/// <summary>
|
||||
/// Updates the point of care list associated with the specified object identifier, replacing or merging it with the provided list of point of care object identifiers.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The identifier of the object whose point of care list is being updated.</param>
|
||||
/// <param name="listPocObId">The collection of point of care object identifiers to apply to the target object.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the resulting <see cref="Display"/> when the update succeeds, or <c>null</c> when no matching object is found.</returns>
|
||||
Task<Display?> UpdatePointOfCareList(ObjectId objectId, List<ObjectId> listPocObId);
|
||||
/// <summary>
|
||||
/// Updates the configuration preset associated with the specified display using the provided configuration display.
|
||||
/// </summary>
|
||||
/// <param name="objectIdDisplay">The unique identifier of the display whose configuration preset will be updated.</param>
|
||||
/// <param name="objectIdConfigDisplay">The unique identifier of the configuration display to apply as the preset.</param>
|
||||
/// <returns>A task that resolves to the updated <see cref="Display"/>, or <c>null</c> if no matching display is found.</returns>
|
||||
Task<Display?> UpdateConfigPreset(ObjectId objectIdDisplay, ObjectId objectIdConfigDisplay);
|
||||
/// <summary>
|
||||
/// Updates the name of the entity identified by the given identifier and returns the resulting <see cref="Display"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the entity whose name should be updated.</param>
|
||||
/// <param name="name">The new name to apply to the entity.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation, containing the updated <see cref="Display"/> or <c>null</c> if no entity was found.</returns>
|
||||
Task<Display?> UpdateName(ObjectId id, string name);
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of displays based on the provided filter criteria.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that defines the page size, page number, and any additional filtering criteria for the display results.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a <see cref="PaginationResponse{Display}"/> with the requested page of displays and pagination metadata.</returns>
|
||||
Task<PaginationResponse<Display>> GetPaginatedDisplays(PaginationFilter filter);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the display identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the display to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a value indicating whether the display was successfully deleted.</returns>
|
||||
Task<bool> DeleteDisplay(ObjectId id);
|
||||
/// <summary>
|
||||
/// Retrieves the list of display configuration locations associated with the specified display configuration.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The unique identifier of the display configuration whose locations are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="DisplayConfigLocationDto"/> objects for the specified display configuration.</returns>
|
||||
Task<List<DisplayConfigLocationDto>> GetDisplayConfigLocations(ObjectId displayConfigId);
|
||||
/// <summary>
|
||||
/// Asynchronously determines whether the specified display configuration is currently in use.
|
||||
/// </summary>
|
||||
/// <param name="displayConfigId">The identifier of the display configuration to check.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the display configuration is in use; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> IsDisplayConfigInUse(ObjectId displayConfigId);
|
||||
/// <summary>
|
||||
/// Deletes display records associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose displays should be removed.</param>
|
||||
Task DeleteDisplaysByUnitId(ObjectId unitId);
|
||||
}
|
||||
@@ -8,9 +8,30 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IFileService
|
||||
{
|
||||
/// <summary>
|
||||
/// Copies the provided update files to the appropriate location for processing or deployment.
|
||||
/// </summary>
|
||||
/// <param name="files">The collection of update files to be copied.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean value indicating whether the copy operation succeeded.</returns>
|
||||
Task<bool> CopyUpdateFiles(ICollection<IFormFile> files);
|
||||
/// <summary>
|
||||
/// Uploads the provided asset files categorized by the specified theme, returning a value indicating whether the operation completed successfully.
|
||||
/// </summary>
|
||||
/// <param name="files">The collection of uploaded form files to process and store as assets.</param>
|
||||
/// <param name="themeParse">The asset theme used to classify and organize the uploaded files.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the asset files were uploaded successfully; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UploadAssetFiles(ICollection<IFormFile> files, FileEnum.AssetTheme themeParse);
|
||||
/// <summary>
|
||||
/// Retrieves all asset files for the specified asset theme as a list of asset data transfer objects.
|
||||
/// </summary>
|
||||
/// <param name="themeParse">The asset theme used to retrieve the corresponding assets.</param>
|
||||
/// <returns>A <see cref="List{AssetDto}"/> containing the asset data transfer objects for the specified theme.</returns>
|
||||
List<AssetDto> GetAllAssetFile(FileEnum.AssetTheme themeParse);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of file names from the specified directory path.
|
||||
/// </summary>
|
||||
/// <param name="directoryPath">The path of the directory from which to retrieve the files.</param>
|
||||
/// <returns>A list of strings representing the names of the files in the specified directory.</returns>
|
||||
List<string> GetFilesInDirectory(string directoryPath);
|
||||
}
|
||||
@@ -7,16 +7,48 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IGroupedObservationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a grouped observation for the specified identifier and grouped field, applying the provided time zone for time-based calculations.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the source entity used to produce the grouped observation.</param>
|
||||
/// <param name="groupedField">The field definition that determines how the observation is grouped.</param>
|
||||
/// <param name="timeZoneId">The identifier of the time zone to apply when interpreting time values. Defaults to "Romance Standard Time".</param>
|
||||
/// <param name="cacheIsChecked">Indicates whether the cache should be consulted before generating the result. Defaults to <c>false</c>.</param>
|
||||
/// <param name="ct">The cancellation token used to cancel the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous generation, producing the resulting <see cref="GroupedObservation"/>.</returns>
|
||||
Task<GroupedObservation> GenerateGroupedObservation(ObjectId id, GroupedField groupedField,
|
||||
string timeZoneId = "Romance Standard Time", bool cacheIsChecked = false, CancellationToken ct = default);
|
||||
string timeZoneId = "Romance Standard Time", bool cacheIsChecked = false, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a <see cref="GroupedObservation"/> for the specified patient based on the provided patient observation,
|
||||
/// grouped field configuration, and any previously recorded grouped observations, applying the supplied time zone for
|
||||
/// date and time handling.
|
||||
/// </summary>
|
||||
/// <param name="obsPatientId">The identifier of the patient whose observation is being grouped.</param>
|
||||
/// <param name="groupedField">The grouped field definition that drives how the observation is categorized and aggregated.</param>
|
||||
/// <param name="wsgLastGroupedObservationObs">The list of the most recent grouped observation entries used as context when building the new grouped observation.</param>
|
||||
/// <param name="obs">The patient observation to be processed and grouped.</param>
|
||||
/// <param name="timeZoneId">The time zone identifier used when computing date and time values for the grouped observation. Defaults to "Romance Standard Time".</param>
|
||||
/// <returns>A <see cref="Task{GroupedObservation}"/> that resolves to the generated grouped observation for the patient.</returns>
|
||||
Task<GroupedObservation> GenerateGroupedObservation(ObjectId obsPatientId, GroupedField groupedField,
|
||||
List<GroupedObservation.GroupedObservationObs> wsgLastGroupedObservationObs, PatientObservation obs,
|
||||
string timeZoneId = "Romance Standard Time");
|
||||
List<GroupedObservation.GroupedObservationObs> wsgLastGroupedObservationObs, PatientObservation obs,
|
||||
string timeZoneId = "Romance Standard Time");
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent observations for a specified patient, optionally filtered by observation types.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being retrieved.</param>
|
||||
/// <param name="num">The maximum number of recent observations to return. Defaults to 2.</param>
|
||||
/// <param name="filterObservations">An optional list of observation names to include; if null, all observation types are considered.</param>
|
||||
/// <returns>A task that resolves to a list of the most recent <see cref="PatientObservation"/> entries for the patient.</returns>
|
||||
Task<List<PatientObservation>> FindLastObservations(ObjectId patientId, int num = 2,
|
||||
List<string>? filterObservations = null);
|
||||
List<string>? filterObservations = null);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously creates a new empty observation for the next available slot in the grouped web service subscription.
|
||||
/// </summary>
|
||||
/// <param name="ws">The grouped web service subscriber for which the next empty observation is created.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the newly created <see cref="GroupedObservation"/>.</returns>
|
||||
Task<GroupedObservation> CreateNextEmptyObs(WsSubscriberGrouped ws);
|
||||
/*
|
||||
*
|
||||
|
||||
@@ -6,20 +6,72 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IHistoricalConfigChangesService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all historical configuration changes recorded in the system.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a collection of all <see cref="HistoricalConfigChanges"/> entries.</returns>
|
||||
Task<ICollection<HistoricalConfigChanges>> GetAll();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a historical configuration change entry by its unique identifier.
|
||||
/// Returns null when no matching historical configuration change is found for the specified id.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the historical configuration change to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the matching <see cref="HistoricalConfigChanges"/> or null if not found.</returns>
|
||||
Task<HistoricalConfigChanges?> Get(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a collection of historical configuration changes filtered by the specified configuration type.
|
||||
/// </summary>
|
||||
/// <param name="type">The configuration type used to filter the historical changes.</param>
|
||||
/// <param name="num">The maximum number of historical change records to return.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of <see cref="HistoricalConfigChanges"/> for the specified type.</returns>
|
||||
Task<ICollection<HistoricalConfigChanges>> GetByType(DisplayConfigEnums.ConfigTypes type, int num);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a collection of historical configuration changes associated with the specified user.
|
||||
/// Results can be filtered by configuration type and limited in count; when the configuration type is not provided, all types are considered.
|
||||
/// </summary>
|
||||
/// <param name="user">The identifier of the user whose historical configuration changes should be retrieved.</param>
|
||||
/// <param name="configTypes">The optional configuration type used to filter the results; if null, changes for all configuration types are returned.</param>
|
||||
/// <param name="num">The maximum number of historical configuration change records to return.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the collection of historical configuration changes matching the specified criteria.</returns>
|
||||
Task<ICollection<HistoricalConfigChanges>> GetByUser(string user, DisplayConfigEnums.ConfigTypes? configTypes,
|
||||
int num);
|
||||
int num);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the most recent historical configuration changes for the specified configuration type.
|
||||
/// Returns null if no historical changes are found for the given type.
|
||||
/// </summary>
|
||||
/// <param name="configTypes">The configuration type used to look up the most recent historical changes.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the last <see cref="HistoricalConfigChanges"/> for the specified type, or null if no changes are available.</returns>
|
||||
Task<HistoricalConfigChanges?> FindLastConfigChanges(DisplayConfigEnums.ConfigTypes configTypes);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a single historical configuration change record into the data store.
|
||||
/// Returns the inserted record, or null if the insert could not be performed.
|
||||
/// </summary>
|
||||
/// <param name="historicalConfigChanges">The historical configuration change entity to be inserted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the inserted <see cref="HistoricalConfigChanges"/> entity, or null if the insertion did not produce a result.</returns>
|
||||
Task<HistoricalConfigChanges?> InsertOne(HistoricalConfigChanges historicalConfigChanges);
|
||||
/// <summary>
|
||||
/// Updates an existing historical configuration change record with the provided data and returns the updated entity.
|
||||
/// </summary>
|
||||
/// <param name="historicalConfigChanges">The historical configuration change entity containing the updated values to be persisted.</param>
|
||||
/// <returns>A task that resolves to the updated <see cref="HistoricalConfigChanges"/>, or <c>null</c> if the record was not found.</returns>
|
||||
Task<HistoricalConfigChanges?> UpdateHistoricalConfigChange(HistoricalConfigChanges historicalConfigChanges);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a historical configuration change identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the historical configuration change to delete.</param>
|
||||
Task DeleteHistoricalConfigChange(ObjectId id);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a configuration change event, recording the user who made the change, the type of configuration affected, and the old and new configuration values.
|
||||
/// </summary>
|
||||
/// <param name="user">The identifier of the user who made the configuration change.</param>
|
||||
/// <param name="configType">The type of configuration that was changed.</param>
|
||||
/// <param name="newConfig">The new configuration value after the change.</param>
|
||||
/// <param name="oldConfig">The previous configuration value before the change.</param>
|
||||
Task LogConfigChanged(string user, DisplayConfigEnums.ConfigTypes configType, string newConfig, string oldConfig);
|
||||
}
|
||||
@@ -9,20 +9,85 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface ILightBeaconService
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends the specified color command to the light beacon associated with the given point of control identifier.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of control that targets the light beacon.</param>
|
||||
/// <param name="color">The color to apply to the light beacon.</param>
|
||||
Task SendColor(ObjectId pocId, LightBeaconColor color);
|
||||
/// <summary>
|
||||
/// Sends a color command to the light beacon of the specified point of care device,
|
||||
/// updating its visual indicator to reflect the requested state.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of care device whose light beacon will be updated.</param>
|
||||
/// <param name="color">The color to apply to the light beacon.</param>
|
||||
/// <returns>A task that represents the asynchronous color send operation.</returns>
|
||||
Task SendColor(PointOfCare pocId, LightBeaconColor color);
|
||||
/// <summary>
|
||||
/// Sends a light beacon broadcast for the specified point of care, setting the beacon to display the specified color.
|
||||
/// </summary>
|
||||
/// <param name="poc">The point of care device or location whose beacon will be updated.</param>
|
||||
/// <param name="color">The color to display on the light beacon.</param>
|
||||
Task SendBeaconBroadcast(PointOfCare poc, LightBeaconColor color);
|
||||
/// <summary>
|
||||
/// Sends a broadcast signal to the beacon associated with the specified point of care identifier using the given beacon color.
|
||||
/// </summary>
|
||||
/// <param name="poc">The identifier of the point of care (or beacon) that will receive the broadcast.</param>
|
||||
/// <param name="color">The color of the light beacon used for the broadcast.</param>
|
||||
Task SendBeaconBroadcast(ObjectId poc, LightBeaconColor color);
|
||||
/// <summary>
|
||||
/// Asynchronously powers off the LED associated with the specified point of connection identifier.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of connection whose LED will be turned off.</param>
|
||||
Task PowerOffLed(ObjectId pocId);
|
||||
/// <summary>
|
||||
/// Asynchronously powers off the LED indicator associated with the specified point of care.
|
||||
/// </summary>
|
||||
/// <param name="poc">The point of care whose LED indicator should be turned off.</param>
|
||||
Task PowerOffLed(PointOfCare poc);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a color-coded alert based on the provided patient observation.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation used to determine the alert level and color.</param>
|
||||
void GenerateColorAlert(PatientObservation obs);
|
||||
|
||||
//TODO refactor, one patient can have multiple beacons
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the <see cref="LightBeaconColor"/> associated with the specified point of care identifier.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of care whose light beacon color is being requested.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="LightBeaconColor"/> for the specified point of care.</returns>
|
||||
public Task<LightBeaconColor> GetColor(ObjectId pocId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the light beacon color associated with the specified point of care.
|
||||
/// </summary>
|
||||
/// <param name="poc">The point of care for which to look up the light beacon color.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="LightBeaconColor"/> for the specified point of care.</returns>
|
||||
public Task<LightBeaconColor> GetColor(PointOfCare poc);
|
||||
/// <summary>
|
||||
/// Updates a single light beacon record in the data store.
|
||||
/// Returns <see langword="null"/> when the beacon to update cannot be found.
|
||||
/// </summary>
|
||||
/// <param name="beacon">The light beacon containing the updated values to persist.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the updated <see cref="LightBeacon"/>, or <see langword="null"/> if no matching beacon was found.</returns>
|
||||
Task<LightBeacon?> UpdateOne(LightBeacon beacon);
|
||||
/// <summary>
|
||||
/// Inserts a single light beacon into the data store.
|
||||
/// </summary>
|
||||
/// <param name="beacon">The light beacon entity to insert.</param>
|
||||
/// <returns>A task that resolves to the inserted <see cref="LightBeacon"/>, or <c>null</c> when no result is produced.</returns>
|
||||
Task<LightBeacon?> InsertOne(LightBeacon beacon);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a paginated collection of light beacons based on the specified pagination filter.
|
||||
/// </summary>
|
||||
/// <param name="request">The pagination filter containing the criteria used to page the beacon results.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a pagination response with the requested light beacons.</returns>
|
||||
Task<PaginationResponse<LightBeacon>> GetPaginatedBeacons(PaginationFilter request);
|
||||
/// <summary>
|
||||
/// Asynchronously searches for light beacons by name using the specified search text.
|
||||
/// </summary>
|
||||
/// <param name="textToSearch">The text used to search for matching light beacons by name.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="LightBeacon"/> objects matching the search criteria.</returns>
|
||||
Task<List<LightBeacon>> GetSearchByName(string textToSearch);
|
||||
}
|
||||
@@ -4,6 +4,19 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface ILocalAuditService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an asynchronous audit log entry capturing the original and modified data along with the user responsible for the change.
|
||||
/// </summary>
|
||||
/// <param name="user">The claims principal representing the user who performed the action being audited. May be null if the action is performed by an unauthenticated or system context.</param>
|
||||
/// <param name="dataOriginal">The original state of the data before the change. May be null when the action creates a new record.</param>
|
||||
/// <param name="dataModified">The modified state of the data after the change. May be null when the action deletes an existing record.</param>
|
||||
/// <param name="reason">An optional explanation or justification for the change. Defaults to null when no reason is provided.</param>
|
||||
/// <returns>A task that represents the asynchronous creation of the audit log entry.</returns>
|
||||
Task CreateAuditLogAsync(ClaimsPrincipal? user, object? dataOriginal, object? dataModified, string? reason = null);
|
||||
/// <summary>
|
||||
/// Asynchronously creates a deep copy of the specified data, producing a new independent instance of the same type.
|
||||
/// </summary>
|
||||
/// <param name="data">The data instance to be deep copied.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the deep-copied instance of type <typeparamref name="T"/>, or <c>null</c> if the copy could not be produced.</returns>
|
||||
Task<T?> DeepCopyAsync<T>(T data);
|
||||
}
|
||||
@@ -9,38 +9,199 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IMasterListService<T> where T : MasterList
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the complete master list of items of type <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an <see cref="IEnumerable{T}"/> with all items from the master list.</returns>
|
||||
Task<IEnumerable<T>> GetAllMasterList();
|
||||
/// <summary>
|
||||
/// Retrieves all master list entries, excluding items categorized as options.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a collection of <see cref="MasterListDto"/> items representing the master list without options.</returns>
|
||||
Task<IEnumerable<MasterListDto>> GetAllMasterListWithoutOptions();
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of master list items with their associated options based on the specified pagination filter.
|
||||
/// </summary>
|
||||
/// <param name="request">The pagination filter containing paging criteria such as page number and page size.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an enumerable collection of <see cref="MasterListWithPaginatedOptionsDto"/> items for the requested page.</returns>
|
||||
Task<IEnumerable<MasterListWithPaginatedOptionsDto>> GetAllMasterListWithPaginatedOptions(PaginationFilter request);
|
||||
/// <summary>
|
||||
/// Retrieves a master list entry identified by the specified <paramref name="id"/>, optionally resolving localized content based on the provided <paramref name="locale"/>. Returns <c>null</c> when no matching entry is found.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the master list entry to retrieve.</param>
|
||||
/// <param name="locale">The optional locale used to resolve localized fields; when <c>null</c>, a default or non-localized representation is returned.</param>
|
||||
/// <returns>A task that resolves to the matching master list entry of type <typeparamref name="T"/>, or <c>null</c> if the entry does not exist.</returns>
|
||||
Task<T?> GetMasterListById(ObjectId id, LocaleEnum? locale);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a master list along with its paginated options identified by the specified id.
|
||||
/// Returns null when no master list is found for the given identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the master list to retrieve.</param>
|
||||
/// <param name="request">The pagination filter used to control the paginated options returned with the master list.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="MasterListWithPaginatedOptionsDto"/> if a matching master list is found, otherwise null.</returns>
|
||||
Task<MasterListWithPaginatedOptionsDto?> GetMasterListByIdWithPaginatedOptions(ObjectId id,
|
||||
PaginationFilter request);
|
||||
PaginationFilter request);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of master list option names associated with the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the master list whose option names are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of option names for the specified master list.</returns>
|
||||
Task<List<string>> GetMasterListOptionsNamesById(ObjectId id);
|
||||
/// <summary>
|
||||
/// Retrieves a master list of <see cref="OptionList"/> entries filtered by the specified identifier and an optional text search criterion.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier used to scope the master list lookup.</param>
|
||||
/// <param name="textSearch">An optional text string used to further filter the results; may be <c>null</c> to return all matching entries.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="OptionList"/> items matching the provided identifier and text search.</returns>
|
||||
Task<List<OptionList>> GetMasterListByIdAndTextSearch(ObjectId id, string? textSearch);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a master list of options associated with the specified identifier, applying the provided search and filter criteria.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier used to locate the master list to retrieve.</param>
|
||||
/// <param name="filterOption">The filter and search options used to refine the returned list.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of <see cref="OptionList"/> entries that match the specified id and filter options.</returns>
|
||||
Task<List<OptionList>> GetMasterListByIdAndSearchOptions(ObjectId id, FilterOptionListElement filterOption);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a master list that matches the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name used to look up the master list.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The result is the matching master list of type <typeparamref name="T"/>, or <c>null</c> if no master list with the specified name is found.</returns>
|
||||
Task<T?> GetMasterListByName(string name);
|
||||
/// <summary>
|
||||
/// Inserts the specified item into the master list and returns the resulting entry, or <c>null</c> if the operation did not produce a result.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to insert into the master list.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that resolves to the inserted item, or <c>null</c> when no result is available.</returns>
|
||||
Task<T?> InsertMasterList(T item);
|
||||
/// <summary>
|
||||
/// Updates the master list with the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to be updated in the master list.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation, containing the updated item or <c>null</c> if the update could not be performed.</returns>
|
||||
Task<T?> UpdateMasterList(T item);
|
||||
/// <summary>
|
||||
/// Deletes a master list identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the master list to delete.</param>
|
||||
Task DeleteMasterListById(ObjectId id);
|
||||
/// <summary>
|
||||
/// Adds the specified option element to the master option list identified by the given identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the master option list to which the option will be added.</param>
|
||||
/// <param name="opt">The filter option list element to append to the master list.</param>
|
||||
/// <returns>A task that returns the updated <see cref="OptionList"/>, or <c>null</c> if the master list could not be found.</returns>
|
||||
Task<OptionList?> AddOptionToMasterList(ObjectId id, FilterOptionListElement opt);
|
||||
/// <summary>
|
||||
/// Updates an option in a master list, localized for the specified locale, and returns the updated option list.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the option to update.</param>
|
||||
/// <param name="opt">The option data to apply to the existing entry.</param>
|
||||
/// <param name="typeName">The name of the master list type that owns the option.</param>
|
||||
/// <param name="locale">The locale used to resolve or apply localized values.</param>
|
||||
/// <returns>A task that returns the updated <see cref="OptionList"/>, or <c>null</c> if the option could not be found.</returns>
|
||||
Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList opt, string typeName, LocaleEnum locale);
|
||||
/// <summary>
|
||||
/// Updates the full master list option identified by the specified identifier and type name.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the option to update.</param>
|
||||
/// <param name="opt">The option list containing the updated values.</param>
|
||||
/// <param name="typeName">The name of the type associated with the master list option.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the updated <see cref="OptionList"/>, or <c>null</c> if the option was not found.</returns>
|
||||
Task<OptionList?> UpdateFullMasterListOption(ObjectId id, OptionList opt, string typeName);
|
||||
/// <summary>
|
||||
/// Updates a master list option for the specified type with the provided option data.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the option to update.</param>
|
||||
/// <param name="opt">The option data to apply to the master list.</param>
|
||||
/// <param name="typeName">The name of the master list type containing the option.</param>
|
||||
/// <returns>The updated <see cref="OptionList"/>, or null if the option is not found.</returns>
|
||||
Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList opt, string typeName);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an option list item identified by the specified master ID, option ID, and locale.
|
||||
/// </summary>
|
||||
/// <param name="masterId">The identifier of the master entity that owns the option list.</param>
|
||||
/// <param name="optionId">The identifier of the specific option item to find.</param>
|
||||
/// <param name="locale">The locale used to retrieve the localized option item.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the matching <see cref="OptionList"/> or <c>null</c> if no item is found.</returns>
|
||||
Task<OptionList?> FindOptionItemById(ObjectId masterId, ObjectId optionId, LocaleEnum locale);
|
||||
/// <summary>
|
||||
/// Updates the option details of a master list entry identified by the specified id.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list entry to update.</param>
|
||||
/// <param name="opt">The master list details to apply to the entry.</param>
|
||||
/// <returns>A task that returns the updated master list details, or null if no matching entry is found.</returns>
|
||||
Task<UpdateMasterListDetailsDto?> UpdateOptionDetailsToMasterList(ObjectId id, UpdateMasterListDetailsDto opt);
|
||||
/// <summary>
|
||||
/// Updates the name of an existing master list identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list to update.</param>
|
||||
/// <param name="name">The new name to assign to the master list.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the master list was successfully updated; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdateMasterListName(ObjectId id, string name);
|
||||
/// <summary>
|
||||
/// Updates the description (name) of a master list entry identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the master list entry to update.</param>
|
||||
/// <param name="name">The new description to apply to the master list entry.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdateMasterListDescription(ObjectId id, string name);
|
||||
/// <summary>
|
||||
/// Asynchronously removes the specified option from the master list identified by the given id.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list from which the option will be removed.</param>
|
||||
/// <param name="oldOpt">The option entry to be removed from the master list.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the option was successfully removed; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> RemoveMasterListOption(ObjectId id, OptionList oldOpt);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the total count of all items in the master list.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the total number of items in the master list.</returns>
|
||||
Task<int> GetAllMasterListCount();
|
||||
/// <summary>
|
||||
/// Retrieves a paginated master list of items based on the specified filter criteria.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that defines paging parameters such as page number and page size.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the paginated response with the requested master list items.</returns>
|
||||
Task<PaginationResponse<T>> GetPaginatedMasterList(PaginationFilter filter);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes a master list option identified by the supplied identifiers.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list that contains the option to delete.</param>
|
||||
/// <param name="optId">The identifier of the specific option to remove from the master list.</param>
|
||||
/// <param name="typeName">The name of the master list type to which the option belongs.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the option was successfully deleted; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> DeleteMasterListOption(ObjectId id, ObjectId optId, string typeName);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a paginated master list along with its associated paginated options.
|
||||
/// </summary>
|
||||
/// <param name="listFilter">The pagination filter applied to the master list results.</param>
|
||||
/// <param name="optionsFilter">The pagination filter applied to the associated options.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the paginated response with master list and options data.</returns>
|
||||
Task<PaginationResponse<MasterListWithPaginatedOptionsDto>> GetPaginatedMasterListWithPaginatedOptions(
|
||||
PaginationFilter listFilter, PaginationFilter optionsFilter);
|
||||
PaginationFilter listFilter, PaginationFilter optionsFilter);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of options associated with the specified list.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination parameters used to control the page size and page index of the returned results.</param>
|
||||
/// <param name="listId">The identifier of the list whose options should be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a <see cref="PaginationResponse{OptionList}"/> with the requested options.</returns>
|
||||
Task<PaginationResponse<OptionList>> GetPaginatedOptions(PaginationFilter filter, ObjectId listId);
|
||||
/// <summary>
|
||||
/// Retrieves the associated list identifier for the given object based on the specified master list types.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the object whose associated list is being requested.</param>
|
||||
/// <param name="masterListType1">The first master list type used to determine the association.</param>
|
||||
/// <param name="masterListType2">The second master list type used to determine the association.</param>
|
||||
/// <returns>A task that returns the associated <see cref="ObjectId"/> if found, or <c>null</c> if no association exists.</returns>
|
||||
Task<ObjectId?> GetAssociatedList(ObjectId id, MasterListType masterListType1, MasterListType masterListType2);
|
||||
/// <summary>
|
||||
/// Retrieves the available options associated with the specified list identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the list whose options are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of option strings for the specified list.</returns>
|
||||
Task<List<string>> GetOptionsOfList(ObjectId id);
|
||||
}
|
||||
@@ -7,17 +7,81 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IMasterListServiceFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the service object of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="serviceType">An object that specifies the type of service object to get.</param>
|
||||
/// <returns>
|
||||
/// A service object of type <paramref name="serviceType"/>, or <c>null</c> if there is no service
|
||||
/// object of that type registered.
|
||||
/// </returns>
|
||||
object GetService(Type serviceType);
|
||||
/// <summary>
|
||||
/// Retrieves a service instance from the master list based on the specified service name.
|
||||
/// </summary>
|
||||
/// <param name="serviceName">The <see cref="MasterListType"/> identifier used to look up the desired service.</param>
|
||||
/// <returns>An <see cref="object"/> representing the resolved service, or <see langword="null"/> if no matching service is found.</returns>
|
||||
object GetService(MasterListType serviceName);
|
||||
/// <summary>
|
||||
/// Retrieves a typed representation of the specified <paramref name="masterList"/> based on the given <paramref name="masterListType"/>.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The type that determines how the master list should be converted or filtered.</param>
|
||||
/// <param name="masterList">The master list to be returned in a typed form.</param>
|
||||
/// <returns>A typed object representing the master list, or <c>null</c> if no matching type is found.</returns>
|
||||
object? GetTypedMasterList(MasterListType masterListType, MasterList masterList);
|
||||
/// <summary>
|
||||
/// Gets the specific <see cref="Type"/> associated with the given master list type, mapping the master list category to its corresponding implementation type.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The master list type whose associated <see cref="Type"/> should be returned.</param>
|
||||
/// <returns>The <see cref="Type"/> that corresponds to the specified <paramref name="masterListType"/>.</returns>
|
||||
Type GetMasterListSpecificType(MasterListType masterListType);
|
||||
/// <summary>
|
||||
/// Inserts a new <see cref="MasterList"/> record based on the specified <paramref name="masterListType"/>.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The type of master list to insert.</param>
|
||||
/// <param name="masterList">The master list entity to be inserted.</param>
|
||||
/// <returns>A task that represents the asynchronous insert operation. The result contains the inserted master list data, or <c>null</c> if the insert was not successful.</returns>
|
||||
Task<object?> InsertMasterList(MasterListType masterListType, MasterList masterList);
|
||||
/// <summary>
|
||||
/// Asynchronously updates an existing master list entry based on the specified master list type and master list data.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The type of master list to update, used to determine the target list or category.</param>
|
||||
/// <param name="masterList">The master list entity containing the updated data to be persisted.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation. The result is an object containing the updated master list information, or <c>null</c> if no matching record was found.</returns>
|
||||
Task<object?> UpdateMasterList(MasterListType masterListType, MasterList masterList);
|
||||
/// <summary>
|
||||
/// Retrieves a master list entry by its identifier and type, optionally filtered by the specified data locale.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The type of the master list to query.</param>
|
||||
/// <param name="masterListId">The unique identifier of the master list entry to retrieve.</param>
|
||||
/// <param name="dataLocale">The optional locale used to resolve localized data; when null, no locale filtering is applied.</param>
|
||||
/// <returns>A task that yields the matching master list entry as an object, or null if no entry is found.</returns>
|
||||
Task<object?> GetMasterListById(MasterListType masterListType, ObjectId masterListId, LocaleEnum? dataLocale);
|
||||
/// <summary>
|
||||
/// Retrieves a list of nurse observation entries as string values.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="List{T}"/> of <see cref="string"/> containing the nurse observation data.</returns>
|
||||
List<string> StringNurseObs();
|
||||
/// <summary>
|
||||
/// Retrieves a translated <see cref="Patient"/> based on the provided <paramref name="unit"/> and <paramref name="locale"/>.
|
||||
/// Returns <c>null</c> when the <paramref name="patient"/>, <paramref name="unit"/>, or <paramref name="locale"/> is not provided or no translation is found.
|
||||
/// </summary>
|
||||
/// <param name="unit">The organizational unit context used to look up the translation. May be <c>null</c>.</param>
|
||||
/// <param name="locale">The target locale for the translation. May be <c>null</c>.</param>
|
||||
/// <param name="patient">The patient to be translated. May be <c>null</c>.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that resolves to the translated <see cref="Patient"/>, or <c>null</c> if no translation is available.</returns>
|
||||
Task<Patient?> GetPatientTraslated(Unit? unit, LocaleEnum? locale, Patient? patient);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a master list option by its identifier, optionally localized to the specified data locale.
|
||||
/// Returns <see langword="null"/> if no matching option is found.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The type of master list to search within.</param>
|
||||
/// <param name="masterListId">The identifier of the master list containing the option.</param>
|
||||
/// <param name="masterListOptionId">The identifier of the master list option to retrieve.</param>
|
||||
/// <param name="dataLocale">The optional locale used to localize the returned option data.</param>
|
||||
/// <returns>A task that yields the matching master list option as an <see cref="object"/>, or <see langword="null"/> if not found.</returns>
|
||||
Task<object?> GetMasterListOptionById(MasterListType masterListType, ObjectId masterListId,
|
||||
ObjectId masterListOptionId,
|
||||
LocaleEnum? dataLocale);
|
||||
ObjectId masterListOptionId,
|
||||
LocaleEnum? dataLocale);
|
||||
}
|
||||
@@ -7,19 +7,89 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IMedicineService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="Medicine"/> entity by its unique code identifier.
|
||||
/// Returns <c>null</c> when no matching medicine is found.
|
||||
/// </summary>
|
||||
/// <param name="code">The unique code used to look up the medicine.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the matching <see cref="Medicine"/> or <c>null</c> if not found.</returns>
|
||||
Task<Medicine?> GetByCode(string code);
|
||||
/// <summary>
|
||||
/// Retrieves a list of medicines that match the provided codes or notes.
|
||||
/// </summary>
|
||||
/// <param name="codeNote">A list of strings representing the codes or notes used to look up medicines.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Medicine"/> objects matching the provided codes or notes.</returns>
|
||||
Task<List<Medicine>> GetByCodeOrNote(List<string> codeNote);
|
||||
/// <summary>
|
||||
/// Retrieves a medicine by its name, returning null if no matching medicine is found.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the medicine to look up.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the matching <see cref="Medicine"/> or null if not found.</returns>
|
||||
Task<Medicine?> GetByName(string name);
|
||||
/// <summary>
|
||||
/// Retrieves the medicines associated with the specified patient treatments.
|
||||
/// </summary>
|
||||
/// <param name="treatments">The collection of patient treatments, which may include null entries, whose medicines are to be obtained.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the collection of medicines linked to the provided treatments.</returns>
|
||||
Task<IEnumerable<Medicine>> GetMedicinesOfTreatments(IEnumerable<PatientTreatment?> treatments);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the collection of active medicines associated with the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose active medicines are being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an enumerable collection of active <see cref="Medicine"/> records for the patient.</returns>
|
||||
Task<IEnumerable<Medicine>> GetActiveMedicinesByPatient(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of medicines based on the provided filter criteria.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter containing page size, page number, and optional search criteria.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a <see cref="PaginationResponse{T}"/> with the requested <see cref="Medicine"/> items and pagination metadata.</returns>
|
||||
Task<PaginationResponse<Medicine>> GetPaginatedMedicines(PaginationFilter filter);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all medicines from the data store.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of all <see cref="Medicine"/> entities.</returns>
|
||||
Task<List<Medicine>> GetAll();
|
||||
/// <summary>
|
||||
/// Retrieves a medicine by its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="medicineId">The unique identifier of the medicine to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="Medicine"/> if found; otherwise, <c>null</c>.</returns>
|
||||
Task<Medicine?> GetMedicineById(ObjectId medicineId);
|
||||
/// <summary>
|
||||
/// Asynchronously posts a new medicine and returns the created medicine, or null if the operation fails.
|
||||
/// </summary>
|
||||
/// <param name="medicine">The medicine entity to be posted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the posted <see cref="Medicine"/>, or null if the medicine could not be posted.</returns>
|
||||
Task<Medicine?> PostMedicine(Medicine medicine);
|
||||
/// <summary>
|
||||
/// Updates an existing medicine in the data store and returns the updated entity, or <c>null</c> if no matching medicine was found.
|
||||
/// </summary>
|
||||
/// <param name="medicine">The medicine entity containing the updated values to be persisted.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation. The result is the updated <see cref="Medicine"/> when the update succeeds, or <c>null</c> when the medicine does not exist.</returns>
|
||||
Task<Medicine?> UpdateMedicine(Medicine medicine);
|
||||
/// <summary>
|
||||
/// Deletes a medicine record identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="medicineId">The unique identifier of the medicine to delete.</param>
|
||||
Task DeleteMedicineById(ObjectId medicineId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all available types as a list of string identifiers.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of type identifiers.</returns>
|
||||
Task<List<string>> GetAllTypes();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all available groups, returning their identifiers or names as a list of strings.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of strings representing all groups.</returns>
|
||||
Task<List<string>> GetAllGroups();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all available names.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of all names.</returns>
|
||||
Task<List<string>> GetAllNames();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the complete list of available codes from the data source.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of code strings.</returns>
|
||||
Task<List<string>> GetAllCodes();
|
||||
}
|
||||
@@ -6,14 +6,61 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface INoticeService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the specified notice.
|
||||
/// </summary>
|
||||
/// <param name="notice">The notice to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation.</returns>
|
||||
Task DeleteNoticeAsync(Notice notice);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes a notice identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="noticeId">The unique identifier of the notice to delete.</param>
|
||||
Task DeleteNoticeByIdAsync(ObjectId noticeId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="Notice"/> entity by its unique identifier from the data store.
|
||||
/// Returns <see langword="null"/> when no notice matches the provided identifier.
|
||||
/// </summary>
|
||||
/// <param name="noticeId">The <see cref="ObjectId"/> that uniquely identifies the notice to retrieve.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> containing the matching <see cref="Notice"/>, or <see langword="null"/> if no notice is found.</returns>
|
||||
Task<Notice?> GetNoticeByIdAsync(ObjectId noticeId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a collection of notices filtered by the specified notice type.
|
||||
/// The task result may be null when no notices match the given type.
|
||||
/// </summary>
|
||||
/// <param name="noticeType">The type of notice to filter by.</param>
|
||||
/// <returns>A task containing an enumerable of matching <see cref="Notice"/> objects, or null if none are found.</returns>
|
||||
Task<IEnumerable<Notice>?> GetNoticeByTypeAsync(string noticeType);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a collection of notices.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an enumerable collection of <see cref="Notice"/> objects.</returns>
|
||||
Task<IEnumerable<Notice>> GetNoticesAsync();
|
||||
/// <summary>
|
||||
/// Inserts a new notice into the data store.
|
||||
/// </summary>
|
||||
/// <param name="notice">The notice entity to insert.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the inserted <see cref="Notice"/>, or <c>null</c> if the notice could not be inserted.</returns>
|
||||
Task<Notice?> InsertNotice(Notice notice);
|
||||
/// <summary>
|
||||
/// Asynchronously updates an existing notice in the system.
|
||||
/// </summary>
|
||||
/// <param name="notice">The notice entity containing the updated information to be persisted.</param>
|
||||
Task UpdateNoticeAsync(Notice notice);
|
||||
/// <summary>
|
||||
/// Persists the provided API request to the data store.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to be saved.</param>
|
||||
Task SaveRequest(ApiRequest apiRequest);
|
||||
/// <summary>
|
||||
/// Asynchronously persists the specified API request.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to save.</param>
|
||||
Task SaveRequestAsync(ApiRequest apiRequest);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the collection of notices associated with the specified display identifier.
|
||||
/// </summary>
|
||||
/// <param name="displayId">The identifier of the display whose notices should be retrieved.</param>
|
||||
/// <returns>A task that returns the notices for the display, or <c>null</c> when no notices are found for the given display.</returns>
|
||||
Task<IEnumerable<Notice>?> GetNoticesByDisplayId(ObjectId displayId);
|
||||
}
|
||||
@@ -6,7 +6,25 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IObservationDemoService
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a list of patient observations by evaluating the specified data fields for the given patient.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient for whom the observations are being generated.</param>
|
||||
/// <param name="dataFields">The collection of fields used to drive the observation generation.</param>
|
||||
/// <returns>A task representing the asynchronous operation that returns the list of generated patient observations.</returns>
|
||||
Task<List<PatientObservation>> GenerateObservationByField(Patient patient, List<Field> dataFields);
|
||||
/// <summary>
|
||||
/// Generates a grouped observation for the specified patient based on the provided grouped field configuration.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient for whom the grouped observation is generated.</param>
|
||||
/// <param name="groupedField">The grouped field definition that determines the grouping criteria for the observation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the generated <see cref="GroupedObservation"/>.</returns>
|
||||
Task<GroupedObservation> GenerateGroupedObservation(Patient patient, GroupedField groupedField);
|
||||
/// <summary>
|
||||
/// Generates a list of patient observation alarms for the specified patient based on the provided data alarm fields.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient for whom the observation alarms are generated.</param>
|
||||
/// <param name="dataAlarmfields">The list of fields used to determine and generate the data alarms.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientObservationAlarm"/> objects generated for the patient.</returns>
|
||||
Task<List<PatientObservationAlarm>> GenerateAlarmByField(Patient patient, List<Field> dataAlarmfields);
|
||||
}
|
||||
@@ -14,72 +14,274 @@ public interface IObservationService : IApiRequestService
|
||||
/*
|
||||
List<PatientObservation> FindLastObservations(ObjectId patientId, string codingSystem, string code, int num = 2);
|
||||
*/
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all patient observations associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are to be retrieved.</param>
|
||||
/// <returns>A task that returns an <see cref="IAsyncCursor{TDocument}"/> of <see cref="PatientObservation"/> instances for the given patient.</returns>
|
||||
Task<IAsyncCursor<PatientObservation>> FindByPatientIdAsync(ObjectId patientId);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves patient observations matching the specified patient identifier, coding system, and name.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="codingSystem">The coding system used to classify the observations (e.g., LOINC, SNOMED).</param>
|
||||
/// <param name="name">The name of the observation to filter by.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an <see cref="IAsyncCursor{TDocument}"/> of <see cref="PatientObservation"/> matching the criteria.</returns>
|
||||
Task<IAsyncCursor<PatientObservation>> FindByPatientIdAndCodingSystemAsync(ObjectId patientId, string codingSystem,
|
||||
string name);
|
||||
string name);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent patient observations for the specified patient, optionally filtered by a set of observation codes.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="num">The maximum number of most recent observations to return. Defaults to 2.</param>
|
||||
/// <param name="filterObservations">An optional list of observation codes used to restrict the result set; if null, observations are not filtered by code.</param>
|
||||
/// <returns>A task that resolves to a list of the most recent <see cref="PatientObservation"/> entries matching the criteria.</returns>
|
||||
Task<List<PatientObservation>> FindLastObservations(ObjectId patientId, int num = 2,
|
||||
List<string>? filterObservations = null);
|
||||
List<string>? filterObservations = null);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the status of the provided patient observations that have reached their expiration.
|
||||
/// </summary>
|
||||
/// <param name="expiredObservations">The list of patient observations to update as expired.</param>
|
||||
Task UpdateExpiredObservations(List<PatientObservation> expiredObservations);
|
||||
/// <summary>
|
||||
/// Asynchronously expires observations that are no longer valid and recalculates the dependent data.
|
||||
/// </summary>
|
||||
Task ExpireObservationsAndRecalculateAsync();
|
||||
/// <summary>
|
||||
/// Asynchronously expires active alerts and powers off the device.
|
||||
/// </summary>
|
||||
Task ExpireAlertsAndPowerOffAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent unique patient observations for the specified patient, filtered by observation name, with an optional cache expiration window in seconds.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="name">The name of the observation to filter by.</param>
|
||||
/// <param name="expires">Optional expiration time in seconds applied to the cached results. If <c>null</c>, no expiration is applied.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of the latest unique <see cref="PatientObservation"/> values matching the specified patient and name.</returns>
|
||||
Task<List<PatientObservation>> FindLatestUniqueValuesByName(ObjectId patientId, string name, int? expires);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent observations recorded for a patient, optionally filtered to a specific set of fields.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose observations are being retrieved.</param>
|
||||
/// <param name="filterObservations">An optional list of fields used to restrict which observations are returned. When null, observations for all fields are considered.</param>
|
||||
/// <param name="mapped">Indicates whether the returned observations should be mapped (default true) or returned in their raw form.</param>
|
||||
/// <param name="ct">The cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that resolves to a list of the patient's most recent <see cref="PatientObservation"/> entries.</returns>
|
||||
Task<List<PatientObservation>> FindLastObservationsByField(ObjectId patientId,
|
||||
List<Field>? filterObservations = null, bool mapped = true, CancellationToken ct = default);
|
||||
List<Field>? filterObservations = null, bool mapped = true, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent intravenous line observations associated with a specific location for the given patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose intravenous line observations are being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of nullable <see cref="PatientObservation"/> entries representing the latest intravenous line observations by location, where individual entries may be <c>null</c> when no data is available.</returns>
|
||||
Task<List<PatientObservation?>> FindLastIntravenousLinesObservationByLocation(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a patient observation, with options to control whether the observation is persisted and whether it is mapped.
|
||||
/// </summary>
|
||||
/// <param name="patientObservation">The patient observation to insert.</param>
|
||||
/// <param name="persistObs">Indicates whether the observation should be persisted; defaults to <c>true</c>.</param>
|
||||
/// <param name="mapObs">Indicates whether the observation should be mapped; defaults to <c>true</c>.</param>
|
||||
Task InsertObservation(PatientObservation patientObservation, bool persistObs = true, bool mapObs = true);
|
||||
/// <summary>
|
||||
/// Inserts the specified patient observation only if it has changed, optionally persisting the observation and applying a mapping during the insert.
|
||||
/// </summary>
|
||||
/// <param name="name">The name associated with the patient observation being evaluated for changes.</param>
|
||||
/// <param name="observation">The patient observation to compare against the existing value and potentially insert.</param>
|
||||
/// <param name="persistObs">Indicates whether the observation should be persisted when it is inserted. Defaults to <c>true</c>.</param>
|
||||
/// <param name="mapObs">Indicates whether the observation should be mapped as part of the insert operation. Defaults to <c>true</c>.</param>
|
||||
/// <returns>A task that returns <c>true</c> if the observation was inserted because a change was detected; otherwise, <c>false</c> if no insert was performed.</returns>
|
||||
Task<bool> InsertIfChanged(string name, PatientObservation observation, bool persistObs = true, bool mapObs = true);
|
||||
/// <summary>
|
||||
/// Inserts a new nurse observation for a patient into the underlying data store.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation data recorded by the nurse to be persisted.</param>
|
||||
Task InsertNurseObservation(PatientObservation obs);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient whose related records should be removed.</param>
|
||||
Task DeleteByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously archives the specified patient observation, preserving it for historical or compliance purposes while removing it from the active set.
|
||||
/// </summary>
|
||||
/// <param name="observation">The patient observation to archive.</param>
|
||||
/// <returns>A task that represents the asynchronous archive operation.</returns>
|
||||
Task Archive(PatientObservation observation);
|
||||
/// <summary>
|
||||
/// Asynchronously maps a <see cref="PatientObservation"/> to a corresponding observation, optionally restricting the lookup to name-based matching. Returns <see langword="null"/> when no matching observation is found.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source <see cref="PatientObservation"/> to be mapped.</param>
|
||||
/// <param name="onlyByName">When <see langword="true"/>, restricts the lookup to name-based matching; otherwise, the default mapping behavior is applied.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing the mapped <see cref="PatientObservation"/>, or <see langword="null"/> if no match is found.</returns>
|
||||
Task<PatientObservation?> MapObservation(PatientObservation obs, bool onlyByName = false);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the most recent observation time for each patient, returning a mapping of patient identifiers to their last observation timestamps.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a dictionary where each key is a patient <see cref="ObjectId"/> and the associated value is the <see cref="DateTime"/> of that patient's latest observation.</returns>
|
||||
Task<Dictionary<ObjectId, DateTime>> FindAllLastPatientObservationTime();
|
||||
/// <summary>
|
||||
/// Asynchronously maps or looks up a <see cref="PatientObservation"/> based on the name of the provided observation, returning the matching observation or <c>null</c> when no match is found.
|
||||
/// </summary>
|
||||
/// <param name="obs">The <see cref="PatientObservation"/> whose name is used to perform the mapping or lookup.</param>
|
||||
/// <returns>A <see cref="Task{PatientObservation}"/> that resolves to the matching <see cref="PatientObservation"/>, or <c>null</c> if no corresponding observation is found.</returns>
|
||||
Task<PatientObservation?> MapObservationsByName(PatientObservation obs);
|
||||
/// <summary>
|
||||
/// Archives the specified patient, moving their record out of the active set so that it is retained for historical or compliance purposes while no longer appearing in routine operational queries.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose record is to be archived.</param>
|
||||
Task Archive(Patient patient);
|
||||
/// <summary>
|
||||
/// Archives records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose records should be archived.</param>
|
||||
Task ArchiveByPatientId(ObjectId id);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing patient observation in the data store.
|
||||
/// </summary>
|
||||
/// <param name="observation">The patient observation containing the updated information.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation.</returns>
|
||||
Task UpdateObservation(PatientObservation observation);
|
||||
/// <summary>
|
||||
/// Updates the specified identifier field (<paramref name="nameId"/>) across multiple objects, replacing the existing value <paramref name="oldId"/> with the new value <paramref name="id"/>.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name of the identifier field to be updated.</param>
|
||||
/// <param name="id">The new <see cref="ObjectId"/> value to assign to the field.</param>
|
||||
/// <param name="oldId">The current <see cref="ObjectId"/> value to be replaced.</param>
|
||||
/// <returns>A task that represents the asynchronous bulk update operation.</returns>
|
||||
Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously broadcasts a patient observation to subscribed listeners or endpoints.
|
||||
/// </summary>
|
||||
/// <param name="obs">The base patient observation to be broadcast.</param>
|
||||
Task SendObsBroadcast(BasePatientObservation obs);
|
||||
/// <summary>
|
||||
/// Sends a broadcast containing the specified patient observations to the given patient location.
|
||||
/// </summary>
|
||||
/// <param name="obs">The list of patient observations to include in the broadcast.</param>
|
||||
/// <param name="location">The target patient location that will receive the broadcast.</param>
|
||||
Task SendObsBroadcast(List<PatientObservation> obs, PatientLocation location);
|
||||
/// <summary>
|
||||
/// Asynchronously sends a broadcast of patient observations to the specified Point of Care (POC) system.
|
||||
/// </summary>
|
||||
/// <param name="obs">The list of patient observations to be transmitted in the broadcast.</param>
|
||||
/// <param name="pocId">The identifier of the Point of Care system that will receive the observations.</param>
|
||||
Task SendObsBroadcast(List<PatientObservation> obs, ObjectId pocId);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the most recent <see cref="PatientObservation"/> for a patient recorded before the specified date, optionally filtered by observation name.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observation is being queried.</param>
|
||||
/// <param name="date">The cutoff date; only observations recorded strictly before this date are considered.</param>
|
||||
/// <param name="obsName">The optional name of the observation to filter by, or <c>null</c> to match any observation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the latest matching <see cref="PatientObservation"/>, or <c>null</c> if none was found before the given date.</returns>
|
||||
Task<PatientObservation?> FindLastBeforeDate(ObjectId patientId, DateTime date, string? obsName);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the patient observations for the specified patient that share the given date, optionally filtered by observation name.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose observations will be searched.</param>
|
||||
/// <param name="date">The date used to match observations.</param>
|
||||
/// <param name="obsName">The optional observation name used to filter the results. When null, observations are not filtered by name.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of matching <see cref="PatientObservation"/> records, or null when no observations match the criteria.</returns>
|
||||
Task<List<PatientObservation>?> FindAnyWithSameDate(ObjectId patientId, DateTime date, string? obsName);
|
||||
|
||||
//TODO To implement
|
||||
/// <summary>
|
||||
/// Retrieves all patient observations recorded before the specified date, optionally filtered to a specific set of observation types.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="date">The cutoff date; only observations recorded before this date will be returned.</param>
|
||||
/// <param name="filterObservations">An optional list of observation identifiers used to restrict the results to specific observation types. When null, all observation types are included.</param>
|
||||
/// <returns>A task that resolves to a list of PatientObservation instances matching the criteria.</returns>
|
||||
Task<List<PatientObservation>> FindAllBeforeDate(ObjectId patientId, DateTime date,
|
||||
List<string>? filterObservations = null);
|
||||
List<string>? filterObservations = null);
|
||||
|
||||
//TODO To implement
|
||||
/// <summary>
|
||||
/// Retrieves all <see cref="PatientObservation"/> entries for the specified patient recorded after the given date, optionally restricted to a subset of observation names.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="date">The cutoff date; only observations with a timestamp after this value are returned.</param>
|
||||
/// <param name="filterObservations">An optional list of observation names to restrict the result to. When <c>null</c> or empty, all observations after the date are returned.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that yields the list of matching <see cref="PatientObservation"/> entries.</returns>
|
||||
Task<List<PatientObservation>> FindAllAfterDate(ObjectId patientId, DateTime date,
|
||||
List<string>? filterObservations = null);
|
||||
List<string>? filterObservations = null);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of patient observations for the specified patient within an optional date range, optionally filtered by observation names and including archived records when requested.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="startDate">The inclusive lower bound of the observation date range, or null to apply no lower bound.</param>
|
||||
/// <param name="endDate">The inclusive upper bound of the observation date range, or null to apply no upper bound.</param>
|
||||
/// <param name="filterObservations">An optional list of observation names used to restrict the returned observations.</param>
|
||||
/// <param name="fromArchived">When true, observations are retrieved from archived records; otherwise, only active records are considered.</param>
|
||||
/// <param name="filter">An optional pagination filter applied to the result set.</param>
|
||||
/// <returns>A task that resolves to a list of <see cref="PatientObservation"/> entries matching the provided criteria.</returns>
|
||||
Task<List<PatientObservation>> FindAllBetweenDates(ObjectId patientId, DateTime? startDate, DateTime? endDate,
|
||||
List<string>? filterObservations = null, bool fromArchived = false, PaginationFilter? filter = null);
|
||||
List<string>? filterObservations = null, bool fromArchived = false, PaginationFilter? filter = null);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the most recent non-expired observations for the specified patient, optionally filtered by observation name and constrained by pagination parameters.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="name">The name of the observation type used to filter the results.</param>
|
||||
/// <param name="endAfter">Optional parameter that defines the pagination boundary; when provided, observations are returned starting after this position.</param>
|
||||
/// <param name="num">Optional parameter that limits the maximum number of observations returned.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing a collection of matching non-expired <see cref="PatientObservation"/> records; an empty collection is returned if none are found.</returns>
|
||||
Task<IEnumerable<PatientObservation>> FindLastNotExpiredObservatonsByPatient(ObjectId patientId, string name,
|
||||
int? endAfter = null, int? num = null);
|
||||
int? endAfter = null, int? num = null);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks observations and expires those that meet the expiration criteria.
|
||||
/// </summary>
|
||||
Task CheckAndExpireObservations();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves patient observations that have not been marked as expired but should be, based on their validity period or business rules.
|
||||
/// </summary>
|
||||
/// <returns>An asynchronous stream of <see cref="PatientObservation"/> instances that are not expired but meet the criteria to be expired.</returns>
|
||||
IAsyncEnumerable<PatientObservation> FindNotExpiredObservationsShouldBeExpired();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Processes a collection of patient observations, associating them with the specified patient and recording the message time.
|
||||
/// </summary>
|
||||
/// <param name="observations">The list of patient observations to process.</param>
|
||||
/// <param name="patient">The patient associated with the observations.</param>
|
||||
/// <param name="messageTime">The timestamp of the message containing the observations.</param>
|
||||
/// <param name="observationData">Optional additional data related to the observations.</param>
|
||||
void ProcessObservations(List<PatientObservation> observations, Patient patient, DateTime messageTime,
|
||||
ObservationData? observationData = null);
|
||||
ObservationData? observationData = null);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously processes and expires observations that have exceeded their validity period.
|
||||
/// </summary>
|
||||
Task ExpireObservations();
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a simple patient observation record.
|
||||
/// </summary>
|
||||
/// <param name="observation">The patient observation to insert.</param>
|
||||
/// <returns>A task that represents the asynchronous insert operation.</returns>
|
||||
Task InsertSimpleObservation(PatientObservation observation);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a paginated collection of patient observations based on the specified filter criteria.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that controls the page size, page number, and any additional query criteria applied to the patient observations.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a <see cref="PaginationResponse{T}"/> of <see cref="PatientObservation"/> with the requested page of results.</returns>
|
||||
Task<PaginationResponse<PatientObservation>> GetPaginatedObservations(PaginationFilter filter);
|
||||
/// <summary>
|
||||
/// Asynchronously saves a nurse observation request.
|
||||
/// </summary>
|
||||
/// <param name="request">The API request containing the nurse observation data to save.</param>
|
||||
Task SaveRequestNurseObsAsync(ApiRequest request);
|
||||
}
|
||||
@@ -6,12 +6,45 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IPatientCarePlanService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a list of patient care plans associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose care plans are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientCarePlan"/> records for the given patient.</returns>
|
||||
Task<List<PatientCarePlan>> FindByPatientId(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Retrieves a list of patient care plans associated with the specified user identifier.
|
||||
/// </summary>
|
||||
/// <param name="userId">The unique identifier of the user whose patient care plans are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of patient care plans associated with the user.</returns>
|
||||
Task<List<PatientCarePlan>> FindByUserId(ObjectId userId);
|
||||
/// <summary>
|
||||
/// Retrieves all patient care plans from the system.
|
||||
/// </summary>
|
||||
/// <returns>A task containing a list of all <see cref="PatientCarePlan"/> records.</returns>
|
||||
Task<List<PatientCarePlan>> FindAll();
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a single patient care plan into the underlying data store.
|
||||
/// </summary>
|
||||
/// <param name="patientCarePlan">The patient care plan to insert.</param>
|
||||
Task InsertOneAsync(PatientCarePlan patientCarePlan);
|
||||
/// <summary>
|
||||
/// Archives the care plan associated with a finished procedure for the specified patient, processing the provided list of items to be archived.
|
||||
/// </summary>
|
||||
/// <param name="patientWithFinishedProcedure">The patient whose procedure has been completed and whose care plan should be archived.</param>
|
||||
/// <param name="itemsToArchive">The collection of option list items to be archived as part of the care plan archival process.</param>
|
||||
Task ArchiveCarePlanFromJob(Patient patientWithFinishedProcedure, List<OptionList> itemsToArchive);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously archives records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientid">The unique identifier of the patient whose records are to be archived.</param>
|
||||
Task ArchiveByPatientId(ObjectId patientid);
|
||||
/// <summary>
|
||||
/// Updates the ObjectId references from the specified old identifier to a new one across multiple records associated with the given patient.
|
||||
/// </summary>
|
||||
/// <param name="patientid">The string identifier of the patient whose related records will be updated.</param>
|
||||
/// <param name="patientId">The ObjectId of the patient used to locate the records to update.</param>
|
||||
/// <param name="oldId">The existing ObjectId value to be replaced in the matched records.</param>
|
||||
Task UpdateManyObjectId(string patientid, ObjectId patientId, ObjectId oldId);
|
||||
}
|
||||
@@ -11,74 +11,358 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IPatientService : IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a patient by their unique patient identifier, optionally including location information.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient to find.</param>
|
||||
/// <param name="withLocation">When set to <c>true</c>, includes location details in the returned patient; otherwise, only basic patient data is returned.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the found <see cref="Patient"/>, or <c>null</c> if no patient matches the specified identifier.</returns>
|
||||
Task<Patient?> FindByPatientId(string patientId, bool withLocation = false);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a patient by their unique identifier, applying the specified locale for localized data.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient to locate.</param>
|
||||
/// <param name="localeEnum">The locale to use when retrieving or formatting localized patient information.</param>
|
||||
/// <returns>A task that resolves to the matching patient, or <c>null</c> if no patient is found for the given identifier.</returns>
|
||||
Task<Patient?> FindByPatientIdWithLocale(string patientId, LocaleEnum localeEnum);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an archived <see cref="Patient"/> matching the specified patient number, returning <c>null</c> when no matching archived record is found.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The unique patient number used to look up the archived patient record.</param>
|
||||
/// <returns>A <see cref="Task{Patient}"/> that yields the matching archived <see cref="Patient"/>, or <c>null</c> if no archived patient is found.</returns>
|
||||
Task<Patient?> FindByPatientNumberArchived(string patientNumber);
|
||||
/// <summary>
|
||||
/// Retrieves a patient by their unique patient number, optionally including location information in the result.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The unique patient number used to look up the patient.</param>
|
||||
/// <param name="withLocation">When <c>true</c>, location information is included in the returned patient; otherwise, it is omitted.</param>
|
||||
/// <returns>A task that yields the matching <see cref="Patient"/> if one is found, or <c>null</c> when no patient matches the given number.</returns>
|
||||
Task<Patient?> FindByPatientNumber(string patientNumber, bool withLocation = false);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a patient associated with the specified location.
|
||||
/// Returns <see langword="null"/> if no matching patient is found or if the location is <see langword="null"/>.
|
||||
/// </summary>
|
||||
/// <param name="location">The patient location used to search for a matching patient. May be <see langword="null"/>.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that yields the matching <see cref="Patient"/>, or <see langword="null"/> if no patient is found.</returns>
|
||||
Task<Patient?> FindByLocation(PatientLocation? location);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a patient associated with the specified Point of Care identifier.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The ObjectId representing the Point of Care identifier used to look up the patient.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="Patient"/> if found; otherwise, <c>null</c>.</returns>
|
||||
Task<Patient?> FindByPointOfCareId(ObjectId pocId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a patient identified by the specified unit and point-of-care identifiers.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unique identifier of the unit to search within.</param>
|
||||
/// <param name="pointOfCare">The unique identifier of the point-of-care associated with the patient.</param>
|
||||
/// <returns>A task that yields the matching <see cref="Patient"/>, or <c>null</c> if no patient is found for the given unit and point-of-care.</returns>
|
||||
Task<Patient?> FindByUnitAndPocId(ObjectId unit, ObjectId pointOfCare);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a list of patients associated with the specified point of care.
|
||||
/// </summary>
|
||||
/// <param name="pointOfCare">The point of care identifier used to locate matching patients.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Patient"/> objects that match the specified point of care.</returns>
|
||||
Task<List<Patient>> FindByPointOfCare(string pointOfCare);
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of patients associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The ObjectId of the unit whose patients should be counted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the total count of patients for the given unit.</returns>
|
||||
Task<long> CountPatientsByUnitId(ObjectId unitId);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously locates a patient using the provided identifiers, supporting lookup by patient
|
||||
/// ID, patient number, or location when <paramref name="findByLocation"/> is enabled.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient to find.</param>
|
||||
/// <param name="patientNumber">The patient number used as an alternative lookup key.</param>
|
||||
/// <param name="location">The patient location used when searching by location.</param>
|
||||
/// <param name="findByLocation">When <c>true</c>, the search is performed using the supplied
|
||||
/// <paramref name="location"/> instead of the patient identifiers.</param>
|
||||
/// <returns>A <see cref="Task{Patient}"/> that yields the matching <see cref="Patient"/>, or
|
||||
/// <c>null</c> if no patient is found.</returns>
|
||||
Task<Patient?> FindPatient(string? patientId, string? patientNumber, PatientLocation? location,
|
||||
bool findByLocation = false);
|
||||
bool findByLocation = false);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a new patient record into the data store.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient entity to be inserted.</param>
|
||||
Task Insert(Patient patient);
|
||||
/// <summary>
|
||||
/// Asynchronously inserts the specified <see cref="Patient"/> into the data store.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient entity to be persisted.</param>
|
||||
Task InsertAsync(Patient patient);
|
||||
/// <summary>
|
||||
/// Archives the specified patient record.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to archive.</param>
|
||||
Task ArchivePatient(Patient patient);
|
||||
/// <summary>
|
||||
/// Archives the patient data identified by the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientid">The unique identifier of the patient whose data should be archived.</param>
|
||||
Task ArchivePatientData(ObjectId patientid);
|
||||
/// <summary>
|
||||
/// Merges the specified patient record with the existing patient identified by the old patient number.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient data to merge into the existing record.</param>
|
||||
/// <param name="oldPatienNumber">The identifier of the existing patient record to be merged.</param>
|
||||
Task MergePatient(Patient patient, string oldPatienNumber);
|
||||
/// <summary>
|
||||
/// Updates the location of a patient identified by the specified object identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose location will be updated.</param>
|
||||
/// <param name="location">The new patient location, or <c>null</c> if no location is provided.</param>
|
||||
Task UpdateLocation(ObjectId id, PatientLocation? location);
|
||||
/// <summary>
|
||||
/// Updates the attending doctor for the entity identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the entity whose attending doctor will be updated.</param>
|
||||
/// <param name="doctor">The new attending doctor to assign to the entity.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation.</returns>
|
||||
Task UpdateAttendingDoctor(ObjectId id, Person doctor);
|
||||
/// <summary>
|
||||
/// Updates the data of an existing patient identified by the given identifier, optionally replacing the patient number when the <paramref name="updatePatientNumber"/> flag is true.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose data will be updated.</param>
|
||||
/// <param name="patientNumber">The patient number to be applied to the patient.</param>
|
||||
/// <param name="data">The person data to assign to the patient.</param>
|
||||
/// <param name="updatePatientNumber">Indicates whether the patient number should also be updated; defaults to true.</param>
|
||||
Task UpdatePatientData(ObjectId id, string patientNumber, Person data, bool updatePatientNumber = true);
|
||||
/// <summary>
|
||||
/// Updates the patient data for the specified patient identified by <paramref name="id"/> and <paramref name="patientNumber"/>, applying the changes from the provided <paramref name="patient"/> object. When <paramref name="updatePatientNumber"/> is <c>true</c>, the patient number is also updated as part of the operation; otherwise, only the remaining patient fields are updated.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient record to update.</param>
|
||||
/// <param name="patientNumber">The current patient number used to locate the patient record.</param>
|
||||
/// <param name="patient">The patient object containing the updated data to be applied.</param>
|
||||
/// <param name="updatePatientNumber">A flag indicating whether the patient number should also be updated; defaults to <c>true</c>.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous update operation.</returns>
|
||||
Task UpdatePatientData(ObjectId id, string patientNumber, Patient patient, bool updatePatientNumber = true);
|
||||
/// <summary>
|
||||
/// Updates the specified patient record.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose information will be updated.</param>
|
||||
Task Update(Patient patient);
|
||||
/// <summary>
|
||||
/// Moves the specified patient from the old point of care to the new point of care.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to be moved.</param>
|
||||
/// <param name="newPocId">The identifier of the destination point of care.</param>
|
||||
/// <param name="oldPocId">The identifier of the source point of care.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the move was successful; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> Move(Patient patient, ObjectId newPocId, ObjectId oldPocId);
|
||||
/// <summary>
|
||||
/// Retrieves a patient by their unique identifier, optionally including location information.
|
||||
/// Returns <c>null</c> when no patient matches the provided identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> used to look up the patient.</param>
|
||||
/// <param name="withLocation">When <c>true</c>, includes the patient's location data in the result; otherwise, location data is omitted.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that resolves to the matching <see cref="Patient"/>, or <c>null</c> if no patient is found.</returns>
|
||||
Task<Patient?> FindById(ObjectId id, bool withLocation = false);
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Box"/> associated with the specified <see cref="PointOfCare"/>, returning <see langword="null"/> when no box is found for the given point of care.
|
||||
/// </summary>
|
||||
/// <param name="poc">The point of care used to look up the associated box.</param>
|
||||
/// <param name="observations">When <see langword="true"/>, observations are included with the returned box; otherwise, observations are omitted.</param>
|
||||
/// <param name="filterObservations">An optional list of observation identifiers used to restrict which observations are loaded when <paramref name="observations"/> is <see langword="true"/>.</param>
|
||||
/// <returns>A <see cref="Task{Box}"/> that resolves to the matching <see cref="Box"/>, or <see langword="null"/> if no box exists for the specified point of care.</returns>
|
||||
Task<Box?> GetBox(PointOfCare poc, bool observations = false, List<string>? filterObservations = null);
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Patient"/> instance from the provided <see cref="ApiRequest"/>, optionally ignoring location information during the creation process.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the data used to construct the patient.</param>
|
||||
/// <param name="ignoreLocation">When <c>true</c>, location information is ignored during patient creation. Defaults to <c>false</c>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the created <see cref="Patient"/>, or <c>null</c> if the patient could not be created.</returns>
|
||||
Task<Patient?> CreatePatientFromRequest(ApiRequest apiRequest, bool ignoreLocation = false);
|
||||
/// <summary>
|
||||
/// Asynchronously finds a patient based on the information provided in the specified API request.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the data used to look up the patient.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="Patient"/> if found, or null if no patient matches the request.</returns>
|
||||
Task<Patient?> FindPatientByApiRequest(ApiRequest apiRequest);
|
||||
/// <summary>
|
||||
/// Archives patients who have no observations recorded since the specified date.
|
||||
/// </summary>
|
||||
/// <param name="date">The cutoff date; patients without observations after this date are archived.</param>
|
||||
Task ArchivePatientWithoutObservationsSinceDate(DateTime date);
|
||||
|
||||
/// <summary>
|
||||
/// Archives patient records for patients who have been discharged longer than the specified time threshold.
|
||||
/// </summary>
|
||||
/// <param name="hoursBeforeArchive">The number of hours a patient must have been discharged before being archived.</param>
|
||||
Task ArchiveDischargedPatients(int hoursBeforeArchive);
|
||||
/// <summary>
|
||||
/// Retrieves all patients, optionally including their location data when requested.
|
||||
/// </summary>
|
||||
/// <param name="withLocation">Indicates whether location information should be included in the returned patient records.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Patient"/> objects.</returns>
|
||||
Task<List<Patient>> FindAll(bool withLocation = false);
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of patients based on the provided pagination filter.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that defines the paging criteria used to retrieve patients.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="PaginationResponse{Patient}"/> with the requested page of patients.</returns>
|
||||
Task<PaginationResponse<Patient>> GetPaginatedPatients(PaginationFilter filter);
|
||||
|
||||
/// <summary>
|
||||
/// Discharges patients who have been inactive since the specified date and archives them after the defined retention period.
|
||||
/// </summary>
|
||||
/// <param name="sinceDate">The date used to identify patients that have been inactive since this point in time.</param>
|
||||
/// <param name="hoursBeforeArchive">The number of hours of inactivity that must elapse before a discharged patient is archived.</param>
|
||||
Task DischargeInactivePatients(DateTime sinceDate, int hoursBeforeArchive);
|
||||
/// <summary>
|
||||
/// Updates an existing patient record with the provided patient data. Returns the updated patient, or <c>null</c> if no matching patient was found.
|
||||
/// </summary>
|
||||
/// <param name="updatedPatient">The patient object containing the updated information to be persisted.</param>
|
||||
/// <returns>A <see cref="Task{Patient}"/> that resolves to the updated <see cref="Patient"/>, or <c>null</c> if the patient could not be found.</returns>
|
||||
Task<Patient?> UpdateOne(Patient updatedPatient);
|
||||
|
||||
/// <summary>
|
||||
/// Sends an asynchronous broadcast notification to inform relevant subscribers or systems about a newly registered patient.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose information will be included in the broadcast notification.</param>
|
||||
Task SendNewPatientBroadcast(Patient patient);
|
||||
/// <summary>
|
||||
/// Asynchronously sends a broadcast notification about an update to the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose update information will be broadcast.</param>
|
||||
Task SendPatientUpdateBroadcast(Patient patient);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the list of inactive Patients of Care (PoC), allowing consumers to identify
|
||||
/// patients that are no longer active in the system for reporting, cleanup, or follow-up workflows.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of inactive <see cref="Patient"/> records.</returns>
|
||||
Task<List<Patient>> FindInActivePoC();
|
||||
/// <summary>
|
||||
/// Retrieves a list of patients associated with inactive PoC records.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Patient"/> objects associated with inactive PoC records.</returns>
|
||||
Task<List<Patient>> FindInInactivePoC();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Patient"/> associated with the specified <paramref name="item"/> point of care, returning <c>null</c> when no matching patient is found.
|
||||
/// When <paramref name="observations"/> is <c>true</c>, the result includes the patient's observations, optionally restricted by the identifiers supplied in <paramref name="filterObservations"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The point of care used to look up the associated patient.</param>
|
||||
/// <param name="observations">Indicates whether the patient's observations should be included in the returned patient.</param>
|
||||
/// <param name="filterObservations">An optional list of observation identifiers used to filter which observations are returned when <paramref name="observations"/> is <c>true</c>.</param>
|
||||
/// <returns>A <see cref="Task{Patient}"/> that resolves to the matching <see cref="Patient"/>, or <c>null</c> if no patient is found for the given point of care.</returns>
|
||||
Task<Patient?> GetByPointOfCare(PointOfCare item, bool observations = false,
|
||||
List<string>? filterObservations = null);
|
||||
List<string>? filterObservations = null);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="Patient"/> matching the specified point of care, optionally narrowed by unit and locale.
|
||||
/// </summary>
|
||||
/// <param name="item">The point of care used to locate the patient.</param>
|
||||
/// <param name="unit">An optional unit that further filters the lookup.</param>
|
||||
/// <param name="localeEnum">An optional locale used to scope the search.</param>
|
||||
/// <returns>A task that yields the matching <see cref="Patient"/>, or <c>null</c> when no patient is found.</returns>
|
||||
Task<Patient?> GetByPointOfCareAndLocale(PointOfCare item, Unit? unit, LocaleEnum? localeEnum);
|
||||
/// <summary>
|
||||
/// Updates the altable (allergy table) information for the specified patient and returns the updated patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose altable is being updated.</param>
|
||||
/// <param name="altable">The option list containing the altable data to apply to the patient.</param>
|
||||
/// <param name="user">The user performing the update, or <c>null</c> when no user context is available.</param>
|
||||
/// <returns>A task that resolves to the updated <see cref="Patient"/>, or <c>null</c> if the patient was not found.</returns>
|
||||
Task<Patient?> UpdatePatientAltable(ObjectId patientId, OptionList altable, User? user);
|
||||
/// <summary>
|
||||
/// Exits a patient identified by the given identifier, optionally archiving the patient record.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient to exit.</param>
|
||||
/// <param name="archivePatient">When true, the patient record is archived as part of the exit process; when false, archiving is skipped.</param>
|
||||
Task ExitPatientById(ObjectId id, bool archivePatient = true);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified master list for a patient with the provided options and returns the updated patient record.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose master list is being updated.</param>
|
||||
/// <param name="typeName">The type of master list to update.</param>
|
||||
/// <param name="updatedOptions">The new list of options to apply to the master list.</param>
|
||||
/// <param name="user">The user performing the update, or null if not specified.</param>
|
||||
/// <param name="carePlanLog">Optional care plan log entries associated with the update.</param>
|
||||
/// <returns>A task that returns the updated <see cref="Patient"/>, or null if the patient was not found.</returns>
|
||||
Task<Patient?> UpdatePatientMasterList(ObjectId patientId, MasterListType typeName,
|
||||
List<OptionList> updatedOptions,
|
||||
User? user, List<OptionList>? carePlanLog);
|
||||
List<OptionList> updatedOptions,
|
||||
User? user, List<OptionList>? carePlanLog);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a nurse care plan of the specified type, using the provided options, and inserts it for the patient.
|
||||
/// </summary>
|
||||
/// <param name="carePlanType">The master list type that determines which nurse care plan template to generate.</param>
|
||||
/// <param name="options">Optional list of options applied during care plan generation. May be null.</param>
|
||||
/// <param name="patient">The patient for whom the nurse care plan is generated and inserted.</param>
|
||||
/// <param name="user">The user associated with the care plan generation. May be null.</param>
|
||||
/// <returns>A task that completes when the nurse care plan has been generated and inserted.</returns>
|
||||
Task GenerateNurseCarePlanAndInsert(MasterListType carePlanType, List<OptionList>? options,
|
||||
Patient patient, User? user);
|
||||
Patient patient, User? user);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously updates the incoming data for the specified patient by applying the provided patient information.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose incoming data is being updated.</param>
|
||||
/// <param name="person">The patient object containing the incoming data to be applied to the patient record.</param>
|
||||
Task UpdatePatientIncomingData(ObjectId patientId, Patient person);
|
||||
/// <summary>
|
||||
/// Asynchronously updates the demographic data of an existing patient identified by the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose demographic data is to be updated.</param>
|
||||
/// <param name="person">The patient object containing the new demographic information to apply.</param>
|
||||
/// <param name="user">The user performing the update operation, or <see langword="null"/> if no user context is available.</param>
|
||||
Task UpdatePatientDemographicData(ObjectId patientId, Patient person, User? user);
|
||||
/// <summary>
|
||||
/// Searches for a patient by their patient number within a distinct unit.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The patient number used to identify the patient.</param>
|
||||
/// <param name="unitId">The identifier of the distinct unit where the patient is registered.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="Patient"/> if found; otherwise, <see langword="null"/>.</returns>
|
||||
Task<Patient?> SearchByPatientNumberAndDistinctUnit(string patientNumber, ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all patients whose procedures have been finished, filtering based on the specified archive threshold for procedure end times.
|
||||
/// </summary>
|
||||
/// <param name="archiveProcedureEndDateAfterMinutes">The number of minutes after the procedure end date used as the archive threshold to qualify patients with finished procedures.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Patient"/> objects that match the finished procedures criteria.</returns>
|
||||
Task<List<Patient>> FindAllPatientWithFinishedProcedures(int archiveProcedureEndDateAfterMinutes);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all patients who have completed a test, using the specified archive window in minutes to determine which tests are considered finished.
|
||||
/// </summary>
|
||||
/// <param name="archiveTestEndDateAfterMinutes">The time window in minutes applied to the test end date to identify tests that should be treated as finished/archived.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="Patient"/> instances with finished tests.</returns>
|
||||
Task<List<Patient>> FindAllPatientWithFinishedTest(int archiveTestEndDateAfterMinutes);
|
||||
/// <summary>
|
||||
/// Retrieves a list of patients whose treatments have finished, based on the specified archive time threshold in minutes after the treatment end date.
|
||||
/// </summary>
|
||||
/// <param name="archiveTreatmentEndDateAfterMinutes">The number of minutes after the treatment end date used to determine which finished treatments should be included.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of patients with finished treatment matching the specified criteria.</returns>
|
||||
Task<List<Patient>> FindAllPatientWithFinishedTreatment(int archiveTreatmentEndDateAfterMinutes);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the incoming income data of a patient using the specified changes and the user performing the operation.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose income data is being updated.</param>
|
||||
/// <param name="person">The patient entity associated with the update.</param>
|
||||
/// <param name="personDataChange">The income data changes to apply to the patient.</param>
|
||||
/// <param name="user">The user performing the update operation.</param>
|
||||
Task UpdatePatientIncomingData(ObjectId patientId, Patient person, PatientIncomeData personDataChange,
|
||||
User user);
|
||||
User user);
|
||||
|
||||
/// <summary>
|
||||
/// Updates a patient master list item change based on the provided update options, unit list, and type name.
|
||||
/// </summary>
|
||||
/// <param name="opt">The update options for the master list item.</param>
|
||||
/// <param name="unitList">The collection of units associated with the update.</param>
|
||||
/// <param name="typeName">The name of the type used to categorize the master list item.</param>
|
||||
Task UpdatePatientMasterListItemChange(UpdateOptionMasterListDto opt, IEnumerable<Unit> unitList,
|
||||
string typeName);
|
||||
string typeName);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a patient master list item based on the specified option, unit list, and type name.
|
||||
/// </summary>
|
||||
/// <param name="opt">The option list containing the details of the patient master list item to delete.</param>
|
||||
/// <param name="unitList">The collection of units associated with the patient master list item.</param>
|
||||
/// <param name="typeName">The name of the type identifying the patient master list item to be deleted.</param>
|
||||
Task DeletePatientMasterListItem(OptionList opt, IEnumerable<Unit> unitList, string typeName);
|
||||
}
|
||||
@@ -6,17 +6,62 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IPermissionService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the set of display permission types applicable to the specified user for the given display.
|
||||
/// </summary>
|
||||
/// <param name="display">The display for which permissions are being evaluated.</param>
|
||||
/// <param name="user">The user whose permissions for the display are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="DisplayPermissionTypes"/> granted to the user for the display.</returns>
|
||||
public Task<DisplayPermissionTypes> GetPermissionsForDisplay(Display display, User user);
|
||||
/// <summary>
|
||||
/// Retrieves the display-friendly permission types applicable to the specified user for the given unit.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose permissions are being queried.</param>
|
||||
/// <param name="user">The user whose permissions for the unit should be evaluated.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="DisplayPermissionTypes"/> for the specified unit and user.</returns>
|
||||
public Task<DisplayPermissionTypes> GetPermissionsForUnit(string unitId, User user);
|
||||
/// <summary>
|
||||
/// Retrieves the panel permission types associated with the specified user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose panel permissions are being queried.</param>
|
||||
/// <returns>The <see cref="PanelPermissionTypes"/> granted to the specified user.</returns>
|
||||
public PanelPermissionTypes GetPermissionsForPanel(User user);
|
||||
/// <summary>
|
||||
/// Retrieves the panel permission types associated with the specified user.
|
||||
/// </summary>
|
||||
/// <param name="user">The identifier or name of the user whose panel permissions are being requested.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="PanelPermissionTypes"/> granted to the user.</returns>
|
||||
public Task<PanelPermissionTypes> GetPermissionsForPanel(string user);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously checks whether the specified user, with the given role and source permission, has access to the specified display.
|
||||
/// </summary>
|
||||
/// <param name="username">The name of the user whose access is being verified.</param>
|
||||
/// <param name="role">The role assigned to the user, used to determine access rights.</param>
|
||||
/// <param name="source">The source permission type considered when evaluating access.</param>
|
||||
/// <param name="displayId">The identifier of the display for which access is being checked.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the user has access to the display; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> HasAccessToDisplay(string username, PermissionEnum.RolesType role,
|
||||
PermissionEnum.SourcePermissionsEnum source, string displayId);
|
||||
PermissionEnum.SourcePermissionsEnum source, string displayId);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously determines whether the specified user, with the given role and permission source, has access to the identified unit.
|
||||
/// </summary>
|
||||
/// <param name="username">The identifier of the user whose access is being evaluated.</param>
|
||||
/// <param name="role">The role assigned to the user, used in the access evaluation.</param>
|
||||
/// <param name="source">The permission source used to resolve the user's access rights.</param>
|
||||
/// <param name="unitId">The identifier of the unit to check access against.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the user has access to the specified unit; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> HasAccessToUnit(string username, PermissionEnum.RolesType role,
|
||||
PermissionEnum.SourcePermissionsEnum source, string unitId);
|
||||
PermissionEnum.SourcePermissionsEnum source, string unitId);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously determines whether the specified user has access to a panel based on their role and the requested source permission.
|
||||
/// </summary>
|
||||
/// <param name="username">The identifier of the user whose panel access is being evaluated.</param>
|
||||
/// <param name="role">The role assigned to the user, used to resolve applicable permissions.</param>
|
||||
/// <param name="source">The source permission being checked against the user's access rights.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the user has access to the panel; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> HasAccessToPanel(string username, PermissionEnum.RolesType role,
|
||||
PermissionEnum.SourcePermissionsEnum source);
|
||||
PermissionEnum.SourcePermissionsEnum source);
|
||||
}
|
||||
@@ -4,5 +4,10 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IPoCMappingService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously maps the specified <paramref name="original"/> patient location to a new <see cref="PatientLocation"/>, returning <c>null</c> when no corresponding mapping result is found.
|
||||
/// </summary>
|
||||
/// <param name="original">The source patient location to map from.</param>
|
||||
/// <returns>A task that yields the mapped <see cref="PatientLocation"/>, or <c>null</c> if no mapping result is available.</returns>
|
||||
Task<PatientLocation?> Map(PatientLocation original);
|
||||
}
|
||||
@@ -9,50 +9,200 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IPointOfCareService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the entity identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the entity to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation.</returns>
|
||||
Task Delete(ObjectId id);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing point of care record with the provided information.
|
||||
/// </summary>
|
||||
/// <param name="pointOfCare">The point of care entity containing the updated data.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the updated point of care, or null if no matching record was found.</returns>
|
||||
Task<PointOfCare?> Update(PointOfCare pointOfCare);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously updates an existing unit identified by the specified identifier with the provided unit data.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the unit to update.</param>
|
||||
/// <param name="unit">The unit data to apply to the existing record.</param>
|
||||
Task UpdateUnit(ObjectId id, Unit unit);
|
||||
/// <summary>
|
||||
/// Retrieves all PointOfCare records asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of all PointOfCare entries.</returns>
|
||||
Task<List<PointOfCare>> GetAll();
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all PointOfCare configurations.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of all <see cref="PointOfCare"/> configurations.</returns>
|
||||
Task<List<PointOfCare>> GetAllConfigs();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all point-of-care location information.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of all <see cref="PointOfCare"/> locations.</returns>
|
||||
Task<List<PointOfCare>> GetAllLocationInfo();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the point of care configuration identified by the specified identifier with the provided configuration data.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the point of care configuration to update.</param>
|
||||
/// <param name="configuration">The new configuration data to apply.</param>
|
||||
Task UpdateConfiguration(ObjectId id, PointOfCareConfiguration configuration);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="PointOfCare"/> entity by its unique identifier, returning null if no matching record is found.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the <see cref="PointOfCare"/> to look up.</param>
|
||||
/// <returns>A task that represents the asynchronous lookup operation. The task result contains the matching <see cref="PointOfCare"/> if found, or null when no entity with the specified identifier exists.</returns>
|
||||
Task<PointOfCare?> FindById(ObjectId id);
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="PointOfCare"/> entity by its identifier, including all of its associated configuration data.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the <see cref="PointOfCare"/> to locate.</param>
|
||||
/// <returns>A task that yields the matching <see cref="PointOfCare"/> with its full configuration, or <c>null</c> if no entity is found for the supplied identifier.</returns>
|
||||
Task<PointOfCare?> FindByIdAllConfig(ObjectId id);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all PointOfCare records associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unique identifier of the unit whose PointOfCare records should be retrieved.</param>
|
||||
/// <returns>A task that returns a collection of PointOfCare records for the specified unit, or <c>null</c> if no records are found.</returns>
|
||||
Task<IEnumerable<PointOfCare>?> FindAllByUnitId(ObjectId unit);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the collection of Points of Care associated with the specified room.
|
||||
/// </summary>
|
||||
/// <param name="room">The room identifier used to look up the associated Points of Care.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an enumerable of Points of Care found for the given room, or <c>null</c> if no matching results are found.</returns>
|
||||
Task<IEnumerable<PointOfCare>?> FindByRoom(string room);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the collection of points of care associated with the specified bed.
|
||||
/// </summary>
|
||||
/// <param name="bed">The bed identifier used to look up the associated points of care.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a collection of <see cref="PointOfCare"/> instances matching the bed, or <c>null</c> if no match is found.</returns>
|
||||
Task<IEnumerable<PointOfCare>?> FindByBed(string bed);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all points of care associated with the specified unit identifiers.
|
||||
/// </summary>
|
||||
/// <param name="unitIds">The list of unit identifiers used to look up the associated points of care.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of points of care matching the provided unit identifiers.</returns>
|
||||
Task<List<PointOfCare>> FindAllByUnitIds(List<ObjectId> unitIds);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the information of a point of care by its identifier, optionally localized and enriched with patient data.
|
||||
/// Returns <c>null</c> when no point of care matches the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the point of care to retrieve.</param>
|
||||
/// <param name="locale">Optional locale used to localize the retrieved point of care information; when <c>null</c>, the default locale is used.</param>
|
||||
/// <param name="fillPatientData">When <c>true</c>, associated patient data is included in the result; when <c>false</c>, only the point of care information is returned.</param>
|
||||
/// <param name="ct">Token to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task that yields the <see cref="PointOfCare"/> corresponding to the given identifier, or <c>null</c> if it is not found.</returns>
|
||||
Task<PointOfCare?> GetInfo(ObjectId id, LocaleEnum? locale = null, bool fillPatientData = true, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new <see cref="PointOfCare"/> record into the system.
|
||||
/// </summary>
|
||||
/// <param name="pointOfCare">The <see cref="PointOfCare"/> entity to be inserted.</param>
|
||||
/// <returns>A <see cref="Task{PointOfCare}"/> containing the inserted <see cref="PointOfCare"/>, or <c>null</c> if the insertion was not performed.</returns>
|
||||
Task<PointOfCare?> InsertPointOfCare(PointOfCare pointOfCare);
|
||||
/// <summary>
|
||||
/// Retrieves the Point of Care associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose Point of Care is being looked up.</param>
|
||||
/// <returns>A task that yields the <see cref="PointOfCare"/> associated with the patient, or <c>null</c> if no Point of Care is found for the given patient identifier.</returns>
|
||||
Task<PointOfCare?> FindPoCByPatientId(ObjectId patientId);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously sets the Point of Care status for the entity identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the entity whose Point of Care status will be updated.</param>
|
||||
/// <param name="status">The Point of Care status to assign to the entity.</param>
|
||||
/// <returns>A task that represents the asynchronous status update operation.</returns>
|
||||
Task SetPointOfCareStatus(ObjectId id, StatusEnum.PointOfCare status);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the points of care associated with the specified unit and point-of-care status, optionally excluding virtual ones.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose points of care are queried.</param>
|
||||
/// <param name="poc">The point-of-care status used to filter the results.</param>
|
||||
/// <param name="excludeVirtual">When <c>true</c>, virtual points of care are excluded from the results; otherwise, they are included.</param>
|
||||
/// <returns>A task that returns the matching collection of <see cref="PointOfCare"/> entries.</returns>
|
||||
Task<IEnumerable<PointOfCare>> FindByUnitAndStatus(ObjectId unitId, StatusEnum.PointOfCare poc,
|
||||
bool excludeVirtual = false);
|
||||
bool excludeVirtual = false);
|
||||
|
||||
/// <summary>
|
||||
/// Checks the next admission for the specified patient location.
|
||||
/// </summary>
|
||||
/// <param name="patientLocation">The optional identifier of the patient location to check.</param>
|
||||
void CheckNextAdmission(ObjectId? patientLocation);
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="PointOfCare"/> associated with the specified bed and unit identifier, returning <c>null</c> when no matching record is found.
|
||||
/// </summary>
|
||||
/// <param name="bed">The bed identifier used to locate the point of care.</param>
|
||||
/// <param name="unitId">The MongoDB <see cref="ObjectId"/> of the unit the point of care belongs to.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> that resolves to the matching <see cref="PointOfCare"/> or <c>null</c> if no record matches the supplied bed and unit.</returns>
|
||||
Task<PointOfCare?> FindByBedAndUnitId(string? bed, ObjectId? unitId);
|
||||
/// <summary>
|
||||
/// Updates the relay configuration associated with the specified point-of-care device.
|
||||
/// </summary>
|
||||
/// <param name="poc">The point-of-care device whose relay configuration should be updated.</param>
|
||||
Task UpdateRelayConfig(PointOfCare poc);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the Point of Care (PoC) associated with the specified patient number.
|
||||
/// Returns <c>null</c> if no matching Point of Care is found for the given patient number.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The unique patient number used to look up the associated Point of Care.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="PointOfCare"/> if found, or <c>null</c> if no match exists.</returns>
|
||||
Task<PointOfCare?> FindPoCByPatientNumber(string patientNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of Points of Contact (PoCs) associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose PoCs should be counted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the total number of PoCs linked to the given unit.</returns>
|
||||
Task<long> CountPoCsByUnitId(ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the total number of virtual Proofs of Concept (PoCs) associated with the specified unit.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose virtual PoCs should be counted.</param>
|
||||
/// <returns>A <see cref="Task{Int64}"/> that represents the asynchronous operation, containing the count of virtual PoCs for the given unit.</returns>
|
||||
Task<long> CountVirtualPoCsByUnitId(ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of Points of Care based on the provided filter.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination parameters used to control page size and page number.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the paginated response of Points of Care.</returns>
|
||||
Task<PaginationResponse<PointOfCare>> GetPaginatedPoCs(PaginationFilter filter);
|
||||
/// <summary>
|
||||
/// Deletes all PoCs associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose PoCs should be removed.</param>
|
||||
Task DeletePoCsByUnitId(ObjectId unitId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the set of all camera identifiers that are currently in use.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a HashSet of ObjectId values representing the cameras currently in use.</returns>
|
||||
Task<HashSet<ObjectId>> FindAllIdCamerasInUse();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the set of all ID relays that are currently in use.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="HashSet{ObjectId}"/> of the ID relays currently in use.</returns>
|
||||
Task<HashSet<ObjectId>> FindAllIdRelaysInUse();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the set of all ID beacons currently in use.
|
||||
/// </summary>
|
||||
/// <returns>A task containing a <see cref="HashSet{ObjectId}"/> of the ID beacons that are active or in use.</returns>
|
||||
Task<HashSet<ObjectId>> FindAllIdBeaconsInUse();
|
||||
/// <summary>
|
||||
/// Retrieves all Point of Care entries associated with the specified unit, including their related devices.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose Point of Care entries should be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a collection of Point of Care entries with their devices, or <c>null</c> if no entries are found for the given unit.</returns>
|
||||
Task<IEnumerable<PointOfCare>?> FindAllByUnitIdWithDevices(ObjectId unitId);
|
||||
}
|
||||
@@ -2,8 +2,31 @@
|
||||
|
||||
public interface IPublisherService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new queue with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="queueName">The name of the queue to create.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if the queue was created successfully; otherwise, <see langword="false"/>.</returns>
|
||||
Task<bool> CreateQueue(string queueName);
|
||||
/// <summary>
|
||||
/// Asynchronously sends an error message to the specified message queue.
|
||||
/// </summary>
|
||||
/// <param name="obj">The error payload or message object to be sent to the queue.</param>
|
||||
/// <param name="queueName">The name of the target message queue.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a boolean value that indicates whether the message was successfully sent.</returns>
|
||||
Task<bool> SendMessageError(object obj, string queueName);
|
||||
/// <summary>
|
||||
/// Asynchronously sends the specified object as a message to the named queue.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object payload to serialize and publish to the queue.</param>
|
||||
/// <param name="queueName">The name of the target queue that will receive the message.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the message was sent successfully, otherwise <c>false</c>.</returns>
|
||||
Task<bool> SendMessage(object obj, string queueName);
|
||||
/// <summary>
|
||||
/// Asynchronously sends a message to the specified message queue and returns a value indicating whether the operation succeeded.
|
||||
/// </summary>
|
||||
/// <param name="message">The message content to be sent to the queue.</param>
|
||||
/// <param name="queueName">The name of the target queue where the message will be delivered.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the message was sent successfully; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> SendMessage(string message, string queueName);
|
||||
}
|
||||
@@ -10,31 +10,104 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
public interface IPumpService: IApiRequestService
|
||||
{
|
||||
// Insert manual
|
||||
/// <summary>
|
||||
/// Asynchronously inserts a pump observation record into the underlying data store.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to persist.</param>
|
||||
Task InsertPumpObservation(PumpObservation obs);
|
||||
|
||||
// Mapping
|
||||
/// <summary>
|
||||
/// Asynchronously maps the specified pump observation to a resulting <see cref="PumpObservation"/>, returning <c>null</c> when no mapping result is produced.
|
||||
/// </summary>
|
||||
/// <param name="obs">The source pump observation to be mapped.</param>
|
||||
/// <returns>A task that represents the asynchronous mapping operation, containing the resulting <see cref="PumpObservation"/> or <c>null</c> if no result is available.</returns>
|
||||
Task<PumpObservation?> MapPumpObservation(PumpObservation obs);
|
||||
|
||||
// Consultas por paciente
|
||||
/// <summary>
|
||||
/// Retrieves the most recent pump observations for the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose pump observations are being queried.</param>
|
||||
/// <param name="num">The maximum number of recent observations to return. Defaults to 1.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of the most recent pump observations for the patient, up to the specified count.</returns>
|
||||
Task<List<PumpObservation>> FindLastPumpObservations(ObjectId patientId, int num = 1);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the most recent observation timestamp for every patient, returning a dictionary keyed by patient identifier.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a dictionary mapping each patient's <see cref="ObjectId"/> to their last observation <see cref="DateTime"/>.</returns>
|
||||
Task<Dictionary<ObjectId, DateTime>> FindAllLastPatientObservationTime();
|
||||
|
||||
// Gestión de configuración
|
||||
/// <summary>
|
||||
/// Retrieves the list of configuration pump items associated with the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier used to look up the configuration pump items.</param>
|
||||
/// <returns>A task that resolves to a list of <see cref="ConfigPumpItem"/> objects matching the given identifier, or <c>null</c> when no items are found.</returns>
|
||||
Task<List<ConfigPumpItem>?> GetItemsById(string id);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the full list of pump configurations from the underlying data source.
|
||||
/// </summary>
|
||||
/// <returns>A task that resolves to a <see cref="List{ConfigPumps}"/> containing all pump configurations, or <c>null</c> if no configurations are available.</returns>
|
||||
Task<List<ConfigPumps>?> GetAllPumpConfig();
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the pump configuration associated with the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the pump configuration to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="ConfigPumps"/> instance matching the specified identifier, or <see langword="null"/> if no configuration is found.</returns>
|
||||
Task<ConfigPumps?> GetPumpConfigsById(string id);
|
||||
/// <summary>
|
||||
/// Asynchronously updates the pump configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">The pump configuration to update.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the updated <see cref="ConfigPumps"/> or null if not found.</returns>
|
||||
Task<ConfigPumps?> UpdatePumpConfig(ConfigPumps config);
|
||||
/// <summary>
|
||||
/// Inserts a new pump configuration into the system asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="config">The pump configuration to be inserted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the inserted <see cref="ConfigPumps"/> or <c>null</c> if the insertion fails.</returns>
|
||||
Task<ConfigPumps?> InsertPumpConfig(ConfigPumps config);
|
||||
/// <summary>
|
||||
/// Deletes the specified pump configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">The pump configuration to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation. The task result is <c>true</c> if the configuration was deleted successfully; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> DeletePumpConfig(ConfigPumps config);
|
||||
|
||||
// Paginación
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of pump observations based on the provided filter criteria.
|
||||
/// Returns null when no pump observations match the supplied pagination filter.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that defines page size, page number, and any additional filtering criteria applied to the pump observations.</param>
|
||||
/// <returns>A task that resolves to a <see cref="PaginationResponse{PumpObservation}"/> containing the matching pump observations, or null when no results are found.</returns>
|
||||
Task<PaginationResponse<PumpObservation>?> GetPaginatedPump(PaginationFilter filter);
|
||||
|
||||
// Archivado / limpieza
|
||||
/// <summary>
|
||||
/// Asynchronously deletes records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The ObjectId of the patient whose related records should be deleted.</param>
|
||||
Task DeleteByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Archives the specified patient, marking the patient record as archived in the system.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to be archived.</param>
|
||||
Task Archive(Patient patient);
|
||||
/// <summary>
|
||||
/// Asynchronously archives the data associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose data should be archived.</param>
|
||||
/// <returns>A task that represents the asynchronous archive operation.</returns>
|
||||
Task ArchiveByPatientId(ObjectId id);
|
||||
|
||||
// Mantenimiento ids
|
||||
/// <summary>
|
||||
/// Asynchronously updates multiple records, replacing the specified old ObjectId with a new one in the field identified by <paramref name="nameId"/>.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name of the field whose ObjectId value should be updated.</param>
|
||||
/// <param name="id">The new ObjectId value to assign.</param>
|
||||
/// <param name="oldId">The existing ObjectId value to be replaced.</param>
|
||||
Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId);
|
||||
}
|
||||
@@ -6,10 +6,35 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IRecordingAlertService : IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the most recent patient recording alerts for the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose recording alerts are being queried.</param>
|
||||
/// <param name="num">The maximum number of most recent recording alerts to return. Defaults to 2.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of the patient's most recent recording alerts.</returns>
|
||||
Task<List<PatientRecordingAlert>> FindLastRecordingAlert(ObjectId patientId, int num = 2);
|
||||
/// <summary>
|
||||
/// Deletes records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose related records should be removed.</param>
|
||||
Task DeleteByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously archives the specified patient, moving their record out of the active set.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to archive.</param>
|
||||
Task Archive(Patient patient);
|
||||
/// <summary>
|
||||
/// Archives the records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> identifying the patient whose related records should be archived.</param>
|
||||
Task ArchiveByPatientId(ObjectId id);
|
||||
|
||||
/// <summary>
|
||||
/// Updates multiple records by replacing the <paramref name="oldId"/> with the new <paramref name="id"/>
|
||||
/// in entries identified by <paramref name="nameId"/>.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The identifier used to select the target records to update.</param>
|
||||
/// <param name="id">The new ObjectId value to assign to the matching records.</param>
|
||||
/// <param name="oldId">The existing ObjectId value to be replaced in the matching records.</param>
|
||||
Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId);
|
||||
}
|
||||
@@ -9,15 +9,54 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IRecordingService : IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously sends a cancel recording request to the recording API for the specified patient and point of care.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose recording should be cancelled.</param>
|
||||
/// <param name="poc">The point of care associated with the recording to be cancelled.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a boolean value indicating the outcome of the cancel recording request.</returns>
|
||||
Task<bool> SendCancelRecordingToRecordingApi(Patient patient, PointOfCare poc);
|
||||
|
||||
/// <summary>
|
||||
/// Queues alarm recording data for the specified patient at the given point of care,
|
||||
/// using the provided alarm details to be processed asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient associated with the recording data.</param>
|
||||
/// <param name="poc">The point of care where the recording originated.</param>
|
||||
/// <param name="date">The start date of the recording, if applicable.</param>
|
||||
/// <param name="endDate">The end date of the recording, if applicable.</param>
|
||||
/// <param name="eventDate">The date of the event, if applicable.</param>
|
||||
/// <param name="alarmName">The name of the alarm, if applicable.</param>
|
||||
/// <param name="severity">The severity level of the alarm.</param>
|
||||
/// <param name="alarmDescription">The description of the alarm, if applicable.</param>
|
||||
/// <param name="start">Indicates whether to start the recording (default is true).</param>
|
||||
/// <param name="type">The type of alarm (default is Manual).</param>
|
||||
Task SendRecordingDataToQueue(Patient patient, PointOfCare poc, DateTime? date, DateTime? endDate,
|
||||
DateTime? eventDate, AlarmEnum.Name? alarmName, AlarmEnum.Severity severity, string? alarmDescription,
|
||||
bool start = true, AlarmEnum.Type type = AlarmEnum.Type.Manual);
|
||||
DateTime? eventDate, AlarmEnum.Name? alarmName, AlarmEnum.Severity severity, string? alarmDescription,
|
||||
bool start = true, AlarmEnum.Type type = AlarmEnum.Type.Manual);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously sends manual recording data for the specified patient at the given point of care, using the provided manual recording and a flag indicating whether to start the recording.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose recording data is being sent.</param>
|
||||
/// <param name="poc">The point of care associated with the recording.</param>
|
||||
/// <param name="manualRecording">The manual recording payload to transmit.</param>
|
||||
/// <param name="start">A flag indicating whether the recording is being started or stopped.</param>
|
||||
Task SendRecordingData(Patient patient, PointOfCare poc, ManualRecording manualRecording, bool start);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously sends automatic recording data for a patient at the specified point of care.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient associated with the automatic recording data.</param>
|
||||
/// <param name="poc">The point of care where the recording was taken.</param>
|
||||
/// <param name="automaticRecording">The automatic recording data to be sent.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a boolean indicating whether the data was sent successfully.</returns>
|
||||
Task<bool> SendAutoRecordingData(Patient patient, PointOfCare poc, RecordingData automaticRecording);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of recordings associated with the specified room.
|
||||
/// </summary>
|
||||
/// <param name="roomName">The identifier of the room whose recordings are being requested.</param>
|
||||
/// <returns>A task that returns a list of <see cref="RecordingData"/> for the room, or <c>null</c> if no recordings are available.</returns>
|
||||
Task<List<RecordingData>?> GetRecordings(int roomName);
|
||||
}
|
||||
@@ -8,17 +8,74 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IRelayService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously checks the current status of the specified relay.
|
||||
/// </summary>
|
||||
/// <param name="relay">The relay whose status is being checked.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the current <see cref="RelayEnum.Status"/> of the relay.</returns>
|
||||
Task<RelayEnum.Status> CheckRelayStatus(Relay relay);
|
||||
/// <summary>
|
||||
/// Asynchronously checks the current status of the relay identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="relayId">The unique identifier of the relay whose status is being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the <see cref="RelayEnum.Status"/> of the requested relay.</returns>
|
||||
Task<RelayEnum.Status> CheckRelayStatus(ObjectId relayId);
|
||||
|
||||
/// <summary>
|
||||
/// Powers on the specified relay.
|
||||
/// </summary>
|
||||
/// <param name="relay">The relay to power on.</param>
|
||||
Task PowerOn(Relay relay);
|
||||
/// <summary>
|
||||
/// Powers off the specified relay by sending a command to deactivate it.
|
||||
/// </summary>
|
||||
/// <param name="relay">The relay to power off.</param>
|
||||
Task PowerOff(Relay relay);
|
||||
/// <summary>
|
||||
/// Sets a manual relay with the specified status for the given point of contact and relay type.
|
||||
/// </summary>
|
||||
/// <param name="status">The status to apply to the manual relay.</param>
|
||||
/// <param name="pocId">The identifier of the point of contact associated with the relay.</param>
|
||||
/// <param name="type">The type of relay to set manually.</param>
|
||||
Task SetManualRelay(RelayEnum.Status status, ObjectId pocId, RelayEnum.Type type);
|
||||
/// <summary>
|
||||
/// Retrieves a relay by its unique identifier, returning <c>null</c> if no matching relay is found.
|
||||
/// </summary>
|
||||
/// <param name="relay">The unique identifier of the relay to look up.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that resolves to the <see cref="Relay"/> if found, or <c>null</c> if no relay matches the provided identifier.</returns>
|
||||
Task<Relay?> GetById(ObjectId relay);
|
||||
/// <summary>
|
||||
/// Retrieves the list of <see cref="Relay"/> objects corresponding to the specified collection of relay identifiers.
|
||||
/// </summary>
|
||||
/// <param name="relayList">The list of <see cref="ObjectId"/> values identifying the relays to retrieve. May be <c>null</c>.</param>
|
||||
/// <returns>A <see cref="List{Relay}"/> containing the relays found for the provided identifiers.</returns>
|
||||
List<Relay> GetRelayInList(List<ObjectId>? relayList);
|
||||
/// <summary>
|
||||
/// Retrieves the relays of a specified type from the provided configuration relay list.
|
||||
/// </summary>
|
||||
/// <param name="configurationRelayList">The list of relay ObjectIds to filter, or null if no configuration relays are available.</param>
|
||||
/// <param name="type">The relay type to match against the configuration relays.</param>
|
||||
/// <returns>A list of <see cref="Relay"/> objects that match the specified <paramref name="type"/>.</returns>
|
||||
List<Relay> GetRelayByTypeInList(List<ObjectId>? configurationRelayList, RelayEnum.Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of relays based on the specified pagination filter.
|
||||
/// </summary>
|
||||
/// <param name="request">The pagination filter containing the criteria used to retrieve the relays.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the paginated response with the requested relays.</returns>
|
||||
Task<PaginationResponse<Relay>> GetPaginatedRelays(PaginationFilter request);
|
||||
/// <summary>
|
||||
/// Inserts a new relay record based on the provided request data.
|
||||
/// </summary>
|
||||
/// <param name="request">The relay entity to be inserted.</param>
|
||||
/// <returns>A task that resolves to the inserted <see cref="Relay"/>, or <c>null</c> if the insert did not return a value.</returns>
|
||||
Task<Relay?> InsertRelay(Relay request);
|
||||
/// <summary>
|
||||
/// Updates an existing relay identified by the specified <paramref name="objectId"/> with the provided <paramref name="relay"/> data.
|
||||
/// Returns <c>null</c> when no relay is found with the given identifier.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The unique identifier of the relay to update.</param>
|
||||
/// <param name="relay">The relay data containing the updated values.</param>
|
||||
/// <returns>A task that resolves to the updated <see cref="Relay"/>, or <c>null</c> if the relay was not found.</returns>
|
||||
Task<Relay?> UpdateRelayById(ObjectId objectId, Relay relay);
|
||||
}
|
||||
@@ -4,7 +4,19 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface ISendAlertService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a list of available queues.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of <see cref="Queue"/> instances.</returns>
|
||||
Task<List<Queue>> GetQueues();
|
||||
/// <summary>
|
||||
/// Retrieves a list of performance records.
|
||||
/// </summary>
|
||||
/// <returns>A list of <see cref="Performance"/> objects.</returns>
|
||||
List<Performance> GetPerformance();
|
||||
/// <summary>
|
||||
/// Retrieves a list of API clients.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="ApiClients"/>.</returns>
|
||||
Task<List<ApiClients>> GetApiClients();
|
||||
}
|
||||
@@ -4,5 +4,10 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IServiceConfigService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="ServiceConfig"/> by its identifier, returning <c>null</c> if no matching configuration is found.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the service configuration to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="ServiceConfig"/> if found, or <c>null</c> if no configuration exists for the given identifier.</returns>
|
||||
Task<ServiceConfig?> Get(string id);
|
||||
}
|
||||
@@ -7,15 +7,55 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface ISubscriberGroupedService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a list of subscribers organized into groups.
|
||||
/// </summary>
|
||||
/// <returns>A list of <see cref="WsSubscriberGrouped"/> instances representing the grouped subscribers.</returns>
|
||||
List<WsSubscriberGrouped> GetGrouped();
|
||||
/// <summary>
|
||||
/// Removes grouped observations associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose grouped observations should be removed.</param>
|
||||
void RemoveGroupedObsByPatientId(string patientId);
|
||||
/// <summary>
|
||||
/// Removes a WebSocket subscriber associated with the specified patient based on the provided location.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose WebSocket subscriber should be removed.</param>
|
||||
/// <param name="newLocation">The new patient location used to identify the subscriber to remove, or null to remove without location filtering.</param>
|
||||
void RemoveWsSubscriberByLocation(string patientId, PatientLocation? newLocation);
|
||||
/// <summary>
|
||||
/// Removes the specified workstation identifier associated with a patient's WS subscriber entry.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose WS subscriber entry will be updated.</param>
|
||||
/// <param name="wsIdToRemove">The workstation identifier to remove from the patient's WS subscriber entry.</param>
|
||||
void RemoveWsSubscriberPatientIdAndWsId(string patientId, string wsIdToRemove);
|
||||
/// <summary>
|
||||
/// Validates the provided <see cref="WsSubscriberGrouped"/> instance to identify empty subscriber groups.
|
||||
/// </summary>
|
||||
/// <param name="wsl">The subscriber group instance to be checked for empty groups.</param>
|
||||
/// <returns>A <see cref="List{String}"/> containing validation messages for any empty subscriber groups found; returns an empty list if all groups are valid.</returns>
|
||||
List<string> CheckEmptySubscriberGroup(WsSubscriberGrouped wsl);
|
||||
/// <summary>
|
||||
/// Adds a new subscriber together with its associated group information to the system.
|
||||
/// </summary>
|
||||
/// <param name="wsSubscriberGrouped">The web service representation of the subscriber and its group assignments to be added.</param>
|
||||
void AddSubscriberGrouped(WsSubscriberGrouped wsSubscriberGrouped);
|
||||
|
||||
/// <summary>
|
||||
/// Checks subscription-related conditions for a grouped field and its associated grouped observation.
|
||||
/// </summary>
|
||||
/// <param name="groupedField">The grouped field to evaluate.</param>
|
||||
/// <param name="patientId">The identifier of the patient associated with the observation.</param>
|
||||
/// <param name="timeZoneId">The time zone identifier used for the observation context.</param>
|
||||
/// <param name="connectionId">The connection identifier for the current request or session.</param>
|
||||
/// <param name="go">The grouped observation to be checked against the subscription.</param>
|
||||
void CheckOnSubscriptionGroup(GroupedField groupedField, ObjectId patientId, string timeZoneId, string connectionId,
|
||||
GroupedObservation go);
|
||||
GroupedObservation go);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the most recent grouped observation in the group identified by the specified hash code with the new grouped observation.
|
||||
/// </summary>
|
||||
/// <param name="wsgHashCode">The hash code identifying the group whose last grouped observation should be updated.</param>
|
||||
/// <param name="newGroupedObservation">The new grouped observation to set as the last grouped observation in the group.</param>
|
||||
void UpdateLastGroupedObsInGroup(string wsgHashCode, GroupedObservation newGroupedObservation);
|
||||
}
|
||||
@@ -5,9 +5,32 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface ISubscribersService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the list of subscribers.
|
||||
/// </summary>
|
||||
/// <returns>A list of <see cref="WsSubscriber"/> instances representing the subscribers.</returns>
|
||||
List<WsSubscriber> GetSubscribers();
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="WsSubscriber"/> associated with the specified context connection identifier.
|
||||
/// </summary>
|
||||
/// <param name="contextConnectionId">The unique identifier of the context connection used to look up the subscriber.</param>
|
||||
/// <returns>The <see cref="WsSubscriber"/> matching the given connection identifier, or <c>null</c> if no subscriber is found.</returns>
|
||||
WsSubscriber? GetById(string contextConnectionId);
|
||||
/// <summary>
|
||||
/// Retrieves the list of subscribers associated with the specified point of contact (POC) identifier.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The identifier of the point of contact whose subscribers should be returned.</param>
|
||||
/// <returns>A list of <see cref="WsSubscriber"/> instances matching the provided POC identifier.</returns>
|
||||
List<WsSubscriber> GetByPocId(ObjectId pocId);
|
||||
/// <summary>
|
||||
/// Removes a connection identified by the specified context connection identifier.
|
||||
/// </summary>
|
||||
/// <param name="contextConnectionId">The unique identifier of the connection to remove.</param>
|
||||
/// <returns>An integer indicating the result of the removal operation.</returns>
|
||||
int RemoveConnectionById(string contextConnectionId);
|
||||
/// <summary>
|
||||
/// Registers the specified WebSocket subscriber to receive notifications or messages.
|
||||
/// </summary>
|
||||
/// <param name="subscriber">The <see cref="WsSubscriber"/> instance to be added to the collection of subscribers.</param>
|
||||
void AddSubscriber(WsSubscriber subscriber);
|
||||
}
|
||||
@@ -9,18 +9,86 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface ITreatmentService : IApiRequestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Inserts a new <see cref="PatientTreatment"/> record into the underlying data store.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The patient treatment entity to be added.</param>
|
||||
Task Insert(PatientTreatment treatment);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes an entity associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> representing the unique identifier of the patient whose related entity should be removed.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing <c>true</c> if the deletion was successful; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> DeleteByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes the entity identified by the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the entity to delete.</param>
|
||||
Task DeleteById(ObjectId id);
|
||||
/// <summary>
|
||||
/// Archives records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The ObjectId of the patient whose records should be archived.</param>
|
||||
Task ArchiveByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Archives the specified patient, marking them as archived in the system.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to archive.</param>
|
||||
Task Archive(Patient patient);
|
||||
/// <summary>
|
||||
/// Updates an existing patient treatment record in the system.
|
||||
/// </summary>
|
||||
/// <param name="patientTreatment">The patient treatment entity containing the updated information to be persisted.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains <c>true</c> if the treatment was updated successfully; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdateTreatment(PatientTreatment patientTreatment);
|
||||
/// <summary>
|
||||
/// Updates many records by replacing the specified old ObjectId with the new ObjectId in the field identified by nameId.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name of the field that contains the ObjectId to be updated.</param>
|
||||
/// <param name="id">The new ObjectId value that will replace the existing one.</param>
|
||||
/// <param name="oldId">The existing ObjectId value to be replaced.</param>
|
||||
Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId);
|
||||
/// <summary>
|
||||
/// Retrieves all treatments associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose treatments are to be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of PatientTreatment objects for the specified patient.</returns>
|
||||
Task<IEnumerable<PatientTreatment>> GetTreatmentsByPatientId(ObjectId id);
|
||||
/// <summary>
|
||||
/// Retrieves the active treatment records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose active treatments should be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an enumerable collection of active <see cref="PatientTreatment"/> records, which may include null entries.</returns>
|
||||
Task<IEnumerable<PatientTreatment?>> GetActiveTreatmentsByPatient(ObjectId id);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a cursor over the <see cref="PatientTreatment"/> records associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose treatments should be returned.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> yielding an <see cref="IAsyncCursor{TDocument}"/> that iterates the matching <see cref="PatientTreatment"/> documents.</returns>
|
||||
Task<IAsyncCursor<PatientTreatment>> FindByPatientIdAsync(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all patient treatments associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose treatments are being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of <see cref="PatientTreatment"/> instances for the specified patient.</returns>
|
||||
Task<IEnumerable<PatientTreatment>> FindByPatientId(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Retrieves the list of bolus treatments associated with the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose bolus treatments are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientTreatment"/> records representing the patient's bolus treatments.</returns>
|
||||
Task<List<PatientTreatment>> GetBolusTreatments(ObjectId patientId);
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of patient treatments based on the specified filter criteria.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter used to control the page number, page size, and other query options.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the paginated patient treatment results.</returns>
|
||||
Task<PaginationResponse<PatientTreatment>> GetPaginatedTreatments(PaginationFilter filter);
|
||||
/// <summary>
|
||||
/// Retrieves the list of active treatments associated with the specified patient, sorted according to the provided order parameter.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose active treatments are being queried.</param>
|
||||
/// <param name="order">A string defining the ordering criteria applied to the returned treatments.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of active <see cref="PatientTreatment"/> entries for the patient.</returns>
|
||||
Task<List<PatientTreatment>> GetActiveTreatmentsByPatientIdAndOrder(ObjectId patientId, string order);
|
||||
}
|
||||
@@ -10,35 +10,150 @@ namespace adas_core.Application.Services.Interfaces;
|
||||
|
||||
public interface IUnitService
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all units, with an option to include their associated POCs.
|
||||
/// </summary>
|
||||
/// <param name="withPocs">Indicates whether the returned units should include their associated POCs.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the list of units.</returns>
|
||||
Task<List<Unit>> GetAll(bool withPocs = false);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a compact list of all units, returning minimal summary information for each unit.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="UnitInfoDto"/> objects with the compact information of all units.</returns>
|
||||
Task<List<UnitInfoDto>> GetAllCompact();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a compact representation of a unit info by its identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the unit to retrieve.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the compact unit information.</returns>
|
||||
Task<UnitInfoDto> GetOneCompact(ObjectId id);
|
||||
|
||||
// Task<Unit?> GetByCodeSysAndCode(string unit);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="Unit"/> by its name.
|
||||
/// </summary>
|
||||
/// <param name="unit">The name of the unit to look up.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="Unit"/> if found; otherwise, <c>null</c>.</returns>
|
||||
Task<Unit?> GetByName(string unit);
|
||||
|
||||
// Task<Unit?> GetByPointOfCare(PointOfCare pointOfCare);
|
||||
/// <summary>
|
||||
/// Retrieves information for a unit identified by the specified <paramref name="id"/>, returning <see langword="null"/> when no matching unit is found.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the unit to look up.</param>
|
||||
/// <param name="dataLocale">Optional locale used to localize the returned unit data.</param>
|
||||
/// <param name="fillLists">When <see langword="true"/>, populates the related lists on the returned unit.</param>
|
||||
/// <param name="withPoCs">When <see langword="true"/>, includes the unit's points of contact in the result.</param>
|
||||
/// <returns>A task that yields the located <see cref="Unit"/>, or <see langword="null"/> if no unit matches the given id.</returns>
|
||||
Task<Unit?> GetInfo(ObjectId id, LocaleEnum? dataLocale, bool fillLists = true, bool withPoCs = false);
|
||||
/// <summary>
|
||||
/// Retrieves information about a unit identified by the specified <paramref name="id"/>, optionally including related points of contact and devices.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the unit to retrieve information for.</param>
|
||||
/// <param name="withPoCs">Indicates whether related points of contact should be included in the result. Defaults to <c>true</c>.</param>
|
||||
/// <param name="withDevices">Indicates whether related devices should be included in the result. Defaults to <c>true</c>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <c>Unit</c> if found, or <c>null</c> if no unit matches the specified identifier.</returns>
|
||||
Task<Unit?> GetInfo(ObjectId id, bool withPoCs = true, bool withDevices = true);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the unit associated with the specified patient identifier, returning <c>null</c> when no matching unit exists.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose unit is being looked up.</param>
|
||||
/// <returns>A task that represents the asynchronous lookup, containing the matching <see cref="Unit"/> if found, or <c>null</c> if no unit is associated with the patient.</returns>
|
||||
Task<Unit?> FindByPatientId(ObjectId patientId);
|
||||
|
||||
// Task<List<Unit>?> FindByLocation(PatientLocation location);
|
||||
/// <summary>
|
||||
/// Asynchronously finds and returns a <see cref="Unit"/> matching the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the <see cref="Unit"/> to retrieve. May be <c>null</c>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="Unit"/> if found; otherwise, <c>null</c>.</returns>
|
||||
Task<Unit?> FindById(ObjectId? id);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="Unit"/> entity matching the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name used to look up the unit. Can be null.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="Unit"/> if found; otherwise, null.</returns>
|
||||
Task<Unit?> FindByName(string? name);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a <see cref="Unit"/> by matching either its unit name or point-of-contact (POC) name.
|
||||
/// Returns <c>null</c> if no matching unit is found.
|
||||
/// </summary>
|
||||
/// <param name="name">The unit name to search for. May be <c>null</c>.</param>
|
||||
/// <param name="pocName">The point-of-contact (POC) name to search for. May be <c>null</c>.</param>
|
||||
/// <returns>A <see cref="Task{Unit}"/> containing the matched <see cref="Unit"/>, or <c>null</c> if no unit is found.</returns>
|
||||
Task<Unit?> FindByUnitNameOrPocName(string? name, string? pocName);
|
||||
|
||||
//Task<Unit?> FindByPointOfCare(PointOfCare pointOfCare);
|
||||
/// <summary>
|
||||
/// Inserts a single <see cref="Unit"/> into the underlying data store and returns the resulting entity wrapped in a task.
|
||||
/// </summary>
|
||||
/// <param name="unit">The <see cref="Unit"/> instance to be inserted.</param>
|
||||
/// <returns>A task that resolves to the inserted <see cref="Unit"/>, or <c>null</c> if the insertion did not produce a result.</returns>
|
||||
Task<Unit?> InsertOne(Unit unit);
|
||||
/// <summary>
|
||||
/// Updates the specified unit in the system.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit containing the updated information to persist.</param>
|
||||
/// <returns>A task that returns the updated <see cref="Unit"/>, or <c>null</c> if the unit was not found.</returns>
|
||||
Task<Unit?> UpdateUnit(Unit unit);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the collection of units associated with the specified master list identifier and master list type.
|
||||
/// Returns null when no matching units are found.
|
||||
/// </summary>
|
||||
/// <param name="masterListId">The unique identifier of the master list whose units should be retrieved.</param>
|
||||
/// <param name="masterListType">The type of the master list used to scope the unit lookup.</param>
|
||||
/// <returns>A task that returns an <see cref="IEnumerable{T}"/> of <see cref="Unit"/> when matches exist, or null when no matching units are found.</returns>
|
||||
Task<IEnumerable<Unit>?> FindUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType);
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of units associated with the specified master list, filtered by the given master list type.
|
||||
/// </summary>
|
||||
/// <param name="masterListId">The unique identifier of the master list whose units should be counted.</param>
|
||||
/// <param name="masterListType">The type of the master list used to scope the count to the appropriate unit category.</param>
|
||||
/// <returns>A <see cref="Task{Int64}"/> that represents the asynchronous operation, containing the total number of units matching the specified master list.</returns>
|
||||
Task<long> CountUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType);
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the collection of units associated with the specified master list identifier.
|
||||
/// </summary>
|
||||
/// <param name="masterListId">The identifier of the master list whose units are to be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the collection of units that belong to the specified master list.</returns>
|
||||
Task<IEnumerable<Unit>> FindUnitsByMasterListId(ObjectId masterListId);
|
||||
/// <summary>
|
||||
/// Updates the unit master list based on the provided unit ID list DTO.
|
||||
/// </summary>
|
||||
/// <param name="updateUnitListDto">The DTO containing the list of unit IDs to be used for updating the master list.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the updated <see cref="Unit"/>, or <c>null</c> if the update was not applicable.</returns>
|
||||
Task<Unit?> UpdateUnitMasterList(UpdateUnitIdListDto updateUnitListDto);
|
||||
/// <summary>
|
||||
/// Asynchronously updates the configuration for the specified unit using the provided configuration data.
|
||||
/// </summary>
|
||||
/// <param name="unitIdParsed">The parsed identifier of the unit whose configuration will be updated.</param>
|
||||
/// <param name="unitConfiguration">The new configuration values to apply to the unit.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the configuration was successfully updated; otherwise, <c>false</c>.</returns>
|
||||
Task<bool> UpdateConfiguration(ObjectId unitIdParsed, UnitConfiguration unitConfiguration);
|
||||
/// <summary>
|
||||
/// Asynchronously deletes a unit identified by the provided <paramref name="unit"/> entity's identifier.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit entity whose identifier is used to locate and delete the record.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous operation, containing a value indicating whether the unit was successfully deleted.</returns>
|
||||
Task<bool> DeleteUnitById(Unit unit);
|
||||
/// <summary>
|
||||
/// Updates the information of an existing unit identified by <paramref name="unitId"/>, including its name, title, and optionally its configuration object ID.
|
||||
/// Returns the updated unit, or <c>null</c> when no matching unit is found.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit to update.</param>
|
||||
/// <param name="name">The new name to assign to the unit.</param>
|
||||
/// <param name="title">The new title to assign to the unit.</param>
|
||||
/// <param name="configObsId">An optional configuration object ID to associate with the unit.</param>
|
||||
/// <returns>A task that yields the updated <see cref="Unit"/>, or <c>null</c> if the unit does not exist.</returns>
|
||||
Task<Unit?> UpdateUnitInfo(ObjectId unitId, string name, string title, string? configObsId = null);
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of units based on the provided filter, optionally including their associated points of contact.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that defines the page size, page number, and any additional query criteria.</param>
|
||||
/// <param name="withPoCs">A flag indicating whether the response should include the points of contact associated with each unit.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the paginated response of units.</returns>
|
||||
Task<PaginationResponse<Unit>> GetPaginatedUnits(PaginationFilter filter, bool withPoCs);
|
||||
}
|
||||
@@ -10,8 +10,15 @@ public class LocalAuditService(
|
||||
ILogger<LocalAuditService> logger)
|
||||
: ILocalAuditService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an audit log entry asynchronously, forwarding the provided data to the audit service with a reason when one is supplied, and logging any errors that occur.
|
||||
/// </summary>
|
||||
/// <param name="user">The claims principal representing the user performing the action; used to attribute the audit log entry.</param>
|
||||
/// <param name="dataOriginal">The original data before the change.</param>
|
||||
/// <param name="dataModified">The modified data after the change.</param>
|
||||
/// <param name="reason">An optional reason for the change; when null, the audit log is created without supplying a reason.</param>
|
||||
public async Task CreateAuditLogAsync(ClaimsPrincipal? user, object? dataOriginal, object? dataModified,
|
||||
string? reason)
|
||||
string? reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -32,6 +39,11 @@ public class LocalAuditService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously deep copies the specified data, attempting a primary copy method and falling back to a JSON-based copy if the primary fails. Returns the default value if both copy attempts fail.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to be deep copied.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the deep copy of the data, or <c>null</c> if both copy methods fail.</returns>
|
||||
public async Task<T?> DeepCopyAsync<T>(T data)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -18,6 +18,11 @@ using MongoDB.Driver;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a generic service implementation for managing <see cref="MasterList"/> entities of type <typeparamref name="T"/>.
|
||||
/// Acts as the concrete implementation of the <see cref="IMasterListService{T}"/> contract for master list operations.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of master list entity managed by the service. Must derive from <see cref="MasterList"/> and expose a public parameterless constructor.</typeparam>
|
||||
public class MasterListService<T> : IMasterListService<T> where T : MasterList, new()
|
||||
{
|
||||
private readonly Lazy<IAdmissionService> _admissionService;
|
||||
@@ -62,6 +67,11 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
_assetsDirectory = Path.Combine(apiSettings.Value.PathToDisplayAssets);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a master list identified by the specified identifier. If the master list is not found, the operation is skipped and logged; if the master list is in use, a conflict exception is thrown.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list to delete.</param>
|
||||
/// <exception cref="ConflictException">Thrown when the master list is currently in use and cannot be deleted.</exception>
|
||||
public async Task DeleteMasterListById(ObjectId id)
|
||||
{
|
||||
var repository = GetRepository();
|
||||
@@ -101,6 +111,12 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
/// <summary>
|
||||
/// Adds a filter option to an existing master list, records the change in the audit log, and broadcasts the update to subscribed clients.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list to which the option will be added.</param>
|
||||
/// <param name="opt">The filter option element to add to the master list.</param>
|
||||
/// <returns>The resulting <see cref="OptionList"/> entry if the option is successfully added; <c>null</c> if the master list cannot be found, no result is produced, or an error is encountered during the operation.</returns>
|
||||
public async Task<OptionList?> AddOptionToMasterList(ObjectId id, FilterOptionListElement opt)
|
||||
{
|
||||
try
|
||||
@@ -132,8 +148,16 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an option within the master list for the specified locale, and when the update succeeds, creates an audit log entry, broadcasts the change, and propagates the update to the affected patient item list. Returns <c>null</c> if the update fails, the result is <c>null</c>, or an exception is thrown during processing.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list whose option will be updated.</param>
|
||||
/// <param name="opt">The option to be applied to the master list.</param>
|
||||
/// <param name="typeName">The type name used when propagating the update to the patient item list.</param>
|
||||
/// <param name="locale">The locale used for the master list option update and retrieval of the updated entity for auditing.</param>
|
||||
/// <returns>The updated <see cref="OptionList"/>, or <c>null</c> if the update failed, the result was <c>null</c>, or an exception occurred.</returns>
|
||||
public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList opt, string typeName,
|
||||
LocaleEnum locale)
|
||||
LocaleEnum locale)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -151,7 +175,7 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
await SendMasterListItemBroadcast(masterList, opt, oldOption, OperationType.UpdateMasterListItem);
|
||||
await UpdatePatientItemList(id,
|
||||
new UpdateOptionMasterListDto
|
||||
{ OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName);
|
||||
{ OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -163,6 +187,13 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a master list option, performing audit logging, broadcasting the change, and updating the patient item list when successful. Returns <c>null</c> if the option cannot be found or an error occurs.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list containing the option to update.</param>
|
||||
/// <param name="opt">The option containing the new values to apply.</param>
|
||||
/// <param name="typeName">The name of the type used when updating the associated patient item list.</param>
|
||||
/// <returns>The updated <see cref="OptionList"/> if the operation succeeds, or <c>null</c> if the master list is not found or an exception is thrown.</returns>
|
||||
public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList opt, string typeName)
|
||||
{
|
||||
try
|
||||
@@ -181,7 +212,7 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
await SendMasterListItemBroadcast(masterList, opt, oldOption, OperationType.UpdateMasterListItem);
|
||||
await UpdatePatientItemList(id,
|
||||
new UpdateOptionMasterListDto
|
||||
{ OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName);
|
||||
{ OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -193,6 +224,15 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a full master list option, records the change in the audit log, notifies subscribers via broadcast, and propagates the update to the associated patient item list.
|
||||
/// Returns <c>null</c> if the underlying repository update fails or an exception is encountered, in which case the error is logged.
|
||||
/// Uses the default locale when retrieving the updated master list for audit purposes (locale handling is pending).
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list that contains the option to update.</param>
|
||||
/// <param name="opt">The option with the new values to apply to the master list.</param>
|
||||
/// <param name="typeName">The name of the entity type used when updating the related patient item list.</param>
|
||||
/// <returns>The updated <see cref="OptionList"/> when the operation succeeds; <c>null</c> when the update fails or an error is caught.</returns>
|
||||
public async Task<OptionList?> UpdateFullMasterListOption(ObjectId id, OptionList opt, string typeName)
|
||||
{
|
||||
try
|
||||
@@ -211,7 +251,7 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
await SendMasterListItemBroadcast(masterList, opt, oldOption, OperationType.UpdateMasterListItem);
|
||||
await UpdatePatientItemList(id,
|
||||
new UpdateOptionMasterListDto
|
||||
{ OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName);
|
||||
{ OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -223,6 +263,13 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an option from a master list by its identifier. On success, cascades the change to related patient item lists, creates an audit log entry, and broadcasts update and deletion events; returns <c>false</c> if the deletion fails, the master list or option is not found, or an error is logged.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list that contains the option.</param>
|
||||
/// <param name="optId">The identifier of the option to remove from the master list.</param>
|
||||
/// <param name="typeName">The name of the type used when cascading the deletion to related patient item lists.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the option is successfully deleted and the side-effects are applied; otherwise, <c>false</c>.</returns>
|
||||
public async Task<bool> DeleteMasterListOption(ObjectId id, ObjectId optId, string typeName)
|
||||
{
|
||||
try
|
||||
@@ -253,8 +300,14 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the option details of a master list identified by the given identifier, records an audit log entry for the change, and broadcasts the update. Returns the updated details, or <c>null</c> if the update fails or an error occurs.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list whose option details will be updated.</param>
|
||||
/// <param name="opt">The new option details to apply to the master list.</param>
|
||||
/// <returns>A task that yields the updated <see cref="UpdateMasterListDetailsDto"/> on success, or <c>null</c> when the update fails or an exception is caught and logged.</returns>
|
||||
public async Task<UpdateMasterListDetailsDto?> UpdateOptionDetailsToMasterList(ObjectId id,
|
||||
UpdateMasterListDetailsDto opt)
|
||||
UpdateMasterListDetailsDto opt)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -278,6 +331,13 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the name of an existing master list identified by the specified id, records an audit log entry for the change, and broadcasts the update to subscribers.
|
||||
/// Returns <c>false</c> if the update fails or an exception is encountered, in which case the error is logged.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list to rename.</param>
|
||||
/// <param name="name">The new name to assign to the master list.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the master list name was updated successfully; otherwise, <c>false</c>.</returns>
|
||||
public async Task<bool> UpdateMasterListName(ObjectId id, string name)
|
||||
{
|
||||
try
|
||||
@@ -302,6 +362,12 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the description of a master list identified by the specified id, creating an audit log entry and broadcasting the change when the update succeeds.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list to update.</param>
|
||||
/// <param name="name">The new description to apply to the master list.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the update succeeded; otherwise, <c>false</c> when the update fails or an exception is caught and logged.</returns>
|
||||
public async Task<bool> UpdateMasterListDescription(ObjectId id, string name)
|
||||
{
|
||||
try
|
||||
@@ -326,6 +392,12 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified option from a master list identified by its id, auditing the change and broadcasting the update and deleted item to subscribers.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list from which the option will be removed.</param>
|
||||
/// <param name="oldOpt">The option to remove from the master list.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the option was successfully removed; <c>false</c> if the operation failed or an error occurred.</returns>
|
||||
public async Task<bool> RemoveMasterListOption(ObjectId id, OptionList oldOpt)
|
||||
{
|
||||
try
|
||||
@@ -351,6 +423,11 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all master list entries of the generic type from the underlying repository.
|
||||
/// If an exception occurs during retrieval, the error is logged and an empty collection is returned as a fallback.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains all retrieved entries, or an empty collection if an error occurs.</returns>
|
||||
public async Task<IEnumerable<T>> GetAllMasterList()
|
||||
{
|
||||
try
|
||||
@@ -365,6 +442,11 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all master list entries without applying any optional filters or parameters.
|
||||
/// If an error occurs, the exception is logged and an empty collection is returned as a fallback.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of <see cref="MasterListDto"/> items, or an empty collection if an error is encountered.</returns>
|
||||
public async Task<IEnumerable<MasterListDto>> GetAllMasterListWithoutOptions()
|
||||
{
|
||||
try
|
||||
@@ -379,6 +461,14 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an <see cref="OptionList"/> item that matches the specified master identifier, option identifier, and locale.
|
||||
/// Returns <c>null</c> and logs the error if the lookup fails.
|
||||
/// </summary>
|
||||
/// <param name="masterId">The identifier of the master item that owns the option.</param>
|
||||
/// <param name="optionId">The identifier of the specific option to locate.</param>
|
||||
/// <param name="locale">The locale used to resolve the localized option item.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> containing the matching <see cref="OptionList"/>, or <c>null</c> if no item is found or an error occurs.</returns>
|
||||
public async Task<OptionList?> FindOptionItemById(ObjectId masterId, ObjectId optionId, LocaleEnum locale)
|
||||
{
|
||||
try
|
||||
@@ -393,8 +483,14 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all master list entries, maps each one to a paginated DTO enriched with its in-use status, and returns the resulting collection.
|
||||
/// Logs the error and returns an empty collection when the retrieval or mapping process fails.
|
||||
/// </summary>
|
||||
/// <param name="request">The pagination filter whose page size is applied when building each result entry.</param>
|
||||
/// <returns>A task that yields an enumerable of <see cref="MasterListWithPaginatedOptionsDto"/> containing the mapped master lists, or an empty enumerable if an error occurs.</returns>
|
||||
public async Task<IEnumerable<MasterListWithPaginatedOptionsDto>> GetAllMasterListWithPaginatedOptions(
|
||||
PaginationFilter request)
|
||||
PaginationFilter request)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -417,6 +513,11 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the total count of all records in the master list for the current entity type.
|
||||
/// Returns 0 if an error occurs while accessing the repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the total count of records, or 0 if an error was encountered.</returns>
|
||||
public async Task<int> GetAllMasterListCount()
|
||||
{
|
||||
try
|
||||
@@ -431,6 +532,13 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a master list by its unique identifier, optionally filtered by locale.
|
||||
/// Logs and returns null if an error occurs during the lookup.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the master list to retrieve.</param>
|
||||
/// <param name="locale">The optional locale used to localize the retrieved master list.</param>
|
||||
/// <returns>The master list of type <typeparamref name="T"/> if found; otherwise, null.</returns>
|
||||
public async Task<T?> GetMasterListById(ObjectId id, LocaleEnum? locale)
|
||||
{
|
||||
try
|
||||
@@ -445,8 +553,15 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a master list by its identifier and returns it with paginated options, including whether the list is currently in use.
|
||||
/// Returns null when the master list is not found or when an error occurs during retrieval.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the master list to retrieve.</param>
|
||||
/// <param name="request">The pagination filter that determines the page size for the returned options.</param>
|
||||
/// <returns>A task that yields a <see cref="MasterListWithPaginatedOptionsDto"/> when the master list is found; otherwise, null.</returns>
|
||||
public async Task<MasterListWithPaginatedOptionsDto?> GetMasterListByIdWithPaginatedOptions(ObjectId id,
|
||||
PaginationFilter request)
|
||||
PaginationFilter request)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -467,6 +582,12 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the names of all options associated with a master list identified by the specified id using the default locale.
|
||||
/// Returns an empty list if the master list is not found or if an error occurs while retrieving the data.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list whose option names are to be retrieved.</param>
|
||||
/// <returns>A list of option names for the found master list, or an empty list if the list is not found or an error is encountered.</returns>
|
||||
public async Task<List<string>> GetMasterListOptionsNamesById(ObjectId id)
|
||||
{
|
||||
try
|
||||
@@ -485,6 +606,13 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a master list of options filtered by the specified identifier and an optional text search term.
|
||||
/// Returns an empty list if an error occurs during retrieval.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier used to locate the master list in the repository.</param>
|
||||
/// <param name="textSearch">The optional text search term used to filter the results; may be null.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of <see cref="OptionList"/> entries that match the specified identifier and text search criteria.</returns>
|
||||
public async Task<List<OptionList>> GetMasterListByIdAndTextSearch(ObjectId id, string? textSearch)
|
||||
{
|
||||
try
|
||||
@@ -499,13 +627,24 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a master list of <see cref="OptionList"/> items from the repository using the specified identifier and search filter options.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier used to locate the master list.</param>
|
||||
/// <param name="filterOption">The filter options applied to narrow the search within the master list.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="OptionList"/> items matching the given criteria.</returns>
|
||||
public async Task<List<OptionList>> GetMasterListByIdAndSearchOptions(ObjectId id,
|
||||
FilterOptionListElement filterOption)
|
||||
FilterOptionListElement filterOption)
|
||||
{
|
||||
var repository = GetRepository();
|
||||
return await repository.GetMasterListByIdAndSearchOptions(id, filterOption);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a master list entry by its name. Returns the matching item, or null if no entry is found or if an error occurs while querying the repository.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the master list entry to look up.</param>
|
||||
/// <returns>A task that resolves to the matching item of type T, or null when no item is found or the lookup fails.</returns>
|
||||
public async Task<T?> GetMasterListByName(string name)
|
||||
{
|
||||
try
|
||||
@@ -521,6 +660,11 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated master list based on the specified pagination filter, applying skip and limit operations according to the page number and page size.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter containing the page number and page size used to determine the subset of records to return.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="PaginationResponse{T}"/> with the paginated data, the current page number, the page size, and the total document count.</returns>
|
||||
public async Task<PaginationResponse<T>> GetPaginatedMasterList(PaginationFilter filter)
|
||||
{
|
||||
var repository = GetRepository();
|
||||
@@ -540,8 +684,15 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of master list items, each enriched with a paginated set of associated options and a usage indicator.
|
||||
/// Applies the supplied <paramref name="listFilter"/> to paginate the master lists and the <paramref name="optionFilter"/> to size the embedded options within each list DTO.
|
||||
/// </summary>
|
||||
/// <param name="listFilter">Pagination parameters (page number and page size) used to slice the master list result set.</param>
|
||||
/// <param name="optionFilter">Pagination parameters whose page size is applied to the options associated with each returned master list item.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing a <see cref="PaginationResponse{T}"/> of <see cref="MasterListWithPaginatedOptionsDto"/> with the requested page of master lists, their paginated options, the current page metadata, and the total document count.</returns>
|
||||
public async Task<PaginationResponse<MasterListWithPaginatedOptionsDto>> GetPaginatedMasterListWithPaginatedOptions(
|
||||
PaginationFilter listFilter, PaginationFilter optionFilter)
|
||||
PaginationFilter listFilter, PaginationFilter optionFilter)
|
||||
{
|
||||
var repository = GetRepository();
|
||||
|
||||
@@ -568,6 +719,12 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
listFilter.PageSize, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated subset of options for the specified list, applying the page number and page size from the filter after fetching the full result set from the repository.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that defines the page number and page size to apply to the results.</param>
|
||||
/// <param name="listId">The identifier of the list whose options are being retrieved.</param>
|
||||
/// <returns>A <see cref="PaginationResponse{OptionList}"/> containing the requested page of options, the current page metadata, and the total count of available options.</returns>
|
||||
public async Task<PaginationResponse<OptionList>> GetPaginatedOptions(PaginationFilter filter, ObjectId listId)
|
||||
{
|
||||
var repository = GetRepository();
|
||||
@@ -579,6 +736,11 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
return new PaginationResponse<OptionList>(data, filter.PageNumber, filter.PageSize, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new master list item, broadcasts the change to subscribers, and records an audit log entry for the operation.
|
||||
/// </summary>
|
||||
/// <param name="item">The master list entity to insert.</param>
|
||||
/// <returns>The inserted entity retrieved from the repository, or <c>null</c> if the operation fails.</returns>
|
||||
public async Task<T?> InsertMasterList(T item)
|
||||
{
|
||||
try
|
||||
@@ -597,6 +759,12 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing item in the master list, broadcasts the update operation, and creates an audit log entry comparing the old and new state.
|
||||
/// Returns <c>null</c> if the update fails, logging the error internally.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to update in the master list.</param>
|
||||
/// <returns>The updated item retrieved from the repository, or <c>null</c> if an error occurred during the update.</returns>
|
||||
public async Task<T?> UpdateMasterList(T item)
|
||||
{
|
||||
try
|
||||
@@ -617,8 +785,17 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the identifier of a master list associated with a given unit, based on the relationship between two master list types.
|
||||
/// Looks up the unit that contains the first master list reference (<paramref name="masterListType1"/>) and returns the identifier of the secondary associated list defined by <paramref name="masterListType2"/>.
|
||||
/// Returns <c>null</c> when no unit is found for the given list, or when <paramref name="masterListType2"/> does not map to a known associated list.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list used to locate the associated unit.</param>
|
||||
/// <param name="masterListType1">The master list type used to find the unit (e.g., the primary list reference on the unit).</param>
|
||||
/// <param name="masterListType2">The master list type that determines which associated list identifier is returned from the resolved unit.</param>
|
||||
/// <returns>A task that yields the associated <see cref="ObjectId"/> when a matching list exists, or <c>null</c> when no unit is found or the type is not mapped.</returns>
|
||||
public async Task<ObjectId?> GetAssociatedList(ObjectId id, MasterListType masterListType1,
|
||||
MasterListType masterListType2)
|
||||
MasterListType masterListType2)
|
||||
{
|
||||
var repository = GetRepository();
|
||||
await repository.FindById(id, LocaleEnum.Default);
|
||||
@@ -657,6 +834,11 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the names of all options associated with the list identified by the specified identifier, using the default locale. Returns an empty list if no list is found for the given identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the list whose options should be retrieved.</param>
|
||||
/// <returns>A list of option names belonging to the matching list, or an empty list if the list is not found.</returns>
|
||||
public async Task<List<string>> GetOptionsOfList(ObjectId id)
|
||||
{
|
||||
var repository = GetRepository();
|
||||
@@ -667,11 +849,21 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves and returns the master list repository instance for the current entity type from the dependency injection service provider.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IMasterListRepository{T}"/> instance retrieved from the configured <see cref="IServiceProvider"/>.</returns>
|
||||
private IMasterListRepository<T> GetRepository()
|
||||
{
|
||||
return _serviceProvider.GetRequiredService<IMasterListRepository<T>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Propagates an update of a master list option to the patient, discharge, and admission services for all units that reference the given master list identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the master list whose dependent units and related records need to be updated.</param>
|
||||
/// <param name="opt">The update payload describing the master list change to apply to the related patient records.</param>
|
||||
/// <param name="typeName">The name of the master list type used to scope the update across the affected services.</param>
|
||||
private async Task UpdatePatientItemList(ObjectId id, UpdateOptionMasterListDto opt, string typeName)
|
||||
{
|
||||
// Necesito saber que unidades tienen el id de lista que estamos modificando
|
||||
@@ -683,6 +875,12 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
await _admissionService.Value.UpdatePatientMasterListItemChange(opt, units, typeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a master list item across all patients, discharges, and admissions that belong to units referencing the specified master list identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The master list identifier used to locate the units that reference it.</param>
|
||||
/// <param name="opt">The option list containing the item to be removed from the affected records.</param>
|
||||
/// <param name="typeName">The type name of the master list item to delete.</param>
|
||||
private async Task DeletePatientItemList(ObjectId id, OptionList opt, string typeName)
|
||||
{
|
||||
// Necesito saber que unidades tienen el id de lista que estamos modificando
|
||||
@@ -694,16 +892,30 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
await _admissionService.Value.DeletePatientMasterListItem(opt, units, typeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves the specified API request by offloading the save operation to a background task.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to be saved.</param>
|
||||
public Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the specified API request.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to save.</param>
|
||||
public Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a master list change to all subscribers linked to the displays of the units associated with the given master list.
|
||||
/// Returns early if the master list type cannot be parsed as a <see cref="MasterListType"/> or if no related units are found, and logs any exception encountered while sending the notifications.
|
||||
/// </summary>
|
||||
/// <param name="masterList">The master list entity triggering the broadcast; its runtime type name is used to determine the master list category.</param>
|
||||
/// <param name="operation">The operation performed on the master list, which is sent to each subscriber along with the localized master list options.</param>
|
||||
private async Task SendMasterListBroadcast(T masterList, OperationType operation)
|
||||
{
|
||||
try
|
||||
@@ -739,8 +951,15 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a broadcast notification to all subscribers linked to a master list when an option list item is added, updated, or deleted. The message payload is tailored per operation: add includes the new item, delete includes the deleted item, and update includes both. Returns early if the master list type cannot be parsed from the type name or if no related units are found.
|
||||
/// </summary>
|
||||
/// <param name="masterList">The master list entity whose type and identifier are used to locate related units and subscribers.</param>
|
||||
/// <param name="updatedItem">The new or modified option list item, included in add and update operations.</param>
|
||||
/// <param name="oldItem">The previous option list item, included in delete and update operations.</param>
|
||||
/// <param name="operation">The operation type that determines the structure of the broadcast message sent to subscribers.</param>
|
||||
private async Task SendMasterListItemBroadcast(T masterList, OptionList? updatedItem, OptionList? oldItem,
|
||||
OperationType operation)
|
||||
OperationType operation)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -803,6 +1022,11 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the units that are associated with the specified master list and maps them to a collection of <see cref="UnitInfoDto"/> objects, returning an empty list when no units are found.
|
||||
/// </summary>
|
||||
/// <param name="list">The master list whose associated units should be retrieved. Its identifier and list type are used to look up the related units.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="UnitInfoDto"/> instances for the units linked to the specified list, or an empty list if none exist.</returns>
|
||||
private async Task<List<UnitInfoDto>> IsInUse(T list)
|
||||
{
|
||||
var units = await _unitService.Value.FindUnitsByMasterListId(list.Id, list.ListType);
|
||||
@@ -818,6 +1042,11 @@ public class MasterListService<T> : IMasterListService<T> where T : MasterList,
|
||||
return unitInfoDtos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the count of units associated with the specified master list, indicating how many records are currently in use.
|
||||
/// </summary>
|
||||
/// <param name="list">The master list entity whose associated unit count is being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the number of units referencing the master list.</returns>
|
||||
private async Task<long> IsInUseCount(T list)
|
||||
{
|
||||
return await _unitService.Value.CountUnitsByMasterListId(list.Id, list.ListType);
|
||||
|
||||
@@ -21,6 +21,13 @@ public class MasterListServiceFactory(
|
||||
private readonly ILogger<MasterListServiceFactory> _logger = logger;
|
||||
|
||||
// Obtiene el servicio basado en MasterListType
|
||||
/// <summary>
|
||||
/// Resolves and returns a service instance for the specified master list type by dynamically constructing the generic <see cref="MasterListService{T}"/> type and retrieving the registered service.
|
||||
/// </summary>
|
||||
/// <param name="serviceName">The master list type used to resolve the corresponding service type from the adas_core.Domain.Models.Masters namespace.</param>
|
||||
/// <returns>An object representing the resolved service instance for the requested master list type.</returns>
|
||||
/// <exception cref="BadRequestException">Thrown when the type corresponding to <paramref name="serviceName"/> cannot be resolved in the adas_core.Domain.Models.Masters namespace.</exception>
|
||||
/// <exception cref="NotFoundException">Thrown when the generic service type cannot be constructed for the resolved type parameter.</exception>
|
||||
public object GetService(MasterListType serviceName)
|
||||
{
|
||||
var typeParameter = Type.GetType($"adas_core.Domain.Models.Masters.{serviceName}, adas-core.Domain") ??
|
||||
@@ -33,6 +40,12 @@ public class MasterListServiceFactory(
|
||||
}
|
||||
|
||||
// Obtiene el servicio basado en un Type genérico
|
||||
/// <summary>
|
||||
/// Resolves a generic <see cref="IMasterListService{T}"/> instance when the requested service type is a generic <c>MasterListService<T></c>. Throws an exception if the service type is not compatible or if the underlying service cannot be resolved.
|
||||
/// </summary>
|
||||
/// <param name="serviceType">The type of the service to resolve. Must be a generic type based on <c>MasterListService<></c> to be successfully handled.</param>
|
||||
/// <returns>The resolved service instance implementing <see cref="IMasterListService{T}"/> for the corresponding item type.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">Thrown when the requested <paramref name="serviceType"/> is not a generic <c>MasterListService<></c>, or when no implementation of <see cref="IMasterListService{T}"/> can be resolved for the requested item type.</exception>
|
||||
public object GetService(Type serviceType)
|
||||
{
|
||||
// Verifica si el tipo es genérico y está basado en MasterList
|
||||
@@ -56,6 +69,11 @@ public class MasterListServiceFactory(
|
||||
throw new InvalidOperationException($"El tipo de servicio {serviceType.FullName} no es compatible.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the specific <see cref="Type"/> associated with the given <paramref name="masterListType"/>, mapping each enumeration value to its corresponding concrete list type (e.g., <see cref="AllergyList"/>, <see cref="DiagnosisList"/>, <see cref="DoctorList"/>, etc.). If no specific mapping is found, the base <see cref="MasterList"/> type is returned as the fallback.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The enumeration value identifying which master list category to resolve to its concrete type.</param>
|
||||
/// <returns>The <see cref="Type"/> that implements the requested master list category, or <see cref="MasterList"/> when no specific type applies.</returns>
|
||||
public Type GetMasterListSpecificType(MasterListType masterListType)
|
||||
{
|
||||
// mapear cada valor de MasterListType a su respectivo tipo específico
|
||||
@@ -87,6 +105,12 @@ public class MasterListServiceFactory(
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a specific <see cref="MasterList"/> subtype instance resolved from <paramref name="masterListType"/>, with properties mapped from the provided <paramref name="masterList"/>. Returns null when the specific type cannot be instantiated via reflection.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The discriminator used to resolve the concrete <see cref="MasterList"/> subtype to create.</param>
|
||||
/// <param name="masterList">The source <see cref="MasterList"/> whose properties are mapped onto the created specific instance.</param>
|
||||
/// <returns>The created specific <see cref="MasterList"/> instance with mapped properties, or null if reflection failed to create the instance.</returns>
|
||||
public object? GetTypedMasterList(MasterListType masterListType, MasterList masterList)
|
||||
{
|
||||
// Obtiene el tipo específico de MasterList basado en masterListType
|
||||
@@ -103,6 +127,13 @@ public class MasterListServiceFactory(
|
||||
return specificMasterList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a master list by dynamically resolving the appropriate service for the given <paramref name="masterListType"/> and invoking its <c>InsertMasterList</c> method via reflection.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The type used to resolve the target service and the specific typed master list instance.</param>
|
||||
/// <param name="masterList">The master list entity to be converted and inserted.</param>
|
||||
/// <returns>The result produced by the dynamically invoked <c>InsertMasterList</c> method, or <c>null</c> when the awaited task exposes no result value.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when no service is registered for the specified <paramref name="masterListType"/>, when the master list cannot be converted to its expected typed instance, or when the <c>InsertMasterList</c> method cannot be found on the resolved service.</exception>
|
||||
public async Task<object?> InsertMasterList(MasterListType masterListType, MasterList masterList)
|
||||
{
|
||||
dynamic service = GetService(masterListType) ??
|
||||
@@ -120,6 +151,15 @@ public class MasterListServiceFactory(
|
||||
return resultProperty?.GetValue(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a master list entry by dynamically resolving the appropriate service for the given <paramref name="masterListType"/> and invoking its <c>UpdateMasterList</c> method via reflection on the typed master list.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The type of master list that determines which service to resolve and which target method to invoke.</param>
|
||||
/// <param name="masterList">The master list instance to be updated, which is converted to its specific type before the service method is invoked.</param>
|
||||
/// <returns>The <c>Result</c> value of the awaited task returned by the invoked <c>UpdateMasterList</c> method, or <see langword="null"/> if the result property is not available.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when no service is registered for the specified <paramref name="masterListType"/>.</exception>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the provided <paramref name="masterList"/> cannot be converted to its specific typed master list.</exception>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the resolved service does not expose an <c>UpdateMasterList</c> method matching the typed master list argument.</exception>
|
||||
public async Task<object?> UpdateMasterList(MasterListType masterListType, MasterList masterList)
|
||||
{
|
||||
dynamic service = GetService(masterListType) ??
|
||||
@@ -137,8 +177,16 @@ public class MasterListServiceFactory(
|
||||
return resultProperty?.GetValue(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a master list item by its identifier, using reflection to dispatch the call to the appropriate service based on the specified master list type.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The type of master list used to resolve the underlying service that will handle the request.</param>
|
||||
/// <param name="masterListId">The unique identifier of the master list item to retrieve.</param>
|
||||
/// <param name="dataLocale">An optional locale used to localize the returned data; may be <c>null</c> when localization is not required.</param>
|
||||
/// <returns>A task that resolves to the retrieved master list item as an <see cref="object"/>, or <c>null</c> when the awaited result has no value.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">Thrown when no service is registered for the supplied <paramref name="masterListType"/>, or when the target service does not expose a compatible <c>GetMasterListById</c> method matching the expected parameters.</exception>
|
||||
public async Task<object?> GetMasterListById(MasterListType masterListType, ObjectId masterListId,
|
||||
LocaleEnum? dataLocale)
|
||||
LocaleEnum? dataLocale)
|
||||
{
|
||||
dynamic service = GetService(masterListType) ??
|
||||
throw new InvalidOperationException("Service not found for type " + masterListType);
|
||||
@@ -155,9 +203,20 @@ public class MasterListServiceFactory(
|
||||
return resultProperty?.GetValue(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a master list option by its identifier using reflection to dynamically resolve and invoke the
|
||||
/// appropriate service method based on the specified master list type.
|
||||
/// </summary>
|
||||
/// <param name="masterListType">The type of master list whose service implementation should be resolved.</param>
|
||||
/// <param name="masterListId">The identifier of the master list containing the option to retrieve.</param>
|
||||
/// <param name="masterListOptionId">The identifier of the specific option to look up within the master list.</param>
|
||||
/// <param name="dataLocale">Optional locale used to localize the returned data; falls back to <see cref="LocaleEnum.Default"/> when not provided.</param>
|
||||
/// <returns>A task that resolves to the located master list option as an <see cref="object"/>, or <see langword="null"/> if the underlying task produced no result.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when no service implementation is registered for the given <paramref name="masterListType"/>.</exception>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the resolved service does not expose the expected <c>FindOptionItemById</c> method.</exception>
|
||||
public async Task<object?> GetMasterListOptionById(MasterListType masterListType, ObjectId masterListId,
|
||||
ObjectId masterListOptionId,
|
||||
LocaleEnum? dataLocale)
|
||||
ObjectId masterListOptionId,
|
||||
LocaleEnum? dataLocale)
|
||||
{
|
||||
dynamic service = GetService(masterListType) ??
|
||||
throw new InvalidOperationException("Service not found for type " + masterListType);
|
||||
@@ -176,6 +235,12 @@ public class MasterListServiceFactory(
|
||||
return resultProperty?.GetValue(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of nurse observation names by reflecting over the configured <see cref="ListSettings"/> instance,
|
||||
/// collecting the <c>ManualObservationName</c> of every <see cref="ListSettingItem"/> property whose value is not null or empty,
|
||||
/// and appending the fixed observation "PatientIncomingData".
|
||||
/// </summary>
|
||||
/// <returns>A list of nurse observation names, including all configured manual observations and the fixed "PatientIncomingData" entry.</returns>
|
||||
public List<string> StringNurseObs()
|
||||
{
|
||||
var stringNurseObs = new List<string>();
|
||||
@@ -205,6 +270,15 @@ public class MasterListServiceFactory(
|
||||
return stringNurseObs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a patient with its option list fields translated to the specified locale, using the master list identifiers from the provided unit.
|
||||
/// If the patient is null, returns null; if the locale is Default or the unit is null, the patient is returned unchanged.
|
||||
/// Translations are applied to single or collection <see cref="OptionList"/> properties (origin, diagnosis, visits, allergies, procedures, etc.) by resolving each option's id against the matching master list and updating its <c>Name</c>.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit whose master list identifiers are used to resolve translations for each patient field.</param>
|
||||
/// <param name="locale">The target locale for translation; when set to <c>Default</c>, no translation is performed.</param>
|
||||
/// <param name="patient">The patient whose option list values will be translated in place.</param>
|
||||
/// <returns>A task that resolves to the same <see cref="Patient"/> instance with translated option names, or null if the input patient is null.</returns>
|
||||
public async Task<Patient?> GetPatientTraslated(Unit? unit, LocaleEnum? locale, Patient? patient)
|
||||
{
|
||||
if (patient == null)
|
||||
@@ -214,18 +288,18 @@ public class MasterListServiceFactory(
|
||||
return patient;
|
||||
|
||||
var listMap = new List<(string field, ObjectId? listId, MasterListType type)>
|
||||
{
|
||||
("origin", unit.OriginListId, MasterListType.OriginList),
|
||||
("diagnosis", unit.DiagnosisListId, MasterListType.DiagnosisList),
|
||||
("visits", unit.VisitOptionListId, MasterListType.VisitOptionList),
|
||||
("insulation", unit.InsulationListId, MasterListType.InsulationList),
|
||||
("languageBarrier", unit.LanguageBarrierListId, MasterListType.LanguageBarrierList),
|
||||
("allergies", unit.AllergyListId, MasterListType.AllergyList),
|
||||
("therapeuticCeiling", unit.TherapeuticCeilingListId, MasterListType.TherapeuticCeilingList),
|
||||
("tests", unit.TestListId, MasterListType.TestList),
|
||||
("procedures", unit.ProcedureListId, MasterListType.ProcedureList),
|
||||
("treatment", unit.TreatmentListId, MasterListType.TreatmentList)
|
||||
};
|
||||
{
|
||||
("origin", unit.OriginListId, MasterListType.OriginList),
|
||||
("diagnosis", unit.DiagnosisListId, MasterListType.DiagnosisList),
|
||||
("visits", unit.VisitOptionListId, MasterListType.VisitOptionList),
|
||||
("insulation", unit.InsulationListId, MasterListType.InsulationList),
|
||||
("languageBarrier", unit.LanguageBarrierListId, MasterListType.LanguageBarrierList),
|
||||
("allergies", unit.AllergyListId, MasterListType.AllergyList),
|
||||
("therapeuticCeiling", unit.TherapeuticCeilingListId, MasterListType.TherapeuticCeilingList),
|
||||
("tests", unit.TestListId, MasterListType.TestList),
|
||||
("procedures", unit.ProcedureListId, MasterListType.ProcedureList),
|
||||
("treatment", unit.TreatmentListId, MasterListType.TreatmentList)
|
||||
};
|
||||
|
||||
foreach (var (field, listId, masterListType) in listMap)
|
||||
{
|
||||
@@ -281,6 +355,12 @@ public class MasterListServiceFactory(
|
||||
return patient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps properties from a <see cref="MasterList"/> source instance to a destination object using reflection,
|
||||
/// copying only those properties whose name and type match between both objects.
|
||||
/// </summary>
|
||||
/// <param name="source">The <see cref="MasterList"/> instance whose property values are read.</param>
|
||||
/// <param name="destination">The target object that receives the matching property values.</param>
|
||||
private void MapMasterListProperties(MasterList source, object destination)
|
||||
{
|
||||
// Obtiene las propiedades del tipo de origen y destino
|
||||
@@ -288,12 +368,12 @@ public class MasterListServiceFactory(
|
||||
var destProps = destination.GetType().GetProperties();
|
||||
|
||||
foreach (var sourceProp in sourceProps)
|
||||
foreach (var destProp in destProps)
|
||||
if (destProp.Name == sourceProp.Name && destProp.PropertyType == sourceProp.PropertyType)
|
||||
{
|
||||
// Asigna el valor de la propiedad de origen a la propiedad de destino
|
||||
destProp.SetValue(destination, sourceProp.GetValue(source));
|
||||
break;
|
||||
}
|
||||
foreach (var destProp in destProps)
|
||||
if (destProp.Name == sourceProp.Name && destProp.PropertyType == sourceProp.PropertyType)
|
||||
{
|
||||
// Asigna el valor de la propiedad de origen a la propiedad de destino
|
||||
destProp.SetValue(destination, sourceProp.GetValue(source));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,26 +25,51 @@ public class MedicineService(
|
||||
{
|
||||
private readonly List<string> _notesIndicatingMedication = apiSettings.Value.NotesIndicatingMedication ?? [];
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all medicines from the repository asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of all <see cref="Medicine"/> entities.</returns>
|
||||
public async Task<List<Medicine>> GetAll()
|
||||
{
|
||||
return await medicineRepository.GetAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a medicine from the repository by its unique code, returning null if no matching medicine is found.
|
||||
/// </summary>
|
||||
/// <param name="code">The unique code identifier of the medicine to retrieve.</param>
|
||||
/// <returns>A <see cref="Medicine"/> instance if a medicine with the specified code exists; otherwise, null.</returns>
|
||||
public async Task<Medicine?> GetByCode(string code)
|
||||
{
|
||||
return await medicineRepository.GetMedicine(code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of medicines that match the provided codes or notes by delegating to the medicine repository.
|
||||
/// </summary>
|
||||
/// <param name="codeNote">A list of strings representing the codes or notes used to search for matching medicines.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of <see cref="Medicine"/> objects that match the specified codes or notes.</returns>
|
||||
public async Task<List<Medicine>> GetByCodeOrNote(List<string> codeNote)
|
||||
{
|
||||
return await medicineRepository.GetMedicineByCodeOrNote(codeNote);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Medicine"/> from the repository by its name.
|
||||
/// Returns <c>null</c> when no matching medicine is found.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the medicine to look up.</param>
|
||||
/// <returns>A <see cref="Medicine"/> if a match is found; otherwise, <c>null</c>.</returns>
|
||||
public async Task<Medicine?> GetByName(string name)
|
||||
{
|
||||
return await medicineRepository.GetMedicineByName(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the medicines associated with a collection of patient treatments, resolving each treatment's requested give codes against the medicine repository. Falls back to parental nutrition calculation when no medicine is found, and to note-based detection when notes indicate medication; medicines of type "Nutrition" returned by the repository are excluded except when produced through the parental nutrition path.
|
||||
/// </summary>
|
||||
/// <param name="treatments">The patient treatments to resolve medicines for. Null entries are skipped.</param>
|
||||
/// <returns>An enumerable of medicines resolved from the supplied treatments, aggregating types, codes, groups, and notes when a treatment specifies a <c>RequestedGiveTreatment</c> name.</returns>
|
||||
public async Task<IEnumerable<Medicine>> GetMedicinesOfTreatments(IEnumerable<PatientTreatment?> treatments)
|
||||
{
|
||||
var totalMedicines = new List<Medicine>();
|
||||
@@ -94,21 +119,21 @@ public class MedicineService(
|
||||
medicines =
|
||||
[
|
||||
new Medicine
|
||||
{
|
||||
Name = treatment.RequestedGiveTreatment,
|
||||
Type = medicines.FindAll(t => t.Type.Any())
|
||||
.Select(m => m.Type.Aggregate((x, y) => x + "," + y)).Distinct()
|
||||
.ToList(),
|
||||
Codes = medicines.FindAll(t => t.Codes.Any())
|
||||
.Select(m => m.Codes.Aggregate((x, y) => x + "," + y)).Distinct()
|
||||
.ToList(),
|
||||
Group = medicines.FindAll(t => t.Group.Any())
|
||||
.Select(m => m.Group.Aggregate((x, y) => x + "," + y)).Distinct()
|
||||
.ToList(),
|
||||
Notes = medicines.FindAll(t => t.Notes.Any())
|
||||
.Select(m => m.Notes.Aggregate((x, y) => x + "," + y)).Distinct()
|
||||
.ToList()
|
||||
}
|
||||
{
|
||||
Name = treatment.RequestedGiveTreatment,
|
||||
Type = medicines.FindAll(t => t.Type.Any())
|
||||
.Select(m => m.Type.Aggregate((x, y) => x + "," + y)).Distinct()
|
||||
.ToList(),
|
||||
Codes = medicines.FindAll(t => t.Codes.Any())
|
||||
.Select(m => m.Codes.Aggregate((x, y) => x + "," + y)).Distinct()
|
||||
.ToList(),
|
||||
Group = medicines.FindAll(t => t.Group.Any())
|
||||
.Select(m => m.Group.Aggregate((x, y) => x + "," + y)).Distinct()
|
||||
.ToList(),
|
||||
Notes = medicines.FindAll(t => t.Notes.Any())
|
||||
.Select(m => m.Notes.Aggregate((x, y) => x + "," + y)).Distinct()
|
||||
.ToList()
|
||||
}
|
||||
];
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -128,6 +153,11 @@ public class MedicineService(
|
||||
return totalMedicines.AsEnumerable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all active medicines associated with a patient by first resolving their active treatments and then collecting the medicines prescribed within them.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose active medicines are being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an enumerable collection of <see cref="Medicine"/> instances linked to the patient's active treatments.</returns>
|
||||
public async Task<IEnumerable<Medicine>> GetActiveMedicinesByPatient(ObjectId patientId)
|
||||
{
|
||||
var activeTreatments = await treatmentService.GetActiveTreatmentsByPatient(patientId);
|
||||
@@ -135,6 +165,11 @@ public class MedicineService(
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of medicines based on the specified pagination filter, including the total document count for the current query.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter containing the page number and page size used to determine which subset of medicines to return.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="PaginationResponse{Medicine}"/> with the medicines for the requested page, the current page number, the page size, and the total count of medicines matching the query.</returns>
|
||||
public async Task<PaginationResponse<Medicine>> GetPaginatedMedicines(PaginationFilter filter)
|
||||
{
|
||||
var result = medicineRepository.GetPaginatedMedicines(filter);
|
||||
@@ -152,12 +187,25 @@ public class MedicineService(
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a medicine by its unique identifier from the repository.
|
||||
/// Throws a <see cref="NotFoundException"/> when no medicine is found matching the provided identifier.
|
||||
/// </summary>
|
||||
/// <param name="medicineId">The unique identifier of the medicine to retrieve.</param>
|
||||
/// <returns>The <see cref="Medicine"/> entity if found; otherwise, the method throws an exception.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no medicine is found for the specified <paramref name="medicineId"/>.</exception>
|
||||
public async Task<Medicine?> GetMedicineById(ObjectId medicineId)
|
||||
{
|
||||
return await medicineRepository.GetMedicineById(medicineId) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new medicine record in the repository and records an audit log entry for the operation. Throws a conflict exception when the repository fails to produce a result.
|
||||
/// </summary>
|
||||
/// <param name="medicine">The medicine entity to create.</param>
|
||||
/// <returns>The newly created <see cref="Medicine"/> entity.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the repository returns a null result, indicating the creation failed.</exception>
|
||||
public async Task<Medicine?> PostMedicine(Medicine medicine)
|
||||
{
|
||||
var result = await medicineRepository.PostMedicine(medicine) ??
|
||||
@@ -166,6 +214,11 @@ public class MedicineService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing medicine record and records the change in the audit log.
|
||||
/// </summary>
|
||||
/// <param name="medicine">The medicine entity containing the updated information.</param>
|
||||
/// <returns>The updated <see cref="Medicine"/>, or <c>null</c> if the update did not produce a result.</returns>
|
||||
public async Task<Medicine?> UpdateMedicine(Medicine medicine)
|
||||
{
|
||||
var oldMedicine = GetMedicineById(medicine.Id);
|
||||
@@ -174,6 +227,11 @@ public class MedicineService(
|
||||
return newMedicine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a medicine identified by its unique identifier and records the operation in the audit log.
|
||||
/// Captures the existing medicine state prior to deletion to preserve audit trail details.
|
||||
/// </summary>
|
||||
/// <param name="medicineId">The unique identifier of the medicine to delete.</param>
|
||||
public async Task DeleteMedicineById(ObjectId medicineId)
|
||||
{
|
||||
var oldMedicine = GetMedicineById(medicineId);
|
||||
@@ -181,6 +239,10 @@ public class MedicineService(
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldMedicine, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all distinct medicine types from the repository. Returns an empty list if an exception occurs during the retrieval process.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of distinct medicine type strings, or an empty list if an error occurs.</returns>
|
||||
public async Task<List<string>> GetAllTypes()
|
||||
{
|
||||
try
|
||||
@@ -195,6 +257,11 @@ public class MedicineService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all distinct "group" values from the medicine repository and returns them as a list of strings.
|
||||
/// Returns an empty list if an error occurs while fetching or converting the data.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of distinct group strings, or an empty list if an exception is encountered.</returns>
|
||||
public async Task<List<string>> GetAllGroups()
|
||||
{
|
||||
try
|
||||
@@ -209,6 +276,10 @@ public class MedicineService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all distinct medicine names from the repository asynchronously, returning an empty list if any error occurs during the data retrieval or mapping process.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of medicine names, or an empty list if the operation fails.</returns>
|
||||
public async Task<List<string>> GetAllNames()
|
||||
{
|
||||
try
|
||||
@@ -223,6 +294,11 @@ public class MedicineService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all distinct code values from the medicine repository.
|
||||
/// Returns an empty list if an exception occurs during retrieval.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of code strings, or an empty list if an error occurs.</returns>
|
||||
public async Task<List<string>> GetAllCodes()
|
||||
{
|
||||
try
|
||||
@@ -238,6 +314,12 @@ public class MedicineService(
|
||||
}
|
||||
|
||||
//Exclusive for H12O, not in calculatedObservations of H12O to dont repeat code in multiple places.
|
||||
/// <summary>
|
||||
/// Calculates a <see cref="Medicine"/> representing the parental nutrition for a patient treatment, returning <c>null</c> when the treatment does not contain a note with the "NPT" comment.
|
||||
/// The medicine name is taken from the first note with the "formularybaseformulation" comment type, defaulting to "UNKNOWN" when absent, and its type is set to ParenteralNutritionLipids when a neonatal lipids note is present, otherwise ParenteralNutrition.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The patient treatment whose notes are used to derive the parental nutrition medicine.</param>
|
||||
/// <returns>A task containing the calculated <see cref="Medicine"/>, or <c>null</c> when no "NPT" note is found or when an error is logged during processing.</returns>
|
||||
private Task<Medicine?> CalculateParentalNutritionMedicine(PatientTreatment treatment)
|
||||
{
|
||||
Medicine? medicine = null;
|
||||
|
||||
@@ -20,11 +20,19 @@ public class NoticeService(
|
||||
ILocalAuditService auditService)
|
||||
: INoticeService
|
||||
{
|
||||
/// <summary>
|
||||
/// Deletes the specified notice by delegating to the ID-based deletion routine.
|
||||
/// </summary>
|
||||
/// <param name="notice">The notice to delete; its <c>Id</c> is used to identify the record to remove.</param>
|
||||
public async Task DeleteNoticeAsync(Notice notice)
|
||||
{
|
||||
await DeleteNoticeByIdAsync(notice.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a notice identified by its unique ID, creating an audit log entry and broadcasting the deletion when successful. If the notice is not found, the operation is logged and the method returns without making changes; any unexpected exception is logged without being rethrown.
|
||||
/// </summary>
|
||||
/// <param name="noticeId">The unique identifier of the notice to delete.</param>
|
||||
public async Task DeleteNoticeByIdAsync(ObjectId noticeId)
|
||||
{
|
||||
try
|
||||
@@ -48,6 +56,11 @@ public class NoticeService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a notice by its unique identifier asynchronously. Returns <c>null</c> if an error occurs during the lookup, with the exception being logged.
|
||||
/// </summary>
|
||||
/// <param name="noticeId">The unique <see cref="ObjectId"/> of the notice to retrieve.</param>
|
||||
/// <returns>A <see cref="Notice"/> instance if found; otherwise, <c>null</c> when an exception is thrown during the search.</returns>
|
||||
public async Task<Notice?> GetNoticeByIdAsync(ObjectId noticeId)
|
||||
{
|
||||
try
|
||||
@@ -61,17 +74,33 @@ public class NoticeService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a collection of notices filtered by the specified notice type.
|
||||
/// If no notices are found for the given type, a <see cref="NotFoundException"/> is thrown.
|
||||
/// </summary>
|
||||
/// <param name="noticeType">The type of notice to filter the search by.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of <see cref="Notice"/> objects matching the specified type.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no notices are found for the specified <paramref name="noticeType"/>.</exception>
|
||||
public async Task<IEnumerable<Notice>?> GetNoticeByTypeAsync(string noticeType)
|
||||
{
|
||||
return await noticeRepository.FindByType(noticeType) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all notices from the repository asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a collection of all <see cref="Notice"/> entities.</returns>
|
||||
public async Task<IEnumerable<Notice>> GetNoticesAsync()
|
||||
{
|
||||
return await noticeRepository.FindAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new notice, creates an audit log entry, and broadcasts a notification about the new notice. Returns null if an exception occurs during the operation.
|
||||
/// </summary>
|
||||
/// <param name="notice">The notice to insert. A new identifier is assigned before persistence.</param>
|
||||
/// <returns>The inserted notice with its newly assigned identifier, or null if the operation fails.</returns>
|
||||
public async Task<Notice?> InsertNotice(Notice notice)
|
||||
{
|
||||
try
|
||||
@@ -92,6 +121,12 @@ public class NoticeService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing notice, records an audit log entry, and broadcasts the update notification.
|
||||
/// If no notice is found by the supplied identifier, a conflict exception is thrown. Errors are caught and logged.
|
||||
/// </summary>
|
||||
/// <param name="notice">The notice entity containing the updated data.</param>
|
||||
/// <exception cref="ConflictException">Thrown when the notice cannot be found in the repository.</exception>
|
||||
public async Task UpdateNoticeAsync(Notice notice)
|
||||
{
|
||||
try
|
||||
@@ -108,6 +143,10 @@ public class NoticeService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes an API request to create, update, or delete a notice based on the request type. Validates required fields for new and updated notices, skips processing when the notice or its description is missing, and logs any exceptions encountered.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the notice payload and the operation type (NewNotice, UpdateNotice, or DeleteNotice) to perform.</param>
|
||||
public async Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
try
|
||||
@@ -118,34 +157,34 @@ public class NoticeService(
|
||||
switch (apiRequest.Type)
|
||||
{
|
||||
case "NewNotice":
|
||||
{
|
||||
if (string.IsNullOrEmpty(apiRequest.Notice.Description))
|
||||
{
|
||||
logger.LogDebug("Error saving notice api request. Some values are required. Notice: {notice}",
|
||||
apiRequest.Notice);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(apiRequest.Notice.Description))
|
||||
{
|
||||
logger.LogDebug("Error saving notice api request. Some values are required. Notice: {notice}",
|
||||
apiRequest.Notice);
|
||||
return;
|
||||
}
|
||||
|
||||
await InsertNotice(apiRequest.Notice);
|
||||
break;
|
||||
}
|
||||
await InsertNotice(apiRequest.Notice);
|
||||
break;
|
||||
}
|
||||
case "UpdateNotice":
|
||||
{
|
||||
if (string.IsNullOrEmpty(apiRequest.Notice.Description))
|
||||
{
|
||||
logger.LogDebug("Error updating notice api request. Some values are required. Notice: {notice}",
|
||||
apiRequest.Notice);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(apiRequest.Notice.Description))
|
||||
{
|
||||
logger.LogDebug("Error updating notice api request. Some values are required. Notice: {notice}",
|
||||
apiRequest.Notice);
|
||||
return;
|
||||
}
|
||||
|
||||
await UpdateNoticeAsync(apiRequest.Notice);
|
||||
break;
|
||||
}
|
||||
await UpdateNoticeAsync(apiRequest.Notice);
|
||||
break;
|
||||
}
|
||||
case "DeleteNotice":
|
||||
{
|
||||
await DeleteNoticeAsync(apiRequest.Notice);
|
||||
break;
|
||||
}
|
||||
{
|
||||
await DeleteNoticeAsync(apiRequest.Notice);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -154,11 +193,22 @@ public class NoticeService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves the specified API request by executing the save operation on a background thread.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to save.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a collection of notices associated with the specified display identifier.
|
||||
/// Returns null if an error occurs while querying the notice repository.
|
||||
/// </summary>
|
||||
/// <param name="displayId">The ObjectId of the display used to look up associated notices.</param>
|
||||
/// <returns>A task containing an <see cref="IEnumerable{T}"/> of <see cref="Notice"/> objects matching the display, or null if the operation fails.</returns>
|
||||
public async Task<IEnumerable<Notice>?> GetNoticesByDisplayId(ObjectId displayId)
|
||||
{
|
||||
try
|
||||
@@ -172,6 +222,12 @@ public class NoticeService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously broadcasts a notice to all subscribers associated with the notice's display.
|
||||
/// Logs an error and aborts the broadcast if the display cannot be resolved; any exception thrown while sending is caught and logged.
|
||||
/// </summary>
|
||||
/// <param name="notice">The notice to broadcast. Its <c>DisplayId</c> is used to resolve the target display and its subscribers.</param>
|
||||
/// <param name="operation">The operation type that identifies the kind of notice being broadcast and is forwarded to each subscriber.</param>
|
||||
private async void SendNoticeBroadcast(Notice notice, OperationType operation)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -13,6 +13,12 @@ public class ObservationDemoService(
|
||||
Lazy<IAlarmService> alarmService)
|
||||
: IObservationDemoService
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a list of patient observation alarms based on the provided alarm fields, randomly skipping fields and skipping those with no matching configuration.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to associate with the generated alarms.</param>
|
||||
/// <param name="dataAlarmfields">The list of fields used to look up alarm configurations and produce alarms.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of mapped <see cref="PatientObservationAlarm"/> instances that were successfully resolved.</returns>
|
||||
public async Task<List<PatientObservationAlarm>> GenerateAlarmByField(Patient patient, List<Field> dataAlarmfields)
|
||||
{
|
||||
var listToReturn = new List<PatientObservationAlarm>();
|
||||
@@ -35,7 +41,8 @@ public class ObservationDemoService(
|
||||
AlarmConfig = conf.Alarm,
|
||||
InactivationState = new InactivationState
|
||||
{
|
||||
Audio = AlarmEnum.AudioVideoState.Enabled, Acknowledge = true,
|
||||
Audio = AlarmEnum.AudioVideoState.Enabled,
|
||||
Acknowledge = true,
|
||||
Visual = AlarmEnum.AudioVideoState.Enabled
|
||||
},
|
||||
EventPhase = AlarmEnum.EventPhase.Continue,
|
||||
@@ -67,6 +74,12 @@ public class ObservationDemoService(
|
||||
return listToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a list of patient observations for the specified patient based on the provided data fields, using the corresponding configuration items to populate observation values, thresholds, and metadata. Fields without a name or without a matching configuration are skipped. When a configuration includes demo settings, the resulting observation is adjusted with a randomized initial date and, if required, an end time.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to associate the generated observations with.</param>
|
||||
/// <param name="dataFields">The list of fields used to look up observation configurations and drive observation generation.</param>
|
||||
/// <returns>A task that returns the list of generated <see cref="PatientObservation"/> instances; entries are omitted when no configuration is found for a field.</returns>
|
||||
public async Task<List<PatientObservation>> GenerateObservationByField(Patient patient, List<Field> dataFields)
|
||||
{
|
||||
var listToReturn = new List<PatientObservation>();
|
||||
@@ -135,6 +148,12 @@ public class ObservationDemoService(
|
||||
return listToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a <see cref="GroupedObservation"/> for the given patient and grouped field, producing time-spaced observations for each configured name and result type based on the field's regularity.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to associate the generated grouped observation with.</param>
|
||||
/// <param name="groupedField">The grouped field configuration that defines the names, results, regularity, and maximum number of observations to generate.</param>
|
||||
/// <returns>A task that returns the populated <see cref="GroupedObservation"/> containing the generated observations.</returns>
|
||||
public async Task<GroupedObservation> GenerateGroupedObservation(Patient patient, GroupedField groupedField)
|
||||
{
|
||||
if (groupedField.Names.IsNullOrEmpty() && groupedField.Name != null) groupedField.Names.Add(groupedField.Name);
|
||||
@@ -203,6 +222,12 @@ public class ObservationDemoService(
|
||||
return go;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random value (or list of values) for a given configuration, falling back to "-" when no configuration is available. When the configuration specifies an array, produces multiple random values using the configured count; otherwise returns a single random value.
|
||||
/// </summary>
|
||||
/// <param name="firstCof">The configuration observation whose <c>DemoConfig</c> drives the value generation. If null or its <c>DemoConfig</c> is null, the method returns "-".</param>
|
||||
/// <param name="prevValue">The previous value, passed through to <c>PickSingleValue</c> as context for the new random value generation.</param>
|
||||
/// <returns>A list of generated values when the configuration indicates an array, or a single generated value otherwise. Null is returned when no value could be picked.</returns>
|
||||
private static object? GenerateValueRandom(ConfigObservation? firstCof, object? prevValue)
|
||||
{
|
||||
if (firstCof?.DemoConfig == null)
|
||||
@@ -223,6 +248,12 @@ public class ObservationDemoService(
|
||||
return firstCof.DemoConfig?.ValueIsArray == true ? results : results.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Picks a single value from a <see cref="DemoConfig"/> following a defined priority: first selects randomly from a predefined list of options when <c>SetInValue</c> is enabled, otherwise generates a random numeric value within the configured range, optionally applying a percentage-based variation to a previous value. Returns <c>null</c> if the configuration is null, if the options list is empty, or if random value generation is disabled.
|
||||
/// </summary>
|
||||
/// <param name="config">The configuration that defines how the value should be selected, including the list of options, the numeric range, and the selection mode.</param>
|
||||
/// <param name="prevValue">The previously selected value, used as the base for percentage-based variation when generating a new numeric value; ignored when no previous integer value is available.</param>
|
||||
/// <returns>A randomly selected value from the configured options or a generated numeric value clamped within the defined range; <c>null</c> when the configuration does not allow a value to be produced.</returns>
|
||||
private static object? PickSingleValue(DemoConfig? config, object? prevValue)
|
||||
{
|
||||
if (config == null) return null;
|
||||
|
||||
@@ -20,11 +20,15 @@ using Patient = adas_core.Domain.Models.MongoModels.Patient;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the concrete implementation of the <see cref="IObservationService"/> contract,
|
||||
/// encapsulating the business logic required to manage and expose observation-related operations.
|
||||
/// </summary>
|
||||
public class ObservationService : IObservationService
|
||||
{
|
||||
private readonly ICacheService _cacheService;
|
||||
private readonly CacheSettings? _cacheSettings;
|
||||
|
||||
|
||||
private readonly IAlarmService _alarmService;
|
||||
private readonly List<string> _allergies;
|
||||
private readonly ILocalAuditService _auditService;
|
||||
@@ -91,7 +95,7 @@ public class ObservationService : IObservationService
|
||||
Lazy<ICalculatedObservationsService> calculatedObservationsService,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILocalAuditService auditService,
|
||||
IPointOfCareService pointOfCareService,
|
||||
IPointOfCareService pointOfCareService,
|
||||
ICacheService cacheService
|
||||
)
|
||||
|
||||
@@ -129,21 +133,35 @@ public class ObservationService : IObservationService
|
||||
_subscriberGroupedService = subscriberGroupedService;
|
||||
_calculatedObservationsService = calculatedObservationsService;
|
||||
_pointOfCareService = pointOfCareService;
|
||||
_cacheSettings = cacheSettings.Value;
|
||||
_cacheSettings = cacheSettings.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent observations for a specified patient by delegating to the observation repository's aggregation pipeline.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="num">The maximum number of recent observations to return. Defaults to 2.</param>
|
||||
/// <param name="filterObservations">An optional list of observation codes/names used to narrow down which observations are considered.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of the patient's most recent <see cref="PatientObservation"/> entries.</returns>
|
||||
public async Task<List<PatientObservation>> FindLastObservations(ObjectId patientId, int num = 2,
|
||||
List<string>? filterObservations = null)
|
||||
List<string>? filterObservations = null)
|
||||
{
|
||||
var result =
|
||||
await _observationRepository.AggregatedPatientLastObservations(patientId, num, filterObservations);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent aggregated patient observations, using a cache-aside pattern to avoid recomputing results within the configured TTL. Field names in <paramref name="filterObservations"/> are normalized (null or whitespace names are dropped) before being used to compute the cache key.
|
||||
/// </summary>
|
||||
/// <param name="patientId">Identifier of the patient whose latest observations are being requested.</param>
|
||||
/// <param name="filterObservations">Optional list of fields to filter the aggregated observations by; entries with null or whitespace names are ignored when building the cache key. When null, an empty field set is used.</param>
|
||||
/// <param name="ct">Cancellation token forwarded to the cache and repository operations.</param>
|
||||
/// <returns>A task containing the list of <see cref="PatientObservation"/> values, either served from cache or freshly aggregated from the repository on a cache miss.</returns>
|
||||
private async Task<List<PatientObservation>> AggregatedLastObsCached(
|
||||
ObjectId patientId,
|
||||
List<Field>? filterObservations,
|
||||
CancellationToken ct = default)
|
||||
ObjectId patientId,
|
||||
List<Field>? filterObservations,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
// Normalizar campos
|
||||
var fieldNames = (filterObservations ?? new())
|
||||
@@ -173,11 +191,20 @@ public class ObservationService : IObservationService
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent observations for a patient from the aggregated cache, optionally filtered by specific fields, and optionally enriched through a name-based mapping.
|
||||
/// When <paramref name="mapped"/> is <c>false</c>, the raw cached observations are returned directly; otherwise each observation is individually mapped and those that yield no result are excluded from the output.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose latest observations are being queried.</param>
|
||||
/// <param name="filterObservations">Optional list of fields used to restrict which observations are retrieved from the cache.</param>
|
||||
/// <param name="mapped">When <c>true</c> (default), applies a name-based mapping to each observation; when <c>false</c>, returns the raw results as they come from the cache.</param>
|
||||
/// <param name="ct">Cancellation token to cancel the asynchronous operation.</param>
|
||||
/// <returns>A task containing the list of patient observations, either as raw cached entries or as mapped values depending on <paramref name="mapped"/>.</returns>
|
||||
public async Task<List<PatientObservation>> FindLastObservationsByField(
|
||||
ObjectId patientId,
|
||||
List<Field>? filterObservations = null,
|
||||
bool mapped = true,
|
||||
CancellationToken ct = default)
|
||||
ObjectId patientId,
|
||||
List<Field>? filterObservations = null,
|
||||
bool mapped = true,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
//lista RAW desde la caché
|
||||
var raw = await AggregatedLastObsCached(patientId, filterObservations, ct);
|
||||
@@ -197,11 +224,22 @@ public class ObservationService : IObservationService
|
||||
return mappedList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the specified patient observation by name by delegating to the configuration observation service using the by-name mapping mode.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation to be mapped.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the mapped <see cref="PatientObservation"/>, or null if no matching mapping is found.</returns>
|
||||
public async Task<PatientObservation?> MapObservationsByName(PatientObservation obs)
|
||||
{
|
||||
return await _configObservationService.Map(obs, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PatientObservation"/> through a sequence of configuration, units, and calculated observations services to produce a fully mapped observation, returning <c>null</c> if any mapping step yields no result or if an error occurs.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation to be mapped.</param>
|
||||
/// <param name="onlyByName">When <c>true</c>, restricts the mapping to name-based lookups only.</param>
|
||||
/// <returns>A task containing the mapped <see cref="PatientObservation"/>, or <c>null</c> if the observation is ignored, not found, or an exception is raised during processing.</returns>
|
||||
public async Task<PatientObservation?> MapObservation(PatientObservation obs, bool onlyByName = false)
|
||||
{
|
||||
try
|
||||
@@ -297,6 +335,13 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a nurse observation by mapping it, persisting it via the observation repository,
|
||||
/// updating the latest-observations cache, broadcasting the change, and creating an audit log entry.
|
||||
/// If mapping returns a null result or an observation with a null name, the method logs the error and returns without inserting.
|
||||
/// Any exception thrown during the operation is caught and logged.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation provided by the nurse to be mapped, persisted, cached, broadcast, and audited.</param>
|
||||
public async Task InsertNurseObservation(PatientObservation obs)
|
||||
{
|
||||
try
|
||||
@@ -311,13 +356,13 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
|
||||
await _observationRepository.InsertOneAsync(obs2);
|
||||
|
||||
|
||||
var (key, ttl) = CacheKeys.LatestObservationsKeyWithTtl(
|
||||
_cacheSettings,
|
||||
obs2.PatientId,
|
||||
[obs2.Name]
|
||||
);
|
||||
|
||||
|
||||
// GET → MISS → LOCK → AGGREGATE → SET
|
||||
var result = _cacheService.GetOrSetObjectAsync(
|
||||
key,
|
||||
@@ -326,7 +371,7 @@ public class ObservationService : IObservationService
|
||||
return obs2;
|
||||
},
|
||||
ttl, default);
|
||||
|
||||
|
||||
_ = SendObsBroadcast(obs2);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, obs2);
|
||||
}
|
||||
@@ -336,8 +381,16 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new patient observation only when its value differs from the most recent observation recorded for the same observation name; otherwise the existing record is kept and no insertion is performed.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the observation used to look up the latest existing value for the patient.</param>
|
||||
/// <param name="observation">The patient observation to compare against the most recent value and to insert when a change is detected.</param>
|
||||
/// <param name="persistObs">Indicates whether the new observation should be persisted when inserted.</param>
|
||||
/// <param name="mapObs">Indicates whether the new observation should be mapped when inserted.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the observation was inserted because the value changed, or <c>false</c> if the most recent observation already has the same value and no insertion was made.</returns>
|
||||
public async Task<bool> InsertIfChanged(string name, PatientObservation observation, bool persistObs = true,
|
||||
bool mapObs = true)
|
||||
bool mapObs = true)
|
||||
{
|
||||
var changedList = await FindLastObservations(observation.PatientId, 1, [name]);
|
||||
var changed = changedList.All(o => observation.Value != o.Value);
|
||||
@@ -349,6 +402,11 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a list of patient observations to all display subscribers whose registered locations match the specified patient location by unit name, bed, and room. Subscribers without any registered locations are excluded from the broadcast.
|
||||
/// </summary>
|
||||
/// <param name="obsList">The list of patient observations to send to matching subscribers.</param>
|
||||
/// <param name="location">The patient location used to identify subscribers to notify.</param>
|
||||
public Task SendObsBroadcast(List<PatientObservation> obsList, PatientLocation location)
|
||||
{
|
||||
// Display Subscription
|
||||
@@ -361,12 +419,18 @@ public class ObservationService : IObservationService
|
||||
|
||||
|
||||
foreach (var subscriber in displaySubscribers)
|
||||
foreach (var obs in obsList)
|
||||
_ = _clientMessageService.SendAsync(subscriber.Id, OperationType.Observation, obs);
|
||||
foreach (var obs in obsList)
|
||||
_ = _clientMessageService.SendAsync(subscriber.Id, OperationType.Observation, obs);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a list of patient observations to all display subscribers whose location identifiers match the specified point of care identifier.
|
||||
/// Subscribers with null or empty location identifiers are excluded, and each observation is dispatched asynchronously to every matching subscriber.
|
||||
/// </summary>
|
||||
/// <param name="obsList">The collection of patient observations to be sent to the matched subscribers.</param>
|
||||
/// <param name="pocId">The point of care identifier used to filter the subscribers by their configured location identifiers.</param>
|
||||
public Task SendObsBroadcast(List<PatientObservation> obsList, ObjectId pocId)
|
||||
{
|
||||
// Display Subscription
|
||||
@@ -377,12 +441,17 @@ public class ObservationService : IObservationService
|
||||
|
||||
|
||||
foreach (var subscriber in displaySubscribers)
|
||||
foreach (var obs in obsList)
|
||||
_ = _clientMessageService.SendAsync(subscriber.Id, OperationType.Observation, obs);
|
||||
foreach (var obs in obsList)
|
||||
_ = _clientMessageService.SendAsync(subscriber.Id, OperationType.Observation, obs);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a patient observation to all display subscribers whose location matches the patient's point of care.
|
||||
/// The method skips the broadcast if the observation has no name, and falls back to looking up the patient by id when it is not included in the observation.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation to broadcast. May include the patient or require a lookup via <see cref="BasePatientObservation.PatientId"/>.</param>
|
||||
public async Task SendObsBroadcast(BasePatientObservation obs)
|
||||
{
|
||||
if (obs.Name == null) return;
|
||||
@@ -409,8 +478,18 @@ public class ObservationService : IObservationService
|
||||
_ = _clientMessageService.SendAsync(subscriber.Id, OperationType.Observation, obs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a batch of patient observations by attaching patient, parent data, and message metadata,
|
||||
/// then persists them asynchronously through the calculated observations mapping service. Missing
|
||||
/// observation or message timestamps default to <see cref="DateTime.UtcNow"/>, and any errors during
|
||||
/// processing are logged without being rethrown.
|
||||
/// </summary>
|
||||
/// <param name="observations">The list of patient observations to be processed and inserted.</param>
|
||||
/// <param name="patient">The patient to whom the observations belong.</param>
|
||||
/// <param name="messageTime">The timestamp associated with the source message.</param>
|
||||
/// <param name="observationData">Optional parent observation metadata used to populate the parent data of each observation.</param>
|
||||
public async void ProcessObservations(List<PatientObservation> observations, Patient patient,
|
||||
DateTime messageTime, ObservationData? observationData = null)
|
||||
DateTime messageTime, ObservationData? observationData = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -464,11 +543,25 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves nurse observations from the provided API request by executing the save operation on a background thread.
|
||||
/// </summary>
|
||||
/// <param name="request">The API request containing the nurse observation data to be persisted.</param>
|
||||
/// <returns>A task that represents the asynchronous nurse observation save operation.</returns>
|
||||
public Task SaveRequestNurseObsAsync(ApiRequest request)
|
||||
{
|
||||
return Task.Run(() => SaveRequestNurseObs(request));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes and persists an inbound medical API request (HL7), routing <c>ORU_R40</c> alerts to the alarm service
|
||||
/// and <c>ORU_R01</c> observations to the appropriate handler (intravenous lines, allergies, drainage, isolation,
|
||||
/// position, diagnosis, or generic observations) based on the observation code. Throws when both patient and
|
||||
/// location are missing or when the request type is not supported, and silently returns when no matching patient
|
||||
/// is found.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The incoming API request containing patient/location identifiers, message type, and observation data.</param>
|
||||
/// <exception cref="ApiRequestException">Thrown when both the patient number and location are null or empty, or when the request type is not valid for observations.</exception>
|
||||
public async Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
if (
|
||||
@@ -563,6 +656,11 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves the provided API request by executing the save operation on a background thread.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to be saved.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
//return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
|
||||
@@ -570,17 +668,35 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all <see cref="PatientObservation"/> records associated with the specified patient by delegating to the observation repository.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <returns>An <see cref="IAsyncCursor{PatientObservation}"/> that iterates over the matching patient observations.</returns>
|
||||
public async Task<IAsyncCursor<PatientObservation>> FindByPatientIdAsync(ObjectId patientId)
|
||||
{
|
||||
return await _observationRepository.FindByPatientIdAsync(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves patient observations filtered by the specified patient identifier, coding system, and name.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="codingSystem">The coding system used to classify the observations.</param>
|
||||
/// <param name="name">The name associated with the observations to filter by.</param>
|
||||
/// <returns>An asynchronous cursor over the matching <see cref="PatientObservation"/> documents.</returns>
|
||||
public async Task<IAsyncCursor<PatientObservation>> FindByPatientIdAndCodingSystemAsync(ObjectId patientId,
|
||||
string codingSystem, string name)
|
||||
string codingSystem, string name)
|
||||
{
|
||||
return await _observationRepository.FindByPatientIdAndCodingSystemAsync(patientId, codingSystem, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all observations associated with the specified patient identifier, clears the related cache entries, and records an audit log.
|
||||
/// If no observations exist for the given patient, the method returns without performing any deletion, cache invalidation, or audit logging.
|
||||
/// Any exception encountered during the process is logged and swallowed without being rethrown.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose observations should be deleted.</param>
|
||||
public async Task DeleteByPatientId(ObjectId id)
|
||||
{
|
||||
try
|
||||
@@ -594,7 +710,7 @@ public class ObservationService : IObservationService
|
||||
await _observationRepository.DeleteByPatientId(id);
|
||||
|
||||
await _cacheService.DeleteByPatternAsync(CacheKeyPatterns.ByPatient("patients:latestObs", id.ToString()));
|
||||
|
||||
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, deletedObservationList,
|
||||
null);
|
||||
}
|
||||
@@ -604,6 +720,10 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives a patient observation by persisting it to the archive repository, removing it from the active observations, and invalidating the related cache entries for the patient's latest observations.
|
||||
/// </summary>
|
||||
/// <param name="observation">The patient observation to be archived.</param>
|
||||
public async Task Archive(PatientObservation observation)
|
||||
{
|
||||
await _observationArchiveRepository.InsertOneAsync(observation);
|
||||
@@ -612,11 +732,19 @@ public class ObservationService : IObservationService
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives the specified patient by delegating to the archive operation keyed by the patient's identifier.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to be archived. Its identifier is used to locate and archive the corresponding record.</param>
|
||||
public async Task Archive(Patient patient)
|
||||
{
|
||||
await ArchiveByPatientId(patient.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives all observations associated with the specified patient by copying them into the archive repository with newly generated identifiers, then removes the originals and invalidates the related cache entries.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose observations should be archived.</param>
|
||||
public async Task ArchiveByPatientId(ObjectId id)
|
||||
{
|
||||
_logger.LogDebug("Archive Observations by Patient Id {id}", id);
|
||||
@@ -634,63 +762,123 @@ public class ObservationService : IObservationService
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent active intravenous lines observations for a patient, aggregated by location.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose intravenous lines observations are being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="PatientObservation"/> objects, which may include null entries, representing the latest active intravenous lines observations grouped by location.</returns>
|
||||
public async Task<List<PatientObservation?>> FindLastIntravenousLinesObservationByLocation(ObjectId patientId)
|
||||
{
|
||||
return await _observationRepository.AggregatedPatientActiveIntravenousLinesObservations(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent observation time for all patients by delegating to the observation repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a dictionary mapping patient <see cref="ObjectId"/> values to their last observation <see cref="DateTime"/>.</returns>
|
||||
public async Task<Dictionary<ObjectId, DateTime>> FindAllLastPatientObservationTime()
|
||||
{
|
||||
return await _observationRepository.FindAllLastPatientObservationTime();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing patient observation if it exists in the repository, invalidates the related cache entries, and records an audit log of the change.
|
||||
/// If no observation with the specified identifier is found, the method performs no action.
|
||||
/// </summary>
|
||||
/// <param name="observation">The patient observation containing the updated data to be persisted.</param>
|
||||
public async Task UpdateObservation(PatientObservation observation)
|
||||
{
|
||||
var obs = await _observationRepository.FindById(observation.Id);
|
||||
if (obs != null)
|
||||
{
|
||||
await _observationRepository.Update(observation);
|
||||
|
||||
await _cacheService.DeleteByPatternAsync(CacheKeyPatterns.ByPatient("patients:latestObs",observation.PatientId.ToString()));
|
||||
|
||||
await _cacheService.DeleteByPatternAsync(CacheKeyPatterns.ByPatient("patients:latestObs", observation.PatientId.ToString()));
|
||||
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, obs, observation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent patient observation recorded before the specified date, optionally filtered by observation name.
|
||||
/// Returns <c>null</c> when no matching observation exists.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observation history is being queried.</param>
|
||||
/// <param name="date">The upper bound date; only observations recorded prior to this date are considered.</param>
|
||||
/// <param name="obsName">The optional name of the observation to filter by. When <c>null</c>, observations of any name are considered.</param>
|
||||
/// <returns>The latest <see cref="PatientObservation"/> recorded before the specified date, or <c>null</c> if none was found.</returns>
|
||||
public async Task<PatientObservation?> FindLastBeforeDate(ObjectId patientId, DateTime date, string? obsName)
|
||||
{
|
||||
return await _observationRepository.FindLastObservationBeforeDate(patientId, obsName, date);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves any patient observations matching the specified patient, date, and optional observation name by delegating to the underlying observation repository.
|
||||
/// Returns null when no matching observations are found for the given criteria.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="date">The date used to find observations recorded on the same day.</param>
|
||||
/// <param name="obsName">The optional name of the observation to filter by; when null, observations of any name on the given date are considered.</param>
|
||||
/// <returns>A task that resolves to a list of matching <see cref="PatientObservation"/> instances, or null if no observations match the specified criteria.</returns>
|
||||
public async Task<List<PatientObservation>?> FindAnyWithSameDate(ObjectId patientId, DateTime date,
|
||||
string? obsName)
|
||||
string? obsName)
|
||||
{
|
||||
return await _observationRepository.FindAnyWithSameDate(patientId, obsName, date);
|
||||
}
|
||||
|
||||
//TODO To implement
|
||||
/// <summary>
|
||||
/// Retrieves all patient observations recorded before the specified date by delegating to the observation repository.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="date">The cutoff date; observations recorded before this date will be returned.</param>
|
||||
/// <param name="filterObservations">An optional list of observation identifiers to filter the results.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of <see cref="PatientObservation"/> entries found before the specified date.</returns>
|
||||
public async Task<List<PatientObservation>> FindAllBeforeDate(ObjectId patientId, DateTime date,
|
||||
List<string>? filterObservations = null)
|
||||
List<string>? filterObservations = null)
|
||||
{
|
||||
return await _observationRepository.FindAnyBeforeDate(patientId, date);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the latest unique observation values for a specified patient and observation name, delegating the lookup to the underlying observation repository.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="name">The name of the observation to search for.</param>
|
||||
/// <param name="expires">An optional expiration value (in seconds) applied to the query.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of the latest unique <see cref="PatientObservation"/> values.</returns>
|
||||
public async Task<List<PatientObservation>> FindLatestUniqueValuesByName(ObjectId patientId, string name,
|
||||
int? expires)
|
||||
int? expires)
|
||||
{
|
||||
return await _observationRepository.FindLatestUniqueValuesByName(patientId, name, expires);
|
||||
}
|
||||
|
||||
//TODO To implement
|
||||
/// <summary>
|
||||
/// Retrieves all patient observations recorded after the specified date, optionally filtered by a list of observation types.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being retrieved.</param>
|
||||
/// <param name="date">The cutoff date; only observations recorded after this date will be returned.</param>
|
||||
/// <param name="filterObservations">An optional list of observation identifiers used to narrow the returned results.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of patient observations matching the criteria.</returns>
|
||||
/// <exception cref="NotImplementedException">Thrown when the method is invoked, as the implementation has not yet been provided.</exception>
|
||||
public Task<List<PatientObservation>> FindAllAfterDate(ObjectId patientId, DateTime date,
|
||||
List<string>? filterObservations = null)
|
||||
List<string>? filterObservations = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent non-expired patient observations matching the specified name, optionally filtered by an end-after threshold and limited in count.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="name">The name of the observation to filter by.</param>
|
||||
/// <param name="endAfter">Optional threshold used to restrict which observations are considered; if null, no end-after filter is applied.</param>
|
||||
/// <param name="num">Optional maximum number of observations to return; if null, all matching observations are returned.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing an enumerable collection of matching <see cref="PatientObservation"/> instances.</returns>
|
||||
public async Task<IEnumerable<PatientObservation>> FindLastNotExpiredObservatonsByPatient(ObjectId patientId,
|
||||
string name, int? endAfter = null, int? num = null)
|
||||
string name, int? endAfter = null, int? num = null)
|
||||
{
|
||||
return await _observationRepository.FindLastNotExpiredObservatonsByPatient(patientId, name, endAfter, num);
|
||||
}
|
||||
@@ -721,6 +909,10 @@ public class ObservationService : IObservationService
|
||||
_logger.LogDebug("found observations {count} ", count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the repository to mark a list of patient observations as expired, invalidates the corresponding cache entries, and creates audit log entries capturing the pre-update state of each observation.
|
||||
/// </summary>
|
||||
/// <param name="patientObservations">The list of patient observations to be marked as expired.</param>
|
||||
public async Task UpdateExpiredObservations(List<PatientObservation> patientObservations)
|
||||
{
|
||||
await _observationRepository.UpdateExpiredObservations(patientObservations);
|
||||
@@ -735,6 +927,11 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves observations that are currently marked as not expired but should be expired based on their configured expiration thresholds.
|
||||
/// For each candidate observation, the patient is validated; missing patients trigger cleanup of their observations and cached entries. Observations with missing names or unparsable expiration values are skipped, and only those whose expected expiration time (observation time plus configured minutes) has passed are yielded.
|
||||
/// </summary>
|
||||
/// <returns>An asynchronous stream of <see cref="PatientObservation"/> instances that are not expired in storage but whose effective expiration time has elapsed.</returns>
|
||||
public async IAsyncEnumerable<PatientObservation> FindNotExpiredObservationsShouldBeExpired()
|
||||
{
|
||||
var expiringObservations = await _configObservationService.GetAllConfigs();
|
||||
@@ -765,7 +962,7 @@ public class ObservationService : IObservationService
|
||||
|
||||
continue;
|
||||
}
|
||||
if(current.Name == null) continue;
|
||||
if (current.Name == null) continue;
|
||||
await _configObservationService.GetConfigObservationItemsByName(current.Name);
|
||||
var configObs = expiringObservations?
|
||||
.FirstOrDefault();
|
||||
@@ -781,6 +978,9 @@ public class ObservationService : IObservationService
|
||||
_logger.LogDebug("expired observations retrieved {count} observations", count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all observation configurations and expires those whose <c>Expires</c> value is set to a positive number, ignoring configurations with a null or non-positive expiry.
|
||||
/// </summary>
|
||||
public async Task ExpireObservations()
|
||||
{
|
||||
//TODO expire each section
|
||||
@@ -792,6 +992,9 @@ public class ObservationService : IObservationService
|
||||
.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expires patient observations that should no longer be active and recalculates the latest observation values per configured field. Sets a global flag while running to signal that expiration is in progress, processes expirations in batches of 1000 to limit memory usage, and clears the patient observations cache once finished; any error is logged and swallowed without rethrowing.
|
||||
/// </summary>
|
||||
public async Task ExpireObservationsAndRecalculateAsync()
|
||||
{
|
||||
try
|
||||
@@ -821,7 +1024,7 @@ public class ObservationService : IObservationService
|
||||
lastPatientObservationsByName)
|
||||
await InsertObservation(obs, false, false); //Not really insert, only makes calcs
|
||||
}
|
||||
|
||||
|
||||
var obsToExpireList = new List<PatientObservation>();
|
||||
var i = 0;
|
||||
var count = 0;
|
||||
@@ -844,7 +1047,7 @@ public class ObservationService : IObservationService
|
||||
obsToExpireList.Add(current);
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
await _cacheService.DeleteByPatternAsync(CacheKeyPatterns.ForEntity(CacheEnum.EntityType.PatientObservations));
|
||||
|
||||
_logger.LogDebug("number of expired observations should be expired:{count}", count);
|
||||
@@ -858,25 +1061,45 @@ public class ObservationService : IObservationService
|
||||
GlobalData.AddData("isCheckingExpiration", false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates multiple observation records by replacing the specified old object identifier with a new one for the given name identifier.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name identifier of the field whose value should be updated across matching records.</param>
|
||||
/// <param name="id">The new <see cref="ObjectId"/> value to assign to the matching records.</param>
|
||||
/// <param name="oldId">The existing <see cref="ObjectId"/> value to be replaced.</param>
|
||||
public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
|
||||
{
|
||||
await _observationRepository.UpdateManyObjectId(nameId, id, oldId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a simple patient observation into the repository and records an audit log entry for the operation using the current HTTP context user.
|
||||
/// </summary>
|
||||
/// <param name="observation">The patient observation to insert.</param>
|
||||
public async Task InsertSimpleObservation(PatientObservation observation)
|
||||
{
|
||||
await _observationRepository.InsertOneAsync(observation);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, observation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of patient observations for a specific patient within an optional date range, optionally filtering by observation names and supporting both active and archived collections.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
|
||||
/// <param name="startDate">The inclusive lower bound of the observation time range. Falls back to <see cref="DateTime.MinValue"/> when null.</param>
|
||||
/// <param name="endDate">The exclusive upper bound of the observation time range. Falls back to <see cref="DateTime.MaxValue"/> when null.</param>
|
||||
/// <param name="filterObservations">Optional list of observation names to restrict the results to. When null or empty, no name-based filter is applied.</param>
|
||||
/// <param name="fromArchived">When true, queries the archived observation collection; otherwise, queries the active observation collection.</param>
|
||||
/// <param name="filter">Optional pagination settings controlling the page number and page size of the returned results.</param>
|
||||
/// <returns>A task that resolves to the list of <see cref="PatientObservation"/> records matching the specified criteria.</returns>
|
||||
public async Task<List<PatientObservation>> FindAllBetweenDates(
|
||||
ObjectId patientId,
|
||||
DateTime? startDate = null,
|
||||
DateTime? endDate = null,
|
||||
List<string>? filterObservations = null,
|
||||
bool fromArchived = false,
|
||||
PaginationFilter? filter = null
|
||||
)
|
||||
ObjectId patientId,
|
||||
DateTime? startDate = null,
|
||||
DateTime? endDate = null,
|
||||
List<string>? filterObservations = null,
|
||||
bool fromArchived = false,
|
||||
PaginationFilter? filter = null
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -887,13 +1110,13 @@ public class ObservationService : IObservationService
|
||||
var filterBuilder = Builders<PatientObservation>.Filter;
|
||||
|
||||
var conditions = new List<FilterDefinition<PatientObservation>>
|
||||
{
|
||||
filterBuilder.Eq(o => o.PatientId, patientId),
|
||||
filterBuilder.Ne(o => o.Name, null),
|
||||
//filterBuilder.In(o => o.Name, filterObservations ?? new List<string?>()),
|
||||
filterBuilder.Gt(o => o.Time, startDate ?? DateTime.MinValue),
|
||||
filterBuilder.Lt(o => o.Time, endDate ?? DateTime.MaxValue)
|
||||
};
|
||||
{
|
||||
filterBuilder.Eq(o => o.PatientId, patientId),
|
||||
filterBuilder.Ne(o => o.Name, null),
|
||||
//filterBuilder.In(o => o.Name, filterObservations ?? new List<string?>()),
|
||||
filterBuilder.Gt(o => o.Time, startDate ?? DateTime.MinValue),
|
||||
filterBuilder.Lt(o => o.Time, endDate ?? DateTime.MaxValue)
|
||||
};
|
||||
|
||||
if (filterObservations != null && filterObservations.Any())
|
||||
conditions.Add(filterBuilder.In(o => o.Name, filterObservations));
|
||||
@@ -920,6 +1143,10 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously processes alert expiration and powers off beacon LEDs for points of care that are not currently in use.
|
||||
/// Skips patients located in virtual/moved/deleted/pushed/unknown locations and ignores emulated beacons and configurations with disabled alarms.
|
||||
/// </summary>
|
||||
public async Task ExpireAlertsAndPowerOffAsync()
|
||||
{
|
||||
try
|
||||
@@ -1001,6 +1228,11 @@ public class ObservationService : IObservationService
|
||||
GlobalData.AddData("isCheckingAlertsExpiration", false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of patient observations from the repository, returning both the requested page of data and the total document count to support client-side pagination.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter that specifies the page number and page size used to compute the skip/limit range.</param>
|
||||
/// <returns>A <see cref="PaginationResponse{PatientObservation}"/> containing the page of patient observations along with pagination metadata.</returns>
|
||||
public async Task<PaginationResponse<PatientObservation>> GetPaginatedObservations(PaginationFilter filter)
|
||||
{
|
||||
var result = _observationRepository.GetPaginatedObservations(filter);
|
||||
@@ -1018,6 +1250,13 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes the configured retention policy actions for a given patient observation, deleting older entries
|
||||
/// based on the resolved policy (days, seconds, count, or none) and auditing the deleted observations. The
|
||||
/// method performs an early return when the retention configuration is unavailable, the policy value is missing,
|
||||
/// or the observation name is empty, and logs any errors that occur during processing.
|
||||
/// </summary>
|
||||
/// <param name="obs">The patient observation used to resolve the retention policy and identify which records to delete.</param>
|
||||
private async Task DoRetentionActions(PatientObservation obs)
|
||||
{
|
||||
try
|
||||
@@ -1046,7 +1285,7 @@ public class ObservationService : IObservationService
|
||||
|
||||
foreach (var observation in deletedObs)
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, observation, null);
|
||||
|
||||
|
||||
await _cacheService.DeleteByPatternAsync(CacheKeyPatterns.ByPatient("patients:latestObs", obs.PatientId.ToString()));
|
||||
|
||||
}
|
||||
@@ -1056,6 +1295,13 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a patient observation by identifying matching subscriber groups, regenerating the grouped
|
||||
/// observation, updating the group's last grouped observation, notifying all group members, and
|
||||
/// clearing the related cache entries. Skips groups where the observation name is not contained
|
||||
/// in the group's names or where the group does not consider the observation relevant.
|
||||
/// </summary>
|
||||
/// <param name="obs">The incoming patient observation used to find and update matching groups.</param>
|
||||
private async void CheckForGroupedObs(PatientObservation obs)
|
||||
{
|
||||
try
|
||||
@@ -1097,9 +1343,9 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
|
||||
//wsg.TimerReestart();
|
||||
|
||||
|
||||
await _cacheService.DeleteByPatternAsync(CacheKeys.GroupedObs(obs.PatientId, obs.Name ?? string.Empty));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -1109,6 +1355,11 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes an intravenous line observation for a patient from the provided API request, extracting catheter type and location from the observation text and mapping additional observations to insertion, removal, and duration details. The observation is only persisted when the line status is recognized as <c>Insertado</c> (Inserted) or <c>Retirado</c> (Removed); otherwise the method logs an error or exits without inserting.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the observation text, parent data, message time, and additional observations used to build the intravenous line record.</param>
|
||||
/// <param name="patient">The patient to associate the resulting observation with.</param>
|
||||
private async Task ProcessIntravenousLinesObservation(ApiRequest apiRequest, Patient patient)
|
||||
{
|
||||
var isInsertable = false;
|
||||
@@ -1128,7 +1379,8 @@ public class ObservationService : IObservationService
|
||||
PatientId = patient.Id,
|
||||
ParentData = new ParentDataClass
|
||||
{
|
||||
Code = apiRequest.ObservationData?.Code, CodingSystem = apiRequest.ObservationData?.CodingSystem
|
||||
Code = apiRequest.ObservationData?.Code,
|
||||
CodingSystem = apiRequest.ObservationData?.CodingSystem
|
||||
},
|
||||
MessageTime = apiRequest.MessageTime
|
||||
};
|
||||
@@ -1187,6 +1439,13 @@ public class ObservationService : IObservationService
|
||||
await InsertObservation(obs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes allergies observation data from the API request and persists it as a patient observation.
|
||||
/// Validates that observation data and timestamps are present, maps SNOMED-coded allergy entries to allergy types, values, and notes,
|
||||
/// and short-circuits when the patient reports "no known allergies" (Sin alergias conocidas).
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the observation data and coded allergy entries to process.</param>
|
||||
/// <param name="patient">The patient associated with the allergies observation being recorded.</param>
|
||||
private async Task ProcessAllergiesObservation(ApiRequest apiRequest, Patient patient)
|
||||
{
|
||||
_logger.LogDebug("INSERT AllergiesObservation");
|
||||
@@ -1291,6 +1550,11 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Processes an isolation observation when the incoming request contains an observation with the text "Aislamiento" and a non-null timestamp, normalizing the value by replacing semicolons with commas and persisting it as a <c>PatientObservation</c> under the "Isolation" name and "ADAS" coding system. If the observation value is null, an error is logged and the method returns without inserting.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The incoming API request whose <c>ObservationData</c> is inspected for the isolation marker text and timestamp.</param>
|
||||
/// <param name="patient">The patient associated with the observation, used to assign the patient identifier to the new record.</param>
|
||||
private async Task ProcessIsolationObservation(ApiRequest apiRequest, Patient patient)
|
||||
{
|
||||
if (apiRequest.ObservationData is { Text: "Aislamiento", Time: not null })
|
||||
@@ -1317,6 +1581,13 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a postural changes observation (<c>CAMBIOS POSTURALES</c>) from the API request, mapping it to a <see cref="PatientObservation"/> entry and persisting it.
|
||||
/// Falls back to the single <c>Observation</c> or the first item of <c>Observations</c> when the observation data value is empty, and skips processing when the value or time is missing.
|
||||
/// Replaces semicolons with commas in the value before insertion to ensure proper formatting.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The incoming API request containing the observation data, observations collection, and message timestamp to be processed.</param>
|
||||
/// <param name="patient">The patient associated with the observation; its identifier is stored in the resulting <see cref="PatientObservation"/>.</param>
|
||||
private async Task ProcessPositionObservation(ApiRequest apiRequest, Patient patient)
|
||||
{
|
||||
if (apiRequest.ObservationData != null &&
|
||||
@@ -1359,6 +1630,11 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Processes and persists a drainage observation for the given patient, mapping incoming observation codes to drainage-specific properties such as type, height, location, and volume. Validates that required observation data and the value object are present before building and inserting the observation; logs a warning and exits early if validation fails.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the observation data, time, and the list of observations to be processed.</param>
|
||||
/// <param name="patient">The patient associated with the drainage observation being recorded.</param>
|
||||
private async Task ProcessDrainageObservation(ApiRequest apiRequest, Patient patient)
|
||||
{
|
||||
_logger.LogDebug("INSERT DrainageObservation");
|
||||
@@ -1432,6 +1708,13 @@ public class ObservationService : IObservationService
|
||||
* Las obs que se insertan de forma manual desde nurse deben seguir la logica contraria a las obs
|
||||
* recibidas desde el censo
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Saves manual nurse observations following the logic opposite to that of observations received from the census.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing the data required to locate the patient and the observations to be saved.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="apiRequest"/> is null.</exception>
|
||||
public async Task SaveRequestNurseObs(ApiRequest apiRequest)
|
||||
{
|
||||
var patient = await _patientService.FindPatientByApiRequest(apiRequest);
|
||||
@@ -1451,8 +1734,15 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Stops manual recordings for a patient when there are no active recording alarms, based on the patient's point of care configuration and the observation configuration list.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose recordings are being evaluated.</param>
|
||||
/// <param name="configs">The list of point of care configurations used to locate the configuration associated with the patient.</param>
|
||||
/// <param name="obsConfigList">The observation configurations from which the recording end-after time is derived, falling back to <paramref name="defaultValue"/> when no recording alarms are configured.</param>
|
||||
/// <param name="defaultValue">The default end-after value applied when no observation configuration specifies a recording alarm.</param>
|
||||
private async Task CheckRecordings(Patient patient, List<PointOfCare> configs,
|
||||
IEnumerable<ConfigObservation> obsConfigList, int defaultValue)
|
||||
IEnumerable<ConfigObservation> obsConfigList, int defaultValue)
|
||||
{
|
||||
//paramos grabación si no hay alarmas activas y hay una grabación
|
||||
var recordingEndAfter = obsConfigList
|
||||
@@ -1502,8 +1792,17 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the relay state for a patient based on observation configurations and active alarms, and powers off
|
||||
/// the configured relays when no active "ADAS_ALARM" observations remain within the configured end-after window.
|
||||
/// Falls back to the provided default value when no observation configurations define an <c>OpenDoor</c> alarm,
|
||||
/// and skips relay control when the patient has no associated point of care.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose relay state is being evaluated; must have a valid <c>PointOfCareId</c>.</param>
|
||||
/// <param name="obsConfigList">Collection of observation configurations used to determine the relay end-after threshold via the <c>OpenDoor</c> alarm.</param>
|
||||
/// <param name="defaultValue">Fallback value used for the relay end-after threshold when no <c>OpenDoor</c> alarm configuration is present.</param>
|
||||
private async Task CheckRelay(Patient patient,
|
||||
IEnumerable<ConfigObservation> obsConfigList, int defaultValue)
|
||||
IEnumerable<ConfigObservation> obsConfigList, int defaultValue)
|
||||
{
|
||||
var relayEndAfter = obsConfigList
|
||||
.Where(c => c.Alarm is { OpenDoor: not null })
|
||||
@@ -1542,8 +1841,14 @@ public class ObservationService : IObservationService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the patient has any active beacon alarms and, if none are found and the patient is assigned to a point of care, powers off the beacon LED.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose beacon state is being evaluated; its identifier and point of care assignment are used to locate recent observations and target the beacon.</param>
|
||||
/// <param name="obsConfigList">The list of observation configurations used to determine the maximum beacon end-after value from the alarms that are both enabled and have their beacon enabled, falling back to <paramref name="defaultValue"/> when no configuration matches or the beacon is null.</param>
|
||||
/// <param name="defaultValue">The fallback value used for the beacon end-after period when no configuration provides a value or when the matching configuration's beacon is null.</param>
|
||||
private async Task CheckBeacon(Patient patient,
|
||||
IEnumerable<ConfigObservation> obsConfigList, int defaultValue)
|
||||
IEnumerable<ConfigObservation> obsConfigList, int defaultValue)
|
||||
{
|
||||
var beaconEndAfter = obsConfigList
|
||||
.Where(c => c.Alarm is { Enabled: true, Beacon.Enabled: true })
|
||||
|
||||
@@ -9,6 +9,10 @@ using MongoDB.Bson;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides operations for managing patient care plans, implementing the contract defined by <see cref="IPatientCarePlanService"/>.
|
||||
/// Serves as the concrete service layer responsible for handling care plan related business logic.
|
||||
/// </summary>
|
||||
public class PatientCarePlanService : IPatientCarePlanService
|
||||
{
|
||||
private readonly IArchivePatientCarePlanService _archivePatientCarePlanService;
|
||||
@@ -35,95 +39,129 @@ public class PatientCarePlanService : IPatientCarePlanService
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of patient care plans associated with the specified patient identifier.
|
||||
/// Returns an empty list if an error occurs while querying the repository.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose care plans are to be retrieved.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing a list of <see cref="PatientCarePlan"/> for the patient, or an empty list if an error occurs.</returns>
|
||||
public async Task<List<PatientCarePlan>> FindByPatientId(ObjectId patientId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _patientCarePlanRepository.FindByPatientId(patientId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error finding Patient care plan by patientId: {PatientId} message: {Message}", patientId,
|
||||
e.Message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<PatientCarePlan>> FindByUserId(ObjectId userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _patientCarePlanRepository.FindByUserId(userId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error finding Patient care plan by userId: {UserId} message: {Message}", userId,
|
||||
e.Message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<PatientCarePlan>> FindAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _patientCarePlanRepository.FindAll();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error finding all Patient care plan message: {Message}", e.Message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InsertOneAsync(PatientCarePlan patientCarePlan)
|
||||
{
|
||||
try
|
||||
{
|
||||
patientCarePlan.Time = DateTime.UtcNow;
|
||||
await _patientCarePlanRepository.InsertOneAsync(patientCarePlan);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, patientCarePlan);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error InsertOneAsync Patient care plan message: {Message} trace: {Trace}", e.Message,
|
||||
e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ArchiveCarePlanFromJob(Patient patientWithFinishedProcedure, List<OptionList> itemsToArchive)
|
||||
{
|
||||
var systemUser = await _userRepository.GetOrCreateSystemUser();
|
||||
foreach (var item in itemsToArchive)
|
||||
{
|
||||
var itemToArchive = new PatientCarePlan
|
||||
try
|
||||
{
|
||||
PatientId = patientWithFinishedProcedure.Id,
|
||||
PatientNumber = patientWithFinishedProcedure.PatientNumber,
|
||||
PointOfCareId = patientWithFinishedProcedure.PointOfCareId,
|
||||
UserId = systemUser.Id,
|
||||
CarePlan = item,
|
||||
Description = "Archive item expired from job",
|
||||
Action = ActionsEnum.CrudAction.Archive,
|
||||
Time = DateTime.UtcNow
|
||||
};
|
||||
await _patientCarePlanRepository.InsertOneAsync(itemToArchive);
|
||||
return await _patientCarePlanRepository.FindByPatientId(patientId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error finding Patient care plan by patientId: {PatientId} message: {Message}", patientId,
|
||||
e.Message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ArchiveByPatientId(ObjectId patientid)
|
||||
{
|
||||
var carePlansToArchive = await FindByPatientId(patientid);
|
||||
await _archivePatientCarePlanService.InsertManyAsync(carePlansToArchive);
|
||||
foreach (var item in carePlansToArchive)
|
||||
/// <summary>
|
||||
/// Retrieves the list of patient care plans associated with the specified user identifier. If an error occurs during retrieval, the error is logged and an empty list is returned as a fallback.
|
||||
/// </summary>
|
||||
/// <param name="userId">The unique identifier of the user whose patient care plans are being retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientCarePlan"/> objects for the user, or an empty list if an error occurs.</returns>
|
||||
public async Task<List<PatientCarePlan>> FindByUserId(ObjectId userId)
|
||||
{
|
||||
await _patientCarePlanRepository.DeleteAsync(item.Id);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, item, null);
|
||||
try
|
||||
{
|
||||
return await _patientCarePlanRepository.FindByUserId(userId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error finding Patient care plan by userId: {UserId} message: {Message}", userId,
|
||||
e.Message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all patient care plans from the repository, returning an empty list if an error occurs while fetching the data.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientCarePlan"/> objects, or an empty list if the retrieval fails.</returns>
|
||||
public async Task<List<PatientCarePlan>> FindAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _patientCarePlanRepository.FindAll();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error finding all Patient care plan message: {Message}", e.Message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new patient care plan into the repository, stamping it with the current UTC time, and records an audit log entry for the operation. Any errors encountered during the insertion or audit logging are caught and logged without rethrowing.
|
||||
/// </summary>
|
||||
/// <param name="patientCarePlan">The patient care plan entity to be inserted into the repository.</param>
|
||||
public async Task InsertOneAsync(PatientCarePlan patientCarePlan)
|
||||
{
|
||||
try
|
||||
{
|
||||
patientCarePlan.Time = DateTime.UtcNow;
|
||||
await _patientCarePlanRepository.InsertOneAsync(patientCarePlan);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, patientCarePlan);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error InsertOneAsync Patient care plan message: {Message} trace: {Trace}", e.Message,
|
||||
e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates archive records in the patient care plan repository for the specified care plan items that have expired from a job, attributing the action to the system user.
|
||||
/// </summary>
|
||||
/// <param name="patientWithFinishedProcedure">The patient associated with the finished procedure whose care plan items are being archived.</param>
|
||||
/// <param name="itemsToArchive">The list of care plan options to archive.</param>
|
||||
public async Task ArchiveCarePlanFromJob(Patient patientWithFinishedProcedure, List<OptionList> itemsToArchive)
|
||||
{
|
||||
var systemUser = await _userRepository.GetOrCreateSystemUser();
|
||||
foreach (var item in itemsToArchive)
|
||||
{
|
||||
var itemToArchive = new PatientCarePlan
|
||||
{
|
||||
PatientId = patientWithFinishedProcedure.Id,
|
||||
PatientNumber = patientWithFinishedProcedure.PatientNumber,
|
||||
PointOfCareId = patientWithFinishedProcedure.PointOfCareId,
|
||||
UserId = systemUser.Id,
|
||||
CarePlan = item,
|
||||
Description = "Archive item expired from job",
|
||||
Action = ActionsEnum.CrudAction.Archive,
|
||||
Time = DateTime.UtcNow
|
||||
};
|
||||
await _patientCarePlanRepository.InsertOneAsync(itemToArchive);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives all care plans associated with the specified patient by moving them to the archive service, removing them from the active repository, and creating audit log entries for each archived record.
|
||||
/// </summary>
|
||||
/// <param name="patientid">The identifier of the patient whose care plans will be archived.</param>
|
||||
public async Task ArchiveByPatientId(ObjectId patientid)
|
||||
{
|
||||
var carePlansToArchive = await FindByPatientId(patientid);
|
||||
await _archivePatientCarePlanService.InsertManyAsync(carePlansToArchive);
|
||||
foreach (var item in carePlansToArchive)
|
||||
{
|
||||
await _patientCarePlanRepository.DeleteAsync(item.Id);
|
||||
await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, item, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates multiple patient care plan records by replacing the specified old object identifier with a new one for the given patient.
|
||||
/// </summary>
|
||||
/// <param name="patientid">The unique identifier of the patient whose care plan records will be updated.</param>
|
||||
/// <param name="patientId">The new object identifier that will replace the old identifier in the matching care plan records.</param>
|
||||
/// <param name="oldId">The existing object identifier to be replaced by the new identifier.</param>
|
||||
public async Task UpdateManyObjectId(string patientid, ObjectId patientId, ObjectId oldId)
|
||||
{
|
||||
await _patientCarePlanRepository.UpdateManyObjectId(patientid, patientId, oldId);
|
||||
}
|
||||
{
|
||||
await _patientCarePlanRepository.UpdateManyObjectId(patientid, patientId, oldId);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,14 @@ public class PermissionService(
|
||||
private readonly ILogger<PermissionService> _logger = logger;
|
||||
private readonly PermissionSettings _permissionsConfig = permissionsConfig.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the display permission configuration for the specified user based on their authorization roles.
|
||||
/// Loads the user's authorities from the repository if not already cached, matches the display or its unit against the authorities, and maps the role to the corresponding display permission set (Guest, Admin, Developer, DoctorChief, Doctor, NursingSupervisor, or Nurse).
|
||||
/// </summary>
|
||||
/// <param name="display">The display whose permissions are being resolved; matched against the user's authorized display and unit identifiers.</param>
|
||||
/// <param name="user">The user whose authorizations and roles are evaluated to determine the applicable display permissions.</param>
|
||||
/// <returns>A <see cref="Task{DisplayPermissionTypes}"/> containing the display permission configuration corresponding to the matched role.</returns>
|
||||
/// <exception cref="ForbbidenException">Thrown when the user has no authorizations available, or when none of the user's authorities match the given display or its unit.</exception>
|
||||
public async Task<DisplayPermissionTypes> GetPermissionsForDisplay(Display display, User user)
|
||||
{
|
||||
var authorities = user.Authorization;
|
||||
@@ -71,6 +79,14 @@ public class PermissionService(
|
||||
throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the permission set associated with a specific unit for the given user, based on the user's role within that unit.
|
||||
/// Iterates the user's authorizations to find a matching unit, parses the role, and returns the corresponding configured permissions (Guest, Admin, Developer, DoctorChief, Doctor, NursingSupervisor, or Nurse).
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose permissions should be resolved.</param>
|
||||
/// <param name="user">The user whose authorizations and role are used to determine the permissions for the unit.</param>
|
||||
/// <returns>A <see cref="Task{DisplayPermissionTypes}"/> that resolves to the permission configuration matching the user's role for the specified unit.</returns>
|
||||
/// <exception cref="ForbbidenException">Thrown when the user has no authorizations, or when no authorization entry matches the provided <paramref name="unitId"/>.</exception>
|
||||
public Task<DisplayPermissionTypes> GetPermissionsForUnit(string unitId, User user)
|
||||
{
|
||||
var authorities = user.Authorization;
|
||||
@@ -111,40 +127,53 @@ public class PermissionService(
|
||||
throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
|
||||
}
|
||||
|
||||
/*
|
||||
private async Task<DisplayPermissionTypes?> CheckPermissions(DisplayPermissionTypes guestUnit, string authUnitId)
|
||||
{
|
||||
var isParsed = ObjectId.TryParse(authUnitId, out var unitId);
|
||||
if(isParsed)
|
||||
/*
|
||||
private async Task<DisplayPermissionTypes?> CheckPermissions(DisplayPermissionTypes guestUnit, string authUnitId)
|
||||
{
|
||||
var unit = await uniService.Value.FindById(unitId);
|
||||
var unitConfiguration = unit?.Configuration;
|
||||
if (unitConfiguration != null)
|
||||
{
|
||||
guestUnit.Admissions.Execute = unitConfiguration.ManualAdmit;
|
||||
var isParsed = ObjectId.TryParse(authUnitId, out var unitId);
|
||||
if(isParsed)
|
||||
{
|
||||
var unit = await uniService.Value.FindById(unitId);
|
||||
var unitConfiguration = unit?.Configuration;
|
||||
if (unitConfiguration != null)
|
||||
{
|
||||
guestUnit.Admissions.Execute = unitConfiguration.ManualAdmit;
|
||||
|
||||
guestUnit.Discharges.Execute = unitConfiguration.ManualDischarge;
|
||||
guestUnit.Discharges.Execute = unitConfiguration.ManualDischarge;
|
||||
|
||||
guestUnit.DemographicData.Create = unitConfiguration.ManualEdit;
|
||||
guestUnit.DemographicData.Delete = unitConfiguration.ManualEdit;
|
||||
guestUnit.DemographicData.Update = unitConfiguration.ManualEdit;
|
||||
guestUnit.DemographicData.Execute = unitConfiguration.ManualEdit;
|
||||
}
|
||||
guestUnit.DemographicData.Create = unitConfiguration.ManualEdit;
|
||||
guestUnit.DemographicData.Delete = unitConfiguration.ManualEdit;
|
||||
guestUnit.DemographicData.Update = unitConfiguration.ManualEdit;
|
||||
guestUnit.DemographicData.Execute = unitConfiguration.ManualEdit;
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("Permissions for unit {unitId}: {@permissions}", authUnitId, guestUnit);
|
||||
|
||||
return guestUnit;
|
||||
}
|
||||
*/
|
||||
|
||||
_logger.LogInformation("Permissions for unit {unitId}: {@permissions}", authUnitId, guestUnit);
|
||||
|
||||
return guestUnit;
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the panel permissions for the specified user, including their assigned authorities.
|
||||
/// </summary>
|
||||
/// <param name="user">The username of the user whose panel permissions are being requested.</param>
|
||||
/// <returns>The <see cref="PanelPermissionTypes"/> that apply to the user based on their authorities.</returns>
|
||||
/// <exception cref="ForbbidenException">Thrown when no user is found with the specified username.</exception>
|
||||
public async Task<PanelPermissionTypes> GetPermissionsForPanel(string user)
|
||||
{
|
||||
var userFound = await userRepository.Value.GetByUserName(user);
|
||||
if(userFound == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
|
||||
if (userFound == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
|
||||
userFound.Authorization = await authorityRepository.Value.GetUserAuthorities(userFound.Id);
|
||||
return GetPermissionsForPanel(userFound);
|
||||
}
|
||||
/// <summary>
|
||||
/// Retrieves the panel permissions assigned to a user based on their authorization role.
|
||||
/// Iterates through the user's authorities, identifies the first entry with panel authorization, and maps the role to the corresponding <see cref="PanelPermissionTypes"/> configuration (Guest, Admin, Developer, DoctorChief, Doctor, NursingSupervisor, or Nurse).
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose panel permissions are being resolved; must carry authorization data.</param>
|
||||
/// <returns>The <see cref="PanelPermissionTypes"/> associated with the matched role.</returns>
|
||||
/// <exception cref="ForbbidenException">Thrown when the user has no authorities or no authority grants panel access.</exception>
|
||||
public PanelPermissionTypes GetPermissionsForPanel(User user)
|
||||
{
|
||||
var authorities = user.Authorization;
|
||||
@@ -178,8 +207,18 @@ public class PermissionService(
|
||||
throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified user has access to a given display, based on the user's role, authorities, and the display record.
|
||||
/// Returns false if the display identifier is not a valid ObjectId, or if either the user or the display cannot be found.
|
||||
/// The user's authorization set is loaded from the authority repository before evaluating access through role checks against the display.
|
||||
/// </summary>
|
||||
/// <param name="username">The username used to look up the user attempting to access the display.</param>
|
||||
/// <param name="userRole">The role of the user, used in the role-based access evaluation against the display.</param>
|
||||
/// <param name="source">The source permission context used when evaluating access to the display.</param>
|
||||
/// <param name="displayId">The string identifier of the display; must be parseable as an ObjectId or access is denied.</param>
|
||||
/// <returns>A task that resolves to true if the user's role and authorities grant access to the specified display; otherwise, false.</returns>
|
||||
public async Task<bool> HasAccessToDisplay(string username, PermissionEnum.RolesType userRole,
|
||||
PermissionEnum.SourcePermissionsEnum source, string displayId)
|
||||
PermissionEnum.SourcePermissionsEnum source, string displayId)
|
||||
{
|
||||
if (!ObjectId.TryParse(displayId, out var displayObjectId)) return false;
|
||||
|
||||
@@ -193,8 +232,17 @@ public class PermissionService(
|
||||
return SearchRoleInDisplay(display, authorities, userRole);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified user has access to a given unit based on their role and assigned authorities.
|
||||
/// Returns false when the user cannot be found by username; otherwise delegates the final check to the role-in-unit search using the retrieved authorities.
|
||||
/// </summary>
|
||||
/// <param name="username">The username of the user whose access is being verified.</param>
|
||||
/// <param name="userRole">The role type to be matched against the user's authorities for the target unit.</param>
|
||||
/// <param name="source">The source permission context associated with the access evaluation.</param>
|
||||
/// <param name="unitId">The identifier of the unit to check access against.</param>
|
||||
/// <returns>A task that resolves to true if the user has the required access to the unit; otherwise, false.</returns>
|
||||
public async Task<bool> HasAccessToUnit(string username, PermissionEnum.RolesType userRole,
|
||||
PermissionEnum.SourcePermissionsEnum source, string unitId)
|
||||
PermissionEnum.SourcePermissionsEnum source, string unitId)
|
||||
{
|
||||
var user = await userRepository.Value.GetByUserName(username);
|
||||
|
||||
@@ -205,8 +253,15 @@ public class PermissionService(
|
||||
return SearchRoleInUnit(unitId, authorities, userRole);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified user has access to a panel by resolving their authorities and checking the required role. Returns false if the user is not found.
|
||||
/// </summary>
|
||||
/// <param name="username">The username of the user to check.</param>
|
||||
/// <param name="userRole">The role required to access the panel.</param>
|
||||
/// <param name="source">The source permission context used for the access check.</param>
|
||||
/// <returns>true if the user exists and has the required role within the panel; otherwise, false.</returns>
|
||||
public async Task<bool> HasAccessToPanel(string username, PermissionEnum.RolesType userRole,
|
||||
PermissionEnum.SourcePermissionsEnum source)
|
||||
PermissionEnum.SourcePermissionsEnum source)
|
||||
{
|
||||
var user = await userRepository.Value.GetByUserName(username);
|
||||
|
||||
@@ -218,8 +273,16 @@ public class PermissionService(
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified role is present in the authorizations matched by the display's ID, its unit ID, or found via a recursive unit search.
|
||||
/// Returns <c>false</c> when the authorities list is null or empty, and parses each authorization's role string into the <see cref="PermissionEnum.RolesType"/> enum for comparison.
|
||||
/// </summary>
|
||||
/// <param name="display">The display whose identifiers are used to match authorizations.</param>
|
||||
/// <param name="authorities">The list of authorizations to search; a null or empty value causes the method to return <c>false</c>.</param>
|
||||
/// <param name="role">The role to find within the matched authorizations or the display's unit.</param>
|
||||
/// <returns><c>true</c> if the role is found in any matched authorization or in the display's unit; otherwise, <c>false</c>.</returns>
|
||||
private static bool SearchRoleInDisplay(Display display, List<Authorization>? authorities,
|
||||
PermissionEnum.RolesType role)
|
||||
PermissionEnum.RolesType role)
|
||||
{
|
||||
if (authorities is not { Count: > 0 }) return false;
|
||||
|
||||
@@ -234,6 +297,13 @@ public class PermissionService(
|
||||
).Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the supplied authorities list for a specific role assigned to the given unit. Returns <c>false</c> when the authorities list is <c>null</c>, or when no matching entry is found.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit to match against the authorities entries.</param>
|
||||
/// <param name="authorities">The list of authorizations to inspect; if <c>null</c>, the method short-circuits and returns <c>false</c>.</param>
|
||||
/// <param name="role">The role type to look for within the authorities of the specified unit.</param>
|
||||
/// <returns><c>true</c> if an authority exists for <paramref name="unitId"/> with a role equal to <paramref name="role"/>; otherwise, <c>false</c>.</returns>
|
||||
private static bool SearchRoleInUnit(string unitId, List<Authorization>? authorities, PermissionEnum.RolesType role)
|
||||
{
|
||||
if (authorities == null)
|
||||
@@ -248,6 +318,14 @@ public class PermissionService(
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether any authorization entry in the provided list grants the specified role for panel access.
|
||||
/// Returns <c>false</c> when the authorities list is <c>null</c>, when an entry has no role mapping, or when no matching role is found with panel authorization enabled.
|
||||
/// </summary>
|
||||
/// <param name="authorities">The collection of <see cref="Authorization"/> entries to search; may be <c>null</c>.</param>
|
||||
/// <param name="role">The role to look for within the panel-authorized entries.</param>
|
||||
/// <returns><c>true</c> if at least one entry has <c>PanelAuthorization</c> enabled and matches the specified role; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown when an entry's <c>Rol</c> value cannot be parsed into a valid <see cref="PermissionEnum.RolesType"/>.</exception>
|
||||
private static bool SearchRoleInPanel(List<Authorization>? authorities, PermissionEnum.RolesType role)
|
||||
{
|
||||
if (authorities == null)
|
||||
|
||||
@@ -7,6 +7,10 @@ using Microsoft.Extensions.Options;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a proof-of-concept implementation of the IPoCMappingService interface,
|
||||
/// delivering the mapping functionality defined by that contract.
|
||||
/// </summary>
|
||||
public class PoCMappingService : IPoCMappingService
|
||||
{
|
||||
private readonly string _key;
|
||||
|
||||
@@ -31,8 +31,13 @@ public class PointOfCareService(
|
||||
IOptions<CacheSettings> cacheSettings)
|
||||
: IPointOfCareService
|
||||
{
|
||||
private readonly CacheSettings? _cacheSettings = cacheSettings.Value ;
|
||||
|
||||
private readonly CacheSettings? _cacheSettings = cacheSettings.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new point of care after validating that the referenced unit exists, and records an audit log entry for the operation. Returns null if the associated unit cannot be found or if an exception is raised during the insertion process.
|
||||
/// </summary>
|
||||
/// <param name="pointOfCare">The point of care to be inserted; its <c>UnitId</c> is validated against the existing unit and reassigned to the resolved unit's identifier.</param>
|
||||
/// <returns>A task that resolves to the newly inserted <see cref="PointOfCare"/>, or <c>null</c> when the referenced unit is not found or the operation fails.</returns>
|
||||
public async Task<PointOfCare?> InsertPointOfCare(PointOfCare pointOfCare)
|
||||
{
|
||||
try
|
||||
@@ -57,40 +62,67 @@ public class PointOfCareService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a Point of Care record by its identifier, but only when the record has no associated admission.
|
||||
/// The deletion is skipped if the record cannot be found or if it is linked to an admission.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the Point of Care record to delete.</param>
|
||||
public async Task Delete(ObjectId id)
|
||||
{
|
||||
var poc = await FindById(id);
|
||||
if (poc is not { AdmissionId: null }) return;
|
||||
|
||||
|
||||
await pointOfCareRepository.Delete(id);
|
||||
|
||||
|
||||
await cacheService.DeleteByPatternAsync(CacheKeys.PointOfCareBase(id));
|
||||
|
||||
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, poc, null);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all Points of Care associated with the specified unit identifier and removes the corresponding cached entries.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose Points of Care records will be removed.</param>
|
||||
public async Task DeletePoCsByUnitId(ObjectId unitId)
|
||||
{
|
||||
await pointOfCareRepository.DeleteManyByUnitId(unitId);
|
||||
|
||||
|
||||
await cacheService.DeleteByPatternAsync(CacheKeyPatterns.ForEntity(CacheEnum.EntityType.PontOfCare));
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the set of camera identifiers that are currently in use by delegating to the point of care repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a <see cref="HashSet{ObjectId}"/> of camera identifiers in use.</returns>
|
||||
public async Task<HashSet<ObjectId>> FindAllIdCamerasInUse()
|
||||
{
|
||||
return await pointOfCareRepository.FindAllIdCamerasInUse();
|
||||
}
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the set of all ID relay identifiers currently in use by delegating to the point of care repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="HashSet{T}"/> of <see cref="ObjectId"/> values representing the ID relays that are in use.</returns>
|
||||
public async Task<HashSet<ObjectId>> FindAllIdRelaysInUse()
|
||||
{
|
||||
return await pointOfCareRepository.FindAllIdRelaysInUse();
|
||||
}
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the set of beacon identifiers that are currently in use from the point of care repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="HashSet{ObjectId}"/> with the identifiers of all beacons in use.</returns>
|
||||
public async Task<HashSet<ObjectId>> FindAllIdBeaconsInUse()
|
||||
{
|
||||
return await pointOfCareRepository.FindAllIdBeaconsInUse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all points of care associated with the specified unit, including their associated devices.
|
||||
/// Returns an empty collection when no results are found by the repository.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose points of care are being queried.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a collection of <see cref="PointOfCare"/> with their devices, or an empty collection if the repository returns no results.</returns>
|
||||
public async Task<IEnumerable<PointOfCare>?> FindAllByUnitIdWithDevices(ObjectId unitId)
|
||||
{
|
||||
var result = await pointOfCareRepository.FindAllByUnitIdWithDevices(unitId);
|
||||
@@ -99,6 +131,12 @@ public class PointOfCareService(
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing point of care record, refreshes the related cache entries, creates an audit log entry for the change, and broadcasts the update.
|
||||
/// Returns the updated point of care, or <c>null</c> if the updated record cannot be retrieved after the update.
|
||||
/// </summary>
|
||||
/// <param name="pointOfCare">The point of care entity containing the updated information to persist.</param>
|
||||
/// <returns>The updated <see cref="PointOfCare"/>, or <c>null</c> if the record was not found after the update.</returns>
|
||||
public async Task<PointOfCare?> Update(PointOfCare pointOfCare)
|
||||
{
|
||||
var oldPoc = await pointOfCareRepository.FindById(pointOfCare.Id);
|
||||
@@ -112,38 +150,63 @@ public class PointOfCareService(
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the unit associated with an existing point of care identified by the given id. If no point of care is found, the method returns without changes; otherwise it persists the new unit, invalidates the related cache entries, records an audit log of the change, and broadcasts the update.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the point of care to update.</param>
|
||||
/// <param name="unit">The unit to assign to the point of care.</param>
|
||||
public async Task UpdateUnit(ObjectId id, Unit unit)
|
||||
{
|
||||
var poc = await FindById(id);
|
||||
if (poc == null)
|
||||
return;
|
||||
await pointOfCareRepository.UpdateUnitId(id, unit);
|
||||
|
||||
|
||||
await cacheService.DeleteByPatternAsync(CacheKeys.PointOfCareBase(id));
|
||||
|
||||
|
||||
var newPoc = await FindById(id);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, poc, newPoc!);
|
||||
SendPointOfCareBroadcast(poc, OperationType.UpdatedPointOfCare);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves all points of care from the repository, returning an empty list when the repository yields a null result.
|
||||
/// </summary>
|
||||
/// <returns>A task representing the asynchronous operation, containing the list of points of care, or an empty list if none are available.</returns>
|
||||
public async Task<List<PointOfCare>> GetAll()
|
||||
{
|
||||
var c = await pointOfCareRepository.GetAll();
|
||||
return c ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all point of care configurations from the repository.
|
||||
/// Returns an empty list if the repository result is null.
|
||||
/// </summary>
|
||||
/// <returns>A list of PointOfCare configurations, or an empty list when no configurations are available.</returns>
|
||||
public async Task<List<PointOfCare>> GetAllConfigs()
|
||||
{
|
||||
var c = await pointOfCareRepository.GetAllConfigs();
|
||||
return c ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all point-of-care location information from the repository.
|
||||
/// Returns an empty list when the repository yields no results, ensuring callers never receive a null collection.
|
||||
/// </summary>
|
||||
/// <returns>A task that resolves to a list of <see cref="PointOfCare"/> entries, or an empty list if no locations are found.</returns>
|
||||
public async Task<List<PointOfCare>> GetAllLocationInfo()
|
||||
{
|
||||
var c = await pointOfCareRepository.GetAllLocationInfo();
|
||||
return c ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of Points of Care (PoCs) based on the provided pagination filter, along with the total document count.
|
||||
/// Applies skip and limit operations to return only the items corresponding to the requested page.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination filter containing the page number and page size used to compute the skip offset and limit the number of returned items.</param>
|
||||
/// <returns>A <see cref="PaginationResponse{PointOfCare}"/> containing the requested page of points of care, the current page number, page size, and the total document count.</returns>
|
||||
public async Task<PaginationResponse<PointOfCare>> GetPaginatedPoCs(PaginationFilter filter)
|
||||
{
|
||||
var result = pointOfCareRepository.GetPaginatedPoCs(filter);
|
||||
@@ -157,27 +220,50 @@ public class PointOfCareService(
|
||||
return new PaginationResponse<PointOfCare>(dataList, filter.PageNumber, filter.PageSize, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Point of Care configuration for the specified identifier, invalidates the associated cache entries, and records an audit log of the change.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the Point of Care configuration to update.</param>
|
||||
/// <param name="configuration">The new configuration values to persist.</param>
|
||||
/// <returns>A task that represents the asynchronous update operation.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when no existing Point of Care configuration is found for the specified identifier.</exception>
|
||||
public async Task UpdateConfiguration(ObjectId id, PointOfCareConfiguration configuration)
|
||||
{
|
||||
var old = await pointOfCareRepository.GetPoCConfiguration(id) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
await pointOfCareRepository.UpdateConfiguration(id, configuration);
|
||||
|
||||
|
||||
await cacheService.DeleteByPatternAsync(CacheKeys.PointOfCareBase(id));
|
||||
|
||||
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, old.Configuration,
|
||||
configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a point of care by its identifier, including all associated configurations.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the point of care to retrieve.</param>
|
||||
/// <returns>The matching <see cref="PointOfCare"/> with all configurations, or <c>null</c> if no point of care is found.</returns>
|
||||
public async Task<PointOfCare?> FindById(ObjectId id)
|
||||
{
|
||||
return await pointOfCareRepository.FindByIdAllConfig(id);
|
||||
}
|
||||
/// <summary>
|
||||
/// Retrieves a point of care entity by its identifier, including all associated configuration data.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the point of care to retrieve.</param>
|
||||
/// <returns>The matching <see cref="PointOfCare"/> with all configuration when found; otherwise, <c>null</c>.</returns>
|
||||
public async Task<PointOfCare?> FindByIdAllConfig(ObjectId id)
|
||||
{
|
||||
return await pointOfCareRepository.FindByIdAllConfig(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all points of care associated with the specified unit identifier.
|
||||
/// Returns an empty collection when the underlying repository yields no results, instead of propagating a null reference.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unique identifier of the unit whose points of care should be retrieved.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of points of care for the given unit, or an empty collection if none are found.</returns>
|
||||
public async Task<IEnumerable<PointOfCare>?> FindAllByUnitId(ObjectId unit)
|
||||
{
|
||||
var result = await pointOfCareRepository.FindAllByUnitId(unit);
|
||||
@@ -185,8 +271,15 @@ public class PointOfCareService(
|
||||
return result ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the points of care associated with the specified unit that match the given status, optionally excluding virtual points of care.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The identifier of the unit whose points of care should be retrieved.</param>
|
||||
/// <param name="poc">The point of care status used to filter the results.</param>
|
||||
/// <param name="excludeVirtual">When set to <c>true</c>, virtual points of care are excluded from the returned collection.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the collection of points of care matching the specified unit and status.</returns>
|
||||
public async Task<IEnumerable<PointOfCare>> FindByUnitAndStatus(ObjectId unitId, StatusEnum.PointOfCare poc,
|
||||
bool excludeVirtual = false)
|
||||
bool excludeVirtual = false)
|
||||
{
|
||||
return await pointOfCareRepository.FindByUnitAndStatus(unitId, poc, excludeVirtual);
|
||||
}
|
||||
@@ -200,7 +293,7 @@ public class PointOfCareService(
|
||||
try
|
||||
{
|
||||
if (patientLocation == null) return;
|
||||
var pocToCheck = await GetInfo(patientLocation.Value,null);
|
||||
var pocToCheck = await GetInfo(patientLocation.Value, null);
|
||||
if (pocToCheck != null && pocToCheck.Status != StatusEnum.PointOfCare.Locked)
|
||||
{
|
||||
if (pocToCheck.AdmissionId != null)
|
||||
@@ -234,12 +327,23 @@ public class PointOfCareService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="PointOfCare"/> by bed and unit identifier. Returns <c>null</c> when the unit identifier is <c>null</c> or the bed is <c>null</c> or empty.
|
||||
/// </summary>
|
||||
/// <param name="bed">The bed identifier used to locate the point of care.</param>
|
||||
/// <param name="unitId">The unit identifier; when <c>null</c>, the method short-circuits and returns <c>null</c>.</param>
|
||||
/// <returns>A <see cref="Task{T}"/> containing the matching <see cref="PointOfCare"/>, or <c>null</c> if no input is valid or no record is found.</returns>
|
||||
public async Task<PointOfCare?> FindByBedAndUnitId(string? bed, ObjectId? unitId)
|
||||
{
|
||||
if (unitId == null || string.IsNullOrEmpty(bed)) return null;
|
||||
return await pointOfCareRepository.FindByBedAndUnitId(bed, unitId.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the relay configuration for the specified Point of Care, refreshing the related cache and recording an audit log entry when a relay ID list is provided.
|
||||
/// The update, cache invalidation, and audit logging are only performed when <see cref="PointOfCare.Configuration"/> and its <c>RelayIdList</c> are not null.
|
||||
/// </summary>
|
||||
/// <param name="poc">The Point of Care whose relay configuration will be updated.</param>
|
||||
public async Task UpdateRelayConfig(PointOfCare poc)
|
||||
{
|
||||
var oldPoc = await pointOfCareRepository.GetPoCConfiguration(poc.Id);
|
||||
@@ -251,22 +355,44 @@ public class PointOfCareService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a collection of points of care associated with the specified room by delegating to the repository.
|
||||
/// </summary>
|
||||
/// <param name="room">The room identifier used to look up matching points of care.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an <see cref="IEnumerable{PointOfCare}"/> of points of care for the given room, or <c>null</c> if no matching points of care are found.</returns>
|
||||
public async Task<IEnumerable<PointOfCare>?> FindByRoom(string room)
|
||||
{
|
||||
return await pointOfCareRepository.FindByRoom(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the collection of points of care associated with the specified bed.
|
||||
/// </summary>
|
||||
/// <param name="bed">The bed identifier used to look up the associated points of care.</param>
|
||||
/// <returns>A task that returns an <see cref="IEnumerable{PointOfCare}"/> of points of care linked to the given bed, or <c>null</c> if no points of care are found.</returns>
|
||||
public async Task<IEnumerable<PointOfCare>?> FindByBed(string bed)
|
||||
{
|
||||
return await pointOfCareRepository.FindByBed(bed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all points of care associated with the specified unit identifiers.
|
||||
/// </summary>
|
||||
/// <param name="unitIds">The list of unit identifiers used to filter the points of care.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the list of matching points of care.</returns>
|
||||
public async Task<List<PointOfCare>> FindAllByUnitIds(List<ObjectId> unitIds)
|
||||
{
|
||||
var filter = Builders<PointOfCare>.Filter.In(p => p.UnitId, unitIds);
|
||||
return await pointOfCareRepository.FindByFilter(filter);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="PointOfCare"/> by its identifier, enriching it with related unit, patient, and admission data.
|
||||
/// Returns <c>null</c> when no point of care matches the supplied id. When <paramref name="fillPatientData"/> is <c>true</c>, the associated patient and, if present, admission are loaded and attached to the result; the related unit's name is always resolved when available.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the point of care to retrieve.</param>
|
||||
/// <param name="fillPatientData">If <c>true</c>, loads and attaches the related patient and admission (when an admission id is present) to the returned point of care.</param>
|
||||
/// <returns>A task that yields the <see cref="PointOfCare"/> with resolved related data, or <c>null</c> if no point of care is found for the given id.</returns>
|
||||
public async Task<PointOfCare?> GetInfo(ObjectId id, bool fillPatientData = true)
|
||||
{
|
||||
var poc = await FindById(id);
|
||||
@@ -293,26 +419,38 @@ public class PointOfCareService(
|
||||
|
||||
return poc;
|
||||
}
|
||||
|
||||
public async Task<PointOfCare?> GetInfo(ObjectId id, LocaleEnum? locale, bool fillPatientData = true,
|
||||
CancellationToken ct = default)
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="PointOfCare"/> by its identifier, using a cache-aside strategy, and optionally enriches
|
||||
/// it with related unit, patient, and admission data. When <paramref name="fillPatientData"/> is <c>false</c> or the
|
||||
/// point of care is not found, the method returns the cached value without further enrichment; the patient lookup
|
||||
/// is performed using the supplied <paramref name="locale"/> when provided, and admission data is only attached
|
||||
/// when an <c>AdmissionId</c> exists on the point of care.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the point of care to retrieve.</param>
|
||||
/// <param name="locale">Optional locale used to select the appropriate patient translation; when <c>null</c>, a locale-independent patient lookup is used.</param>
|
||||
/// <param name="fillPatientData">When <c>true</c>, enriches the result with unit, patient, and admission data; when <c>false</c>, returns the point of care as-is.</param>
|
||||
/// <param name="ct">Token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>The retrieved and optionally enriched <see cref="PointOfCare"/>, or <c>null</c> if no point of care is found for the given identifier.</returns>
|
||||
public async Task<PointOfCare?> GetInfo(ObjectId id, LocaleEnum? locale, bool fillPatientData = true,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var (key, ttl) = CacheKeys.PointOfCareBaseKeyWithTtl(_cacheSettings, id);
|
||||
var poc = await cacheService.GetOrSetObjectAsync(key,
|
||||
() => pointOfCareRepository.FindById(id),
|
||||
ttl, ct);
|
||||
|
||||
if (poc == null || !fillPatientData)
|
||||
if (poc == null || !fillPatientData)
|
||||
return poc;
|
||||
|
||||
|
||||
var unit = await unitService.Value.FindById(poc.UnitId);
|
||||
if (unit?.Name != null)
|
||||
poc.UnitName = unit.Name;
|
||||
|
||||
|
||||
var patient = locale != null
|
||||
? await patientService.Value.GetByPointOfCareAndLocale(poc, unit, locale)
|
||||
: await patientService.Value.GetByPointOfCare(poc);
|
||||
|
||||
|
||||
if (patient != null)
|
||||
{
|
||||
poc.Patientid = patient.Id;
|
||||
@@ -320,15 +458,21 @@ public class PointOfCareService(
|
||||
}
|
||||
|
||||
if (poc.AdmissionId == null) return poc;
|
||||
|
||||
|
||||
var admission = await admissionService.Value.GetAdmissionByIdAsync(poc.AdmissionId.Value);
|
||||
if (admission != null)
|
||||
poc.Admission = admission;
|
||||
return poc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the Point of Care associated with the specified patient by resolving the patient's assigned Point of Care identifier.
|
||||
/// Returns null if the patient cannot be found or if the patient has no Point of Care assigned.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose Point of Care is being requested.</param>
|
||||
/// <returns>The <see cref="PointOfCare"/> associated with the patient if one is assigned; otherwise, <c>null</c>.</returns>
|
||||
public async Task<PointOfCare?> FindPoCByPatientId(ObjectId patientId)
|
||||
{
|
||||
var patient = await patientService.Value.FindById(patientId);
|
||||
@@ -337,6 +481,12 @@ public class PointOfCareService(
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the Point of Care associated with the specified patient number.
|
||||
/// Returns null if no patient is found with the given number, or if the patient exists but has no associated Point of Care.
|
||||
/// </summary>
|
||||
/// <param name="patientNumber">The unique patient number used to look up the patient.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing the associated <see cref="PointOfCare"/> if found; otherwise, null.</returns>
|
||||
public async Task<PointOfCare?> FindPoCByPatientNumber(string patientNumber)
|
||||
{
|
||||
var patient = await patientService.Value.FindByPatientNumber(patientNumber);
|
||||
@@ -345,28 +495,46 @@ public class PointOfCareService(
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of Points of Care (PoCs) associated with the specified unit identifier.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose PoCs should be counted.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the total number of PoCs linked to the given unit.</returns>
|
||||
public async Task<long> CountPoCsByUnitId(ObjectId unitId)
|
||||
{
|
||||
return await pointOfCareRepository.CountByUnitId(unitId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of virtual Points of Care (PoCs) associated with the specified unit identifier by delegating to the repository.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit whose virtual PoCs are being counted.</param>
|
||||
/// <returns>A <see cref="Task{long}"/> representing the asynchronous operation, containing the total number of virtual PoCs linked to the given unit.</returns>
|
||||
public async Task<long> CountVirtualPoCsByUnitId(ObjectId unitId)
|
||||
{
|
||||
return await pointOfCareRepository.CountVirtualsByUnitId(unitId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the status of an existing point of care identified by its id, persisting the change,
|
||||
/// invalidating the related cache entries, recording an audit log of the change, and broadcasting
|
||||
/// the update. If no point of care is found for the given id, the method returns without making
|
||||
/// any changes.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the point of care whose status should be updated.</param>
|
||||
/// <param name="status">The new point of care status to apply.</param>
|
||||
public async Task SetPointOfCareStatus(ObjectId id, StatusEnum.PointOfCare status)
|
||||
{
|
||||
var pointOfCare = await GetInfo(id,null);
|
||||
var pointOfCare = await GetInfo(id, null);
|
||||
|
||||
if (pointOfCare == null) return;
|
||||
|
||||
pointOfCare.Status = status;
|
||||
|
||||
await pointOfCareRepository.Update(pointOfCare);
|
||||
|
||||
|
||||
await cacheService.DeleteByPatternAsync(CacheKeys.PointOfCareBase(id));
|
||||
|
||||
|
||||
var oldPoc = await auditService.DeepCopyAsync(pointOfCare);
|
||||
if (oldPoc != null)
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldPoc, pointOfCare);
|
||||
@@ -374,11 +542,23 @@ public class PointOfCareService(
|
||||
SendPointOfCareBroadcast(pointOfCare, OperationType.UpdatedPointOfCare);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a Point of Care associated with the specified patient location by delegating to the repository.
|
||||
/// Returns a null result if no matching Point of Care is found.
|
||||
/// </summary>
|
||||
/// <param name="patientLocation">The patient location used to look up the associated Point of Care.</param>
|
||||
/// <returns>A task that yields the matching <see cref="PointOfCare"/>, or <c>null</c> if no Point of Care is found for the given location.</returns>
|
||||
public async Task<PointOfCare?> FindPoCByPatientLocation(PatientLocation patientLocation)
|
||||
{
|
||||
return await pointOfCareRepository.FindByPatientLocation(patientLocation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a broadcast notification about a point of care change to all subscribers associated with its location.
|
||||
/// Any exception thrown while sending the broadcast is caught and logged, so the method never propagates failures to the caller.
|
||||
/// </summary>
|
||||
/// <param name="pointOfCare">The point of care whose related subscribers should receive the notification. Its <c>Id</c> is used to match subscriber location identifiers.</param>
|
||||
/// <param name="operation">The type of operation (e.g., create, update, delete) being broadcast, sent as the message payload.</param>
|
||||
private void SendPointOfCareBroadcast(PointOfCare pointOfCare, OperationType operation)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace adas_core.Application.Services
|
||||
IConfigUnitsService configUnitsService)
|
||||
: IPumpService
|
||||
{
|
||||
|
||||
|
||||
// Settings
|
||||
private readonly int _pumpExpiresSeconds = apiSettings.Value.PumpExpiresSeconds;
|
||||
private readonly bool _sendPumpsZero = apiSettings.Value.SendPumpsZero;
|
||||
@@ -53,6 +53,10 @@ namespace adas_core.Application.Services
|
||||
// ======================================================================
|
||||
// ENTRYPOINT
|
||||
// ======================================================================
|
||||
/// <summary>
|
||||
/// Processes an incoming <see cref="ApiRequest"/> by normalizing its pump observations, resolving or creating the associated patient, and handling each observation according to its message type (HL7 PCD-01/04/10 or AlarisPump). For each observation, it dispatches to the appropriate observation or alarm pipeline, updates the pump state snapshot, broadcasts the resulting snapshots with any active device alarms, and applies retention rules on the historical observations. AlarisPump observations without a PatientId are discarded, and per-observation processing errors are logged without aborting the whole request.
|
||||
/// </summary>
|
||||
/// <param name="req">The API request containing the pump observations and message type to be persisted and broadcast.</param>
|
||||
public async Task SaveRequest(ApiRequest req)
|
||||
{
|
||||
// Normalizar single vs list (Alaris puede mandar 1 sola)
|
||||
@@ -134,6 +138,10 @@ namespace adas_core.Application.Services
|
||||
// ======================================================================
|
||||
// OBSERVACIONES (PCD-01 / PCD-10)
|
||||
// ======================================================================
|
||||
/// <summary>
|
||||
/// Processes a pump observation by assigning an identifier and expiration, mapping it to the persistence model, and persisting it together with an audit log entry when the mapping succeeds.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to process. Its <c>Id</c> and <c>Expires</c> are populated before mapping.</param>
|
||||
private async Task ProcessObservation(PumpObservation obs)
|
||||
{
|
||||
logger.LogDebug("Insertando OBSERVATION DeviceId={dev} Time={time}", obs.DeviceId, obs.Time);
|
||||
@@ -146,13 +154,17 @@ namespace adas_core.Application.Services
|
||||
{
|
||||
await pumpObservationRepository.InsertAsync(mapped);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, mapped);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// ALARMAS (PCD-04)
|
||||
// ======================================================================
|
||||
/// <summary>
|
||||
/// Persists a pump alarm event derived from the supplied observation and updates the associated alarm state.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation providing the device, infusion, alarm, and patient context used to build the alarm event.</param>
|
||||
private async Task ProcessAlarm(PumpObservation obs)
|
||||
{
|
||||
logger.LogDebug("Insertando ALARM DeviceId={dev}, Phase={phase}, Type={type}",
|
||||
@@ -189,6 +201,10 @@ namespace adas_core.Application.Services
|
||||
await UpdateAlarmState(obs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the alarm state for a pump observation, removing the active alarm when the event phase is "end" and upserting a new alarm state record for start or continue phases.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation providing the event phase, device identifier, alarm type, and related alarm metadata used to drive the alarm state change.</param>
|
||||
private async Task UpdateAlarmState(PumpObservation obs)
|
||||
{
|
||||
if (obs.EventPhase == null) return;
|
||||
@@ -230,6 +246,11 @@ namespace adas_core.Application.Services
|
||||
// ======================================================================
|
||||
// SNAPSHOT (devuelve el PumpState actualizado)
|
||||
// ======================================================================
|
||||
/// <summary>
|
||||
/// Updates the persisted pump state for the device associated with the supplied observation, creating a new state record if none exists, and persists the merged result.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation whose values are merged into the current state and used to locate the device's existing record.</param>
|
||||
/// <returns>The updated <see cref="PumpState"/> instance reflecting the merged observation and the new <c>LastUpdated</c> timestamp.</returns>
|
||||
private async Task<PumpState> UpdatePumpState(PumpObservation obs)
|
||||
{
|
||||
var current = await pumpStateRepo.FindByDeviceIdAsync(obs.DeviceId!)
|
||||
@@ -246,6 +267,13 @@ namespace adas_core.Application.Services
|
||||
return current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the data from a <see cref="PumpObservation"/> into a <see cref="PumpState"/>,
|
||||
/// updating each field only when the observation provides a non-null, non-whitespace,
|
||||
/// or otherwise valid value, thereby preserving existing state data when no new information is present.
|
||||
/// </summary>
|
||||
/// <param name="state">The target <see cref="PumpState"/> instance whose fields will be updated in place.</param>
|
||||
/// <param name="obs">The <see cref="PumpObservation"/> instance supplying the new candidate values to merge.</param>
|
||||
private static void MergePumpState(PumpState state, PumpObservation obs)
|
||||
{
|
||||
// Identidad / físico
|
||||
@@ -310,10 +338,20 @@ namespace adas_core.Application.Services
|
||||
state.PatientId = obs.PatientId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified nullable <see cref="CommonPumpTypes.PumpValue"/> contains a non-null <c>Value</c>.
|
||||
/// </summary>
|
||||
/// <param name="v">The nullable pump value to inspect.</param>
|
||||
/// <returns><c>true</c> when <paramref name="v"/> is not null and its <c>Value</c> is not null; otherwise, <c>false</c>.</returns>
|
||||
private static bool HasValue(CommonPumpTypes.PumpValue? v) => v is { Value: not null };
|
||||
|
||||
// MAP
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="PumpObservation"/> through the configuration pumps and configuration units services in sequence, and additionally evaluates it against the calculated observations service to determine whether the mapping should be ignored (logged as debug when the result is null). Returns the observation produced after the configuration units mapping stage.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to be transformed through the mapping pipeline.</param>
|
||||
/// <returns>A task that yields the mapped <see cref="PumpObservation"/>, or <c>null</c> when the calculated observations stage produces no result.</returns>
|
||||
public async Task<PumpObservation?> MapPumpObservation(PumpObservation obs)
|
||||
{
|
||||
var obs2 = await configPumpsService.Map(obs);
|
||||
@@ -327,11 +365,22 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
|
||||
//Métodos
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves an API request by delegating to the underlying synchronous save operation.
|
||||
/// </summary>
|
||||
/// <param name="req">The API request to be saved.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public Task SaveRequestAsync(ApiRequest req) => SaveRequest(req);
|
||||
|
||||
|
||||
// Últimas N observaciones por paciente
|
||||
/// <summary>
|
||||
/// Retrieves the most recent pump observations for a specified patient, returning up to the requested number of records ordered by time in descending order. Returns an empty list if the repository result is not a list of pump observations or if no observations exist for the patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose pump observations are being retrieved.</param>
|
||||
/// <param name="num">The maximum number of recent pump observations to return. Defaults to 1.</param>
|
||||
/// <returns>A task that resolves to a list of the most recent <see cref="PumpObservation"/> records for the patient, or an empty list when none are available.</returns>
|
||||
public async Task<List<PumpObservation>> FindLastPumpObservations(ObjectId patientId, int num = 1)
|
||||
{
|
||||
var list = await pumpObservationRepository.FindByPatientId(patientId);
|
||||
@@ -343,12 +392,21 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
|
||||
// Última fecha de observación por paciente (para todos los pacientes)
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the most recent pump observation timestamp for every patient by delegating to the pump observation repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that resolves to a dictionary mapping each patient's <see cref="ObjectId"/> to the <see cref="DateTime"/> of their last pump observation.</returns>
|
||||
public async Task<Dictionary<ObjectId, DateTime>> FindAllLastPatientObservationTime()
|
||||
{
|
||||
return await pumpObservationRepository.FindAllLastPatientObservationTimeAsync();
|
||||
}
|
||||
|
||||
// Borrado completo por paciente (observaciones + alarmas + alarmState)
|
||||
/// <summary>
|
||||
/// Asynchronously deletes all pump observation, alarm event, and alarm state data associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose related pump data should be removed.</param>
|
||||
/// <returns>A task that represents the asynchronous deletion of the patient's data across the pump observation, alarm event, and alarm state repositories.</returns>
|
||||
public async Task DeleteByPatientId(ObjectId id)
|
||||
{
|
||||
logger.LogDebug("Delete Pump data by Patient Id {id}", id);
|
||||
@@ -358,9 +416,17 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
|
||||
// Archivo → por entidad paciente
|
||||
/// <summary>
|
||||
/// Archives the specified patient by delegating the operation to the archive routine identified by the patient's identifier.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to be archived.</param>
|
||||
public async Task Archive(Patient patient) => await ArchiveByPatientId(patient.Id);
|
||||
|
||||
// Archivo → por PatientId (mueve a archive_pumpobservations y elimina del activo)
|
||||
/// <summary>
|
||||
/// Archives all pump observations associated with the specified patient by moving them to the archive repository and then deleting the active data. If no pump observations are found for the patient, the archive insertion is skipped while the deletion of active data still proceeds.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose pump observations should be archived.</param>
|
||||
public async Task ArchiveByPatientId(ObjectId id)
|
||||
{
|
||||
var list = await pumpObservationRepository.FindByPatientId(id);
|
||||
@@ -373,14 +439,21 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
|
||||
// Actualización masiva
|
||||
/// <summary>
|
||||
/// Updates the ObjectId of pump observations and related alarms from an old identifier to a new one for the specified field, logging the operation and recording an audit entry.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name of the field whose ObjectId should be updated.</param>
|
||||
/// <param name="id">The new ObjectId to assign.</param>
|
||||
/// <param name="oldId">The existing ObjectId to be replaced.</param>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="nameId"/> is null, empty, or whitespace.</exception>
|
||||
public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
|
||||
{
|
||||
|
||||
|
||||
if (string.IsNullOrWhiteSpace(nameId))
|
||||
throw new ArgumentException("nameId no puede ser nulo o vacío.", nameof(nameId));
|
||||
|
||||
|
||||
var updatedObs = await pumpObservationRepository.UpdateManyObjectIdByFieldAsync(nameId, id, oldId);
|
||||
|
||||
|
||||
// actualizar también alarmas activas e históricas
|
||||
_ = await alarmEventRepo.UpdateManyObjectIdByFiledNameAsync(nameId, id, oldId);
|
||||
_ = await alarmStateRepo.UpdateManyObjectIdByFieldNameAsync(nameId, id, oldId);
|
||||
@@ -398,17 +471,41 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
|
||||
// ConfigPumpsService
|
||||
/// <summary>
|
||||
/// Retrieves a list of configuration pump items associated with the specified identifier by delegating to the configuration pumps service.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier used to look up the configuration pump items.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is a list of <see cref="ConfigPumpItem"/> matching the given identifier, or <c>null</c> if no items are found.</returns>
|
||||
public async Task<List<ConfigPumpItem>?> GetItemsById(string id)
|
||||
=> await configPumpsService.GetConfigItems(id);
|
||||
=> await configPumpsService.GetConfigItems(id);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all available pump configurations from the configuration service.
|
||||
/// Throws a <see cref="NotFoundException"/> if the service returns no data, indicating that the pump configuration resource is missing.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="ConfigPumps"/> configurations.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the underlying service returns a null result, meaning the pump configuration resource was not found.</exception>
|
||||
public async Task<List<ConfigPumps>?> GetAllPumpConfig()
|
||||
=> await configPumpsService.GetAllPumpConfigs()
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
=> await configPumpsService.GetAllPumpConfigs()
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the pump configuration that matches the specified identifier.
|
||||
/// Throws a not-found exception when no matching configuration exists in the underlying service.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the pump configuration to retrieve.</param>
|
||||
/// <returns>The matching <see cref="ConfigPumps"/> instance, or <c>null</c> if the service returns one; otherwise a <see cref="NotFoundException"/> is thrown.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the configuration service returns <c>null</c>, indicating that the requested resource is missing.</exception>
|
||||
public async Task<ConfigPumps?> GetPumpConfigsById(string id)
|
||||
=> await configPumpsService.GetPumpConfigById(id)
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
=> await configPumpsService.GetPumpConfigById(id)
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the pump configuration identified by <paramref name="config"/>, recording an audit log entry that captures the previous configuration and the new values before applying the change.
|
||||
/// </summary>
|
||||
/// <param name="config">The pump configuration containing the updated values to persist.</param>
|
||||
/// <returns>The updated <see cref="ConfigPumps"/> configuration if the update succeeds.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the underlying update operation does not return a configuration, indicating the resource is missing.</exception>
|
||||
public async Task<ConfigPumps?> UpdatePumpConfig(ConfigPumps config)
|
||||
{
|
||||
var oldConfig = await configPumpsService.GetPumpConfigById(config.Id);
|
||||
@@ -417,6 +514,12 @@ namespace adas_core.Application.Services
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new pump configuration after recording an audit log entry. Throws a conflict exception if the underlying service fails to create the configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">The pump configuration to insert.</param>
|
||||
/// <returns>The newly inserted <see cref="ConfigPumps"/>, or <see langword="null"/> if the operation yields no result.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the pump configuration could not be created by the underlying service.</exception>
|
||||
public async Task<ConfigPumps?> InsertPumpConfig(ConfigPumps config)
|
||||
{
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, config);
|
||||
@@ -424,6 +527,12 @@ namespace adas_core.Application.Services
|
||||
?? throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified pump configuration and records an audit log entry when the operation succeeds.
|
||||
/// </summary>
|
||||
/// <param name="config">The pump configuration to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a value indicating whether the deletion was successful.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the underlying deletion operation fails.</exception>
|
||||
public async Task<bool> DeletePumpConfig(ConfigPumps config)
|
||||
{
|
||||
var result = await configPumpsService.DeletePumpConfig(config);
|
||||
@@ -433,13 +542,18 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
|
||||
// Paginación de observaciones
|
||||
/// <summary>
|
||||
/// Retrieves a paginated collection of <see cref="PumpObservation"/> records based on the supplied filter. When a <c>PatientId</c> is provided, the patient identifier is validated and results are filtered by an optional date range; when a <c>DeviceId</c> is provided instead, the repository query already applies the date range. If no filter criteria or only invalid input is supplied, an empty paged response is returned and the total count is reported as zero. Results are ordered by observation time in descending order before pagination is applied.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination and filter criteria, including page number, page size, optional patient identifier, device identifier, and date range.</param>
|
||||
/// <returns>A task that yields the paginated response of pump observations, or <c>null</c> if the operation cannot be performed.</returns>
|
||||
public async Task<PaginationResponse<PumpObservation>?> GetPaginatedPump(PaginationFilter filter)
|
||||
{
|
||||
// Implementación compatible sin nuevos métodos en los repos:
|
||||
// 1) Si llega PatientId, paginamos en memoria desde FindByPatientId.
|
||||
// 2) Si llega DeviceId, usamos FindByDeviceIdAsync y paginamos en memoria.
|
||||
// 3) Si no hay filtro, devolvemos vacío para evitar lecturas completas.
|
||||
|
||||
|
||||
var page = filter.PageNumber <= 0 ? 1 : filter.PageNumber;
|
||||
var size = filter.PageSize <= 0 ? 20 : filter.PageSize;
|
||||
|
||||
@@ -482,6 +596,10 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
|
||||
// Inserción manual
|
||||
/// <summary>
|
||||
/// Inserts a pump observation into the repository, applying default values for missing identifier and timestamp, and triggers post-insertion side effects such as audit logging, state updates, snapshot broadcasting, and retention actions. If the mapped observation is null, no further action is taken. Observations with a number of zero are skipped from post-insertion processing when zero-value emissions are disabled.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to insert. Its <c>Id</c> is generated if not set, and its <c>Time</c> defaults to UTC now if set to <see cref="DateTime.MinValue"/>.</param>
|
||||
public async Task InsertPumpObservation(PumpObservation obs)
|
||||
{
|
||||
logger.LogDebug("Insert {obs}", obs);
|
||||
@@ -505,37 +623,41 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the configured retention policy to pump observations, executing deletion by age (days) or by count (keeping the last N), and logging a warning for unrecognized policies. Returns early when no retention policy value is available.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation used to resolve the applicable retention configuration.</param>
|
||||
private async Task DoRetentionActions(PumpObservation obs)
|
||||
{
|
||||
var result = await configPumpsService.RetentionActions(obs);
|
||||
if (result is not { RetentionPolicyValue: not null })
|
||||
if (result is not { RetentionPolicyValue: not null })
|
||||
return;
|
||||
|
||||
|
||||
switch (result.RetentionPolicy)
|
||||
{
|
||||
case RetentionPolicy.DeleteOlderDays:
|
||||
{
|
||||
var removed = await pumpObservationRepository.DeleteOlderThanDaysAsync(
|
||||
result.RetentionPolicyValue.Value);
|
||||
{
|
||||
var removed = await pumpObservationRepository.DeleteOlderThanDaysAsync(
|
||||
result.RetentionPolicyValue.Value);
|
||||
|
||||
logger.LogInformation(
|
||||
"Retention DeleteOlderDays: {removed} deleted (>{days} days)",
|
||||
removed, result.RetentionPolicyValue);
|
||||
logger.LogInformation(
|
||||
"Retention DeleteOlderDays: {removed} deleted (>{days} days)",
|
||||
removed, result.RetentionPolicyValue);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RetentionPolicy.DeleteOlderNumber:
|
||||
{
|
||||
var removed = await pumpObservationRepository.DeleteKeepLastNAsync(
|
||||
result.RetentionPolicyValue.Value);
|
||||
{
|
||||
var removed = await pumpObservationRepository.DeleteKeepLastNAsync(
|
||||
result.RetentionPolicyValue.Value);
|
||||
|
||||
logger.LogInformation(
|
||||
"Retention DeleteOlderNumber: {removed} deleted (keeping {max})",
|
||||
removed, result.RetentionPolicyValue);
|
||||
logger.LogInformation(
|
||||
"Retention DeleteOlderNumber: {removed} deleted (keeping {max})",
|
||||
removed, result.RetentionPolicyValue);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RetentionPolicy.NoDelete:
|
||||
case RetentionPolicy.DeleteOlderSeconds:
|
||||
@@ -546,11 +668,22 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
|
||||
// BROADCAST SNAPSHOTS: PumpState + PumpAlarmState (activos)
|
||||
/// <summary>
|
||||
/// Broadcasts the current pump state and active alarms to all WebSocket subscribers whose
|
||||
/// configured location matches either the patient's location (when a patient identifier is
|
||||
/// supplied and found) or the location carried by the request. The method performs no
|
||||
/// broadcast and returns early when no matching subscribers exist.
|
||||
/// </summary>
|
||||
/// <param name="state">The current <see cref="PumpState"/> snapshot to send to every matched subscriber.</param>
|
||||
/// <param name="activeAlarms">The collection of active <see cref="PumpAlarmState"/> entries to forward to every matched subscriber.</param>
|
||||
/// <param name="patientId">Optional patient identifier used to resolve the patient and derive the target location; when null the request location is used instead.</param>
|
||||
/// <param name="req">Optional API request whose <c>Location</c> is used as a fallback to resolve the target location when no patient identifier is provided.</param>
|
||||
/// <returns>A <see cref="Task"/> that completes once the pump state and all active alarms have been dispatched, or immediately when there are no matching subscribers.</returns>
|
||||
private async Task SendSnapshotsBroadcast(
|
||||
PumpState state,
|
||||
IEnumerable<PumpAlarmState> activeAlarms,
|
||||
ObjectId? patientId,
|
||||
ApiRequest? req)
|
||||
PumpState state,
|
||||
IEnumerable<PumpAlarmState> activeAlarms,
|
||||
ObjectId? patientId,
|
||||
ApiRequest? req)
|
||||
{
|
||||
var subscribers = new List<WsSubscriber>();
|
||||
|
||||
@@ -594,7 +727,13 @@ namespace adas_core.Application.Services
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates the patient identifier on the pump observation by first preserving any existing value, then falling back to a previously found patient id, and finally attempting to parse the patient id provided in the API request.
|
||||
/// </summary>
|
||||
/// <param name="req">The API request that may contain a patient id to be parsed and assigned.</param>
|
||||
/// <param name="obs">The pump observation whose patient id is being updated in place.</param>
|
||||
/// <param name="foundPatientId">An optional patient id obtained from a prior lookup, used as a fallback when the observation has no patient id assigned.</param>
|
||||
private static void UpdatePatientFromRequest(ApiRequest req, PumpObservation obs, ObjectId? foundPatientId)
|
||||
{
|
||||
if (obs.PatientId == null && foundPatientId != null)
|
||||
@@ -602,7 +741,7 @@ namespace adas_core.Application.Services
|
||||
|
||||
|
||||
if (obs.PatientId != null || string.IsNullOrWhiteSpace(req.PatientId)) return;
|
||||
|
||||
|
||||
if (ObjectId.TryParse(req.PatientId, out var parsed))
|
||||
obs.PatientId = parsed;
|
||||
}
|
||||
|
||||
@@ -22,11 +22,21 @@ public class RecordingAlertService(
|
||||
ILocalAuditService auditService)
|
||||
: IRecordingAlertService
|
||||
{
|
||||
/// <summary>
|
||||
/// Archives the specified patient by delegating to the patient identifier-based archive method.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to be archived.</param>
|
||||
/// <returns>A task that represents the asynchronous archive operation.</returns>
|
||||
public async Task Archive(Patient patient)
|
||||
{
|
||||
await ArchiveByPatientId(patient.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives all recording alerts associated with the specified patient identifier by copying them
|
||||
/// to the archive repository and then deleting them from the source repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose recording alerts should be archived.</param>
|
||||
public async Task ArchiveByPatientId(ObjectId id)
|
||||
{
|
||||
logger.LogDebug("Archive Recording Alerts by Patient Id {id}", id);
|
||||
@@ -40,23 +50,44 @@ public class RecordingAlertService(
|
||||
await DeleteByPatientId(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all recording alerts associated with the specified patient identifier by delegating to the recording alert repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose recording alerts should be removed.</param>
|
||||
public async Task DeleteByPatientId(ObjectId id)
|
||||
{
|
||||
logger.LogDebug("Delete Recording Alerts by Patient Id {id}", id);
|
||||
await recordingAlertRepository.DeleteByPatientId(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the most recent patient recording alerts by aggregating the last observations for the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The unique identifier of the patient whose recording alerts are being queried.</param>
|
||||
/// <param name="num">The maximum number of recent alerts to return. Defaults to 2.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of the patient's most recent <see cref="PatientRecordingAlert"/> entries.</returns>
|
||||
public async Task<List<PatientRecordingAlert>> FindLastRecordingAlert(ObjectId patientId, int num = 2)
|
||||
{
|
||||
return await recordingAlertRepository.AggregatedPatientLastObservations(patientId, num);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves the specified API request by executing the save operation on a background thread.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to save.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves observation data from an API request, processing ORU_R01, ORU_R40, and RecordingAlert types by resolving the associated patient and persisting each recording alert.
|
||||
/// If the request lacks both patient and location identifiers, or no matching patient is found, the request is ignored.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request containing patient or location identifiers and the recording alerts to persist.</param>
|
||||
/// <exception cref="ApiRequestException">Thrown when <paramref name="apiRequest"/>.Type is not a valid type for Observations.</exception>
|
||||
public async Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
if (string.IsNullOrEmpty(apiRequest.PatientNumber) && string.IsNullOrEmpty(apiRequest.Location?.UnitName) &&
|
||||
@@ -120,11 +151,21 @@ public class RecordingAlertService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates multiple recording alert records, replacing the <paramref name="oldId"/> with the new <paramref name="id"/> filtered by the specified <paramref name="nameId"/>.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name identifier used to filter the records to update.</param>
|
||||
/// <param name="id">The new <see cref="ObjectId"/> value to assign to the matching records.</param>
|
||||
/// <param name="oldId">The existing <see cref="ObjectId"/> value to be replaced in the matching records.</param>
|
||||
public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
|
||||
{
|
||||
await recordingAlertRepository.UpdateManyObjectId(nameId, id, oldId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new patient recording alert by persisting it, creating an audit log entry, broadcasting the alert, and executing any associated retention actions.
|
||||
/// </summary>
|
||||
/// <param name="recAlert">The patient recording alert to be inserted and processed.</param>
|
||||
private async Task InsertRecordingAlert(PatientRecordingAlert recAlert)
|
||||
{
|
||||
logger.LogDebug("Insert {recAlert}", recAlert);
|
||||
@@ -134,6 +175,10 @@ public class RecordingAlertService(
|
||||
await DoRetentionActions(recAlert);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously broadcasts a patient observation to subscribers whose registered locations include the patient's point of care, sending a recording alert when the observation is a recording alert and a generic observation otherwise. The method returns early without sending any message if the observation has no name, the patient cannot be resolved, or the patient has no point of care assigned.
|
||||
/// </summary>
|
||||
/// <param name="recAlert">The patient observation to broadcast to matching subscribers.</param>
|
||||
private async Task SendObsBroadcast(BasePatientObservation recAlert)
|
||||
{
|
||||
if (recAlert.Name == null) return;
|
||||
@@ -151,6 +196,13 @@ public class RecordingAlertService(
|
||||
foreach (var subscriber in subscribers) await clientMessageService.SendAsync(subscriber.Id, type, recAlert);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the configured retention policy for the given patient recording alert, deleting older recordings
|
||||
/// based on either a days-based or count-based retention value. The method performs no action and returns early
|
||||
/// if the retention configuration, its value, or the alert name is null.
|
||||
/// </summary>
|
||||
/// <param name="recAlert">The patient recording alert whose retention actions will be evaluated and applied.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the resolved <see cref="RetentionPolicy"/> value is not handled by the switch statement.</exception>
|
||||
private async Task DoRetentionActions(PatientRecordingAlert recAlert)
|
||||
{
|
||||
var result = await configObservationService.RetentionActions(recAlert);
|
||||
|
||||
@@ -18,6 +18,9 @@ using Patient = adas_core.Domain.Models.MongoModels.Patient;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides functionality for recording operations as defined by the <see cref="IRecordingService"/> contract.
|
||||
/// </summary>
|
||||
public class RecordingService : IRecordingService
|
||||
{
|
||||
private readonly IAuthService _authService;
|
||||
@@ -69,6 +72,14 @@ public class RecordingService : IRecordingService
|
||||
private string ErrorRecordingQueueName { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sends a cancel recording request to the recording API for the specified patient and point of care.
|
||||
/// Throws an exception if the recording API URL is not configured or a valid authentication token cannot be obtained.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose recording in progress should be cancelled.</param>
|
||||
/// <param name="poc">The point of care associated with the recording.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the cancel request succeeded; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="Exception">Thrown when the recording API URL is not defined or the authentication token is null or empty.</exception>
|
||||
public async Task<bool> SendCancelRecordingToRecordingApi(Patient patient, PointOfCare poc)
|
||||
{
|
||||
var token = await _authService.GetToken();
|
||||
@@ -80,9 +91,9 @@ public class RecordingService : IRecordingService
|
||||
var request =
|
||||
new HttpRequestMessage(HttpMethod.Post,
|
||||
$"{_url}/videos/delete-video-in-progress") //las fechas no se mandan
|
||||
{
|
||||
Content = new StringContent(body, Encoding.UTF8)
|
||||
};
|
||||
{
|
||||
Content = new StringContent(body, Encoding.UTF8)
|
||||
};
|
||||
request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {token}");
|
||||
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
@@ -92,13 +103,28 @@ public class RecordingService : IRecordingService
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the manual recording data to the processing queue, forwarding the recording's start and stop times along with the patient and point of care information.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient associated with the manual recording.</param>
|
||||
/// <param name="poc">The point of care where the recording was performed.</param>
|
||||
/// <param name="manualRecording">The manual recording whose start and stop times are forwarded to the queue.</param>
|
||||
/// <param name="start">A flag indicating whether the recording is being started or stopped.</param>
|
||||
public async Task SendRecordingData(Patient patient, PointOfCare poc, ManualRecording manualRecording,
|
||||
bool start)
|
||||
bool start)
|
||||
{
|
||||
await SendRecordingDataToQueue(patient, poc, manualRecording.Recording?.StartRecordingTime,
|
||||
manualRecording.Recording?.StopRecordingTime, null, null, AlarmEnum.Severity.None, null, start);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends automatic recording data for the specified patient and point of care to the recording data queue.
|
||||
/// Validates that the alarm name can be parsed to <see cref="AlarmEnum.Name"/>; returns <c>false</c> if the name is invalid or if an exception occurs during the send operation.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient associated with the automatic recording.</param>
|
||||
/// <param name="poc">The point of care where the recording was captured.</param>
|
||||
/// <param name="automaticRecording">The automatic recording payload, including alarm name, start/stop/event timestamps, severity, and description.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the recording data was queued successfully; otherwise, <c>false</c> when the alarm name cannot be parsed or the send operation fails.</returns>
|
||||
public async Task<bool> SendAutoRecordingData(Patient patient, PointOfCare poc, RecordingData automaticRecording)
|
||||
{
|
||||
try
|
||||
@@ -120,9 +146,26 @@ public class RecordingService : IRecordingService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends recording data for a patient to the recording message queue, broadcasting the recording after a successful send.
|
||||
/// Validates the patient number when the start-without-patient-number feature is disabled, ensures the point of care is in use,
|
||||
/// defaults a missing alarm description to "UNKNOWN", and retries the send on transient HTTP connection failures up to the configured maximum,
|
||||
/// routing the last failure to the error recording queue.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose recording is being sent; its patient number is validated when required.</param>
|
||||
/// <param name="poc">The point of care (box) that must be in use to allow the recording to be sent.</param>
|
||||
/// <param name="date">Optional recording date used to build the recording payload.</param>
|
||||
/// <param name="endDate">Optional end date used to build the recording payload.</param>
|
||||
/// <param name="eventDate">Optional event date used to build the recording payload.</param>
|
||||
/// <param name="alarmName">Optional alarm name to associate with the recording.</param>
|
||||
/// <param name="severity">The alarm severity for the recording.</param>
|
||||
/// <param name="alarmDescription">Optional alarm description; when null or empty it is replaced with "UNKNOWN".</param>
|
||||
/// <param name="start">Flag indicating whether the recording is a start event; defaults to true.</param>
|
||||
/// <param name="type">The alarm type for the recording; defaults to <see cref="AlarmEnum.Type.Manual"/>.</param>
|
||||
/// <exception cref="Exception">Thrown when the point of care status is not <see cref="StatusEnum.PointOfCare.InUse"/>, including the serialized point of care in the message.</exception>
|
||||
public async Task SendRecordingDataToQueue(Patient patient, PointOfCare poc, DateTime? date, DateTime? endDate,
|
||||
DateTime? eventDate, AlarmEnum.Name? alarmName, AlarmEnum.Severity severity, string? alarmDescription,
|
||||
bool start = true, AlarmEnum.Type type = AlarmEnum.Type.Manual)
|
||||
DateTime? eventDate, AlarmEnum.Name? alarmName, AlarmEnum.Severity severity, string? alarmDescription,
|
||||
bool start = true, AlarmEnum.Type type = AlarmEnum.Type.Manual)
|
||||
{
|
||||
if (!_startRecordingWithoutPatientNumber && string.IsNullOrEmpty(patient.PatientNumber))
|
||||
{
|
||||
@@ -178,6 +221,12 @@ public class RecordingService : IRecordingService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves the list of in-progress recordings for the specified room from the recording API.
|
||||
/// Performs token-based authentication, retries transient HTTP request failures up to the configured maximum, and falls back to an empty list or <c>null</c> when the API is unreachable, the room has no recordings, or retries are exhausted.
|
||||
/// </summary>
|
||||
/// <param name="roomId">The identifier of the room whose recordings should be fetched.</param>
|
||||
/// <returns>A task that resolves to a list of <see cref="RecordingData"/> for the room, an empty list when the request fails or the room has no recordings, or <c>null</c> when the recording API URL or authentication token is not configured or when the maximum number of retries is reached.</returns>
|
||||
public async Task<List<RecordingData>?> GetRecordings(int roomId)
|
||||
{
|
||||
var retryCount = 1;
|
||||
@@ -262,6 +311,11 @@ public class RecordingService : IRecordingService
|
||||
return [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the recording data from the given API request to the Recording API with authentication, retrying on failure up to the configured maximum and routing unsuccessful requests to the error queue. Clears the access token on 401 responses, maps the returned video data into a recording broadcast message on success, and falls back to the error queue when retries are exhausted or a connection error occurs.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request whose recording data and patient information will be sent to the Recording API.</param>
|
||||
/// <exception cref="Exception">Rethrown via Task.FromException when a non-HTTP error occurs while sending the recording request.</exception>
|
||||
public async Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
var retryCount = 1;
|
||||
@@ -346,16 +400,37 @@ public class RecordingService : IRecordingService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the specified API request. The method is not yet implemented.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The API request to save.</param>
|
||||
/// <exception cref="NotImplementedException">Always thrown because the method has not been implemented.</exception>
|
||||
public Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
//return Task.CompletedTask;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a <see cref="RecordingData"/> object for the specified patient at the given point of care, incorporating alarm metadata and timing details.
|
||||
/// Returns <c>null</c> and logs an error when the patient number is missing, the room identifier is unavailable, or the room identifier cannot be parsed as an integer.
|
||||
/// The stop recording time is derived from <paramref name="endDate"/> or, when not provided, calculated from <paramref name="minToExpired"/>; alarm names are translated to their description via <see cref="EnumUtils.GetDescription"/>.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient whose recording data is being generated; used to populate patient demographics and validate the patient number.</param>
|
||||
/// <param name="poc">The point of care providing the room identifier for the recording.</param>
|
||||
/// <param name="startDate">The optional start time of the recording.</param>
|
||||
/// <param name="endDate">The optional stop time of the recording; when omitted, the stop time is derived from <paramref name="minToExpired"/>.</param>
|
||||
/// <param name="eventTime">The optional time at which the triggering event occurred.</param>
|
||||
/// <param name="minToExpired">The optional number of minutes added to the current UTC time to compute the stop recording time when <paramref name="endDate"/> is not supplied.</param>
|
||||
/// <param name="alarmName">The optional alarm name whose description is resolved via the enum utility; stored as a string when present.</param>
|
||||
/// <param name="severity">The severity associated with the alarm.</param>
|
||||
/// <param name="alarmDescription">A textual description of the alarm.</param>
|
||||
/// <param name="alarm">The alarm type, defaulting to <see cref="AlarmEnum.Type.Manual"/> when not specified.</param>
|
||||
/// <returns>A configured <see cref="RecordingData"/> instance, or <c>null</c> if any required identifier is invalid.</returns>
|
||||
private RecordingData? GenerateRecordingData(Patient patient, PointOfCare poc, DateTime? startDate,
|
||||
DateTime? endDate, DateTime? eventTime, int? minToExpired, AlarmEnum.Name? alarmName,
|
||||
AlarmEnum.Severity severity,
|
||||
string alarmDescription, AlarmEnum.Type alarm = AlarmEnum.Type.Manual)
|
||||
DateTime? endDate, DateTime? eventTime, int? minToExpired, AlarmEnum.Name? alarmName,
|
||||
AlarmEnum.Severity severity,
|
||||
string alarmDescription, AlarmEnum.Type alarm = AlarmEnum.Type.Manual)
|
||||
{
|
||||
if (patient.PatientNumber == null)
|
||||
{
|
||||
@@ -406,6 +481,12 @@ public class RecordingService : IRecordingService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sends a recording status broadcast to WebSocket subscribers associated with the patient's bed and unit.
|
||||
/// Skips processing when the recording list is empty, logs an error and returns when the patient number is missing,
|
||||
/// and silently returns when the patient cannot be resolved.
|
||||
/// </summary>
|
||||
/// <param name="recording">The list of recording entries to broadcast; the first item is used to resolve the patient and room context.</param>
|
||||
private async Task SendRecordingBroadcast(List<RecordingData> recording)
|
||||
{
|
||||
if (!recording.IsNullOrEmpty())
|
||||
@@ -433,6 +514,11 @@ public class RecordingService : IRecordingService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the specified plain text to a Base64 string using UTF-8 encoding.
|
||||
/// </summary>
|
||||
/// <param name="plainText">The text to encode.</param>
|
||||
/// <returns>The Base64 encoded representation of the input text.</returns>
|
||||
protected static string Base64Encode(string plainText)
|
||||
{
|
||||
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
|
||||
@@ -16,6 +16,9 @@ using Options = Microsoft.Extensions.Options.Options;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides scheduling functionality as a service to manage and coordinate timed or periodic operations.
|
||||
/// </summary>
|
||||
public class SchedulerService
|
||||
{
|
||||
private readonly bool _activateCheckExpiredAlerts;
|
||||
@@ -54,19 +57,35 @@ public class SchedulerService
|
||||
private readonly Lazy<IDiagnosisService> _diagnosisService;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SchedulerService"/> class, assigning its dependencies and loading scheduler intervals and activation flags from the supplied application settings.
|
||||
/// </summary>
|
||||
/// <param name="apiSettings">Application configuration providing scheduler intervals, activation flags, and archival thresholds.</param>
|
||||
/// <param name="providersSettings">Configuration describing the external providers whose observations are retrieved.</param>
|
||||
/// <param name="patientService">Lazy provider of patient data operations.</param>
|
||||
/// <param name="treatmentService">Lazy provider of treatment data operations.</param>
|
||||
/// <param name="appointmentService">Lazy provider of appointment data operations.</param>
|
||||
/// <param name="diagnosisService">Lazy provider of diagnosis data operations.</param>
|
||||
/// <param name="medicineService">Lazy provider of medicine data operations.</param>
|
||||
/// <param name="observationService">Lazy provider of observation data operations.</param>
|
||||
/// <param name="configObservationService">Lazy provider of observation configuration operations.</param>
|
||||
/// <param name="logger">Logger used to record scheduler activity and diagnostics.</param>
|
||||
/// <param name="httpClientFactory">Factory used to create HTTP clients for provider integrations.</param>
|
||||
/// <param name="calculatedObservationsService">Lazy provider of calculated observations operations.</param>
|
||||
/// <param name="patientProcedureService">Lazy provider of patient care plan operations.</param>
|
||||
public SchedulerService(IOptions<ApiSettings> apiSettings,
|
||||
IOptions<List<ProvidersSettings>> providersSettings,
|
||||
Lazy<IPatientService> patientService,
|
||||
Lazy<ITreatmentService> treatmentService,
|
||||
Lazy<IAppointmentService> appointmentService,
|
||||
Lazy<IDiagnosisService> diagnosisService,
|
||||
Lazy<IMedicineService> medicineService,
|
||||
Lazy<IObservationService> observationService,
|
||||
Lazy<IConfigObservationService> configObservationService,
|
||||
ILogger<SchedulerService> logger,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
Lazy<ICalculatedObservationsService> calculatedObservationsService,
|
||||
Lazy<IPatientCarePlanService> patientProcedureService)
|
||||
IOptions<List<ProvidersSettings>> providersSettings,
|
||||
Lazy<IPatientService> patientService,
|
||||
Lazy<ITreatmentService> treatmentService,
|
||||
Lazy<IAppointmentService> appointmentService,
|
||||
Lazy<IDiagnosisService> diagnosisService,
|
||||
Lazy<IMedicineService> medicineService,
|
||||
Lazy<IObservationService> observationService,
|
||||
Lazy<IConfigObservationService> configObservationService,
|
||||
ILogger<SchedulerService> logger,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
Lazy<ICalculatedObservationsService> calculatedObservationsService,
|
||||
Lazy<IPatientCarePlanService> patientProcedureService)
|
||||
{
|
||||
_patientService = patientService;
|
||||
_treatmentService = treatmentService;
|
||||
@@ -145,6 +164,9 @@ public class SchedulerService
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Quartz scheduler by wiring up service dependencies for background jobs, creating job and trigger definitions, and conditionally scheduling them based on feature flag configuration settings before starting the scheduler.
|
||||
/// </summary>
|
||||
private async Task InitScheduler()
|
||||
{
|
||||
//TODO con el cambio a .net core y el repaso a la inyección de dependencias de estructure map
|
||||
@@ -185,10 +207,10 @@ public class SchedulerService
|
||||
CalculateNewsJob.ExpiresIn = _checkNewsJobIntervalMinutes;
|
||||
|
||||
var jobDataMap = new JobDataMap
|
||||
{
|
||||
{ "archivePatientsWithoutObservationsSinceHours", _archivePatientsWithoutObservationsSinceHours },
|
||||
{ "sinceDischargeTime", _sinceDischargeTimeToArchive }
|
||||
};
|
||||
{
|
||||
{ "archivePatientsWithoutObservationsSinceHours", _archivePatientsWithoutObservationsSinceHours },
|
||||
{ "sinceDischargeTime", _sinceDischargeTimeToArchive }
|
||||
};
|
||||
|
||||
// Grab the Scheduler instance from the Factory
|
||||
var factory = new StdSchedulerFactory();
|
||||
@@ -370,6 +392,12 @@ public class SchedulerService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scheduled job that checks the hydric balance of the RyC component, implemented as a Quartz.NET job.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The DisallowConcurrentExecution attribute prevents overlapping executions of this job.
|
||||
/// </remarks>
|
||||
[DisallowConcurrentExecution]
|
||||
public class CheckHydricBalanceRyCJob : IJob
|
||||
{
|
||||
@@ -379,6 +407,12 @@ public class CheckHydricBalanceRyCJob : IJob
|
||||
public static ICalculatedObservationsService CalculatedObservationsService { get; set; } = null!;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes the scheduled job that checks hydric balance for all patients and maps the most recent observation recorded within the current hour window.
|
||||
/// Iterates over every patient, retrieves the latest "Hydric_Balance" observation before a cutoff time, and forwards it to the calculated observations service when it falls within the current hour.
|
||||
/// All exceptions raised during processing are caught and logged, allowing the job to finish without interrupting the scheduler.
|
||||
/// </summary>
|
||||
/// <param name="context">The Quartz job execution context provided by the scheduler when the job trigger fires.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
@@ -417,6 +451,12 @@ public class CheckHydricBalanceRyCJob : IJob
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scheduled job that checks the status of active treatments.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The DisallowConcurrentExecution attribute ensures that only one instance of this job can execute at any given time.
|
||||
/// </remarks>
|
||||
[DisallowConcurrentExecution]
|
||||
public class CheckActiveTreatmentsJob : IJob
|
||||
{
|
||||
@@ -430,6 +470,10 @@ public class CheckActiveTreatmentsJob : IJob
|
||||
public static ICalculatedObservationsService CalculatedObservationsService { get; set; } = null!;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes the scheduled job that iterates through all patients, retrieves their active treatments and the associated medicines (excluding nutrition-type medicines), and calculates medicine observations for each patient. Exceptions are caught and logged without rethrowing, and the total execution duration is logged upon completion.
|
||||
/// </summary>
|
||||
/// <param name="context">The job execution context provided by the scheduler.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
@@ -475,6 +519,12 @@ public class CheckActiveTreatmentsJob : IJob
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scheduled job that checks for active appointments, implemented as an <see cref="IJob"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="DisallowConcurrentExecutionAttribute"/> attribute prevents multiple instances of this job from running at the same time.
|
||||
/// </remarks>
|
||||
[DisallowConcurrentExecution]
|
||||
public class CheckActiveAppointmentsJob : IJob
|
||||
{
|
||||
@@ -486,6 +536,10 @@ public class CheckActiveAppointmentsJob : IJob
|
||||
public static IPatientService PatientService { get; set; } = null!;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes the scheduled check active appointments job, logging the start and completion times along with the total elapsed time. Any exception thrown during execution is caught and logged without being rethrown.
|
||||
/// </summary>
|
||||
/// <param name="context">The Quartz scheduler context that provides runtime information for the job execution.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
@@ -507,6 +561,12 @@ public class CheckActiveAppointmentsJob : IJob
|
||||
(end - start).TotalMilliseconds);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents a scheduled job that checks for inactive patients.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The DisallowConcurrentExecution attribute prevents multiple instances of this job from running simultaneously.
|
||||
/// </remarks>
|
||||
[DisallowConcurrentExecution]
|
||||
public class CheckInactivePatientsJob : IJob
|
||||
{
|
||||
@@ -514,6 +574,11 @@ public class CheckInactivePatientsJob : IJob
|
||||
|
||||
public static IPatientService PatientService { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the scheduled job that discharges patients who have been inactive (without observations) for a configured period of hours.
|
||||
/// Skips processing when another instance is already running, as indicated by the global <c>isCheckingInactivePatients</c> flag, and logs the elapsed execution time.
|
||||
/// </summary>
|
||||
/// <param name="context">The Quartz job execution context whose <see cref="IJobExecutionContext.JobDetail"/> data map supplies the inactivity and discharge thresholds.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
//LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true);
|
||||
@@ -546,6 +611,12 @@ public class CheckInactivePatientsJob : IJob
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scheduled job that checks opiate boluses, implementing the <see cref="IJob"/> interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <c>DisallowConcurrentExecution</c> attribute ensures that overlapping executions of this job are prevented, guaranteeing that only one instance runs at a time.
|
||||
/// </remarks>
|
||||
[DisallowConcurrentExecution]
|
||||
public class CheckOpiateBolusesJob : IJob
|
||||
{
|
||||
@@ -553,6 +624,12 @@ public class CheckOpiateBolusesJob : IJob
|
||||
public static IPatientService PatientService { get; set; } = null!;
|
||||
public static ICalculatedObservationsService CalculatedObservationsService { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Executes a scheduled job that calculates opiate bolus observations for all patients.
|
||||
/// Retrieves every patient via the patient service and triggers the bolus opiates calculation for each one,
|
||||
/// logging any errors that occur and reporting the total execution time upon completion.
|
||||
/// </summary>
|
||||
/// <param name="context">The Quartz job execution context provided by the scheduler for this job run.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
@@ -577,6 +654,12 @@ public class CheckOpiateBolusesJob : IJob
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a background job that checks for expired observations, scheduled to run using the Quartz.NET job scheduling system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="DisallowConcurrentExecutionAttribute"/> attribute ensures that only one instance of this job can execute at a given time, preventing overlapping runs that could lead to duplicate processing of expired observations.
|
||||
/// </remarks>
|
||||
[DisallowConcurrentExecution]
|
||||
public class CheckExpiredObservationsJob : IJob
|
||||
{
|
||||
@@ -585,6 +668,10 @@ public class CheckExpiredObservationsJob : IJob
|
||||
public static IObservationService ObservationService { get; set; } = null!;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes the scheduled job that checks and expires observations, recalculating dependent data. Uses a global flag to prevent concurrent execution of the expiration logic, and logs the start, duration, and any errors encountered.
|
||||
/// </summary>
|
||||
/// <param name="context">The job execution context provided by the scheduler.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
@@ -632,6 +719,15 @@ public class CalculateNewsJob : IJob
|
||||
public static int ExpiresIn { get; set; } = 15;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes the scheduled job that calculates the National Early Warning Score (NEWS) for each patient
|
||||
/// located in an active Point of Care, based on the most recent vital sign observations
|
||||
/// (respiratory rate, SpO2, temperature, systolic blood pressure, and heart rate).
|
||||
/// Observations flagged as expired or whose configured expiration time has elapsed are skipped,
|
||||
/// and the resulting aggregate score is stored as a new NEWS observation for the patient when
|
||||
/// the latest underlying observation is not expired.
|
||||
/// </summary>
|
||||
/// <param name="context">The Quartz job execution context provided by the scheduler.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
//Filter patients in real locations
|
||||
@@ -783,6 +879,12 @@ public class CalculateNewsJob : IJob
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scheduled job that checks for expired alerts.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This job is decorated with the <see cref="DisallowConcurrentExecutionAttribute"/> to prevent overlapping executions of the same job instance.
|
||||
/// </remarks>
|
||||
[DisallowConcurrentExecution]
|
||||
public class CheckExpiredAlertsJob : IJob
|
||||
{
|
||||
@@ -791,6 +893,11 @@ public class CheckExpiredAlertsJob : IJob
|
||||
public static IObservationService ObservationService { get; set; } = null!;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes the scheduled job that checks for expired alerts and triggers the power-off routine, while preventing concurrent executions and logging execution duration.
|
||||
/// Runs the expiration logic only when the global "isCheckingAlertsExpiration" flag is absent or set to false; any exception is caught and logged without being rethrown.
|
||||
/// </summary>
|
||||
/// <param name="context">The Quartz job execution context provided by the scheduler when the job trigger fires.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
@@ -816,6 +923,12 @@ public class CheckExpiredAlertsJob : IJob
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scheduled job that archives nurse data, preventing concurrent executions of the same job instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="DisallowConcurrentExecutionAttribute"/> attribute ensures that the job will not be triggered while a previous execution is still running.
|
||||
/// </remarks>
|
||||
[DisallowConcurrentExecution]
|
||||
public class ArchiveNurseDataJob : IJob
|
||||
{
|
||||
@@ -828,6 +941,10 @@ public class ArchiveNurseDataJob : IJob
|
||||
|
||||
public static IPatientCarePlanService PatientCarePlanService { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ArchiveNurseDataJob which archives finished nurse procedures, tests, and treatments for patients whose associated end dates exceed the configured thresholds defined in <see cref="ArchiveNurseDataSettings"/>. Each archive category is only processed when its corresponding setting flag is active, and any exception is caught and logged without interrupting the remaining work.
|
||||
/// </summary>
|
||||
/// <param name="context">The Quartz.NET job execution context that provides runtime information for the scheduled job execution.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
@@ -932,6 +1049,10 @@ public class ArchiveNurseDataJob : IJob
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scheduled job that retrieves observations associated with providers.
|
||||
/// </summary>
|
||||
/// <remarks>The DisallowConcurrentExecution attribute prevents overlapping executions of this job instance.</remarks>
|
||||
[DisallowConcurrentExecution]
|
||||
public class GetProvidersObservationsJob : IJob
|
||||
{
|
||||
@@ -946,6 +1067,12 @@ public class GetProvidersObservationsJob : IJob
|
||||
|
||||
public static IHttpClientFactory HttpClientFactory { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Executes the scheduled job that retrieves observations from all configured providers for every patient.
|
||||
/// Uses reflection to resolve and instantiate each provider type (mapping the assembly name by replacing '-' with '_'),
|
||||
/// skips providers whose type or required constructor cannot be found, and logs and recovers from any exception raised during processing.
|
||||
/// </summary>
|
||||
/// <param name="context">The Quartz job execution context provided by the scheduler when the job is triggered.</param>
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
@@ -970,7 +1097,7 @@ public class GetProvidersObservationsJob : IJob
|
||||
|
||||
var ctor = providerType.GetConstructor([
|
||||
typeof(IOptions<ProvidersSettings>),
|
||||
typeof(IHttpClientFactory)
|
||||
typeof(IHttpClientFactory)
|
||||
]);
|
||||
if (ctor == null)
|
||||
{
|
||||
@@ -980,7 +1107,7 @@ public class GetProvidersObservationsJob : IJob
|
||||
|
||||
var customProvider = (BaseProvider)ctor.Invoke([
|
||||
Options.Create(provider),
|
||||
HttpClientFactory
|
||||
HttpClientFactory
|
||||
]);
|
||||
|
||||
var patients = await PatientService.FindAll();
|
||||
|
||||
@@ -16,6 +16,13 @@ public class ServiceConfigService(
|
||||
private readonly ILogger<ServiceConfigService> _logger = logger;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="ServiceConfig"/> by its identifier, attempting lookup by the string id first
|
||||
/// and falling back to an <see cref="ObjectId"/> lookup when the initial query returns no result.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the service configuration to retrieve. May be a string id or a valid ObjectId representation.</param>
|
||||
/// <returns>The matching <see cref="ServiceConfig"/>, or <c>null</c> when no record is found for the provided id.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the resource cannot be found after attempting both the string id and the parsed <see cref="ObjectId"/> lookup.</exception>
|
||||
public async Task<ServiceConfig?> Get(string id)
|
||||
{
|
||||
var result = await serviceConfigRepository.FindById(id);
|
||||
|
||||
@@ -4,49 +4,79 @@ using MongoDB.Bson;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides operations for managing subscribers by implementing the <see cref="ISubscribersService"/> contract.
|
||||
/// Acts as the concrete service layer responsible for subscriber-related functionality.
|
||||
/// </summary>
|
||||
public class SubscribersService : ISubscribersService
|
||||
{
|
||||
private readonly List<WsSubscriber> _subscribers = [];
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a thread-safe snapshot of the current list of WebSocket subscribers.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="List{WsSubscriber}"/> containing a copy of the current subscribers.</returns>
|
||||
public List<WsSubscriber> GetSubscribers()
|
||||
{
|
||||
lock (_subscribers)
|
||||
{
|
||||
return _subscribers.ToList();
|
||||
lock (_subscribers)
|
||||
{
|
||||
return _subscribers.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a WebSocket subscriber by its connection identifier, returning null if no matching subscriber is found.
|
||||
/// Thread-safe access to the subscribers collection is ensured via locking.
|
||||
/// </summary>
|
||||
/// <param name="contextConnectionId">The unique connection identifier of the subscriber to look up.</param>
|
||||
/// <returns>The matching <see cref="WsSubscriber"/> if found; otherwise, null.</returns>
|
||||
public WsSubscriber? GetById(string contextConnectionId)
|
||||
{
|
||||
lock (_subscribers)
|
||||
{
|
||||
return _subscribers.FirstOrDefault(s => s.Id == contextConnectionId);
|
||||
lock (_subscribers)
|
||||
{
|
||||
return _subscribers.FirstOrDefault(s => s.Id == contextConnectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all subscribers whose associated location identifiers include the specified point-of-contact identifier.
|
||||
/// Ensures thread-safe access to the underlying subscriber collection during the read operation.
|
||||
/// </summary>
|
||||
/// <param name="pocId">The point-of-contact identifier used to match subscribers by their location list.</param>
|
||||
/// <returns>A list of <see cref="WsSubscriber"/> instances that have <paramref name="pocId"/> in their location identifiers; returns an empty list when no matches are found.</returns>
|
||||
public List<WsSubscriber> GetByPocId(ObjectId pocId)
|
||||
{
|
||||
lock (_subscribers)
|
||||
{
|
||||
return _subscribers.Where(s => s.LocationIds.Contains(pocId)).ToList();
|
||||
lock (_subscribers)
|
||||
{
|
||||
return _subscribers.Where(s => s.LocationIds.Contains(pocId)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all subscriber connections matching the specified connection identifier in a thread-safe manner.
|
||||
/// </summary>
|
||||
/// <param name="contextConnectionId">The unique identifier of the connection to remove.</param>
|
||||
/// <returns>The number of connections that were removed from the subscribers list.</returns>
|
||||
public int RemoveConnectionById(string contextConnectionId)
|
||||
{
|
||||
lock (_subscribers)
|
||||
{
|
||||
return _subscribers.RemoveAll(s => s.Id == contextConnectionId);
|
||||
lock (_subscribers)
|
||||
{
|
||||
return _subscribers.RemoveAll(s => s.Id == contextConnectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a WebSocket subscriber to the internal subscribers collection in a thread-safe manner.
|
||||
/// Ensures that concurrent calls to add subscribers are serialized to prevent race conditions.
|
||||
/// </summary>
|
||||
/// <param name="subscriber">The WebSocket subscriber to add to the collection.</param>
|
||||
public void AddSubscriber(WsSubscriber subscriber)
|
||||
{
|
||||
lock (_subscribers)
|
||||
{
|
||||
_subscribers.Add(subscriber);
|
||||
lock (_subscribers)
|
||||
{
|
||||
_subscribers.Add(subscriber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,18 @@ using MongoDB.Driver;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides business logic for managing patient treatments, including insertion,
|
||||
/// update, deletion, archiving, pagination, and broadcasting of treatment events
|
||||
/// to subscribed clients.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This service orchestrates interactions between the treatment repository, the
|
||||
/// patient service, configuration/calculation observation services, and audit
|
||||
/// services. It also handles inbound HL7-like pharmacy/treatment messages
|
||||
/// (<c>OMP_O09</c>, <c>ORM_O01</c>, <c>RAS_O17</c>) and emits broadcasts to
|
||||
/// subscribers based on the patient's point of care.
|
||||
/// </remarks>
|
||||
public class TreatmentService(
|
||||
ITreatmentRepository treatmentRepository,
|
||||
ITreatmentArchiveRepository treatmentArchiveRepository,
|
||||
@@ -27,11 +39,29 @@ public class TreatmentService(
|
||||
ILocalAuditService auditService)
|
||||
: ITreatmentService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all treatments associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// an <see cref="IEnumerable{T}"/> of <see cref="PatientTreatment"/> for the patient.
|
||||
/// </returns>
|
||||
public async Task<IEnumerable<PatientTreatment>> GetTreatmentsByPatientId(ObjectId id)
|
||||
{
|
||||
return await treatmentRepository.GetByPatientId(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new patient treatment after applying mapping/calculation rules
|
||||
/// and creates an audit log entry.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The <see cref="PatientTreatment"/> to insert.</param>
|
||||
/// <returns>A task that represents the asynchronous insert operation.</returns>
|
||||
/// <remarks>
|
||||
/// If the treatment cannot be mapped, it is silently ignored. A broadcast
|
||||
/// is dispatched asynchronously upon successful insertion.
|
||||
/// </remarks>
|
||||
public async Task Insert(PatientTreatment treatment)
|
||||
{
|
||||
logger.LogDebug("Insert {treatment}", treatment);
|
||||
@@ -44,6 +74,20 @@ public class TreatmentService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all treatments belonging to the specified patient and creates
|
||||
/// an audit log entry capturing the deleted state.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient whose treatments will be deleted.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result is
|
||||
/// <see langword="true"/> if the deletion succeeds or there is nothing to delete;
|
||||
/// otherwise the method throws.
|
||||
/// </returns>
|
||||
/// <exception cref="ConflictException">
|
||||
/// Thrown when the patient has no treatments to delete (no record was found)
|
||||
/// or when the underlying delete operation fails.
|
||||
/// </exception>
|
||||
public async Task<bool> DeleteByPatientId(ObjectId id)
|
||||
{
|
||||
var treatmentToDelete = await FindByPatientId(id) ??
|
||||
@@ -59,11 +103,22 @@ public class TreatmentService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives all treatments associated with the supplied patient.
|
||||
/// </summary>
|
||||
/// <param name="patient">The <see cref="Patient"/> whose treatments will be archived.</param>
|
||||
/// <returns>A task that represents the asynchronous archive operation.</returns>
|
||||
public async Task Archive(Patient patient)
|
||||
{
|
||||
await ArchiveByPatientId(patient.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies all treatments of the specified patient into the treatment archive
|
||||
/// repository and subsequently removes them from the primary repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient whose treatments will be archived.</param>
|
||||
/// <returns>A task that represents the asynchronous archive operation.</returns>
|
||||
public async Task ArchiveByPatientId(ObjectId id)
|
||||
{
|
||||
logger.LogDebug("ArchiveB Treatments by Patient Id PatientId {id}", id);
|
||||
@@ -77,6 +132,18 @@ public class TreatmentService(
|
||||
await DeleteByPatientId(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing treatment and writes an audit log entry comparing the
|
||||
/// previous and updated values.
|
||||
/// </summary>
|
||||
/// <param name="patientTreatment">The <see cref="PatientTreatment"/> containing the updated values.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result is
|
||||
/// <see langword="true"/> if the update succeeds.
|
||||
/// </returns>
|
||||
/// <exception cref="ConflictException">
|
||||
/// Thrown when the treatment does not exist or the update operation fails.
|
||||
/// </exception>
|
||||
public async Task<bool> UpdateTreatment(PatientTreatment patientTreatment)
|
||||
{
|
||||
var oldTreatment = await treatmentRepository.GetById(patientTreatment.Id) ??
|
||||
@@ -91,26 +158,69 @@ public class TreatmentService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an asynchronous cursor over all treatments of the given patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// an <see cref="IAsyncCursor{T}"/> of <see cref="PatientTreatment"/>.
|
||||
/// </returns>
|
||||
public async Task<IAsyncCursor<PatientTreatment>> FindByPatientIdAsync(ObjectId patientId)
|
||||
{
|
||||
return await treatmentRepository.FindByPatientIdAsync(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all treatments of the given patient as an enumerable.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// an <see cref="IEnumerable{T}"/> of <see cref="PatientTreatment"/>.
|
||||
/// </returns>
|
||||
public async Task<IEnumerable<PatientTreatment>> FindByPatientId(ObjectId patientId)
|
||||
{
|
||||
return await treatmentRepository.FindByPatientId(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all bolus treatments of the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// a <see cref="List{T}"/> of <see cref="PatientTreatment"/> representing the bolus treatments.
|
||||
/// </returns>
|
||||
public async Task<List<PatientTreatment>> GetBolusTreatments(ObjectId patientId)
|
||||
{
|
||||
return await treatmentRepository.FindBolusTreatments(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous entry point that delegates to <see cref="SaveRequest(ApiRequest)"/>.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The inbound <see cref="ApiRequest"/> to process.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public async Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
await SaveRequest(apiRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes an inbound API request and persists the corresponding treatments
|
||||
/// for the referenced patient.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The <see cref="ApiRequest"/> to process.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
/// <exception cref="ApiRequestException">
|
||||
/// Thrown when the patient number is missing or the request type is not supported.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Supported message types are <c>OMP_O09</c>, <c>ORM_O01</c> and <c>RAS_O17</c>.
|
||||
/// The method also enforces the unit's <c>AutoAdt</c> configuration when the
|
||||
/// request does not originate from a panel.
|
||||
/// </remarks>
|
||||
public async Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
if (string.IsNullOrEmpty(apiRequest.PatientNumber))
|
||||
@@ -186,6 +296,17 @@ public class TreatmentService(
|
||||
/*
|
||||
* Return active treatments of patient PatientType active = NW, canceled= DC
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns the active (non-canceled, in-range) treatments of the specified patient,
|
||||
/// keeping the most recent active order per placer-order identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// an <see cref="IEnumerable{T}"/> of nullable <see cref="PatientTreatment"/>
|
||||
/// representing the active treatments. Treatments marked as <c>Dc</c> or
|
||||
/// outside their validity window are excluded.
|
||||
/// </returns>
|
||||
public async Task<IEnumerable<PatientTreatment?>> GetActiveTreatmentsByPatient(ObjectId id)
|
||||
{
|
||||
var treatments = await treatmentRepository.GetByPatientId(id);
|
||||
@@ -200,6 +321,15 @@ public class TreatmentService(
|
||||
return activeTreatments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates every occurrence of an identifier (e.g. a referenced <see cref="ObjectId"/>)
|
||||
/// in stored treatments from <paramref name="oldId"/> to <paramref name="id"/>
|
||||
/// and broadcasts the changes to subscribers.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name of the field that holds the identifier.</param>
|
||||
/// <param name="id">The new <see cref="ObjectId"/> value.</param>
|
||||
/// <param name="oldId">The previous <see cref="ObjectId"/> value being replaced.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
|
||||
{
|
||||
await treatmentRepository.UpdateManyObjectId(nameId, id, oldId);
|
||||
@@ -208,7 +338,14 @@ public class TreatmentService(
|
||||
foreach (var treat in treatments) _ = SendBroadcast(treat, OperationType.UpdateTreatment);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a paginated list of treatments based on the supplied filter.
|
||||
/// </summary>
|
||||
/// <param name="filter">The <see cref="PaginationFilter"/> containing page number and page size.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// a <see cref="PaginationResponse{T}"/> of <see cref="PatientTreatment"/>.
|
||||
/// </returns>
|
||||
public async Task<PaginationResponse<PatientTreatment>> GetPaginatedTreatments(PaginationFilter filter)
|
||||
{
|
||||
var result = treatmentRepository.GetPaginatedTreatments(filter);
|
||||
@@ -225,12 +362,29 @@ public class TreatmentService(
|
||||
return new PaginationResponse<PatientTreatment>(dataList, filter.PageNumber, filter.PageSize, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the active treatments of a patient that match the specified placer order.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <param name="order">The placer-order identifier to filter by.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// a <see cref="List{T}"/> of <see cref="PatientTreatment"/>.
|
||||
/// </returns>
|
||||
public async Task<List<PatientTreatment>> GetActiveTreatmentsByPatientIdAndOrder(ObjectId patientId, string order)
|
||||
{
|
||||
return await treatmentRepository.GetActiveTreatmentsByPatientIdAndOrder(patientId, order);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a treatment by its identifier and writes an audit log entry
|
||||
/// capturing the deleted state.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the treatment to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation.</returns>
|
||||
/// <exception cref="ConflictException">
|
||||
/// Thrown when the treatment does not exist.
|
||||
/// </exception>
|
||||
public async Task DeleteById(ObjectId id)
|
||||
{
|
||||
var oldTreatment = await treatmentRepository.GetById(id) ??
|
||||
@@ -239,6 +393,20 @@ public class TreatmentService(
|
||||
await treatmentRepository.DeleteAsync(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the calculated-observations and configuration-observations
|
||||
/// mapping pipeline to a treatment.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The <see cref="PatientTreatment"/> to map.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// the mapped <see cref="PatientTreatment"/>, or <see langword="null"/> when
|
||||
/// the treatment is ignored by the configuration mapping.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// If the configuration-observation service returns <see langword="null"/>,
|
||||
/// the treatment is considered ignored and a debug entry is logged.
|
||||
/// </remarks>
|
||||
private async Task<PatientTreatment?> MapTreatment(PatientTreatment treatment)
|
||||
{
|
||||
var treatment2 = await calculatedObservationsService.Value.Map(treatment);
|
||||
@@ -249,7 +417,17 @@ public class TreatmentService(
|
||||
return treatment;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sends a broadcast message to all subscribers whose location list includes
|
||||
/// the patient's point of care.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The <see cref="PatientTreatment"/> to broadcast.</param>
|
||||
/// <param name="operationType">The <see cref="OperationType"/> describing the change.</param>
|
||||
/// <returns>A task that represents the asynchronous broadcast operation.</returns>
|
||||
/// <remarks>
|
||||
/// If the patient cannot be located or has no <c>PointOfCareId</c>, the
|
||||
/// broadcast is skipped.
|
||||
/// </remarks>
|
||||
private async Task SendBroadcast(PatientTreatment treatment, OperationType operationType)
|
||||
{
|
||||
var patient = await patientService.FindById(treatment.PatientId);
|
||||
@@ -264,6 +442,16 @@ public class TreatmentService(
|
||||
await clientMessageService.SendAsync(subscriber.Id, operationType, treatment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a treatment is currently active (not canceled and
|
||||
/// within its start/end time window).
|
||||
/// </summary>
|
||||
/// <param name="treatment">The <see cref="PatientTreatment"/> to evaluate.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the treatment has a placer order, is not
|
||||
/// discontinued (<see cref="OrderControlType.Dc"/>) and the current UTC
|
||||
/// time lies within its validity window; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
private static bool IsValidTreatment(PatientTreatment treatment)
|
||||
{
|
||||
if (treatment.PlacerOrder == null)
|
||||
@@ -278,6 +466,18 @@ public class TreatmentService(
|
||||
return treatment.OrderControl != OrderControlType.Dc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the most recent active treatment from a group of treatments
|
||||
/// sharing the same placer-order identifier.
|
||||
/// </summary>
|
||||
/// <param name="group">
|
||||
/// A group of <see cref="PatientTreatment"/> instances sharing the same
|
||||
/// placer-order entity identifier.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The most recent treatment whose <see cref="OrderControlType"/> is
|
||||
/// <c>Nw</c> or <c>Xo</c>, or <see langword="null"/> if none qualifies.
|
||||
/// </returns>
|
||||
private static PatientTreatment? GetMostRecentActiveTreatment(IGrouping<string?, PatientTreatment> group)
|
||||
{
|
||||
return group
|
||||
|
||||
@@ -28,407 +28,557 @@ public class UnitService(
|
||||
ILocalAuditService auditService)
|
||||
: IUnitService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all units, optionally including their associated PointOfCares.
|
||||
/// When <paramref name="withPoCs"/> is <c>true</c>, the PointOfCares collection is populated for each unit using the point of care service; otherwise, only the unit data is returned.
|
||||
/// </summary>
|
||||
/// <param name="withPoCs">If <c>true</c>, loads and assigns the PointOfCares for each unit; if <c>false</c>, returns units without their PointOfCares.</param>
|
||||
/// <returns>A task representing the asynchronous operation, containing the list of all units, with PointOfCares populated when requested.</returns>
|
||||
public async Task<List<Unit>> GetAll(bool withPoCs = false)
|
||||
{
|
||||
var units = await unitRepository.GetAll();
|
||||
|
||||
if (withPoCs)
|
||||
foreach (var unit in units)
|
||||
{
|
||||
var pocList = await pointOfCareService.FindAllByUnitId(unit.Id);
|
||||
unit.PointOfCares = pocList?.ToList();
|
||||
}
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
public async Task<List<UnitInfoDto>> GetAllCompact()
|
||||
{
|
||||
var units = await unitRepository.GetAll();
|
||||
var result = new List<UnitInfoDto>();
|
||||
foreach (var unit in units)
|
||||
result.Add(new UnitInfoDto
|
||||
{
|
||||
Id = unit.Id,
|
||||
Name = unit.Name ?? string.Empty,
|
||||
Title = unit.Title ?? string.Empty
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<UnitInfoDto> GetOneCompact(ObjectId id)
|
||||
{
|
||||
var unit = await unitRepository.FindById(id);
|
||||
var result = new UnitInfoDto
|
||||
{
|
||||
Id = unit?.Id,
|
||||
Name = unit?.Name ?? string.Empty,
|
||||
Title = unit?.Title ?? string.Empty
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<PaginationResponse<Unit>> GetPaginatedUnits(PaginationFilter filter, bool withPoCs = false)
|
||||
{
|
||||
var result = unitRepository.GetPaginatedUnits(filter);
|
||||
|
||||
var count = await result.CountDocumentsAsync();
|
||||
|
||||
var data = await result.Skip((filter.PageNumber - 1) * filter.PageSize)
|
||||
.Limit(filter.PageSize)
|
||||
.ToListAsync();
|
||||
|
||||
|
||||
if (withPoCs && data.Any())
|
||||
{
|
||||
var unitIds = data.Select(u => u.Id).ToList();
|
||||
var allPocsForUnits = await pointOfCareService.FindAllByUnitIds(unitIds);
|
||||
|
||||
foreach (var unit in data) unit.PointOfCares = allPocsForUnits.Where(poc => poc.UnitId == unit.Id).ToList();
|
||||
var units = await unitRepository.GetAll();
|
||||
|
||||
if (withPoCs)
|
||||
foreach (var unit in units)
|
||||
{
|
||||
var pocList = await pointOfCareService.FindAllByUnitId(unit.Id);
|
||||
unit.PointOfCares = pocList?.ToList();
|
||||
}
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
return new PaginationResponse<Unit>(data, filter.PageNumber, filter.PageSize, count);
|
||||
}
|
||||
/// <summary>
|
||||
/// Retrieves all units in a compact representation, mapping each unit to a <see cref="UnitInfoDto"/> containing its identifier, name, and title, with null name and title values safely replaced by empty strings.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="UnitInfoDto"/> objects for all available units.</returns>
|
||||
public async Task<List<UnitInfoDto>> GetAllCompact()
|
||||
{
|
||||
var units = await unitRepository.GetAll();
|
||||
var result = new List<UnitInfoDto>();
|
||||
foreach (var unit in units)
|
||||
result.Add(new UnitInfoDto
|
||||
{
|
||||
Id = unit.Id,
|
||||
Name = unit.Name ?? string.Empty,
|
||||
Title = unit.Title ?? string.Empty
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a compact representation of a unit by its identifier, returning a <see cref="UnitInfoDto"/> populated with the unit's id, name, and title. If no unit is found for the given id, the returned DTO contains a null id with empty name and title values.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the unit to retrieve.</param>
|
||||
/// <returns>A <see cref="Task{UnitInfoDto}"/> containing the compact unit information, or a DTO with null/empty fields if the unit does not exist.</returns>
|
||||
public async Task<UnitInfoDto> GetOneCompact(ObjectId id)
|
||||
{
|
||||
var unit = await unitRepository.FindById(id);
|
||||
var result = new UnitInfoDto
|
||||
{
|
||||
Id = unit?.Id,
|
||||
Name = unit?.Name ?? string.Empty,
|
||||
Title = unit?.Title ?? string.Empty
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of units, optionally enriched with their associated Points of Care (PoCs).
|
||||
/// When <paramref name="withPoCs"/> is true, all PoCs for the returned units are loaded in a single batch call and assigned to each unit.
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination parameters controlling the page number, page size, and total count.</param>
|
||||
/// <param name="withPoCs">Indicates whether the response units should be populated with their related Points of Care. Defaults to false.</param>
|
||||
/// <returns>A <see cref="Task{PaginationResponse{Unit}}"/> containing the requested page of units along with pagination metadata.</returns>
|
||||
public async Task<PaginationResponse<Unit>> GetPaginatedUnits(PaginationFilter filter, bool withPoCs = false)
|
||||
{
|
||||
var result = unitRepository.GetPaginatedUnits(filter);
|
||||
|
||||
var count = await result.CountDocumentsAsync();
|
||||
|
||||
var data = await result.Skip((filter.PageNumber - 1) * filter.PageSize)
|
||||
.Limit(filter.PageSize)
|
||||
.ToListAsync();
|
||||
|
||||
|
||||
if (withPoCs && data.Any())
|
||||
{
|
||||
var unitIds = data.Select(u => u.Id).ToList();
|
||||
var allPocsForUnits = await pointOfCareService.FindAllByUnitIds(unitIds);
|
||||
|
||||
foreach (var unit in data) unit.PointOfCares = allPocsForUnits.Where(poc => poc.UnitId == unit.Id).ToList();
|
||||
}
|
||||
|
||||
return new PaginationResponse<Unit>(data, filter.PageNumber, filter.PageSize, count);
|
||||
}
|
||||
|
||||
// public Task<Unit?> GetByPointOfCare(PointOfCare pointOfCare)
|
||||
// {
|
||||
// return _unitRepository.FindByPointOfCare(pointOfCare);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Unit"/> by its identifier, optionally hydrating its related master lists (e.g., allergy, diagnosis, origin, doctor, procedure, service, treatment, visit option, access control, language barrier, passive sitting, generic lists) for the specified locale, and optionally including its associated Points of Care.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the unit to retrieve.</param>
|
||||
/// <param name="dataLocale">The locale used to resolve localized values for the related master lists; can be <see langword="null"/>.</param>
|
||||
/// <param name="fillLists">When <see langword="true"/> (default), populates every available related master list referenced by the unit using the given locale; when <see langword="false"/>, only the base unit is returned.</param>
|
||||
/// <param name="withPoCs">When <see langword="true"/>, also loads and assigns the Points of Care associated with the unit; when <see langword="false"/> (default), the Points of Care collection is not populated.</param>
|
||||
/// <returns>A <see cref="Task{Unit}"/> that yields the requested <see cref="Unit"/> with its optional related lists and Points of Care, or <see langword="null"/> when no unit matches the identifier.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no unit is found for the supplied <paramref name="id"/>.</exception>
|
||||
public async Task<Unit?> GetInfo(ObjectId id, LocaleEnum? dataLocale, bool fillLists = true, bool withPoCs = false)
|
||||
{
|
||||
var unit = await Get(id.ToString()) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
//if (unit == null )// || unit.PointOfCares == null tiene sentido?
|
||||
//{
|
||||
// _logger.LogError("Section not found: {id}", id);
|
||||
// return null;
|
||||
//}
|
||||
|
||||
if (fillLists)
|
||||
{
|
||||
if (unit.AltableOptionListId.HasValue)
|
||||
unit.AltableOptionList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.AltableOptionList,
|
||||
unit.AltableOptionListId.Value, dataLocale) as AltableOptionList;
|
||||
if (unit.AllergyListId.HasValue)
|
||||
unit.AllergyList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.AllergyList,
|
||||
unit.AllergyListId.Value, dataLocale) as AllergyList;
|
||||
if (unit.DestinationListId.HasValue)
|
||||
unit.DestinationList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DestinationList,
|
||||
unit.DestinationListId.Value, dataLocale) as DestinationList;
|
||||
if (unit.InternalDestinationListId.HasValue)
|
||||
unit.InternalDestinationList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.InternalDestinationList,
|
||||
unit.InternalDestinationListId.Value, dataLocale) as InternalDestinationList;
|
||||
if (unit.DiagnosisListId.HasValue)
|
||||
unit.DiagnosisList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DiagnosisList,
|
||||
unit.DiagnosisListId.Value, dataLocale) as DiagnosisList;
|
||||
if (unit.DischargeStatusListId.HasValue)
|
||||
unit.DischargeStatusList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DischargeStatusList,
|
||||
unit.DischargeStatusListId.Value, dataLocale) as DischargeStatusList;
|
||||
if (unit.DoctorListId.HasValue)
|
||||
unit.DoctorList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DoctorList, unit.DoctorListId.Value,
|
||||
dataLocale) as DoctorList;
|
||||
if (unit.DoctorTypeListId.HasValue)
|
||||
unit.DoctorTypeList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DoctorTypeList,
|
||||
unit.DoctorTypeListId.Value, dataLocale) as DoctorTypeList;
|
||||
if (unit.InsulationListId.HasValue)
|
||||
unit.InsulationList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.InsulationList,
|
||||
unit.InsulationListId.Value, dataLocale) as InsulationList;
|
||||
if (unit.MobilityOptionListId.HasValue)
|
||||
unit.MobilityOptionList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.MobilityOptionList,
|
||||
unit.MobilityOptionListId.Value, dataLocale) as MobilityOptionList;
|
||||
if (unit.OriginListId.HasValue)
|
||||
unit.OriginList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.OriginList, unit.OriginListId.Value,
|
||||
dataLocale) as OriginList;
|
||||
if (unit.PatientStatusListId.HasValue)
|
||||
unit.PatientStatusList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.PatientStatusList,
|
||||
unit.PatientStatusListId.Value, dataLocale) as PatientStatusList;
|
||||
if (unit.ProcedureListId.HasValue)
|
||||
unit.ProcedureList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.ProcedureList,
|
||||
unit.ProcedureListId.Value, dataLocale) as ProcedureList;
|
||||
if (unit.TestListId.HasValue)
|
||||
unit.TestList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.TestList, unit.TestListId.Value,
|
||||
dataLocale) as TestList;
|
||||
if (unit.ServiceListId.HasValue)
|
||||
unit.ServiceList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.ServiceList,
|
||||
unit.ServiceListId.Value, dataLocale) as ServiceList;
|
||||
if (unit.TherapeuticCeilingListId.HasValue)
|
||||
unit.TherapeuticCeilingList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.TherapeuticCeilingList,
|
||||
unit.TherapeuticCeilingListId.Value, dataLocale) as TherapeuticCeilingList;
|
||||
if (unit.TreatmentListId.HasValue)
|
||||
unit.TreatmentList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.TreatmentList,
|
||||
unit.TreatmentListId.Value, dataLocale) as TreatmentList;
|
||||
if (unit.VisitOptionListId.HasValue)
|
||||
unit.VisitOptionList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.VisitOptionList,
|
||||
unit.VisitOptionListId.Value, dataLocale) as VisitOptionList;
|
||||
if (unit.AccessControlListId.HasValue)
|
||||
unit.AccessControlList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.AccessControlList,
|
||||
unit.AccessControlListId.Value, dataLocale) as AccessControlList;
|
||||
if (unit.LanguageBarrierListId.HasValue)
|
||||
unit.LanguageBarrierList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.LanguageBarrierList,
|
||||
unit.LanguageBarrierListId.Value, dataLocale) as LanguageBarrierList;
|
||||
if (unit.PassiveSittingListId.HasValue)
|
||||
unit.PassiveSittingList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.PassiveSittingList,
|
||||
unit.PassiveSittingListId.Value, dataLocale) as PassiveSittingList;
|
||||
if (unit.GenericListId.HasValue)
|
||||
unit.GenericList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.GenericList,
|
||||
unit.GenericListId.Value, dataLocale) as GenericList;
|
||||
var unit = await Get(id.ToString()) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
//if (unit == null )// || unit.PointOfCares == null tiene sentido?
|
||||
//{
|
||||
// _logger.LogError("Section not found: {id}", id);
|
||||
// return null;
|
||||
//}
|
||||
|
||||
if (fillLists)
|
||||
{
|
||||
if (unit.AltableOptionListId.HasValue)
|
||||
unit.AltableOptionList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.AltableOptionList,
|
||||
unit.AltableOptionListId.Value, dataLocale) as AltableOptionList;
|
||||
if (unit.AllergyListId.HasValue)
|
||||
unit.AllergyList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.AllergyList,
|
||||
unit.AllergyListId.Value, dataLocale) as AllergyList;
|
||||
if (unit.DestinationListId.HasValue)
|
||||
unit.DestinationList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DestinationList,
|
||||
unit.DestinationListId.Value, dataLocale) as DestinationList;
|
||||
if (unit.InternalDestinationListId.HasValue)
|
||||
unit.InternalDestinationList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.InternalDestinationList,
|
||||
unit.InternalDestinationListId.Value, dataLocale) as InternalDestinationList;
|
||||
if (unit.DiagnosisListId.HasValue)
|
||||
unit.DiagnosisList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DiagnosisList,
|
||||
unit.DiagnosisListId.Value, dataLocale) as DiagnosisList;
|
||||
if (unit.DischargeStatusListId.HasValue)
|
||||
unit.DischargeStatusList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DischargeStatusList,
|
||||
unit.DischargeStatusListId.Value, dataLocale) as DischargeStatusList;
|
||||
if (unit.DoctorListId.HasValue)
|
||||
unit.DoctorList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DoctorList, unit.DoctorListId.Value,
|
||||
dataLocale) as DoctorList;
|
||||
if (unit.DoctorTypeListId.HasValue)
|
||||
unit.DoctorTypeList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.DoctorTypeList,
|
||||
unit.DoctorTypeListId.Value, dataLocale) as DoctorTypeList;
|
||||
if (unit.InsulationListId.HasValue)
|
||||
unit.InsulationList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.InsulationList,
|
||||
unit.InsulationListId.Value, dataLocale) as InsulationList;
|
||||
if (unit.MobilityOptionListId.HasValue)
|
||||
unit.MobilityOptionList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.MobilityOptionList,
|
||||
unit.MobilityOptionListId.Value, dataLocale) as MobilityOptionList;
|
||||
if (unit.OriginListId.HasValue)
|
||||
unit.OriginList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.OriginList, unit.OriginListId.Value,
|
||||
dataLocale) as OriginList;
|
||||
if (unit.PatientStatusListId.HasValue)
|
||||
unit.PatientStatusList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.PatientStatusList,
|
||||
unit.PatientStatusListId.Value, dataLocale) as PatientStatusList;
|
||||
if (unit.ProcedureListId.HasValue)
|
||||
unit.ProcedureList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.ProcedureList,
|
||||
unit.ProcedureListId.Value, dataLocale) as ProcedureList;
|
||||
if (unit.TestListId.HasValue)
|
||||
unit.TestList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.TestList, unit.TestListId.Value,
|
||||
dataLocale) as TestList;
|
||||
if (unit.ServiceListId.HasValue)
|
||||
unit.ServiceList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.ServiceList,
|
||||
unit.ServiceListId.Value, dataLocale) as ServiceList;
|
||||
if (unit.TherapeuticCeilingListId.HasValue)
|
||||
unit.TherapeuticCeilingList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.TherapeuticCeilingList,
|
||||
unit.TherapeuticCeilingListId.Value, dataLocale) as TherapeuticCeilingList;
|
||||
if (unit.TreatmentListId.HasValue)
|
||||
unit.TreatmentList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.TreatmentList,
|
||||
unit.TreatmentListId.Value, dataLocale) as TreatmentList;
|
||||
if (unit.VisitOptionListId.HasValue)
|
||||
unit.VisitOptionList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.VisitOptionList,
|
||||
unit.VisitOptionListId.Value, dataLocale) as VisitOptionList;
|
||||
if (unit.AccessControlListId.HasValue)
|
||||
unit.AccessControlList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.AccessControlList,
|
||||
unit.AccessControlListId.Value, dataLocale) as AccessControlList;
|
||||
if (unit.LanguageBarrierListId.HasValue)
|
||||
unit.LanguageBarrierList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.LanguageBarrierList,
|
||||
unit.LanguageBarrierListId.Value, dataLocale) as LanguageBarrierList;
|
||||
if (unit.PassiveSittingListId.HasValue)
|
||||
unit.PassiveSittingList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.PassiveSittingList,
|
||||
unit.PassiveSittingListId.Value, dataLocale) as PassiveSittingList;
|
||||
if (unit.GenericListId.HasValue)
|
||||
unit.GenericList =
|
||||
await masterListServiceFactory.GetMasterListById(MasterListType.GenericList,
|
||||
unit.GenericListId.Value, dataLocale) as GenericList;
|
||||
}
|
||||
|
||||
if (withPoCs)
|
||||
{
|
||||
var pocList = await pointOfCareService.FindAllByUnitId(unit.Id);
|
||||
unit.PointOfCares = pocList?.ToList();
|
||||
}
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
if (withPoCs)
|
||||
{
|
||||
var pocList = await pointOfCareService.FindAllByUnitId(unit.Id);
|
||||
unit.PointOfCares = pocList?.ToList();
|
||||
}
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a unit by its identifier and, when requested, enriches it with its associated points of care (including their devices).
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the unit to look up.</param>
|
||||
/// <param name="withPoCs">When true, loads and assigns the unit's points of care to the result.</param>
|
||||
/// <param name="withDevices">Flag intended to control device inclusion alongside the points of care.</param>
|
||||
/// <returns>The matching <see cref="Unit"/> instance.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no unit exists for the supplied <paramref name="id"/>.</exception>
|
||||
public async Task<Unit?> GetInfo(ObjectId id, bool withPoCs = true, bool withDevices = true)
|
||||
{
|
||||
var unit = await Get(id.ToString()) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
if (withPoCs)
|
||||
{
|
||||
var pocList = await pointOfCareService.FindAllByUnitIdWithDevices(unit.Id);
|
||||
unit.PointOfCares = pocList?.ToList();
|
||||
var unit = await Get(id.ToString()) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
if (withPoCs)
|
||||
{
|
||||
var pocList = await pointOfCareService.FindAllByUnitIdWithDevices(unit.Id);
|
||||
unit.PointOfCares = pocList?.ToList();
|
||||
}
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a unit by its name from the repository. If no matching unit is found, a not-found exception is thrown.
|
||||
/// </summary>
|
||||
/// <param name="itemUnitName">The name of the unit to search for.</param>
|
||||
/// <returns>The unit matching the specified name.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no unit exists with the provided name.</exception>
|
||||
public async Task<Unit?> GetByName(string itemUnitName)
|
||||
{
|
||||
return await unitRepository.FindByName(itemUnitName) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
public async Task<Unit?> FindByPatientId(ObjectId patientId)
|
||||
{
|
||||
//var sections = await GetAll();
|
||||
var patient = await patientService.Value.FindById(patientId);
|
||||
// if (patient != null && !string.IsNullOrEmpty(patient.Bed) && patient.IsInActivePoC())
|
||||
// {
|
||||
// // Deberia ser una lista? revisar como gestionar varias unidades con eel mismo pointOfCare
|
||||
// return sections.FirstOrDefault(s => s.PointOfCares.Any(c=>c.Bed==patient.Bed && c.UnitName == patient.UnitString));
|
||||
// }
|
||||
if (patient is { UnitId: not null }) return await FindById(patient.UnitId);
|
||||
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Unit>?> FindUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await unitRepository.FindByMasterListId(masterListId, masterListType);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("Exception finding by MasterId{id} section exception:{e} ", masterListId, ex.Message);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<long> CountUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType)
|
||||
{
|
||||
return await unitRepository.CountUnitsByMasterListId(masterListId, masterListType);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Unit>> FindUnitsByMasterListId(ObjectId masterListId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await unitRepository.FindByMasterListId(masterListId);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("Exception finding by MasterId{id} section exception:{e} ", masterListId, ex.Message);
|
||||
|
||||
return new List<Unit>();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Unit?> UpdateUnitMasterList(UpdateUnitIdListDto updateUnitListDto)
|
||||
{
|
||||
var unit = await FindById(updateUnitListDto.UnitId) ??
|
||||
return await unitRepository.FindByName(itemUnitName) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var updatedUnit = await unitRepository.UpdateUnitMasterList(updateUnitListDto) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, unit, updatedUnit);
|
||||
return updatedUnit;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateConfiguration(ObjectId unitIdParsed, UnitConfiguration unitConfiguration)
|
||||
{
|
||||
var oldConfig = await FindById(unitIdParsed) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var result = await unitRepository.UpdateConfiguration(unitIdParsed, unitConfiguration);
|
||||
var newConfig = await FindById(unitIdParsed) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
if (!result) throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldConfig, newConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<Unit?> FindById(ObjectId? id)
|
||||
{
|
||||
if (id == null)
|
||||
return null;
|
||||
return await unitRepository.FindById(id);
|
||||
}
|
||||
|
||||
public async Task<Unit?> FindByName(string? name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return null;
|
||||
return await unitRepository.FindByName(name);
|
||||
}
|
||||
|
||||
public async Task<Unit?> FindByUnitNameOrPocName(string? name, string? pocName)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
return await unitRepository.FindByName(name);
|
||||
|
||||
if (!string.IsNullOrEmpty(pocName))
|
||||
{
|
||||
var poc = await pointOfCareService.FindByBed(pocName);
|
||||
if (poc != null)
|
||||
return await FindById(poc.FirstOrDefault()?.UnitId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Finds the <see cref="Unit"/> associated with a given patient by resolving the patient record and returning its linked unit.
|
||||
/// Returns the unit only when the patient exists and has a non-null <c>UnitId</c>; otherwise, throws a not-found exception.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The identifier of the patient whose associated unit should be retrieved.</param>
|
||||
/// <returns>A <see cref="Task{Unit}"/> containing the associated <see cref="Unit"/> if found, or <c>null</c> when no matching unit exists.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the patient has no associated <c>UnitId</c> (i.e., the resource is missing).</exception>
|
||||
public async Task<Unit?> FindByPatientId(ObjectId patientId)
|
||||
{
|
||||
//var sections = await GetAll();
|
||||
var patient = await patientService.Value.FindById(patientId);
|
||||
// if (patient != null && !string.IsNullOrEmpty(patient.Bed) && patient.IsInActivePoC())
|
||||
// {
|
||||
// // Deberia ser una lista? revisar como gestionar varias unidades con eel mismo pointOfCare
|
||||
// return sections.FirstOrDefault(s => s.PointOfCares.Any(c=>c.Bed==patient.Bed && c.UnitName == patient.UnitString));
|
||||
// }
|
||||
if (patient is { UnitId: not null }) return await FindById(patient.UnitId);
|
||||
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves a collection of <see cref="Unit"/> entities associated with the specified master list identifier and type.
|
||||
/// If an exception occurs during the lookup, the error is logged and <c>null</c> is returned.
|
||||
/// </summary>
|
||||
/// <param name="masterListId">The identifier of the master list used to find the associated units.</param>
|
||||
/// <param name="masterListType">The type of the master list used to filter the units.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a collection of <see cref="Unit"/> entities if found, or <c>null</c> if an error occurs.</returns>
|
||||
public async Task<IEnumerable<Unit>?> FindUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await unitRepository.FindByMasterListId(masterListId, masterListType);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("Exception finding by MasterId{id} section exception:{e} ", masterListId, ex.Message);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously counts the number of units associated with the specified master list identifier and master list type by delegating to the unit repository.
|
||||
/// </summary>
|
||||
/// <param name="masterListId">The unique identifier of the master list whose units should be counted.</param>
|
||||
/// <param name="masterListType">The type of the master list used to filter the units to be counted.</param>
|
||||
/// <returns>A <see cref="Task{Int64}"/> representing the asynchronous operation, containing the total number of units that match the given master list identifier and type.</returns>
|
||||
public async Task<long> CountUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType)
|
||||
{
|
||||
return await unitRepository.CountUnitsByMasterListId(masterListId, masterListType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a collection of units associated with the specified master list identifier.
|
||||
/// On failure, logs the exception and returns an empty list as a fallback.
|
||||
/// </summary>
|
||||
/// <param name="masterListId">The identifier of the master list whose units should be retrieved.</param>
|
||||
/// <returns>A task that yields the collection of <see cref="Unit"/> items matching the master list identifier, or an empty list if an error occurs.</returns>
|
||||
public async Task<IEnumerable<Unit>> FindUnitsByMasterListId(ObjectId masterListId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await unitRepository.FindByMasterListId(masterListId);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("Exception finding by MasterId{id} section exception:{e} ", masterListId, ex.Message);
|
||||
|
||||
return new List<Unit>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the master list information of an existing unit identified by the provided identifier.
|
||||
/// Throws a not found exception if the unit does not exist and a conflict exception if the update operation fails.
|
||||
/// </summary>
|
||||
/// <param name="updateUnitListDto">The data transfer object containing the unit identifier and the updated master list information.</param>
|
||||
/// <returns>The updated <see cref="Unit"/> if the operation succeeds; otherwise, <c>null</c> when the underlying update returns no result.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no unit is found matching the identifier specified in <paramref name="updateUnitListDto"/>.</exception>
|
||||
/// <exception cref="ConflictException">Thrown when the update operation performed by the repository fails to produce a result.</exception>
|
||||
public async Task<Unit?> UpdateUnitMasterList(UpdateUnitIdListDto updateUnitListDto)
|
||||
{
|
||||
var unit = await FindById(updateUnitListDto.UnitId) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var updatedUnit = await unitRepository.UpdateUnitMasterList(updateUnitListDto) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, unit, updatedUnit);
|
||||
return updatedUnit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the configuration of an existing unit and records an audit log entry capturing the previous and resulting state.
|
||||
/// </summary>
|
||||
/// <param name="unitIdParsed">The parsed identifier of the unit whose configuration should be updated.</param>
|
||||
/// <param name="unitConfiguration">The new configuration values to apply to the unit.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the configuration was successfully updated; otherwise, <c>false</c>.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the unit cannot be found either before or after the update operation.</exception>
|
||||
/// <exception cref="ConflictException">Thrown when the underlying update operation fails to persist the new configuration.</exception>
|
||||
public async Task<bool> UpdateConfiguration(ObjectId unitIdParsed, UnitConfiguration unitConfiguration)
|
||||
{
|
||||
var oldConfig = await FindById(unitIdParsed) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var result = await unitRepository.UpdateConfiguration(unitIdParsed, unitConfiguration);
|
||||
var newConfig = await FindById(unitIdParsed) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
if (!result) throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldConfig, newConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a unit by its identifier. Returns null when the provided identifier is null; otherwise, delegates the lookup to the unit repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the unit to find.</param>
|
||||
/// <returns>The unit matching the specified identifier, or null if the identifier is null.</returns>
|
||||
public async Task<Unit?> FindById(ObjectId? id)
|
||||
{
|
||||
if (id == null)
|
||||
return null;
|
||||
return await unitRepository.FindById(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Unit"/> by its name, returning <see langword="null"/> when the provided name is null or empty.
|
||||
/// Otherwise, delegates the lookup to the unit repository.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the unit to look up. Can be <see langword="null"/> or empty.</param>
|
||||
/// <returns>A <see cref="Task{Unit}"/> containing the matching <see cref="Unit"/>, or <see langword="null"/> if no name was provided.</returns>
|
||||
public async Task<Unit?> FindByName(string? name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return null;
|
||||
return await unitRepository.FindByName(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a unit by its name, or alternatively by a point of care bed identifier when no name is provided.
|
||||
/// If the name is supplied, the unit is looked up directly; otherwise, the point of care is resolved from the bed and the associated unit is returned.
|
||||
/// Returns <c>null</c> when neither a name nor a matching point of care is available, or when no unit is found.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the unit to search for. Takes precedence over <paramref name="pocName"/> when provided.</param>
|
||||
/// <param name="pocName">The point of care bed identifier used as a fallback to locate the unit when <paramref name="name"/> is not supplied.</param>
|
||||
/// <returns>A task containing the matching <see cref="Unit"/>, or <c>null</c> if no unit can be resolved from the given inputs.</returns>
|
||||
public async Task<Unit?> FindByUnitNameOrPocName(string? name, string? pocName)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
return await unitRepository.FindByName(name);
|
||||
|
||||
if (!string.IsNullOrEmpty(pocName))
|
||||
{
|
||||
var poc = await pointOfCareService.FindByBed(pocName);
|
||||
if (poc != null)
|
||||
return await FindById(poc.FirstOrDefault()?.UnitId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
// public async Task<Unit?> FindByPointOfCare(PointOfCare pointOfCare)
|
||||
// {
|
||||
// return await _unitRepository.FindByPointOfCare(pointOfCare);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new unit into the repository and records an audit log entry for the operation using the current HTTP context user. Throws a <see cref="ConflictException"/> when the repository fails to create the unit.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit entity to be inserted.</param>
|
||||
/// <returns>The newly created unit returned by the repository.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the repository returns null, indicating that the unit could not be created.</exception>
|
||||
public async Task<Unit?> InsertOne(Unit unit)
|
||||
{
|
||||
var newUnit = await unitRepository.InsertOneUnit(unit) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, newUnit);
|
||||
return newUnit;
|
||||
}
|
||||
{
|
||||
var newUnit = await unitRepository.InsertOneUnit(unit) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, newUnit);
|
||||
return newUnit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing <see cref="Unit"/> in the repository, creating an audit log entry and broadcasting the change to interested parties.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit containing the updated information, including the identifier of the existing unit to modify.</param>
|
||||
/// <returns>The updated <see cref="Unit"/> if the operation succeeded; <c>null</c> if the repository could not persist the update.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no unit exists with the specified identifier.</exception>
|
||||
public async Task<Unit?> UpdateUnit(Unit unit)
|
||||
{
|
||||
var oldUnit = await FindById(unit.Id) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var newUnit = await unitRepository.UpdateUnit(unit);
|
||||
if (newUnit == null)
|
||||
return null;
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldUnit, newUnit);
|
||||
|
||||
SendUnitBroadcast(newUnit, OperationType.UpdateUnit);
|
||||
return newUnit;
|
||||
}
|
||||
{
|
||||
var oldUnit = await FindById(unit.Id) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var newUnit = await unitRepository.UpdateUnit(unit);
|
||||
if (newUnit == null)
|
||||
return null;
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldUnit, newUnit);
|
||||
|
||||
SendUnitBroadcast(newUnit, OperationType.UpdateUnit);
|
||||
return newUnit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the name and title of an existing unit, records the change in the audit log, and broadcasts the update to subscribed clients.
|
||||
/// </summary>
|
||||
/// <param name="unitId">The unique identifier of the unit to update.</param>
|
||||
/// <param name="name">The new name to assign to the unit.</param>
|
||||
/// <param name="title">The new title to assign to the unit.</param>
|
||||
/// <param name="configObsId">Optional configuration observer identifier associated with the update.</param>
|
||||
/// <returns>The updated unit when the operation succeeds; otherwise, null.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no unit is found for the provided identifier.</exception>
|
||||
/// <exception cref="ConflictException">Thrown when the underlying unit update operation fails.</exception>
|
||||
public async Task<Unit?> UpdateUnitInfo(ObjectId unitId, string name, string title, string? configObsId = null)
|
||||
{
|
||||
var oldUnit = await FindById(unitId) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var newUnit = await unitRepository.UpdateUnitInfo(unitId, name, title) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldUnit, newUnit);
|
||||
|
||||
if (newUnit != null) SendUnitBroadcast(newUnit, OperationType.UpdateUnit);
|
||||
return newUnit;
|
||||
}
|
||||
{
|
||||
var oldUnit = await FindById(unitId) ??
|
||||
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
var newUnit = await unitRepository.UpdateUnitInfo(unitId, name, title) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldUnit, newUnit);
|
||||
|
||||
if (newUnit != null) SendUnitBroadcast(newUnit, OperationType.UpdateUnit);
|
||||
return newUnit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a unit from the repository by its identifier and records an audit log entry for the operation.
|
||||
/// Throws a <see cref="ConflictException"/> when the underlying delete operation does not complete successfully.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit entity to delete, identified by its <c>Id</c>.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the unit is successfully deleted.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the delete operation fails (returns <c>null</c>).</exception>
|
||||
public async Task<bool> DeleteUnitById(Unit unit)
|
||||
{
|
||||
//if (unit is not { Status: null }) throw new ConflictException(ErrorMessage.Conflict_ResourceInUse);
|
||||
_ = await unitRepository.DeleteAsync(unit.Id) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, unit, null);
|
||||
return true;
|
||||
}
|
||||
{
|
||||
//if (unit is not { Status: null }) throw new ConflictException(ErrorMessage.Conflict_ResourceInUse);
|
||||
_ = await unitRepository.DeleteAsync(unit.Id) ??
|
||||
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, unit, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Unit"/> by attempting multiple lookup strategies: first by ObjectId, then by title, and finally by name.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier used to locate the unit. It can be an ObjectId, a title, or a name.</param>
|
||||
/// <returns>The matching <see cref="Unit"/> if found; otherwise, <c>null</c>.</returns>
|
||||
public async Task<Unit?> Get(string id)
|
||||
{
|
||||
Unit? section = null;
|
||||
|
||||
if (ObjectId.TryParse(id, out var oid)) section = await FindById(oid);
|
||||
|
||||
var sections = await GetAll();
|
||||
|
||||
section ??= sections.FirstOrDefault(s => s.Title == id);
|
||||
|
||||
section ??= sections.FirstOrDefault(s => s.Name == id);
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
public async Task<List<Unit>?> FindByLocation(PatientLocation location)
|
||||
{
|
||||
try
|
||||
{
|
||||
Unit? section = null;
|
||||
|
||||
if (ObjectId.TryParse(id, out var oid)) section = await FindById(oid);
|
||||
|
||||
var sections = await GetAll();
|
||||
// Cambiar por comparacion con Location?
|
||||
return sections.Where(section => section.PointOfCares != null &&
|
||||
section.PointOfCares.Any(c =>
|
||||
c.Bed == location.Bed && c.UnitName == location.UnitName)).ToList();
|
||||
|
||||
section ??= sections.FirstOrDefault(s => s.Title == id);
|
||||
|
||||
section ??= sections.FirstOrDefault(s => s.Name == id);
|
||||
|
||||
return section;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Exception finding by location section exception:{e} location: {location} ", e.Message,
|
||||
location);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of <see cref="Unit"/> entries that contain a point of care matching the specified <see cref="PatientLocation"/>, based on bed and unit name criteria.
|
||||
/// </summary>
|
||||
/// <param name="location">The <see cref="PatientLocation"/> providing the <c>Bed</c> and <c>UnitName</c> values used to filter the results.</param>
|
||||
/// <returns>A task containing a list of <see cref="Unit"/> entries whose point of care matches the given location, or <c>null</c> if an error occurs while retrieving the data.</returns>
|
||||
public async Task<List<Unit>?> FindByLocation(PatientLocation location)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sections = await GetAll();
|
||||
// Cambiar por comparacion con Location?
|
||||
return sections.Where(section => section.PointOfCares != null &&
|
||||
section.PointOfCares.Any(c =>
|
||||
c.Bed == location.Bed && c.UnitName == location.UnitName)).ToList();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Exception finding by location section exception:{e} location: {location} ", e.Message,
|
||||
location);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an asynchronous broadcast message about a unit operation to all subscribers whose location IDs match the points of care associated with the given unit. If no points of care are found for the unit, the method returns without sending any messages. Any errors encountered during the broadcast are logged without rethrowing.
|
||||
/// </summary>
|
||||
/// <param name="unit">The unit whose operation is being broadcast.</param>
|
||||
/// <param name="operation">The type of operation performed on the unit, sent as part of the broadcast message.</param>
|
||||
private async void SendUnitBroadcast(Unit unit, OperationType operation)
|
||||
{
|
||||
try
|
||||
{
|
||||
var locations = new List<ObjectId>();
|
||||
var pocs = await pointOfCareService.FindAllByUnitId(unit.Id);
|
||||
if (pocs == null)
|
||||
return;
|
||||
|
||||
locations.AddRange(pocs.Select(c => c.Id));
|
||||
var subscribers = subscribersService.GetSubscribers()
|
||||
.Where(s => s.LocationIds.Any(id => locations.Contains(id)))
|
||||
.ToList();
|
||||
|
||||
foreach (var subscriber in subscribers)
|
||||
_ = clientMessageService.Value.SendAsync(subscriber.Id, operation, unit);
|
||||
try
|
||||
{
|
||||
var locations = new List<ObjectId>();
|
||||
var pocs = await pointOfCareService.FindAllByUnitId(unit.Id);
|
||||
if (pocs == null)
|
||||
return;
|
||||
|
||||
locations.AddRange(pocs.Select(c => c.Id));
|
||||
var subscribers = subscribersService.GetSubscribers()
|
||||
.Where(s => s.LocationIds.Any(id => locations.Contains(id)))
|
||||
.ToList();
|
||||
|
||||
foreach (var subscriber in subscribers)
|
||||
_ = clientMessageService.Value.SendAsync(subscriber.Id, operation, unit);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(
|
||||
"Error sending Unit Broadcast {unit} with operation type {operationToString()} message: {eMessage}",
|
||||
unit, operation.ToString(), e.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(
|
||||
"Error sending Unit Broadcast {unit} with operation type {operationToString()} message: {eMessage}",
|
||||
unit, operation.ToString(), e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user