using System.Reflection; using System.Text; using adas_core.Application.Providers; using adas_core.Application.Services.Interfaces; using adas_core.Domain.Models; using adas_core.Domain.Models.AppSettings; using adas_core.Domain.Models.Providers; using adas_core.Domain.Utils; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Quartz; using Quartz.Impl; using Serilog; using ILogger = Serilog.ILogger; using Options = Microsoft.Extensions.Options.Options; namespace adas_core.Application.Services; public class SchedulerService { private readonly bool _activateCheckExpiredAlerts; private readonly bool _activateCheckExpiredObservations; private readonly bool _activateCheckInactivePatients; private readonly bool _activateSchedulerCheckActiveTreatmentsJob; private readonly bool _activateSchedulerCheckActiveAppointmentsJob; private readonly bool _activateSchedulerCheckHydricBalance; private readonly bool _activateSchedulerCheckNewsJob; private readonly bool _activateSchedulerCheckOpiateBoluses; private readonly bool _activateSchedulerGetProvidersObservations; private readonly ArchiveNurseData _archiveNurseData; private readonly int _archivePatientsWithoutObservationsSinceHours; private readonly Lazy _calculatedObservationsService; private readonly int _checkActiveTreatmentsSchedulerIntervalMinutes; private readonly int _checkActiveAppointmentsSchedulerIntervalMinutes; private readonly int _checkExpiredAlertsIntervalSeconds; private readonly int _checkExpiredObservationsIntervalMinutes; private readonly int _checkInactivePatientSchedulerIntervalHours; private readonly int _checkNewsJobIntervalMinutes; private readonly Lazy _configObservationService; private readonly int _getProviderObservationsIntervalMinutes; private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly Lazy _medicineService; private readonly Lazy _observationService; private readonly Lazy _patientProcedureService; private readonly Lazy _patientService; private readonly IOptions> _providersSettings; private readonly int _sinceDischargeTimeToArchive; private readonly Lazy _treatmentService; private readonly Lazy _appointmentService; private readonly Lazy _diagnosisService; public SchedulerService(IOptions apiSettings, IOptions> providersSettings, Lazy patientService, Lazy treatmentService, Lazy appointmentService, Lazy diagnosisService, Lazy medicineService, Lazy observationService, Lazy configObservationService, ILogger logger, IHttpClientFactory httpClientFactory, Lazy calculatedObservationsService, Lazy patientProcedureService) { _patientService = patientService; _treatmentService = treatmentService; _appointmentService = appointmentService; _diagnosisService = diagnosisService; _medicineService = medicineService; _observationService = observationService; _configObservationService = configObservationService; _providersSettings = providersSettings; _httpClientFactory = httpClientFactory; _calculatedObservationsService = calculatedObservationsService; _patientProcedureService = patientProcedureService; _logger = logger; //LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true); _archivePatientsWithoutObservationsSinceHours = apiSettings.Value.ArchivePatientsWithoutObservationsSinceHours ?? 24; var checkInactivePatientSchedulerIntervalHours = apiSettings.Value.CheckInactivePatientSchedulerIntervalHours; _checkInactivePatientSchedulerIntervalHours = checkInactivePatientSchedulerIntervalHours ?? 12; var checkActiveTreatmentsSchedulerIntervalMinutes = apiSettings.Value.CheckActiveTreatmentsSchedulerIntervalMinutes; _checkActiveTreatmentsSchedulerIntervalMinutes = checkActiveTreatmentsSchedulerIntervalMinutes ?? 5; var checkActiveAppointmentsSchedulerIntervalMinutes = apiSettings.Value.CheckActiveAppointmentsSchedulerIntervalMinutes; _checkActiveAppointmentsSchedulerIntervalMinutes = checkActiveAppointmentsSchedulerIntervalMinutes ?? 60; var getProvidersObservationsIntervalMinutes = apiSettings.Value.GetProvidersObservationsSchedulerIntervalMinutes; _getProviderObservationsIntervalMinutes = getProvidersObservationsIntervalMinutes ?? 5; var sinceDischargeTimeToArchive = apiSettings.Value.SinceDischargeTimeToArchive; _sinceDischargeTimeToArchive = sinceDischargeTimeToArchive ?? 48; var activateSchedulerCheckExpiredObservations = apiSettings.Value.ActivateSchedulerCheckExpiredObservation; _activateCheckExpiredObservations = activateSchedulerCheckExpiredObservations; _activateCheckExpiredAlerts = apiSettings.Value.ActivateCheckExpiredAlerts; _activateCheckInactivePatients = apiSettings.Value.ActivateCheckInactivePatients; _activateSchedulerCheckActiveTreatmentsJob = apiSettings.Value.ActivateSchedulerCheckActiveTreatmentsJob; _activateSchedulerCheckActiveAppointmentsJob = apiSettings.Value.ActivateSchedulerCheckActiveAppointmentsJob; _activateSchedulerCheckOpiateBoluses = apiSettings.Value.ActivateSchedulerCheckOpiateBoluses; _activateSchedulerCheckHydricBalance = apiSettings.Value.ActivateSchedulerCheckHydricBalance; _activateSchedulerGetProvidersObservations = apiSettings.Value.ActivateSchedulerGetProvidersObservations; _archiveNurseData = apiSettings.Value.ArchiveNurseData; var schedulerCheckExpiredObservationsIntervalMinutes = apiSettings.Value.SchedulerCheckExpiredObservationsIntervalMinutes; _checkExpiredObservationsIntervalMinutes = schedulerCheckExpiredObservationsIntervalMinutes ?? 3; var schedulerCheckExpiredAlertsIntervalSeconds = apiSettings.Value.SchedulerCheckExpiredAlertsIntervalSeconds; _checkExpiredAlertsIntervalSeconds = schedulerCheckExpiredAlertsIntervalSeconds ?? 6; _activateSchedulerCheckNewsJob = apiSettings.Value.ActivateSchedulerCheckNewsJob; _checkNewsJobIntervalMinutes = apiSettings.Value.CheckNewsJobIntervalMinutes; Task.Run(InitScheduler); } private async Task InitScheduler() { //TODO con el cambio a .net core y el repaso a la inyección de dependencias de estructure map //probar a inyectar la dependencia así https://dev.to/bohdanstupak1/dependency-injection-for-quartz-net-in-net-core-3oh7 CheckInactivePatientsJob.PatientService = _patientService.Value; CheckActiveTreatmentsJob.MedicineService = _medicineService.Value; CheckActiveTreatmentsJob.PatientService = _patientService.Value; CheckActiveTreatmentsJob.TreatmentService = _treatmentService.Value; CheckActiveTreatmentsJob.CalculatedObservationsService = _calculatedObservationsService.Value; CheckActiveAppointmentsJob.AppointmentService = _appointmentService.Value; CheckActiveAppointmentsJob.DiagnosisService = _diagnosisService.Value; CheckActiveAppointmentsJob.PatientService = _patientService.Value; ArchiveNurseDataJob.PatientCarePlanService = _patientProcedureService.Value; ArchiveNurseDataJob.PatientService = _patientService.Value; ArchiveNurseDataJob.ArchiveNurseDataSettings = _archiveNurseData; CheckOpiateBolusesJob.PatientService = _patientService.Value; CheckOpiateBolusesJob.CalculatedObservationsService = _calculatedObservationsService.Value; CheckHydricBalanceRyCJob.ObservationService = _observationService.Value; CheckHydricBalanceRyCJob.PatientService = _patientService.Value; CheckHydricBalanceRyCJob.CalculatedObservationsService = _calculatedObservationsService.Value; CheckExpiredObservationsJob.ObservationService = _observationService.Value; CheckExpiredAlertsJob.ObservationService = _observationService.Value; GetProvidersObservationsJob.ObservationService = _observationService.Value; GetProvidersObservationsJob.PatientService = _patientService.Value; GetProvidersObservationsJob.Providers = _providersSettings.Value; GetProvidersObservationsJob.HttpClientFactory = _httpClientFactory; CalculateNewsJob.ObservationService = _observationService.Value; CalculateNewsJob.ConfigObservationService = _configObservationService.Value; CalculateNewsJob.PatientService = _patientService.Value; CalculateNewsJob.ExpiresIn = _checkNewsJobIntervalMinutes; var jobDataMap = new JobDataMap { { "archivePatientsWithoutObservationsSinceHours", _archivePatientsWithoutObservationsSinceHours }, { "sinceDischargeTime", _sinceDischargeTimeToArchive } }; // Grab the Scheduler instance from the Factory var factory = new StdSchedulerFactory(); var scheduler = await factory.GetScheduler(); // and start it off await scheduler.Start(); var checkInactivePatientsJob = JobBuilder.Create() .UsingJobData(jobDataMap) .Build(); // Trigger the job to run now, and then repeat every 10 seconds var checkInactivePatientsTrigger = TriggerBuilder.Create() .StartNow() .WithSimpleSchedule(x => x .WithIntervalInHours(_checkInactivePatientSchedulerIntervalHours) .RepeatForever()) .Build(); var treatmentsJob = JobBuilder.Create() .Build(); // Trigger the job to run now, and then repeat every 10 seconds var treatmentsTrigger = TriggerBuilder.Create() .StartNow() .WithSimpleSchedule(x => x .WithIntervalInMinutes(_checkActiveTreatmentsSchedulerIntervalMinutes) .RepeatForever()) .Build(); var appointmentsJob = JobBuilder.Create() .Build(); var appointmentsTrigger = TriggerBuilder.Create() .StartNow() .WithSimpleSchedule(x => x .WithIntervalInMinutes(_checkActiveAppointmentsSchedulerIntervalMinutes) .RepeatForever()) .Build(); //Job only for RYC hydric balance. //Check every hour if hydric balance of that hour exists and recalculate. var hydricBalanceJob = JobBuilder.Create() .UsingJobData(jobDataMap) .Build(); var datetimePlusOneHour = DateTime.UtcNow.AddHours(1); var hydricBalanceTrigger = TriggerBuilder.Create() .StartAt(new DateTime(datetimePlusOneHour.Year, datetimePlusOneHour.Month, datetimePlusOneHour.Day, datetimePlusOneHour.Hour, 0, 0)) .WithSimpleSchedule(x => x .WithIntervalInHours(1) .RepeatForever()) .Build(); //Check_error_queues var opiateBolusesJob = JobBuilder.Create() .Build(); var opiateBolusesTrigger = TriggerBuilder.Create() .StartNow() .WithSimpleSchedule(x => x .WithIntervalInMinutes(5) .RepeatForever()) .Build(); //Job to expire expired observations //Check every minute if observations are expired and recalculate. var checkExpiredObservationsJob = JobBuilder.Create() .Build(); var checkExpiredObservationsTrigger = TriggerBuilder.Create() .StartNow() .WithSimpleSchedule(x => x .WithIntervalInMinutes(_checkExpiredObservationsIntervalMinutes) .RepeatForever()) .Build(); //Job to expire Alert //Check every minute if Alerts are expired and power off. var checkExpiredAlertsJob = JobBuilder.Create() .Build(); var checkExpiredAlertsTrigger = TriggerBuilder.Create() .StartAt(DateBuilder.FutureDate(30, IntervalUnit.Second)) // Comienza 30segundos después del inicio .WithSimpleSchedule(x => x //.WithIntervalInMinutes(_checkExpiredAlertsIntervalMinutes) .WithIntervalInSeconds(_checkExpiredAlertsIntervalSeconds) .RepeatForever()) .Build(); //Job to get Providers (ADAS) observations //Check every minute if Alerts are expired and power off. var getProvidersObservationsJob = JobBuilder.Create() .Build(); var getProvidersObservationsTrigger = TriggerBuilder.Create() .StartNow() .WithSimpleSchedule(x => x .WithIntervalInMinutes(_getProviderObservationsIntervalMinutes) .RepeatForever()) .Build(); //Job to get Providers (ADAS) observations //Check every minute if Alerts are expired and power off. var calculateNewsJob = JobBuilder.Create() .Build(); var calculateNewsJobTrigger = TriggerBuilder.Create() .StartNow() .WithSimpleSchedule(x => x .WithIntervalInMinutes(_checkNewsJobIntervalMinutes) .RepeatForever()) .StartNow() .Build(); //Job to ArchiveNurseDataConfig //Check every 15 minute if Patients has data to archive. var archiveNurseDataJob = JobBuilder.Create() .Build(); var archiveNurseDataJobTrigger = TriggerBuilder.Create() .StartNow() .WithSimpleSchedule(x => x .WithIntervalInMinutes(_archiveNurseData.IntervalMinutes) .RepeatForever()) .StartNow() .Build(); var jobsDictionary = new Dictionary>(); if (_activateCheckExpiredObservations) jobsDictionary.Add(checkExpiredObservationsJob, new List { checkExpiredObservationsTrigger }); if (_activateCheckExpiredAlerts) jobsDictionary.Add(checkExpiredAlertsJob, new List { checkExpiredAlertsTrigger }); if (_activateCheckInactivePatients) jobsDictionary.Add(checkInactivePatientsJob, new List { checkInactivePatientsTrigger }); if (_activateSchedulerCheckActiveTreatmentsJob) jobsDictionary.Add(treatmentsJob, new List { treatmentsTrigger }); if (_activateSchedulerCheckOpiateBoluses) jobsDictionary.Add(opiateBolusesJob, new List { opiateBolusesTrigger }); if (_activateSchedulerCheckHydricBalance) jobsDictionary.Add(hydricBalanceJob, new List { hydricBalanceTrigger }); if (_activateSchedulerGetProvidersObservations && _providersSettings.Value.Any()) jobsDictionary.Add(getProvidersObservationsJob, new List { getProvidersObservationsTrigger }); if (_activateSchedulerCheckNewsJob) jobsDictionary.Add(calculateNewsJob, new List { calculateNewsJobTrigger }); if (_archiveNurseData.Active) jobsDictionary.Add(archiveNurseDataJob, new List { archiveNurseDataJobTrigger }); _logger.LogDebug("Tell to schedule the job using our trigger. {jobsDictionary}", jobsDictionary); // Tell to schedule the job using our trigger await scheduler.ScheduleJobs(jobsDictionary, true); // and last shut down the scheduler when you are ready to close your program //await scheduler.Shutdown(); } } [DisallowConcurrentExecution] public class CheckHydricBalanceRyCJob : IJob { private readonly ILogger _logger = Log.ForContext(); public static IObservationService ObservationService { get; set; } = null!; public static IPatientService PatientService { get; set; } = null!; public static ICalculatedObservationsService CalculatedObservationsService { get; set; } = null!; public async Task Execute(IJobExecutionContext context) { var start = DateTime.Now; //LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true); _logger.Debug("Start check hydric balance job"); try { var patients = await PatientService.FindAll(); foreach (var patient in patients) { var lastHydricBalance = await ObservationService.FindLastBeforeDate(patient.Id, DateTime.UtcNow.AddMinutes(50), "Hydric_Balance"); if (lastHydricBalance != null && DateTime.Compare( new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0), new DateTime(lastHydricBalance.Time.Year, lastHydricBalance.Time.Month, lastHydricBalance.Time.Day, lastHydricBalance.Time.Hour, 0, 0)) == 0 ) await CalculatedObservationsService.Map(lastHydricBalance); } } catch (Exception ex) { _logger.Error("ERROR executing check hydric balance job {exMessage}", ex.Message); } var end = DateTime.Now; _logger.Debug("Finished check hydric balance job in {TotalMilliseconds:F1} ms", (end - start).TotalMilliseconds); } } [DisallowConcurrentExecution] public class CheckActiveTreatmentsJob : IJob { private readonly ILogger _logger = Log.ForContext(); public static ITreatmentService TreatmentService { get; set; } = null!; public static IMedicineService MedicineService { get; set; } = null!; public static IPatientService PatientService { get; set; } = null!; public static ICalculatedObservationsService CalculatedObservationsService { get; set; } = null!; public async Task Execute(IJobExecutionContext context) { var start = DateTime.Now; //LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true); _logger.Debug("Executing check active treatments job"); try { var patients = await PatientService.FindAll(); foreach (var patient in patients) { var activeMedicines = new List(); //var treatments = treatmentService.GetActiveTreatmentsByPatient(patient.id); var treatments = await CalculatedObservationsService.GetActiveTreatmentsByPatient(patient.Id); var medicines = await MedicineService.GetMedicinesOfTreatments(treatments); var medicinesList = medicines.ToList(); var medicinesWithoutNutrition = medicinesList.Where(m => !m.Type.Contains("Nutrition")) .ToList(); activeMedicines.AddRange(medicinesWithoutNutrition); await CalculatedObservationsService.CalculateMedicineObservation(activeMedicines, patient.Id); } } catch (Exception ex) { _logger.Error("ERROR executing check active treatments job {exMessage}", ex.Message); } var end = DateTime.Now; _logger.Debug("Finished check active treatments job in {TotalMilliseconds:F1} ms", (end - start).TotalMilliseconds); } } [DisallowConcurrentExecution] public class CheckActiveAppointmentsJob : IJob { private readonly ILogger _logger = Log.ForContext(); public static IAppointmentService AppointmentService { get; set; } = null!; public static IDiagnosisService DiagnosisService { get; set; } = null!; public static IPatientService PatientService { get; set; } = null!; public async Task Execute(IJobExecutionContext context) { var start = DateTime.Now; _logger.Debug("Executing check active appointments job"); try { //TODO falta definir } catch (Exception ex) { _logger.Error("ERROR executing check active treatments job {exMessage}", ex.Message); } var end = DateTime.Now; _logger.Debug("Finished check active treatments job in {TotalMilliseconds:F1} ms", (end - start).TotalMilliseconds); } } [DisallowConcurrentExecution] public class CheckInactivePatientsJob : IJob { private readonly ILogger _logger = Log.ForContext(); public static IPatientService PatientService { get; set; } = null!; public async Task Execute(IJobExecutionContext context) { //LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true); var start = DateTime.Now; var dataMap = context.JobDetail.JobDataMap; var archivePatientsWithoutObservationsSinceHours = dataMap.GetInt("archivePatientsWithoutObservationsSinceHours"); var sinceDischargeTime = dataMap.GetInt("sinceDischargeTime"); var sinceDate = DateTime.Now.AddHours(-archivePatientsWithoutObservationsSinceHours); _logger.Debug( "Executing Check inactive patients job archivePatientsWithoutObservationsSinceHours: {archivePatientsWithoutObservationsSinceHours} sinceDate: {sinceDate} sinceDischargeTime: {sinceDischargeTime}", archivePatientsWithoutObservationsSinceHours, sinceDate, sinceDischargeTime); try { var exists = GlobalData.Data.TryGetValue("isCheckingInactivePatients", out var isChecking); if (!exists || isChecking is false) await PatientService.DischargeInactivePatients(sinceDate, sinceDischargeTime); } catch (Exception ex) { _logger.Error("ERROR executing check inactive patients job {ExMessage}", ex.Message); } var end = DateTime.Now; _logger.Debug("Finished Check inactive patients job in {TotalMilliseconds:F1} ms", (end - start).TotalMilliseconds); } } [DisallowConcurrentExecution] public class CheckOpiateBolusesJob : IJob { private readonly ILogger _logger = Log.ForContext(); public static IPatientService PatientService { get; set; } = null!; public static ICalculatedObservationsService CalculatedObservationsService { get; set; } = null!; public async Task Execute(IJobExecutionContext context) { var start = DateTime.Now; //LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true); _logger.Debug("Executing check opiate boluses job"); try { var patients = await PatientService.FindAll(); foreach (var patient in patients) await CalculatedObservationsService.CalculateBolusOpiates(patient.Id); } catch (Exception ex) { _logger.Error("ERROR executing check opiate boluses job {ExMessage} trace: {ExStackTrace}", ex.Message, ex.StackTrace); } var end = DateTime.Now; _logger.Debug("Finished Check opiate boluses job in {TotalSeconds:F1} seconds", (end - start).TotalSeconds); } } [DisallowConcurrentExecution] public class CheckExpiredObservationsJob : IJob { private readonly ILogger _logger = Log.ForContext(); public static IObservationService ObservationService { get; set; } = null!; public async Task Execute(IJobExecutionContext context) { var start = DateTime.Now; //LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true); _logger.Debug("Executing check expired observations job"); try { var exists = GlobalData.Data.TryGetValue("isCheckingExpiration", out var isChecking); if (!exists) { GlobalData.AddData("isCheckingExpiration", false); isChecking = false; } if (!exists || isChecking is false) await ObservationService.ExpireObservationsAndRecalculateAsync(); } catch (Exception ex) { _logger.Error("ERROR executing check expired observations {ExMessage} trace: {ExStackTrace}", ex.Message, ex.StackTrace); } var end = DateTime.Now; _logger.Debug("Finished Check expired observations job in {TotalSeconds:F1} seconds", (end - start).TotalSeconds); } } /// /// Job for calculate NEWS based on FR, SPo2, Temperature, Tas and FC /// [DisallowConcurrentExecution] public class CalculateNewsJob : IJob { private readonly ILogger _logger = Log.ForContext(); public static IObservationService ObservationService { get; set; } = null!; public static IConfigObservationService ConfigObservationService { get; set; } = null!; public static IPatientService PatientService { get; set; } = null!; public static int ExpiresIn { get; set; } = 15; public async Task Execute(IJobExecutionContext context) { //Filter patients in real locations //LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true); var patients = await PatientService.FindInActivePoC(); var observationList = new List { "Resp_Rate_Calculated", "SpO2", "Temperature", "TAs", "FC" }; foreach (var patient in patients) { var observations = await CheckExpiredAlertsJob.ObservationService.FindLastObservations(patient.Id, 1, observationList); if (!observations.Any()) continue; var newsScore = 0; var msg = new StringBuilder(); _logger.Debug("CalculateNewsJob - patient ({patientPointOfCare} {patientBed})", patient.UnitString, patient.Bed); foreach (var obs in observations) { var conf = await ConfigObservationService.Get(obs); var expires = conf?.Expires; if (obs.Expired || (expires != null && obs.Time.AddSeconds(expires.Value).ToUniversalTime() < DateTime.UtcNow)) { _logger.Verbose( "CalculateNewsJob - patient ({patientPointOfCare} {patientBed}) obs ({obsName} value {obsValue} time {obsTime} expired {obsExpired}) confexpires: {expires} EXPIRED", patient.UnitString, patient.Bed, obs.Name, obs.Value, obs.Time, obs.Expired, expires); continue; } _logger.Verbose( "CalculateNewsJob - patient ({patientPointOfCare} {patientBed}) obs ({obsName} value {obsValue} time {obsTime} expired {obsExpired}) confexpires: {expires} NOT EXPIRED", patient.UnitString, patient.Bed, obs.Name, obs.Value, obs.Time, obs.Expired, expires); var partialNewsScore = 0; switch (obs.Name) { case "Resp_Rate_Calculated": if (!double.TryParse(obs.Value.ToString(), out var frValue)) continue; switch (Math.Round(frValue, 0)) { case >= 12 and <= 20: break; case >= 25: case <= 8: partialNewsScore = 3; break; case >= 9 and <= 11: partialNewsScore = 1; break; case >= 21 and <= 24: partialNewsScore = 2; break; } break; case "SpO2": if (!double.TryParse(obs.Value.ToString(), out var spo2Value)) continue; switch (Math.Round(spo2Value, 0)) { case <= 91: partialNewsScore = 3; break; case >= 92 and <= 93: partialNewsScore = 2; break; case >= 94 and <= 95: partialNewsScore = 1; break; } break; case "Temperature": if (!double.TryParse(obs.Value.ToString(), out var tempValue)) continue; switch (Math.Round(tempValue, 1)) { case <= 35: partialNewsScore = 3; break; case >= 35.1 and <= 36.0: case >= 38.1 and <= 39.0: partialNewsScore = 1; break; case >= 39.1: partialNewsScore = 2; break; } break; case "TAs": if (!double.TryParse(obs.Value.ToString(), out var tasValue)) continue; switch (Math.Round(tasValue, 0)) { case <= 90: case >= 220: partialNewsScore = 3; break; case >= 91 and <= 100: partialNewsScore = 2; break; case >= 101 and <= 110: partialNewsScore = 1; break; } break; case "FC": if (!double.TryParse(obs.Value.ToString(), out var fcValue)) continue; switch (Math.Round(fcValue, 0)) { case <= 40: case >= 131: partialNewsScore = 3; break; case >= 111 and <= 130: partialNewsScore = 2; break; case >= 41 and <= 50: case >= 91 and <= 110: partialNewsScore = 1; break; } break; } msg.Append($"[{obs.Name} {obs.Value} {partialNewsScore}] "); newsScore += partialNewsScore; } var lastObs = await CheckExpiredAlertsJob.ObservationService.FindLastObservations(patient.Id, 1, observationList); if (lastObs.FirstOrDefault()?.Expired != true) { var newObs = new PatientObservation { Name = "NEWS", CodingSystem = "ADAS", Expires = ExpiresIn, Value = newsScore, PatientId = patient.Id, Time = DateTime.Now }; await CheckExpiredAlertsJob.ObservationService.InsertObservation(newObs); _logger.Debug( $"CalculateNewsJob - patient ({patient.UnitString} {patient.Bed}) newsScore: {newsScore} from {msg}"); } } } } [DisallowConcurrentExecution] public class CheckExpiredAlertsJob : IJob { private readonly ILogger _logger = Log.ForContext(); public static IObservationService ObservationService { get; set; } = null!; public async Task Execute(IJobExecutionContext context) { var start = DateTime.Now; //LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true); _logger.Debug("Executing check expired alerts and power off job"); try { var exists = GlobalData.Data.TryGetValue("isCheckingAlertsExpiration", out var isChecking); if (!exists || isChecking is false) await ObservationService.ExpireAlertsAndPowerOffAsync(); } catch (Exception ex) { _logger.Error("ERROR executing check expired alerts and power off {ExMessage} trace: {ExStackTrace}", ex.Message, ex.StackTrace); } var end = DateTime.Now; _logger.Debug("Finished Check check expired alerts and power off job in {TotalSeconds:F1} seconds", (end - start).TotalSeconds); } } [DisallowConcurrentExecution] public class ArchiveNurseDataJob : IJob { private readonly ILogger _logger = Log.ForContext(); //private readonly ArchiveNurseData _archiveNurseData; public static ArchiveNurseData ArchiveNurseDataSettings { get; set; } = new(); public static IPatientService PatientService { get; set; } = null!; public static IPatientCarePlanService PatientCarePlanService { get; set; } = null!; public async Task Execute(IJobExecutionContext context) { var start = DateTime.Now; _logger.Debug("Executing ArchiveNurseDataJob job"); try { if (ArchiveNurseDataSettings.ArchiveProcedure.Active) { var patientWithFinishedProcedures = await PatientService.FindAllPatientWithFinishedProcedures(ArchiveNurseDataSettings.ArchiveProcedure .EndDateAfterMinutes); foreach (var patientWithFinishedProcedure in patientWithFinishedProcedures) { var itemsToArchive = patientWithFinishedProcedure.Procedures? .Where(c => c is { //OptionType: "procedure", EndDate: not null } && c.EndDate.Value.AddMinutes(ArchiveNurseDataSettings.ArchiveProcedure.EndDateAfterMinutes) < DateTime.UtcNow) .ToList(); if (itemsToArchive != null) { _ = PatientCarePlanService.ArchiveCarePlanFromJob(patientWithFinishedProcedure, itemsToArchive); patientWithFinishedProcedure.Procedures = patientWithFinishedProcedure.Procedures? .Where(c => !itemsToArchive.Contains(c)).ToList(); await PatientService.Update(patientWithFinishedProcedure); } } } if (ArchiveNurseDataSettings.ArchiveTest.Active) { var patientWithFinishedTests = await PatientService.FindAllPatientWithFinishedTest(ArchiveNurseDataSettings.ArchiveTest .EndDateAfterMinutes); foreach (var patientWithFinishedTest in patientWithFinishedTests) { var itemsToArchive = patientWithFinishedTest.Tests? .Where(c => c is { //OptionType: "test", EndDate: not null } && c.EndDate.Value.AddMinutes(ArchiveNurseDataSettings.ArchiveTest.EndDateAfterMinutes) < DateTime.UtcNow) .ToList(); if (itemsToArchive != null) { _ = PatientCarePlanService.ArchiveCarePlanFromJob(patientWithFinishedTest, itemsToArchive); patientWithFinishedTest.Tests = patientWithFinishedTest.Tests? .Where(c => !itemsToArchive.Contains(c)).ToList(); await PatientService.Update(patientWithFinishedTest); } } } if (ArchiveNurseDataSettings.ArchiveTreatment.Active) { var patientWithFinishedTreatments = await PatientService.FindAllPatientWithFinishedTreatment(ArchiveNurseDataSettings.ArchiveTreatment .EndDateAfterMinutes); foreach (var patientWithFinishedTreatment in patientWithFinishedTreatments) { var itemsToArchive = patientWithFinishedTreatment.Treatment? .Where(c => c.EndDate.HasValue && c.EndDate.Value.AddMinutes(ArchiveNurseDataSettings.ArchiveTest.EndDateAfterMinutes) < DateTime.UtcNow) .ToList(); if (itemsToArchive != null) { _ = PatientCarePlanService.ArchiveCarePlanFromJob(patientWithFinishedTreatment, itemsToArchive); patientWithFinishedTreatment.Treatment = patientWithFinishedTreatment.Treatment? .Where(c => !itemsToArchive.Contains(c)) .ToList(); await PatientService.Update(patientWithFinishedTreatment); } } } } catch (Exception ex) { _logger.Error("ERROR executing ArchiveNurseDataJob {ExMessage} trace: {ExStackTrace}", ex.Message, ex.StackTrace); } var end = DateTime.Now; _logger.Debug("Finished ArchiveNurseDataJob {TotalSeconds:F1} seconds", (end - start).TotalSeconds); } } [DisallowConcurrentExecution] public class GetProvidersObservationsJob : IJob { private readonly ILogger _logger = Log.ForContext(); public static IObservationService ObservationService { get; set; } = null!; public static IPatientService PatientService { get; set; } = null!; public static List Providers { get; set; } = null!; public static IHttpClientFactory HttpClientFactory { get; set; } = null!; public async Task Execute(IJobExecutionContext context) { var start = DateTime.Now; _logger.Debug("Executing get providers job"); try { if (!Providers.Any()) return; foreach (var provider in Providers) { //replace - to _ to fit name var providerType = Type.GetType( $"{Assembly.GetExecutingAssembly().GetName().Name?.Replace("-", "_")}.Providers.{provider.Name}Provider"); if (providerType == null) { Log.Error("Provider {ProviderName} not able lo load by reflection", provider.Name); continue; } var ctor = providerType.GetConstructor([ typeof(IOptions), typeof(IHttpClientFactory) ]); if (ctor == null) { Log.Error("not able to find constructor for provider: {ProviderName}", provider.Name); continue; } var customProvider = (BaseProvider)ctor.Invoke([ Options.Create(provider), HttpClientFactory ]); var patients = await PatientService.FindAll(); foreach (var patient in patients) { var observations = await customProvider.GetObservations(patient); if (observations.Any()) observations.ForEach(o => ObservationService.InsertObservation(o)); } } } catch (Exception ex) { _logger.Error( "ERROR executing GetByCodeSysAndCode providers observations job error: {ExMessage} trace: {ExStackTrace}", ex.Message, ex.StackTrace); } var end = DateTime.Now; _logger.Debug("Finished GetByCodeSysAndCode providers observations job in {TotalMilliseconds:F1} ms", (end - start).TotalMilliseconds); } }