private void CleanupDatabase() { var log = Log.GetLogger(); var cfg = CapiConfig.GetConfig(); if (cfg == null) { throw new ApplicationException("Unable to find config file."); } var dbBroker = new DbBroker(cfg.AgentDbConnectionString); if (dbBroker.Database.EnsureCreated()) { log.Info("Database not found, so it has been created."); } else { log.Info("Database found."); } if (!dbBroker.Database.CanConnect()) { log.Error("Could not connect to database using string: " + cfg.AgentDbConnectionString); } else { log.Info("DB Connection established"); } try { log.Info("DB Connection good? " + dbBroker.Database.CanConnect()); var failedCases = dbBroker.GetCaseByStatus("Processing"); foreach (var c in failedCases) { var tmp = c; tmp.Status = "Pending"; dbBroker.Attempts.Update(tmp); dbBroker.SaveChanges(); } var failedJobs = dbBroker.GetJobByStatus("Processing"); foreach (var j in failedJobs) { var tmp = j; tmp.Status = "Failed"; dbBroker.Jobs.Update(tmp); dbBroker.SaveChanges(); } } catch (Exception e) { log.Error("Error while accesing database. If this is due to an incompatible schema, maybe try wiping the database and starting again?"); log.Error(e); } }
/// <summary> /// Builds a Job using details in Recipe /// </summary> /// <param name="recipe">Contains details of studies to process, source and destinations</param> /// <returns></returns> public Job Build(Recipe recipe, Attempt attempt) { if (string.IsNullOrEmpty(recipe.CurrentAccession)) { recipe.CurrentAccession = attempt.CurrentAccession; } // TODO: We probably don't need to replace the prior accession since the attempt shouldn't have anything at this stage. if (string.IsNullOrEmpty(recipe.PriorAccession)) { recipe.PriorAccession = attempt.PriorAccession; } // Setup out dicom service. var dicomSource = CreateDicomSource(recipe.SourceAet); // Get patient id var patientId = GetPatientId(recipe, dicomSource); // A list of all studies for the patient. // TODO: I don't love the fact that we can't just use the patient ID which we should have above. // But this code is defensive so I don't want to change it and introduce more bugs. var allStudiesForPatient = GetStudiesForPatient(patientId, recipe.PatientFullName, recipe.PatientBirthDate, dicomSource); // Update attempt in DB @attempt.PatientId = recipe.PatientId; _context.Attempts.Update(@attempt); _context.SaveChanges(); // If there are no studies, have a cry. if (allStudiesForPatient.Count < 1) { throw new StudyNotFoundException( $"No studies for patient [{recipe.PatientFullName}] could be found in node AET: [{dicomSource.RemoteNode.AeTitle}]"); } // Update patient ID in DB for a second time? @attempt.PatientId = allStudiesForPatient.FirstOrDefault().PatientId; @attempt.PatientFullName = allStudiesForPatient.FirstOrDefault().PatientsName; @attempt.PatientBirthDate = allStudiesForPatient.FirstOrDefault().PatientBirthDate.ToString("yyyyMMdd"); _context.Attempts.Update(@attempt); _context.SaveChanges(); // Update the recipe with all the details for the patient? recipe = UpdateRecipeWithPatientDetails(recipe, allStudiesForPatient); // Find Current Study _log.Info("Finding current series using recipe provided..."); var currentDicomStudy = GetCurrentDicomStudy(recipe, allStudiesForPatient, dicomSource); // Update case in DB with patient name and DOB @attempt.PatientFullName = recipe.PatientFullName; @attempt.PatientBirthDate = recipe.PatientBirthDate; _context.Attempts.Update(@attempt); _context.SaveChanges(); // If we don't have a matching study for current have a cry. if (currentDicomStudy == null || currentDicomStudy.Series.Count == 0) { throw new StudyNotFoundException("No workable series were found for accession"); } // Update attempt with accession number. @attempt.CurrentAccession = currentDicomStudy.AccessionNumber; @attempt.CurrentSeriesUID = currentDicomStudy.Series.FirstOrDefault().SeriesInstanceUid; _context.Attempts.Update(@attempt); _context.SaveChanges(); // Create a new job based on the recipe. var job = new Job(recipe, attempt); job.Status = "Pending"; job.AttemptId = attempt.Id; // Setup temp directory for images. var capiConfig = CapiConfig.GetConfig(); try { job.ProcessingFolder = Path.GetFullPath(Path.Combine(capiConfig.ImagePaths.ImageRepositoryPath, job.Attempt.CurrentAccession + "-" + job.Id)); } catch (Exception ex) { Log.GetLogger().Error($"{capiConfig.ImagePaths.ImageRepositoryPath} may not be a valid path."); Log.GetLogger().Error(ex.Message); } try { job.ResultSeriesDicomFolder = Path.GetFullPath(Path.Combine(job.ProcessingFolder, Results)); } catch (Exception ex) { Log.GetLogger().Error($"{Path.Combine(job.ProcessingFolder, Results)} may not be a valid path."); Log.GetLogger().Error(ex.Message); } try { job.PriorReslicedSeriesDicomFolder = Path.GetFullPath(Path.Combine(job.ProcessingFolder, PriorResliced)); } catch (Exception ex) { Log.GetLogger().Error($"{Path.Combine(job.ProcessingFolder, PriorResliced)} may not be a valid path."); Log.GetLogger().Error(ex.Message); } try { job.ReferenceSeriesDicomFolder = Path.GetFullPath(GetReferenceSeriesForRegistration(job, allStudiesForPatient, dicomSource)); } catch (Exception ex) { Log.GetLogger().Info($"{GetReferenceSeriesForRegistration(job, allStudiesForPatient, dicomSource)} may not be a valid path."); } // Find Prior Study (Floating) _log.Info("Finding prior series using recipe provided..."); var studyFixedIndex = allStudiesForPatient.IndexOf(currentDicomStudy); var priorDicomStudy = GetPriorDicomStudy(recipe, studyFixedIndex, allStudiesForPatient, dicomSource); // If we couldn't find the prior study, have a cry. if (priorDicomStudy == null) { throw new StudyNotFoundException("No prior workable series were found"); } // Otherwise update the attempt job.Attempt.PriorAccession = priorDicomStudy.AccessionNumber; job.Attempt.PatientId = currentDicomStudy.PatientId; // Update attempt with prior accession details. @attempt.PriorAccession = priorDicomStudy?.AccessionNumber; @attempt.PriorSeriesUID = priorDicomStudy?.Series?.FirstOrDefault()?.SeriesInstanceUid; @attempt.SourceAet = recipe.SourceAet; @attempt.DestinationAet = JsonConvert.SerializeObject(recipe.OutputSettings.DicomDestinations); @job.RecipeString = JsonConvert.SerializeObject(job.Recipe, new CapiConfigJsonConverter()); _context.Attempts.Update(@attempt); _context.Jobs.Update(@job); _context.SaveChanges(); // If both current and prior are found, save them to disk for processing _log.Info("Saving current series to disk..."); try { job.CurrentSeriesDicomFolder = SaveDicomFilesToFilesystem(currentDicomStudy, job.ProcessingFolder, Current, dicomSource); } catch (Exception ex) { _log.Error("Failed to save current series dicom files to disk.", ex); throw ex; } if (Directory.EnumerateFiles(job.CurrentSeriesDicomFolder).Count() < 1) { _log.Error($"Could not find any dicom files for current series in directory {job.CurrentSeriesDicomFolder}. Check that Accession exists and you can perform a CMOVE to this machine."); throw new Exception($"Could not find any dicom files for current series in directory {job.CurrentSeriesDicomFolder}. Check that Accession exists and you can perform a CMOVE to this machine."); } _log.Info($"Saved current series to [{job.CurrentSeriesDicomFolder}]"); _log.Info("Saving prior series to disk..."); try { job.PriorSeriesDicomFolder = SaveDicomFilesToFilesystem( priorDicomStudy, job.ProcessingFolder, Prior, dicomSource); } catch (Exception ex) { _log.Error("Failed to save prior series dicom files to disk.", ex); throw; } if (Directory.EnumerateFiles(job.PriorSeriesDicomFolder).Count() < 1) { _log.Error($"Could not find any dicom files for prior series in directory {job.PriorSeriesDicomFolder}. Check that Accession exists and you can perform a CMOVE to this machine."); throw new Exception($"Could not find any dicom files for prior series in directory {job.PriorSeriesDicomFolder}. Check that Accession exists and you can perform a CMOVE to this machine."); } _log.Info($"Saved prior series to [{job.PriorReslicedSeriesDicomFolder}]"); // All done. return(job); }