예제 #1
0
        public DataTable ProcessPipelineData(DataTable toProcess, IDataLoadEventListener listener, GracefulCancellationToken cancellationToken)
        {
            //Things we ignore, Lookups, SupportingSql etc
            if (_extractCommand == null)
            {
                listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Ignoring non dataset command "));
                return(toProcess);
            }

            //if it isn't a dicom dataset don't process it
            if (!toProcess.Columns.Contains(RelativeArchiveColumnName))
            {
                listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Warning, "Dataset " + _extractCommand.DatasetBundle.DataSet + " did not contain field '" + RelativeArchiveColumnName + "' so we will not attempt to extract images"));
                return(toProcess);
            }

            if (_putter == null)
            {
                _putter = (IPutDicomFilesInExtractionDirectories)  new ObjectConstructor().Construct(PutterType);
            }

            var projectNumber = _extractCommand.Configuration.Project.ProjectNumber.Value;

            var mappingServer        = new MappingRepository(UIDMappingServer);
            var destinationDirectory = new DirectoryInfo(Path.Combine(_extractCommand.GetExtractionDirectory().FullName, "Images"));

            var releaseCol = _extractCommand.QueryBuilder.SelectColumns.Select(c => c.IColumn).Single(c => c.IsExtractionIdentifier);

            // See: ftp://medical.nema.org/medical/dicom/2011/11_15pu.pdf

            var flags = DicomAnonymizer.SecurityProfileOptions.BasicProfile |
                        DicomAnonymizer.SecurityProfileOptions.CleanStructdCont |
                        DicomAnonymizer.SecurityProfileOptions.CleanDesc |
                        DicomAnonymizer.SecurityProfileOptions.RetainUIDs;

            if (RetainDates)
            {
                flags |= DicomAnonymizer.SecurityProfileOptions.RetainLongFullDates;
            }

            var profile = DicomAnonymizer.SecurityProfile.LoadProfile(null, flags);

            var anonymiser = new DicomAnonymizer(profile);

            using (var pool = new ZipPool())
            {
                _sw.Start();

                foreach (DataRow row in toProcess.Rows)
                {
                    if (_errors > 0 && _errors > ErrorThreshold)
                    {
                        throw new Exception($"Number of errors reported ({_errors}) reached the threshold ({ErrorThreshold})");
                    }

                    cancellationToken.ThrowIfAbortRequested();

                    var path = new AmbiguousFilePath(ArchiveRootIfAny, (string)row[RelativeArchiveColumnName]);

                    DicomFile dicomFile;

                    try
                    {
                        dicomFile = path.GetDataset(pool);
                    }
                    catch (Exception e)
                    {
                        listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Error, $"Failed to get image at path '{path.FullPath}'", e));
                        _errors++;
                        continue;
                    }

                    //get the new patient ID
                    var releaseId = row[releaseCol.GetRuntimeName()].ToString();

                    DicomDataset ds;

                    try
                    {
                        ds = anonymiser.Anonymize(dicomFile.Dataset);
                    }
                    catch (Exception e)
                    {
                        listener.OnNotify(this, new NotifyEventArgs(ProgressEventType.Error, $"Failed to anonymize image at path '{path.FullPath}'", e));
                        _errors++;
                        continue;
                    }

                    //now we want to explicitly use our own release Id regardless of what FoDicom said
                    ds.AddOrUpdate(DicomTag.PatientID, releaseId);

                    //rewrite the UIDs
                    foreach (var kvp in UIDMapping.SupportedTags)
                    {
                        if (!ds.Contains(kvp.Key))
                        {
                            continue;
                        }

                        var value = ds.GetValue <string>(kvp.Key, 0);

                        //if it has a value for this UID
                        if (value == null)
                        {
                            continue;
                        }
                        var releaseValue = mappingServer.GetOrAllocateMapping(value, projectNumber, kvp.Value);

                        //change value in dataset
                        ds.AddOrUpdate(kvp.Key, releaseValue);

                        //and change value in DataTable
                        if (toProcess.Columns.Contains(kvp.Key.DictionaryEntry.Keyword))
                        {
                            row[kvp.Key.DictionaryEntry.Keyword] = releaseValue;
                        }
                    }

                    var newPath = _putter.WriteOutDataset(destinationDirectory, releaseId, ds);
                    row[RelativeArchiveColumnName] = newPath;

                    _anonymisedImagesCount++;

                    listener.OnProgress(this, new ProgressEventArgs("Writing ANO images", new ProgressMeasurement(_anonymisedImagesCount, ProgressType.Records), _sw.Elapsed));
                }

                _sw.Stop();
            }

            return(toProcess);
        }
예제 #2
0
        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        /// <exception cref="OperationCanceledException"></exception>
        public ExitCodeType Run(IDataLoadJob job, GracefulCancellationToken cancellationToken)
        {
            job.StartLogging();
            job.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Starting load for " + job.LoadMetadata.Name));

            try
            {
                foreach (var component in Components)
                {
                    cancellationToken.ThrowIfAbortRequested();

                    try
                    {
                        //schedule the component for disposal
                        job.PushForDisposal(component);

                        //run current component
                        var exitCodeType = component.Run(job, cancellationToken);

                        //current component failed so jump out, either because load not nessesary or crash
                        if (exitCodeType == ExitCodeType.OperationNotRequired)
                        {
                            TryDispose(exitCodeType, job);
                            //load not nessesary so abort entire DLE process but also cleanup still
                            return(exitCodeType);
                        }

                        if (exitCodeType != ExitCodeType.Success)
                        {
                            throw new Exception("Component " + component.Description + " returned result " + exitCodeType);
                        }
                    }
                    catch (OperationCanceledException e)
                    {
                        job.OnNotify(this, new NotifyEventArgs(ProgressEventType.Warning, component.Description + " has been cancelled by the user", e));
                        throw;
                    }
                    catch (Exception e)
                    {
                        job.OnNotify(this, new NotifyEventArgs(ProgressEventType.Error, component.Description + " crashed while running Job ", e));
                        job.OnNotify(this, new NotifyEventArgs(ProgressEventType.Error, "Job crashed", e));
                        TryDispose(ExitCodeType.Error, job);
                        return(ExitCodeType.Error);
                    }
                }

                TryDispose(ExitCodeType.Success, job);

                job.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Completed job " + job.JobID));

                return(ExitCodeType.Success);
            }
            catch (OperationCanceledException)
            {
                if (cancellationToken.IsAbortRequested)
                {
                    job.OnNotify(this, new NotifyEventArgs(ProgressEventType.Information, "Job " + job.JobID + "cancelled in pipeline"));
                }

                TryDispose(cancellationToken.IsAbortRequested ? ExitCodeType.Abort : ExitCodeType.Success, job);
                throw;
            }
        }