public void GivenCommonResourceWithStu3OnlyElement_WhenAnonymizing_ExceptionShouldBeThrown(string testFile)
        {
            AnonymizerEngine engine      = new AnonymizerEngine("r4-configuration-sample.json");
            string           testContent = File.ReadAllText(ResourceTestsFile(testFile));

            Assert.Throws <StructuralTypeException>(() => engine.AnonymizeJson(testContent));
        }
示例#2
0
        public void GivenCommonResourceWithR4OnlyField_WhenAnonymizing_ExceptionShouldBeThrown(string testFile)
        {
            AnonymizerEngine engine      = new AnonymizerEngine("stu3-configuration-sample.json");
            string           testContent = File.ReadAllText(ResourceTestsFile(testFile));

            Assert.Throws <FormatException>(() => engine.AnonymizeJson(testContent));
        }
示例#3
0
        public void GivenAPatientResource_WhenAnonymizingWithProcessingError_IfParameterNotGiven_ExceptionWillBeThrown()
        {
            AnonymizerEngine engine      = new AnonymizerEngine(Path.Combine("Configurations", "configuration-without-processing-error.json"));
            string           testContent = File.ReadAllText(ResourceTestsFile("patient-basic.json"));

            Assert.Throws <AnonymizerProcessingException>(() => engine.AnonymizeJson(testContent));
        }
示例#4
0
        public void GivenABundleResource_WhenAnonymizingWithProcessingError_IfRaise_ExceptionWillBeThrown()
        {
            AnonymizerEngine engine      = new AnonymizerEngine(Path.Combine("Configurations", "configuration-raise-processing-error.json"));
            string           testContent = File.ReadAllText(CollectionResourceTestsFile("bundle-basic.json"));

            Assert.Throws <AnonymizerProcessingException>(() => engine.AnonymizeJson(testContent));
        }
示例#5
0
        internal static async Task AnonymizeAsync(AnonymizerOptions options)
        {
            var engine = new AnonymizerEngine(
                options.ConfigurationFilePath,
                new AnonymizerEngineOptions(options.ValidateInput, options.ValidateOutput));

            if (options.InputFile != null && options.OutputFile != null)
            {
                if (IsSamePath(options.InputFile, options.OutputFile))
                {
                    throw new ArgumentException("Input and output file path are the same! Please check file names.");
                }

                await AnonymizeOneFileAsync(options.InputFile, options.OutputFile, engine);
            }
            else if (options.InputFolder != null && options.OutputFolder != null)
            {
                if (IsSamePath(options.InputFolder, options.OutputFolder))
                {
                    throw new ArgumentException("Input and output folders are the same! Please choose another folder.");
                }

                foreach (string file in Directory.EnumerateFiles(options.InputFolder, "*.dcm", SearchOption.AllDirectories))
                {
                    await AnonymizeOneFileAsync(file, Path.Join(options.OutputFolder, Path.GetRelativePath(options.InputFolder, file)), engine);
                }

                Console.WriteLine("Anonymization finished!");
            }
            else
            {
                throw new ArgumentException("Invalid parameters. Please specify inputFile (or inputFolder) and outputFile (or outputFolder) at the same time.\r\nSamples:\r\n [-i inputFile -o outputFile]\r\nor\r\n [-I inputFolder -O outputFolder]");
            }
        }
        public void Run(bool force = false)
        {
            _engine = new AnonymizerEngine("./configuration-sample.json");

            var input = LoadActivityInput();

            AnonymizeDataset(input, force).Wait();
        }
示例#7
0
        public void GivenIsPrettyOutputSetFalse_WhenAnonymizeJson_OneLineJsonOutputShouldBeReturned()
        {
            AnonymizerEngine engine = new AnonymizerEngine(Path.Combine("TestConfigurations", "configuration-test-sample.json"));

            var result = engine.AnonymizeJson(TestPatientSample);

            Assert.Equal(OneLineOutputTarget, result);
        }
示例#8
0
        public void GivenAR4OnlyResource_WhenAnonymizing_ExceptionShouldBeThrown(string testFile, string ResourceName)
        {
            AnonymizerEngine engine      = new AnonymizerEngine("stu3-configuration-sample.json");
            string           testContent = File.ReadAllText(ResourceTestsFile(testFile));
            var ex            = Assert.Throws <FormatException>(() => engine.AnonymizeJson(testContent));
            var expectedError = "type (at Cannot locate type information for type '" + ResourceName + "')";

            Assert.Equal(expectedError, ex.Message.ToString());
        }
        public static void VerifySingleJsonResourceFromFile(AnonymizerEngine engine, string testFile, string targetFile)
        {
            Console.WriteLine($"VerifySingleJsonResourceFromFile. TestFile: {testFile}, TargetFile: {targetFile}");
            string testContent          = File.ReadAllText(testFile);
            string targetContent        = File.ReadAllText(targetFile);
            string resultAfterAnonymize = engine.AnonymizeJson(testContent);

            Assert.Equal(Standardize(targetContent), Standardize(resultAfterAnonymize));
        }
示例#10
0
        public void Load(IServiceCollection services)
        {
            AnonymizerEngine.InitializeFhirPathExtensionSymbols();

            services.AddFactory <IScoped <IAnonymizer> >();

            services.Add <ExportAnonymizerFactory>()
            .Transient()
            .AsService <IAnonymizerFactory>();
        }
        public void GivenAPatientResource_WhenAnonymizingWithMultipleSubstituteConfig_ThenAnonymizedJsonShouldBeReturned()
        {
            // Child node is substituted first
            AnonymizerEngine engine = new AnonymizerEngine(Path.Combine("Configurations", "substitute-multiple.json"));

            FunctionalTestUtility.VerifySingleJsonResourceFromFile(engine, ResourceTestsFile("patient-substitute-multiple.json"), ResourceTestsFile("patient-substitute-multiple-target.json"));
            // Parent node is substituted first
            engine = new AnonymizerEngine(Path.Combine("Configurations", "substitute-multiple-2.json"));
            FunctionalTestUtility.VerifySingleJsonResourceFromFile(engine, ResourceTestsFile("patient-substitute-multiple-2.json"), ResourceTestsFile("patient-substitute-multiple-2-target.json"));
        }
        public async Task Run(bool force = false)
        {
            // Increase connection limit of single endpoint: 2 => 128
            System.Net.ServicePointManager.DefaultConnectionLimit = 128;
            AnonymizerEngine.InitializeFhirPathExtensionSymbols();

            var input = LoadActivityInput();

            await AnonymizeDataset(input, force).ConfigureAwait(false);
        }
示例#13
0
        public void GivenCommonResourceWithR4OnlyValue_WhenAnonymizing_ExceptionShouldBeThrown(string testFile)
        {
            AnonymizerEngine engine      = new AnonymizerEngine("stu3-configuration-sample.json");
            string           testContent = File.ReadAllText(ResourceTestsFile(testFile));

            var exception = Assert.Throws <InvalidInputException>(() => engine.AnonymizeJson(testContent));

            Assert.Equal("The input FHIR resource is invalid", exception.Message);
            Assert.True(exception.InnerException is StructuralTypeException);
            Assert.StartsWith("Type checking the data: Encountered unknown element", exception.InnerException.Message);
        }
示例#14
0
        public void GivenAR4OnlyResource_WhenAnonymizing_ExceptionShouldBeThrown(string testFile, string ResourceName)
        {
            AnonymizerEngine engine      = new AnonymizerEngine("stu3-configuration-sample.json");
            string           testContent = File.ReadAllText(ResourceTestsFile(testFile));
            var exception     = Assert.Throws <InvalidInputException>(() => engine.AnonymizeJson(testContent));
            var expectedError = "type (at Cannot locate type information for type '" + ResourceName + "')";

            Assert.StartsWith("The input FHIR resource JSON is invalid.", exception.Message);
            Assert.True(exception.InnerException is FormatException);
            Assert.Equal(expectedError, exception.InnerException.Message);
        }
示例#15
0
        public void GivenIsPrettyOutputSetTrue_WhenAnonymizeJson_PrettyJsonOutputShouldBeReturned()
        {
            AnonymizerEngine engine = new AnonymizerEngine(Path.Combine("TestConfigurations", "configuration-test-sample.json"));
            var settings            = new AnonymizerSettings()
            {
                IsPrettyOutput = true
            };
            var result = engine.AnonymizeJson(TestPatientSample, settings);

            Assert.Equal(PrettyOutputTarget, result);
        }
        public void GivenAnonymizerEngine_AddingCustomProcessor_WhenAnonymize_CorrectResultWillBeReturned()
        {
            var factory = new CustomProcessorFactory();

            factory.RegisterProcessors(typeof(MaskProcessor));
            AnonymizerEngine engine = new AnonymizerEngine(Path.Combine("TestConfigurations", "configuration-custom-Processor.json"), factory);

            var result = engine.AnonymizeJson(TestPatientSample);

            Assert.Equal(CustomTarget, result);
        }
        private async Task AnonymizeSingleBlobInJsonFormatAsync(BlobClient inputBlobClient, BlockBlobClient outputBlobClient, string blobName, string inputFolderPrefix)
        {
            try
            {
                using Stream contentStream = await OperationExecutionHelper.InvokeWithTimeoutRetryAsync <Stream>(async () =>
                {
                    Stream contentStream = new MemoryStream();
                    await inputBlobClient.DownloadToAsync(contentStream).ConfigureAwait(false);
                    contentStream.Position = 0;

                    return(contentStream);
                },
                                                                                                                 TimeSpan.FromSeconds(FhirAzureConstants.DefaultBlockDownloadTimeoutInSeconds),
                                                                                                                 FhirAzureConstants.DefaultBlockDownloadTimeoutRetryCount,
                                                                                                                 isRetrableException : OperationExecutionHelper.IsRetrableException).ConfigureAwait(false);

                using (var reader = new StreamReader(contentStream))
                {
                    string input = await reader.ReadToEndAsync();

                    var engine   = AnonymizerEngine.CreateWithFileContext(_configFile, blobName, inputFolderPrefix);
                    var settings = new AnonymizerSettings()
                    {
                        IsPrettyOutput = true
                    };
                    string output = engine.AnonymizeJson(input, settings);

                    using (MemoryStream outputStream = new MemoryStream(reader.CurrentEncoding.GetBytes(output)))
                    {
                        await OperationExecutionHelper.InvokeWithTimeoutRetryAsync(async() =>
                        {
                            outputStream.Position     = 0;
                            using MemoryStream stream = new MemoryStream();
                            await outputStream.CopyToAsync(stream).ConfigureAwait(false);
                            stream.Position = 0;

                            return(await outputBlobClient.UploadAsync(stream).ConfigureAwait(false));
                        },
                                                                                   TimeSpan.FromSeconds(FhirAzureConstants.DefaultBlockUploadTimeoutInSeconds),
                                                                                   FhirAzureConstants.DefaultBlockUploadTimeoutRetryCount,
                                                                                   isRetrableException : OperationExecutionHelper.IsRetrableException).ConfigureAwait(false);
                    }

                    Console.WriteLine($"[{blobName}]: Anonymize completed.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[{blobName}]: Anonymize failed, you can find detail error message in stderr.txt.");
                Console.Error.WriteLine($"[{blobName}]: Failed to anonymize blob. \nErrorMessage: {ex.Message}\n Details: {ex.ToString()} \nStackTrace: {ex.StackTrace}");
            }
        }
        public FilesAnonymizerForJsonFormatResource(
            string configFilePath,
            string inputFolder,
            string outputFolder,
            AnonymizationToolOptions options)
        {
            _inputFolder    = inputFolder;
            _outputFolder   = outputFolder;
            _configFilePath = configFilePath;
            _options        = options;

            AnonymizerEngine.InitializeFhirPathExtensionSymbols();
        }
示例#19
0
        internal static async Task AnonymizeAsync(Options options)
        {
            try
            {
                DicomFile dicomFile = await DicomFile.OpenAsync(options.InputFile).ConfigureAwait(false);

                //FilterByVR(dicomFile, "PN");
                var engine = new AnonymizerEngine(options.ConfigurationFilePath);
                engine.Anonymize(dicomFile.Dataset);
                dicomFile.Save(options.OutputFile);

                Console.WriteLine($"Finished processing '{options.InputFile}'!");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Process failed! {ex}");
            }
        }
        private async Task AnonymizeSingleBlobInNdJsonFormatAsync(BlobClient inputBlobClient, BlockBlobClient outputBlobClient, string blobName, string inputFolderPrefix)
        {
            var processedCount = 0;
            int skippedCount   = 0;
            var consumedCount  = 0;

            using FhirBlobDataStream inputStream = new FhirBlobDataStream(inputBlobClient);
            FhirStreamReader reader   = new FhirStreamReader(inputStream);
            FhirBlobConsumer consumer = new FhirBlobConsumer(outputBlobClient);
            var engine = AnonymizerEngine.CreateWithFileContext(_configFile, blobName, inputFolderPrefix);
            Func <string, string> anonymizerFunction = (item) =>
            {
                try
                {
                    return(engine.AnonymizeJson(item));
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"[{blobName}]: Anonymize partial failed, you can find detail error message in stderr.txt.");
                    Console.Error.WriteLine($"[{blobName}]: Resource: {item}\nErrorMessage: {ex.Message}\n Details: {ex.ToString()}\nStackTrace: {ex.StackTrace}");
                    throw;
                }
            };

            Stopwatch stopWatch = Stopwatch.StartNew();
            FhirPartitionedExecutor <string, string> executor = new FhirPartitionedExecutor <string, string>(reader, consumer, anonymizerFunction);

            executor.PartitionCount = Environment.ProcessorCount * 2;
            Progress <BatchAnonymizeProgressDetail> progress = new Progress <BatchAnonymizeProgressDetail>();

            progress.ProgressChanged += (obj, args) =>
            {
                Interlocked.Add(ref processedCount, args.ProcessCompleted);
                Interlocked.Add(ref skippedCount, args.ProcessSkipped);
                Interlocked.Add(ref consumedCount, args.ConsumeCompleted);

                Console.WriteLine($"[{stopWatch.Elapsed.ToString()}][tid:{args.CurrentThreadId}]: {processedCount} Completed. {skippedCount} Skipped. {consumedCount} consume completed.");
            };

            await executor.ExecuteAsync(CancellationToken.None, progress).ConfigureAwait(false);
        }
        public async Task <string> FileAnonymize(string fileName)
        {
            var resourceOutputFileName = GetResourceOutputFileName(fileName, _inputFolder, _outputFolder);

            if (_options.IsRecursive)
            {
                var resourceOutputFolder = Path.GetDirectoryName(resourceOutputFileName);
                Directory.CreateDirectory(resourceOutputFolder);
            }

            if (_options.SkipExistedFile && File.Exists(resourceOutputFileName))
            {
                Console.WriteLine($"Skip processing on file {fileName}.");
                return(string.Empty);
            }

            string resourceJson = await File.ReadAllTextAsync(fileName).ConfigureAwait(false);

            try
            {
                var engine   = AnonymizerEngine.CreateWithFileContext(_configFilePath, fileName, _inputFolder);
                var settings = new AnonymizerSettings()
                {
                    IsPrettyOutput = true,
                    ValidateInput  = _options.ValidateInput,
                    ValidateOutput = _options.ValidateOutput
                };
                var resourceResult = engine.AnonymizeJson(resourceJson, settings);

                await File.WriteAllTextAsync(resourceOutputFileName, resourceResult).ConfigureAwait(false);
            }
            catch (Exception innerException)
            {
                Console.Error.WriteLine($"[{fileName}] Error:\nResource: {resourceJson}\nErrorMessage: {innerException.ToString()}");
                throw;
            }

            return(string.Empty);
        }
示例#22
0
        public async Task <IAnonymizer> CreateAnonymizerAsync(string configurationLocation, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNullOrEmpty(configurationLocation, nameof(configurationLocation));

            using (Stream stream = new MemoryStream())
            {
                try
                {
                    await _artifactProvider.FetchAsync(configurationLocation, stream, cancellationToken);

                    stream.Position = 0;
                }
                catch (FileNotFoundException ex)
                {
                    throw new AnonymizationConfigurationNotFoundException(ex.Message, ex);
                }
                catch (Exception ex)
                {
                    _logger.LogError($"Failed to fetch Anonymization configuration file: {configurationLocation}");
                    throw new AnonymizationConfigurationFetchException(ex.Message, ex);
                }

                using (StreamReader reader = new StreamReader(stream))
                {
                    string configurationContent = await reader.ReadToEndAsync();

                    try
                    {
                        var engine = new AnonymizerEngine(AnonymizerConfigurationManager.CreateFromSettingsInJson(configurationContent));
                        return(new ExportAnonymizer(engine));
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError($"Failed to parse configuration file: {ex.Message}");
                        throw new FailedToParseAnonymizationConfigurationException(ex.Message, ex);
                    }
                }
            }
        }
示例#23
0
        public async Task <string> FileAnonymize(string fileName)
        {
            var resourceOutputFileName = GetResourceOutputFileName(fileName, _inputFolder, _outputFolder);

            if (_isRecursive)
            {
                var resourceOutputFolder = Path.GetDirectoryName(resourceOutputFileName);
                Directory.CreateDirectory(resourceOutputFolder);
            }

            string resourceJson = await File.ReadAllTextAsync(fileName).ConfigureAwait(false);

            using (FileStream outputStream = new FileStream(resourceOutputFileName, FileMode.Create))
            {
                using StreamWriter writer = new StreamWriter(outputStream);
                try
                {
                    var engine   = AnonymizerEngine.CreateWithFileContext(_configFilePath, fileName, _inputFolder);
                    var settings = new AnonymizerSettings()
                    {
                        IsPrettyOutput = true,
                        ValidateInput  = _validateInput,
                        ValidateOutput = _validateOutput
                    };
                    var resourceResult = engine.AnonymizeJson(resourceJson, settings);
                    await writer.WriteAsync(resourceResult).ConfigureAwait(false);

                    await writer.FlushAsync().ConfigureAwait(false);
                }
                catch (Exception innerException)
                {
                    Console.Error.WriteLine($"[{fileName}] Error:\nResource: {resourceJson}\nErrorMessage: {innerException.ToString()}");
                    throw;
                }
            }

            return(string.Empty);
        }
示例#24
0
        public ExportAnonymizer(AnonymizerEngine engine)
        {
            EnsureArg.IsNotNull(engine, nameof(engine));

            _engine = engine;
        }
        public async Task AnonymizeAsync()
        {
            var directorySearchOption = _isRecursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
            var bulkResourceFileList  = Directory.EnumerateFiles(_inputFolder, "*.ndjson", directorySearchOption).ToList();

            Console.WriteLine($"Find {bulkResourceFileList.Count()} bulk data resource files in '{_inputFolder}'.");

            foreach (var bulkResourceFileName in bulkResourceFileList)
            {
                Console.WriteLine($"Processing {bulkResourceFileName}");

                var bulkResourceOutputFileName = GetResourceOutputFileName(bulkResourceFileName, _inputFolder, _outputFolder);
                if (_isRecursive)
                {
                    var resourceOutputFolder = Path.GetDirectoryName(bulkResourceOutputFileName);
                    Directory.CreateDirectory(resourceOutputFolder);
                }

                int completedCount        = 0;
                int failedCount           = 0;
                int consumeCompletedCount = 0;
                using (FileStream inputStream = new FileStream(bulkResourceFileName, FileMode.Open))
                    using (FileStream outputStream = new FileStream(bulkResourceOutputFileName, FileMode.Create))
                    {
                        using FhirStreamReader reader     = new FhirStreamReader(inputStream);
                        using FhirStreamConsumer consumer = new FhirStreamConsumer(outputStream);
                        Func <string, string> anonymizeFunction = (content) =>
                        {
                            try
                            {
                                var engine   = AnonymizerEngine.CreateWithFileContext(_configFilePath, bulkResourceFileName, _inputFolder);
                                var settings = new AnonymizerSettings()
                                {
                                    IsPrettyOutput = false,
                                    ValidateInput  = _validateInput,
                                    ValidateOutput = _validateOutput
                                };
                                return(engine.AnonymizeJson(content, settings));
                            }
                            catch (Exception ex)
                            {
                                Console.Error.WriteLine($"Error:\nResource: {content}\nErrorMessage: {ex.ToString()}");
                                throw;
                            }
                        };

                        Stopwatch stopWatch = new Stopwatch();
                        stopWatch.Start();

                        FhirPartitionedExecutor <string, string> executor = new FhirPartitionedExecutor <string, string>(reader, consumer, anonymizeFunction);
                        executor.PartitionCount = Environment.ProcessorCount * 2;

                        Progress <BatchAnonymizeProgressDetail> progress = new Progress <BatchAnonymizeProgressDetail>();
                        progress.ProgressChanged += (obj, args) =>
                        {
                            Interlocked.Add(ref completedCount, args.ProcessCompleted);
                            Interlocked.Add(ref failedCount, args.ProcessFailed);
                            Interlocked.Add(ref consumeCompletedCount, args.ConsumeCompleted);

                            Console.WriteLine($"[{stopWatch.Elapsed.ToString()}][tid:{args.CurrentThreadId}]: {completedCount} Process completed. {failedCount} Process failed. {consumeCompletedCount} Consume completed.");
                        };

                        await executor.ExecuteAsync(CancellationToken.None, false, progress).ConfigureAwait(false);
                    }

                Console.WriteLine($"Finished processing '{bulkResourceFileName}'!");
            }
        }
示例#26
0
        public void GivenCommonResourceWithStu3OnlyField_WhenAnonymizing_AnonymizedJsonShouldBeReturned(string testFile, string targetFile)
        {
            AnonymizerEngine engine = new AnonymizerEngine("stu3-configuration-sample.json");

            FunctionalTestUtility.VerifySingleJsonResourceFromFile(engine, ResourceTestsFile(testFile), ResourceTestsFile(targetFile));
        }
 public FhirResourceDataProcessor(string configFilePath)
 {
     _engine = new AnonymizerEngine(configFilePath);
 }
        public void GivenAPatientResource_WhenRedactAll_ThenRedactedJsonShouldBeReturned()
        {
            AnonymizerEngine engine = new AnonymizerEngine(Path.Combine("Configurations", "redact-all-config.json"));

            FunctionalTestUtility.VerifySingleJsonResourceFromFile(engine, ResourceTestsFile("patient-basic.json"), ResourceTestsFile("patient-redact-all-target.json"));
        }
        public void GivenAPatientResource_WhenAnonymizingWithNoPartialRedactConfig_ThenAnonymizedJsonShouldBeReturned()
        {
            AnonymizerEngine engine = new AnonymizerEngine(Path.Combine("Configurations", "common-no-partial-config.json"));

            FunctionalTestUtility.VerifySingleJsonResourceFromFile(engine, ResourceTestsFile("patient-no-partial.json"), ResourceTestsFile("patient-no-partial-target.json"));
        }
        public void GivenAPatientResourceWithNullDatetime_WhenAnonymizing_ThenAnonymizedJsonShouldBeReturned()
        {
            AnonymizerEngine engine = new AnonymizerEngine(Path.Combine("Configurations", "common-config.json"));

            FunctionalTestUtility.VerifySingleJsonResourceFromFile(engine, ResourceTestsFile("patient-null-date.json"), ResourceTestsFile("patient-null-date-target.json"));
        }