diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41e3295..e5b97ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: merge_group: jobs: - launch-unit-tests: + lint-and-test: runs-on: ubuntu-latest strategy: @@ -23,21 +23,16 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - - name: Cache Python dependencies - uses: actions/cache@v4 - id: pip-cache - with: - path: ~/.cache/pip - key: pip-${{ matrix.python-version }}-${{ hashFiles('**/setup.py') }} - restore-keys: | - pip-${{ matrix.python-version }}-${{ hashFiles('**/setup.py') }} + cache: 'pip' + cache-dependency-path: setup.py - name: Install python dependencies - if: steps.pip-cache.outputs.cache-hit != 'true' + run: pip install -e .[dev] + + - name: Check code style run: | - python -m pip install --upgrade pip - pip install -e .[dev] + ruff check + ruff format --check - name: Run test run: pytest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5707941 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.4 + hooks: + - id: ruff + args: [ --fix ] + - id: ruff-format diff --git a/README.md b/README.md index 153553e..07d9934 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,44 @@ Dicom fields are separated into different groups. Each groups will be anonymized Installation can be done via pip `pip install dicom-anonymizer` or conda `conda install -c conda-forge dicom-anonymizer`. -# How to test it? -- One time set up: - - virtual environment for this package and activate it. For - example set up using `virtualenv venv` and activate using - `venv\Scripts\activate.bat` (on Windows) - - Install editable version and development requirements using - `pip install -e .[dev]` -- Run unit test using `pytest` + +# Local Development Setup + +To get started with local development, follow these steps: + +1. Create a Virtual Environment: + - On Windows: + ```sh + virtualenv env + .\env\Scripts\activate.bat + ``` + - On MacOS/Linux: + ```sh + python -m venv env + source env/bin/activate + ``` + +2. Install Dependencies: + - Install an editable version of the package and the development requirements: + ```sh + pip install -e .[dev] + ``` + +3. Set Up Pre-Commit Hooks: + - Install the pre-commit hooks to ensure code quality: + ```sh + pre-commit install + ``` + + +## How to test it? + +To run the unit tests, use the following command: + +```sh +pytest +``` + # How to build it? These instructions rely on wheel build-package format. Install it if you have not done it already using: diff --git a/dicomanonymizer/anonymizer.py b/dicomanonymizer/anonymizer.py index 710b73b..fbb4bab 100644 --- a/dicomanonymizer/anonymizer.py +++ b/dicomanonymizer/anonymizer.py @@ -1,4 +1,5 @@ import sys + if sys.version_info >= (3, 8): import importlib.metadata as metadata else: @@ -8,10 +9,12 @@ import ast import json import os -import sys import tqdm -from dicomanonymizer.simpledicomanonymizer import anonymize_dicom_file, ActionsMapNameFunctions +from dicomanonymizer.simpledicomanonymizer import ( + anonymize_dicom_file, + ActionsMapNameFunctions, +) def isDICOMType(filePath): @@ -19,14 +22,19 @@ def isDICOMType(filePath): :returns True if input file is a DICOM File. False otherwise. """ try: - with open(filePath, 'rb') as tempFile: + with open(filePath, "rb") as tempFile: tempFile.seek(0x80, os.SEEK_SET) - return tempFile.read(4) == b'DICM' + return tempFile.read(4) == b"DICM" except IOError: return False -def anonymize(input_path: str, output_path: str, anonymization_actions: dict, delete_private_tags: bool) -> None: +def anonymize( + input_path: str, + output_path: str, + anonymization_actions: dict, + delete_private_tags: bool, +) -> None: """ Read data from input path (folder or file) and launch the anonymization. @@ -37,37 +45,42 @@ def anonymize(input_path: str, output_path: str, anonymization_actions: dict, de :param deletePrivateTags: Whether to delete private tags. """ # Get input arguments - input_folder = '' - output_folder = '' + input_folder = "" + output_folder = "" if os.path.isdir(input_path): input_folder = input_path if os.path.isdir(output_path): output_folder = output_path - if input_folder == '': + if input_folder == "": output_path = os.path.join(output_folder, os.path.basename(input_path)) - if input_folder != '' and output_folder == '': - print('Error, please set a correct output folder path') + if input_folder != "" and output_folder == "": + print("Error, please set a correct output folder path") sys.exit() # Generate list of input file if a folder has been set input_files_list = [] output_files_list = [] - if input_folder == '': + if input_folder == "": input_files_list.append(input_path) output_files_list.append(output_path) else: files = os.listdir(input_folder) for fileName in files: - if isDICOMType(input_folder + '/' + fileName): - input_files_list.append(input_folder + '/' + fileName) - output_files_list.append(output_folder + '/' + fileName) + if isDICOMType(input_folder + "/" + fileName): + input_files_list.append(input_folder + "/" + fileName) + output_files_list.append(output_folder + "/" + fileName) progress_bar = tqdm.tqdm(total=len(input_files_list)) for cpt in range(len(input_files_list)): - anonymize_dicom_file(input_files_list[cpt], output_files_list[cpt], anonymization_actions, delete_private_tags) + anonymize_dicom_file( + input_files_list[cpt], + output_files_list[cpt], + anonymization_actions, + delete_private_tags, + ) progress_bar.update(1) progress_bar.close() @@ -83,7 +96,9 @@ def parse_tag_actions_arguments(t_arguments: list, new_anonymization_actions: di for current_tag_parameters in t_arguments: nb_parameters = len(current_tag_parameters) if nb_parameters < 2: - raise ValueError("-t parameters should always have 2 values: tag and action") + raise ValueError( + "-t parameters should always have 2 values: tag and action" + ) tag = current_tag_parameters[0] action_name = current_tag_parameters[1] @@ -96,9 +111,15 @@ def parse_tag_actions_arguments(t_arguments: list, new_anonymization_actions: di action_arguments = current_tag_parameters[2:] if len(action_arguments) != action_object.number_of_expected_arguments: - raise ValueError(f"Wrong number of arguments for action {action_name}: found {len(action_arguments)}") - - action = action_object.function if not len(action_arguments) else action_object.function(action_arguments) + raise ValueError( + f"Wrong number of arguments for action {action_name}: found {len(action_arguments)}" + ) + + action = ( + action_object.function + if not len(action_arguments) + else action_object.function(action_arguments) + ) tag_list = [ast.literal_eval(tag)] new_anonymization_actions.update({tag: action for tag in tag_list}) @@ -116,7 +137,7 @@ def parse_dictionary_argument(dictionary_argument, new_anonymization_actions): for tag, action_or_options in data.items(): if isinstance(action_or_options, dict): try: - action_name = action_or_options.pop('action') + action_name = action_or_options.pop("action") except KeyError as e: raise ValueError(f"Missing field in tag {tag}: {e.args[0]}") try: @@ -125,7 +146,8 @@ def parse_dictionary_argument(dictionary_argument, new_anonymization_actions): raise ValueError(f"Action {action_name} is not recognized.") if len(action_or_options) != action_object.number_of_expected_arguments: raise ValueError( - f"Wrong number of arguments for action {action_name}: found {len(action_or_options)}") + f"Wrong number of arguments for action {action_name}: found {len(action_or_options)}" + ) action = action_object.function(action_or_options) else: try: @@ -140,18 +162,35 @@ def parse_dictionary_argument(dictionary_argument, new_anonymization_actions): def main(): version_info = metadata.version("dicom_anonymizer") parser = argparse.ArgumentParser(add_help=True) - parser.add_argument('input', help='Path to the input dicom file or input directory which contains dicom files') parser.add_argument( - 'output', help='Path to the output dicom file or output directory which will contains dicom files') + "input", + help="Path to the input dicom file or input directory which contains dicom files", + ) + parser.add_argument( + "output", + help="Path to the output dicom file or output directory which will contains dicom files", + ) + parser.add_argument( + "-t", + action="append", + nargs="*", + help="tags action : Defines a new action to apply on the tag.'regexp' action takes two arguments: " + "1. regexp to find substring 2. the string that will replace the previous found string", + ) + parser.add_argument( + "-v", "--version", action="version", version=f"%(prog)s {version_info}" + ) + parser.add_argument( + "--dictionary", + action="store", + help="File which contains a dictionary that can be added to the original one", + ) parser.add_argument( - '-t', action='append', nargs='*', - help='tags action : Defines a new action to apply on the tag.\'regexp\' action takes two arguments: ' - '1. regexp to find substring 2. the string that will replace the previous found string') - parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {version_info}') - parser.add_argument('--dictionary', action='store', - help='File which contains a dictionary that can be added to the original one') - parser.add_argument('--keepPrivateTags', action='store_true', dest='keepPrivateTags', - help='If used, then private tags won\'t be deleted') + "--keepPrivateTags", + action="store_true", + dest="keepPrivateTags", + help="If used, then private tags won't be deleted", + ) parser.set_defaults(keepPrivateTags=False) args = parser.parse_args() @@ -168,8 +207,10 @@ def main(): parse_dictionary_argument(args.dictionary, new_anonymization_actions) # Launch the anonymization - anonymize(input_path, output_path, new_anonymization_actions, not args.keepPrivateTags) + anonymize( + input_path, output_path, new_anonymization_actions, not args.keepPrivateTags + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/dicomanonymizer/dicomfields.py b/dicomanonymizer/dicomfields.py index 3667c67..98dcb74 100644 --- a/dicomanonymizer/dicomfields.py +++ b/dicomanonymizer/dicomfields.py @@ -4,648 +4,651 @@ # Replaced tags D_TAGS = [ - (0x0018, 0x11BB), # Acquisition Field Of View Label - (0x006A, 0x0005), # Annotation Group Label - (0x006A, 0x0003), # Annotation Group UID - (0x0044, 0x0104), # Assertion DateTime - (0x0400, 0x0562), # Attribute Modification DateTime - (0x300C, 0x0127), # Beam Hold Transition DateTime - (0x0400, 0x0115), # Certificate of Signer - (0x0012, 0x0081), # Clinical Trial Protocol Ethics Committee Name - (0x0012, 0x0020), # Clinical Trial Protocol ID - (0x0012, 0x0010), # Clinical Trial Sponsor Name - (0x0012, 0x0040), # Clinical Trial Subject ID - (0x0012, 0x0042), # Clinical Trial Subject Reading ID - (0x0040, 0x0512), # Container Identifier - (0x0040, 0xA730), # Content Sequence - (0x0008, 0x0107), # Context Group Local Version - (0x0008, 0x0106), # Context Group Version - (0x0040, 0xA121), # Date - (0x0040, 0xA120), # DateTime - (0x0018, 0x9701), # Decay Correction DateTime - (0x2100, 0x0140), # Destination AE - (0x3010, 0x002D), # Device Label - (0x0400, 0x0105), # Digital Signature DateTime - (0x0068, 0x6226), # Effective DateTime - (0x0042, 0x0011), # Encapsulated Document - (0x3010, 0x0035), # Entity Label - (0x3010, 0x0038), # Entity Long Label - (0x0018, 0x9804), # Exclusion Start DateTime - (0x0034, 0x0002), # Flow Identifier - (0x0034, 0x0001), # Flow Identifier Sequence - (0x0018, 0x9074), # Frame Acquisition DateTime - (0x0034, 0x0007), # Frame Origin Timestamp - (0x0018, 0x9151), # Frame Reference DateTime - (0x0018, 0x9623), # Functional Sync Pulse - (0x0070, 0x0001), # Graphic Annotation Sequence - (0x0072, 0x000A), # Hanging Protocol Creation DateTime - (0x003A, 0x0314), # Impedance Measurement DateTime - (0x0068, 0x6270), # Information Issue DateTime - (0x300A, 0x0741), # Interlock DateTime - (0x300A, 0x0742), # Interlock Description - (0x300A, 0x0783), # Interlock Origin Description - (0x0400, 0x0563), # Modifying System - (0x300A, 0x0760), # Override DateTime - (0x0040, 0x1101), # Person Identification Code Sequence - (0x0040, 0xA123), # Person Name - (0x300A, 0x0002), # RT Plan Label - (0x3010, 0x0054), # RT Prescription Label - (0x300A, 0x062A), # RT Tolerance Set Label - (0x300A, 0x0619), # Radiation Dose Identification Label - (0x300A, 0x0623), # Radiation Dose In-Vivo Measurement Label - (0x300A, 0x067C), # Radiation Generation Mode Label - (0x0400, 0x0565), # Reason for the Attribute Modification - (0x300A, 0x073A), # Recorded RT Control Point DateTime - (0x0040, 0xA13A), # Referenced DateTime - (0x3008, 0x0162), # Safe Position Exit Date - (0x3008, 0x0164), # Safe Position Exit Time - (0x3008, 0x0166), # Safe Position Return Date - (0x3008, 0x0168), # Safe Position Return Time - (0x0072, 0x005E), # Selector AE Value - (0x0072, 0x005F), # Selector AS Value - (0x0072, 0x0061), # Selector DA Value - (0x0072, 0x0063), # Selector DT Value - (0x0072, 0x0066), # Selector LO Value - (0x0072, 0x0068), # Selector LT Value - (0x0072, 0x0065), # Selector OB Value - (0x0072, 0x006A), # Selector PN Value - (0x0072, 0x006C), # Selector SH Value - (0x0072, 0x006E), # Selector ST Value - (0x0072, 0x006B), # Selector TM Value - (0x0072, 0x006D), # Selector UN Value - (0x0072, 0x0071), # Selector UR Value - (0x0072, 0x0070), # Selector UT Value - (0x0018, 0x936A), # Source End DateTime - (0x0034, 0x0005), # Source Identifier - (0x0018, 0x9369), # Source Start DateTime - (0x300A, 0x022C), # Source Strength Reference Date - (0x300A, 0x022E), # Source Strength Reference Time - (0x0040, 0x0551), # Specimen Identifier - (0x3006, 0x0002), # Structure Set Label - (0x0040, 0xA122), # Time - (0x3008, 0x0024), # Treatment Control Point Date - (0x3008, 0x0025), # Treatment Control Point Time - (0x300A, 0x0608), # Treatment Position Group Label - (0x300A, 0x0736), # Treatment Tolerance Violation DateTime - (0x300A, 0x0734), # Treatment Tolerance Violation Description - (0x3010, 0x0033), # User Content Label - (0x3010, 0x0034), # User Content Long Label - (0x0040, 0xA030), # Verification DateTime - (0x0040, 0xA075), # Verifying Observer Name - (0x0040, 0xA073), # Verifying Observer Sequence - (0x0040, 0xA027), # Verifying Organization - (0x0018, 0x9371), # X-Ray Detector ID - (0x0018, 0x9367), # X-Ray Source ID + (0x0018, 0x11BB), # Acquisition Field Of View Label + (0x006A, 0x0005), # Annotation Group Label + (0x006A, 0x0003), # Annotation Group UID + (0x0044, 0x0104), # Assertion DateTime + (0x0400, 0x0562), # Attribute Modification DateTime + (0x300C, 0x0127), # Beam Hold Transition DateTime + (0x0400, 0x0115), # Certificate of Signer + (0x0012, 0x0081), # Clinical Trial Protocol Ethics Committee Name + (0x0012, 0x0020), # Clinical Trial Protocol ID + (0x0012, 0x0010), # Clinical Trial Sponsor Name + (0x0012, 0x0040), # Clinical Trial Subject ID + (0x0012, 0x0042), # Clinical Trial Subject Reading ID + (0x0040, 0x0512), # Container Identifier + (0x0040, 0xA730), # Content Sequence + (0x0008, 0x0107), # Context Group Local Version + (0x0008, 0x0106), # Context Group Version + (0x0040, 0xA121), # Date + (0x0040, 0xA120), # DateTime + (0x0018, 0x9701), # Decay Correction DateTime + (0x2100, 0x0140), # Destination AE + (0x3010, 0x002D), # Device Label + (0x0400, 0x0105), # Digital Signature DateTime + (0x0068, 0x6226), # Effective DateTime + (0x0042, 0x0011), # Encapsulated Document + (0x3010, 0x0035), # Entity Label + (0x3010, 0x0038), # Entity Long Label + (0x0018, 0x9804), # Exclusion Start DateTime + (0x0034, 0x0002), # Flow Identifier + (0x0034, 0x0001), # Flow Identifier Sequence + (0x0018, 0x9074), # Frame Acquisition DateTime + (0x0034, 0x0007), # Frame Origin Timestamp + (0x0018, 0x9151), # Frame Reference DateTime + (0x0018, 0x9623), # Functional Sync Pulse + (0x0070, 0x0001), # Graphic Annotation Sequence + (0x0072, 0x000A), # Hanging Protocol Creation DateTime + (0x003A, 0x0314), # Impedance Measurement DateTime + (0x0068, 0x6270), # Information Issue DateTime + (0x300A, 0x0741), # Interlock DateTime + (0x300A, 0x0742), # Interlock Description + (0x300A, 0x0783), # Interlock Origin Description + (0x0400, 0x0563), # Modifying System + (0x300A, 0x0760), # Override DateTime + (0x0040, 0x1101), # Person Identification Code Sequence + (0x0040, 0xA123), # Person Name + (0x300A, 0x0002), # RT Plan Label + (0x3010, 0x0054), # RT Prescription Label + (0x300A, 0x062A), # RT Tolerance Set Label + (0x300A, 0x0619), # Radiation Dose Identification Label + (0x300A, 0x0623), # Radiation Dose In-Vivo Measurement Label + (0x300A, 0x067C), # Radiation Generation Mode Label + (0x0400, 0x0565), # Reason for the Attribute Modification + (0x300A, 0x073A), # Recorded RT Control Point DateTime + (0x0040, 0xA13A), # Referenced DateTime + (0x3008, 0x0162), # Safe Position Exit Date + (0x3008, 0x0164), # Safe Position Exit Time + (0x3008, 0x0166), # Safe Position Return Date + (0x3008, 0x0168), # Safe Position Return Time + (0x0072, 0x005E), # Selector AE Value + (0x0072, 0x005F), # Selector AS Value + (0x0072, 0x0061), # Selector DA Value + (0x0072, 0x0063), # Selector DT Value + (0x0072, 0x0066), # Selector LO Value + (0x0072, 0x0068), # Selector LT Value + (0x0072, 0x0065), # Selector OB Value + (0x0072, 0x006A), # Selector PN Value + (0x0072, 0x006C), # Selector SH Value + (0x0072, 0x006E), # Selector ST Value + (0x0072, 0x006B), # Selector TM Value + (0x0072, 0x006D), # Selector UN Value + (0x0072, 0x0071), # Selector UR Value + (0x0072, 0x0070), # Selector UT Value + (0x0018, 0x936A), # Source End DateTime + (0x0034, 0x0005), # Source Identifier + (0x0018, 0x9369), # Source Start DateTime + (0x300A, 0x022C), # Source Strength Reference Date + (0x300A, 0x022E), # Source Strength Reference Time + (0x0040, 0x0551), # Specimen Identifier + (0x3006, 0x0002), # Structure Set Label + (0x0040, 0xA122), # Time + (0x3008, 0x0024), # Treatment Control Point Date + (0x3008, 0x0025), # Treatment Control Point Time + (0x300A, 0x0608), # Treatment Position Group Label + (0x300A, 0x0736), # Treatment Tolerance Violation DateTime + (0x300A, 0x0734), # Treatment Tolerance Violation Description + (0x3010, 0x0033), # User Content Label + (0x3010, 0x0034), # User Content Long Label + (0x0040, 0xA030), # Verification DateTime + (0x0040, 0xA075), # Verifying Observer Name + (0x0040, 0xA073), # Verifying Observer Sequence + (0x0040, 0xA027), # Verifying Organization + (0x0018, 0x9371), # X-Ray Detector ID + (0x0018, 0x9367), # X-Ray Source ID ] # Replaced with empty values (0, '', ...) Z_TAGS = [ - (0x0008, 0x0050), # Accession Number - (0x0018, 0x1203), # Calibration DateTime - (0x0012, 0x0060), # Clinical Trial Coordinating Center Name - (0x0012, 0x0021), # Clinical Trial Protocol Name - (0x0012, 0x0030), # Clinical Trial Site ID - (0x0012, 0x0031), # Clinical Trial Site Name - (0x0012, 0x0050), # Clinical Trial Time Point ID - (0x3010, 0x000F), # Conceptual Volume Combination Description - (0x3010, 0x0017), # Conceptual Volume Description - (0x0008, 0x009C), # Consulting Physician's Name - (0x3010, 0x001B), # Device Alternate Identifier - (0x0040, 0x2017), # Filler Order Number / Imaging Service Request - (0x3010, 0x007F), # Fractionation Notes - (0x0040, 0x0513), # Issuer of the Container Identifier Sequence - (0x0040, 0x0562), # Issuer of the Specimen Identifier Sequence - (0x3010, 0x0043), # Manufacturer's Device Identifier - (0x0040, 0xA082), # Participation DateTime - (0x0010, 0x0020), # Patient ID - (0x0010, 0x0030), # Patient's Birth Date - (0x0010, 0x0010), # Patient's Name - (0x0010, 0x0040), # Patient's Sex - (0x0040, 0x2016), # Placer Order Number / Imaging Service Request - (0x3010, 0x007B), # Prescription Notes - (0x3010, 0x0081), # Prescription Notes Sequence - (0x3006, 0x00A6), # ROI Interpreter - (0x3006, 0x0026), # ROI Name - (0x300A, 0x0615), # RT Accessory Device Slot ID - (0x300A, 0x0611), # RT Accessory Holder Slot ID - (0x3010, 0x005A), # RT Physician Intent Narrative - (0x300A, 0x067D), # Radiation Generation Mode Description - (0x3010, 0x005C), # Reason for Superseding - (0x0008, 0x0090), # Referring Physician's Name - (0x300E, 0x0004), # Review Date - (0x300E, 0x0005), # Review Time - (0x0400, 0x0564), # Source of Previous Values - (0x0040, 0x0610), # Specimen Preparation Sequence - (0x3006, 0x0008), # Structure Set Date - (0x3006, 0x0009), # Structure Set Time - (0x0008, 0x0020), # Study Date - (0x0020, 0x0010), # Study ID - (0x0008, 0x0030), # Study Time - (0x3010, 0x007A), # Treatment Technique Notes - (0x0040, 0xA088), # Verifying Observer Identification Code Sequence + (0x0008, 0x0050), # Accession Number + (0x0018, 0x1203), # Calibration DateTime + (0x0012, 0x0060), # Clinical Trial Coordinating Center Name + (0x0012, 0x0021), # Clinical Trial Protocol Name + (0x0012, 0x0030), # Clinical Trial Site ID + (0x0012, 0x0031), # Clinical Trial Site Name + (0x0012, 0x0050), # Clinical Trial Time Point ID + (0x3010, 0x000F), # Conceptual Volume Combination Description + (0x3010, 0x0017), # Conceptual Volume Description + (0x0008, 0x009C), # Consulting Physician's Name + (0x3010, 0x001B), # Device Alternate Identifier + (0x0040, 0x2017), # Filler Order Number / Imaging Service Request + (0x3010, 0x007F), # Fractionation Notes + (0x0040, 0x0513), # Issuer of the Container Identifier Sequence + (0x0040, 0x0562), # Issuer of the Specimen Identifier Sequence + (0x3010, 0x0043), # Manufacturer's Device Identifier + (0x0040, 0xA082), # Participation DateTime + (0x0010, 0x0020), # Patient ID + (0x0010, 0x0030), # Patient's Birth Date + (0x0010, 0x0010), # Patient's Name + (0x0010, 0x0040), # Patient's Sex + (0x0040, 0x2016), # Placer Order Number / Imaging Service Request + (0x3010, 0x007B), # Prescription Notes + (0x3010, 0x0081), # Prescription Notes Sequence + (0x3006, 0x00A6), # ROI Interpreter + (0x3006, 0x0026), # ROI Name + (0x300A, 0x0615), # RT Accessory Device Slot ID + (0x300A, 0x0611), # RT Accessory Holder Slot ID + (0x3010, 0x005A), # RT Physician Intent Narrative + (0x300A, 0x067D), # Radiation Generation Mode Description + (0x3010, 0x005C), # Reason for Superseding + (0x0008, 0x0090), # Referring Physician's Name + (0x300E, 0x0004), # Review Date + (0x300E, 0x0005), # Review Time + (0x0400, 0x0564), # Source of Previous Values + (0x0040, 0x0610), # Specimen Preparation Sequence + (0x3006, 0x0008), # Structure Set Date + (0x3006, 0x0009), # Structure Set Time + (0x0008, 0x0020), # Study Date + (0x0020, 0x0010), # Study ID + (0x0008, 0x0030), # Study Time + (0x3010, 0x007A), # Treatment Technique Notes + (0x0040, 0xA088), # Verifying Observer Identification Code Sequence ] # Deleted tags X_TAGS = [ - (0x0018, 0x4000), # Acquisition Comments - (0x0018, 0x9424), # Acquisition Protocol Description - (0x0040, 0x4035), # Actual Human Performers Sequence - (0x0010, 0x21B0), # Additional Patient History - (0x0040, 0xA353), # Address (Trial) - (0x0038, 0x0010), # Admission ID - (0x0038, 0x0020), # Admitting Date - (0x0008, 0x1084), # Admitting Diagnoses Code Sequence - (0x0008, 0x1080), # Admitting Diagnoses Description - (0x0038, 0x0021), # Admitting Time - (0x0000, 0x1000), # Affected SOP Instance UID - (0x0010, 0x2110), # Allergies - (0x006A, 0x0006), # Annotation Group Description - (0x0044, 0x0004), # Approval Status DateTime - (0x4000, 0x0010), # Arbitrary - (0x0044, 0x0105), # Assertion Expiration DateTime - (0x0040, 0xA078), # Author Observer Sequence - (0x300A, 0x00C3), # Beam Description - (0x300A, 0x00DD), # Bolus Description - (0x0010, 0x1081), # Branch of Service - (0x0014, 0x407E), # Calibration Date - (0x0014, 0x407C), # Calibration Time - (0x0016, 0x004D), # Camera Owner Name - (0x0018, 0x1007), # Cassette ID - (0x0400, 0x0310), # Certified Timestamp - (0x0012, 0x0082), # Clinical Trial Protocol Ethics Committee Approval Number - (0x0012, 0x0072), # Clinical Trial Series Description - (0x0012, 0x0071), # Clinical Trial Series ID - (0x0012, 0x0051), # Clinical Trial Time Point Description - (0x0040, 0x0310), # Comments on Radiation Dose - (0x0040, 0x0280), # Comments on the Performed Procedure Step - (0x300A, 0x02EB), # Compensator Description - (0x0040, 0x3001), # Confidentiality Constraint on Patient Data Description - (0x0008, 0x009D), # Consulting Physician Identification Sequence - (0x0050, 0x001B), # Container Component ID - (0x0040, 0x051A), # Container Description - (0x0070, 0x0086), # Content Creator's Identification Code Sequence - (0x0018, 0x1042), # Contrast/Bolus Start Time - (0x0018, 0x1043), # Contrast/Bolus Stop Time - (0x0018, 0xA002), # Contribution DateTime - (0x0018, 0xA003), # Contribution Description - (0x0010, 0x2150), # Country of Residence - (0x2100, 0x0040), # Creation Date - (0x2100, 0x0050), # Creation Time - (0x0040, 0xA307), # Current Observer (Trial) - (0x0038, 0x0300), # Current Patient Location - (0x5000, 0x0000, 0xFF00, 0x0000), # Curve Data - (0x0008, 0x0025), # Curve Date - (0x0008, 0x0035), # Curve Time - (0x0040, 0xA07C), # Custodial Organization Sequence - (0xFFFC, 0xFFFC), # Data Set Trailing Padding - (0x0040, 0xA110), # Date of Document or Verbal Transaction (Trial) - (0x0018, 0x1205), # Date of Installation - (0x0018, 0x1200), # Date of Last Calibration - (0x0018, 0x1204), # Date of Manufacture - (0x0018, 0x1012), # Date of Secondary Capture - (0x0018, 0x1202), # DateTime of Last Calibration - (0x0018, 0x937F), # Decomposition Description - (0x0008, 0x2111), # Derivation Description - (0x0050, 0x0020), # Device Description - (0x0016, 0x004B), # Device Setting Description - (0xFFFA, 0xFFFA), # Digital Signatures Sequence - (0x0038, 0x0030), # Discharge Date - (0x0038, 0x0040), # Discharge Diagnosis Description - (0x0038, 0x0032), # Discharge Time - (0x300A, 0x079A), # Displacement Reference Label - (0x4008, 0x011A), # Distribution Address - (0x4008, 0x0119), # Distribution Name - (0x300A, 0x0016), # Dose Reference Description - (0x3010, 0x0037), # Entity Description - (0x3010, 0x0036), # Entity Name - (0x300A, 0x0676), # Equipment Frame of Reference Description - (0x0012, 0x0087), # Ethics Committee Approval Effectiveness End Date - (0x0012, 0x0086), # Ethics Committee Approval Effectiveness Start Date - (0x0010, 0x2160), # Ethnic Group - (0x0040, 0x4011), # Expected Completion DateTime - (0x003A, 0x032B), # Filter Lookup Table Description - (0x0040, 0xA023), # Findings Group Recording Date (Trial) - (0x0040, 0xA024), # Findings Group Recording Time (Trial) - (0x300A, 0x0196), # Fixation Device Description - (0x300A, 0x0072), # Fraction Group Description - (0x0020, 0x9158), # Frame Comments - (0x0016, 0x0076), # GPS Altitude - (0x0016, 0x0075), # GPS Altitude Ref - (0x0016, 0x008C), # GPS Area Information - (0x0016, 0x007B), # GPS DOP - (0x0016, 0x008D), # GPS Date Stamp - (0x0016, 0x0088), # GPS Dest Bearing - (0x0016, 0x008A), # GPS Dest Distance - (0x0016, 0x0089), # GPS Dest Distance Ref - (0x0016, 0x0086), # GPS Dest Longitude - (0x0016, 0x0085), # GPS Dest Longitude Ref - (0x0016, 0x0087), # GPS Dest Bearing Ref - (0x0016, 0x0084), # GPS Dest Latitude - (0x0016, 0x0083), # GPS Dest Latitude Ref - (0x0016, 0x008E), # GPS Differential - (0x0016, 0x0081), # GPS Img Direction - (0x0016, 0x0080), # GPS Img Direction Ref - (0x0016, 0x0072), # GPS Latitude - (0x0016, 0x0071), # GPS Latitude Ref - (0x0016, 0x0074), # GPS Longitude - (0x0016, 0x0073), # GPS Longitude Ref - (0x0016, 0x0082), # GPS Map Datum - (0x0016, 0x007A), # GPS Measure Mode - (0x0016, 0x008B), # GPS Processing Method - (0x0016, 0x0078), # GPS Satellites - (0x0016, 0x007D), # GPS Speed - (0x0016, 0x007C), # GPS Speed Ref - (0x0016, 0x0079), # GPS Status - (0x0016, 0x0077), # GPS Time Stamp - (0x0016, 0x007F), # GPS Track - (0x0016, 0x007E), # GPS Track Ref - (0x0016, 0x0070), # GPS Version ID - (0x0018, 0x1008), # Gantry ID - (0x0018, 0x1005), # Generator ID - (0x0040, 0xE004), # HL7 Document Effective Time - (0x0040, 0x4037), # Human Performer's Name - (0x0040, 0x4036), # Human Performer's Organization - (0x0088, 0x0200), # Icon Image Sequence - (0x0008, 0x4000), # Identifying Comments - (0x0020, 0x4000), # Image Comments - (0x0028, 0x4000), # Image Presentation Comments - (0x0040, 0x2400), # Imaging Service Request Comments - (0x4008, 0x0300), # Impressions - (0x0008, 0x0015), # Instance Coercion DateTime - (0x0400, 0x0600), # Instance Origin Status - (0x0008, 0x0081), # Institution Address - (0x0008, 0x1040), # Institutional Department Name - (0x0008, 0x1041), # Institutional Department Type Code Sequence - (0x0010, 0x1050), # Insurance Plan Identification - (0x3010, 0x0085), # Intended Fraction Start Time - (0x0040, 0x1011), # Intended Recipients of Results Identification Sequence - (0x4008, 0x0112), # Interpretation Approval Date - (0x4008, 0x0113), # Interpretation Approval Time - (0x4008, 0x0111), # Interpretation Approver Sequence - (0x4008, 0x010C), # Interpretation Author - (0x4008, 0x0115), # Interpretation Diagnosis Description - (0x4008, 0x0200), # Interpretation ID - (0x4008, 0x0202), # Interpretation ID Issuer - (0x4008, 0x0100), # Interpretation Recorded Date - (0x4008, 0x0101), # Interpretation Recorded Time - (0x4008, 0x0102), # Interpretation Recorder - (0x4008, 0x010B), # Interpretation Text - (0x4008, 0x010A), # Interpretation Transcriber - (0x4008, 0x0108), # Interpretation Transcription Date - (0x4008, 0x0109), # Interpretation Transcription Time - (0x0018, 0x0035), # Intervention Drug Start Time - (0x0018, 0x0027), # Intervention Drug Stop Time - (0x0040, 0x2004), # Issue Date of Imaging Service Request - (0x0040, 0x2005), # Issue Time of Imaging Service Request - (0x0038, 0x0011), # Issuer of Admission ID - (0x0038, 0x0014), # Issuer of Admission ID Sequence - (0x0010, 0x0021), # Issuer of Patient ID - (0x0038, 0x0061), # Issuer of Service Episode ID - (0x0038, 0x0064), # Issuer of Service Episode ID Sequence - (0x0010, 0x21D0), # Last Menstrual Date - (0x0016, 0x004F), # Lens Make - (0x0016, 0x0050), # Lens Model - (0x0016, 0x0051), # Lens Serial Number - (0x0016, 0x004E), # Lens Specification - (0x0050, 0x0021), # Long Device Description - (0x0400, 0x0404), # MAC - (0x0016, 0x002B), # Maker Note - (0x0010, 0x2000), # Medical Alerts - (0x0010, 0x1090), # Medical Record Locator - (0x0010, 0x1080), # Military Rank - (0x0400, 0x0550), # Modified Attributes Sequence - (0x0020, 0x3403), # Modified Image Date - (0x0020, 0x3406), # Modified Image Description - (0x0020, 0x3405), # Modified Image Time - (0x0020, 0x3401), # Modifying Device ID - (0x0018, 0x937B), # Multi-energy Acquisition Description - (0x0008, 0x1060), # Name of Physician(s) Reading Study - (0x0040, 0x1010), # Names of Intended Recipients of Results - (0x0008, 0x1000), # Network ID - (0x0400, 0x0552), # Nonconforming Data Element Value - (0x0400, 0x0551), # Nonconforming Modified Attributes Sequence - (0x0040, 0xA192), # Observation Date (Trial) - (0x0040, 0xA033), # Observation Start DateTime - (0x0040, 0xA193), # Observation Time (Trial) - (0x0010, 0x2180), # Occupation - (0x0040, 0x2010), # Order Callback Phone Number - (0x0040, 0x2011), # Order Callback Telecom Information - (0x0040, 0x2008), # Order Entered By - (0x0040, 0x2009), # Order Enterer's Location - (0x0400, 0x0561), # Original Attributes Sequence - (0x2100, 0x0070), # Originator - (0x0010, 0x1000), # Other Patient IDs - (0x0010, 0x1002), # Other Patient IDs Sequence - (0x0010, 0x1001), # Other Patient Names - (0x6000, 0x4000, 0xFF00, 0xFFFF), # Overlay Comments - (0x6000, 0x3000, 0xFF00, 0xFFFF), # Overlay Data - (0x0008, 0x0024), # Overlay Date - (0x0008, 0x0034), # Overlay Time - (0x0040, 0xA07A), # Participant Sequence - (0x0010, 0x4000), # Patient Comments - (0x300A, 0x0794), # Patient Setup Photo Description - (0x0038, 0x0500), # Patient State - (0x0040, 0x1004), # Patient Transport Arrangements - (0x300A, 0x0792), # Patient Treatment Preparation Method Description - (0x300A, 0x078E), # Patient Treatment Preparation Procedure Parameter Description - (0x0010, 0x1040), # Patient's Address - (0x0010, 0x1010), # Patient's Age - (0x0010, 0x1005), # Patient's Birth Name - (0x0010, 0x0032), # Patient's Birth Time - (0x0038, 0x0400), # Patient's Institution Residence - (0x0010, 0x0050), # Patient's Insurance Plan Code Sequence - (0x0010, 0x1060), # Patient's Mother's Birth Name - (0x0010, 0x0101), # Patient's Primary Language Code Sequence - (0x0010, 0x0102), # Patient's Primary Language Modifier Code Sequence - (0x0010, 0x21F0), # Patient's Religious Preference - (0x0010, 0x1020), # Patient's Size - (0x0010, 0x2155), # Patient's Telecom Information - (0x0010, 0x2154), # Patient's Telephone Numbers - (0x0010, 0x1030), # Patient's Weight - (0x0040, 0x0243), # Performed Location - (0x0040, 0x0254), # Performed Procedure Step Description - (0x0040, 0x0250), # Performed Procedure Step End Date - (0x0040, 0x4051), # Performed Procedure Step End DateTime - (0x0040, 0x0251), # Performed Procedure Step End Time - (0x0040, 0x0253), # Performed Procedure Step ID - (0x0040, 0x0244), # Performed Procedure Step Start Date - (0x0040, 0x4050), # Performed Procedure Step Start DateTime - (0x0040, 0x0245), # Performed Procedure Step Start Time - (0x0040, 0x0241), # Performed Station AE Title - (0x0040, 0x4030), # Performed Station Geographic Location Code Sequence - (0x0040, 0x0242), # Performed Station Name - (0x0040, 0x4028), # Performed Station Name Code Sequence - (0x0008, 0x1052), # Performing Physician Identification Sequence - (0x0008, 0x1050), # Performing Physician's Name - (0x0040, 0x1102), # Person's Address - (0x0040, 0x1104), # Person's Telecom Information - (0x0040, 0x1103), # Person's Telephone Numbers - (0x4008, 0x0114), # Physician Approving Interpretation - (0x0008, 0x1062), # Physician(s) Reading Study Identification Sequence - (0x0008, 0x1048), # Physician(s) of Record - (0x0008, 0x1049), # Physician(s) of Record Identification Sequence - (0x0018, 0x1004), # Plate ID - (0x3002, 0x0123), # Position Acquisition Template Description - (0x3002, 0x0121), # Position Acquisition Template Name - (0x0040, 0x0012), # Pre-Medication - (0x0010, 0x21C0), # Pregnancy Status - (0x300A, 0x000E), # Prescription Description - (0x0070, 0x0082), # Presentation Creation Date - (0x0070, 0x0083), # Presentation Creation Time - (0x3010, 0x0061), # Prior Treatment Dose Description - (0x0040, 0x4052), # Procedure Step Cancellation DateTime - (0x0044, 0x000B), # Product Expiration DateTime - (0x0008, 0x1088), # Pyramid Description - (0x0020, 0x0027), # Pyramid Label - (0x3006, 0x0028), # ROI Description - (0x3006, 0x0038), # ROI Generation Description - (0x3006, 0x0088), # ROI Observation Description - (0x3006, 0x0085), # ROI Observation Label - (0x300A, 0x0004), # RT Plan Description - (0x300A, 0x0003), # RT Plan Name - (0x0018, 0x1078), # Radiopharmaceutical Start DateTime - (0x0018, 0x1072), # Radiopharmaceutical Start Time - (0x0018, 0x1079), # Radiopharmaceutical Stop DateTime - (0x0018, 0x1073), # Radiopharmaceutical Stop Time - (0x300C, 0x0113), # Reason for Omission Description - (0x0040, 0x100A), # Reason for Requested Procedure Code Sequence - (0x0032, 0x1030), # Reason for Study - (0x0032, 0x1066), # Reason for Visit - (0x0032, 0x1067), # Reason for Visit Code Sequence - (0x0040, 0x2001), # Reason for the Imaging Service Request - (0x0040, 0x1002), # Reason for the Requested Procedure - (0x0074, 0x1234), # Receiving AE - (0x0400, 0x0402), # Referenced Digital Signature Sequence - (0x0038, 0x0004), # Referenced Patient Alias Sequence - (0x0010, 0x1100), # Referenced Patient Photo Sequence - (0x0008, 0x1120), # Referenced Patient Sequence - (0x0400, 0x0403), # Referenced SOP Instance MAC Sequence - (0x0008, 0x0096), # Referring Physician Identification Sequence - (0x0008, 0x0092), # Referring Physician's Address - (0x0008, 0x0094), # Referring Physician's Telephone Numbers - (0x0010, 0x2152), # Region of Residence - (0x0040, 0x0275), # Request Attributes Sequence - (0x0032, 0x1070), # Requested Contrast Agent - (0x0040, 0x1400), # Requested Procedure Comments - (0x0040, 0x1001), # Requested Procedure ID - (0x0040, 0x1005), # Requested Procedure Location - (0x0018, 0x9937), # Requested Series Description - (0x0074, 0x1236), # Requesting AE - (0x0032, 0x1032), # Requesting Physician - (0x0032, 0x1033), # Requesting Service - (0x0018, 0x9185), # Respiratory Motion Compensation Technique Description - (0x0010, 0x2299), # Responsible Organization - (0x0010, 0x2297), # Responsible Person - (0x4008, 0x4000), # Results Comments - (0x4008, 0x0118), # Results Distribution List Sequence - (0x4008, 0x0040), # Results ID - (0x4008, 0x0042), # Results ID Issuer - (0x0008, 0x0054), # Retrieve AE Title - (0x0100, 0x0420), # SOP Authorization DateTime - (0x0038, 0x001A), # Scheduled Admission Date - (0x0038, 0x001B), # Scheduled Admission Time - (0x0038, 0x001C), # Scheduled Discharge Date - (0x0038, 0x001D), # Scheduled Discharge Time - (0x0040, 0x4034), # Scheduled Human Performers Sequence - (0x0038, 0x001E), # Scheduled Patient Institution Residence - (0x0040, 0x000B), # Scheduled Performing Physician Identification Sequence - (0x0040, 0x0006), # Scheduled Performing Physician's Name - (0x0040, 0x0007), # Scheduled Procedure Step Description - (0x0040, 0x0004), # Scheduled Procedure Step End Date - (0x0040, 0x0005), # Scheduled Procedure Step End Time - (0x0040, 0x4008), # Scheduled Procedure Step Expiration DateTime - (0x0040, 0x0009), # Scheduled Procedure Step ID - (0x0040, 0x0011), # Scheduled Procedure Step Location - (0x0040, 0x4010), # Scheduled Procedure Step Modification DateTime - (0x0040, 0x0002), # Scheduled Procedure Step Start Date - (0x0040, 0x4005), # Scheduled Procedure Step Start DateTime - (0x0040, 0x0003), # Scheduled Procedure Step Start Time - (0x0040, 0x0001), # Scheduled Station AE Title - (0x0040, 0x4027), # Scheduled Station Geographic Location Code Sequence - (0x0040, 0x0010), # Scheduled Station Name - (0x0040, 0x4025), # Scheduled Station Name Code Sequence - (0x0032, 0x1020), # Scheduled Study Location - (0x0032, 0x1021), # Scheduled Study Location AE Title - (0x0032, 0x1000), # Scheduled Study Start Date - (0x0032, 0x1001), # Scheduled Study Start Time - (0x0032, 0x1010), # Scheduled Study Stop Date - (0x0032, 0x1011), # Scheduled Study Stop Time - (0x0008, 0x103E), # Series Description - (0x0038, 0x0062), # Service Episode Description - (0x0038, 0x0060), # Service Episode ID - (0x300A, 0x01B2), # Setup Technique Description - (0x300A, 0x01A6), # Shielding Device Description - (0x0040, 0x06FA), # Slide Identifier - (0x0010, 0x21A0), # Smoking Status - (0x300A, 0x0216), # Source Manufacturer - (0x0038, 0x0050), # Special Needs - (0x0040, 0x050A), # Specimen Accession Number - (0x0040, 0x0602), # Specimen Detailed Description - (0x0040, 0x0600), # Specimen Short Description - (0x0008, 0x0055), # Station AE Title - (0x3006, 0x0006), # Structure Set Description - (0x3006, 0x0004), # Structure Set Name - (0x0032, 0x1040), # Study Arrival Date - (0x0032, 0x1041), # Study Arrival Time - (0x0032, 0x4000), # Study Comments - (0x0032, 0x1050), # Study Completion Date - (0x0032, 0x1051), # Study Completion Time - (0x0008, 0x1030), # Study Description - (0x0032, 0x0012), # Study ID Issuer - (0x0032, 0x0034), # Study Read Date - (0x0032, 0x0035), # Study Read Time - (0x0032, 0x0032), # Study Verified Date - (0x0032, 0x0033), # Study Verified Time - (0x0044, 0x0010), # Substance Administration DateTime - (0x0040, 0xA354), # Telephone Number (Trial) - (0x0040, 0xDB07), # Template Local Version - (0x0040, 0xDB06), # Template Version - (0x4000, 0x4000), # Text Comments - (0x2030, 0x0020), # Text String - (0x0040, 0xA112), # Time of Document Creation or Verbal Transaction (Trial) - (0x0018, 0x1201), # Time of Last Calibration - (0x0018, 0x1014), # Time of Secondary Capture - (0x0008, 0x0201), # Timezone Offset From UTC - (0x0088, 0x0910), # Topic Author - (0x0088, 0x0912), # Topic Keywords - (0x0088, 0x0906), # Topic Subject - (0x0088, 0x0904), # Topic Title - (0x0018, 0x5011), # Transducer Identification Sequence - (0x300A, 0x000B), # Treatment Sites - (0x0018, 0x100A), # UDI Sequence - (0x0018, 0x1009), # Unique Device Identifier - (0x0040, 0xA352), # Verbal Source (Trial) - (0x0040, 0xA358), # Verbal Source Identifier Code Sequence (Trial) - (0x0038, 0x4000), # Visit Comments - (0x0018, 0x9373), # X-Ray Detector Label - (0x003A, 0x0329), # Waveform Filter Description + (0x0018, 0x4000), # Acquisition Comments + (0x0018, 0x9424), # Acquisition Protocol Description + (0x0040, 0x4035), # Actual Human Performers Sequence + (0x0010, 0x21B0), # Additional Patient History + (0x0040, 0xA353), # Address (Trial) + (0x0038, 0x0010), # Admission ID + (0x0038, 0x0020), # Admitting Date + (0x0008, 0x1084), # Admitting Diagnoses Code Sequence + (0x0008, 0x1080), # Admitting Diagnoses Description + (0x0038, 0x0021), # Admitting Time + (0x0000, 0x1000), # Affected SOP Instance UID + (0x0010, 0x2110), # Allergies + (0x006A, 0x0006), # Annotation Group Description + (0x0044, 0x0004), # Approval Status DateTime + (0x4000, 0x0010), # Arbitrary + (0x0044, 0x0105), # Assertion Expiration DateTime + (0x0040, 0xA078), # Author Observer Sequence + (0x300A, 0x00C3), # Beam Description + (0x300A, 0x00DD), # Bolus Description + (0x0010, 0x1081), # Branch of Service + (0x0014, 0x407E), # Calibration Date + (0x0014, 0x407C), # Calibration Time + (0x0016, 0x004D), # Camera Owner Name + (0x0018, 0x1007), # Cassette ID + (0x0400, 0x0310), # Certified Timestamp + (0x0012, 0x0082), # Clinical Trial Protocol Ethics Committee Approval Number + (0x0012, 0x0072), # Clinical Trial Series Description + (0x0012, 0x0071), # Clinical Trial Series ID + (0x0012, 0x0051), # Clinical Trial Time Point Description + (0x0040, 0x0310), # Comments on Radiation Dose + (0x0040, 0x0280), # Comments on the Performed Procedure Step + (0x300A, 0x02EB), # Compensator Description + (0x0040, 0x3001), # Confidentiality Constraint on Patient Data Description + (0x0008, 0x009D), # Consulting Physician Identification Sequence + (0x0050, 0x001B), # Container Component ID + (0x0040, 0x051A), # Container Description + (0x0070, 0x0086), # Content Creator's Identification Code Sequence + (0x0018, 0x1042), # Contrast/Bolus Start Time + (0x0018, 0x1043), # Contrast/Bolus Stop Time + (0x0018, 0xA002), # Contribution DateTime + (0x0018, 0xA003), # Contribution Description + (0x0010, 0x2150), # Country of Residence + (0x2100, 0x0040), # Creation Date + (0x2100, 0x0050), # Creation Time + (0x0040, 0xA307), # Current Observer (Trial) + (0x0038, 0x0300), # Current Patient Location + (0x5000, 0x0000, 0xFF00, 0x0000), # Curve Data + (0x0008, 0x0025), # Curve Date + (0x0008, 0x0035), # Curve Time + (0x0040, 0xA07C), # Custodial Organization Sequence + (0xFFFC, 0xFFFC), # Data Set Trailing Padding + (0x0040, 0xA110), # Date of Document or Verbal Transaction (Trial) + (0x0018, 0x1205), # Date of Installation + (0x0018, 0x1200), # Date of Last Calibration + (0x0018, 0x1204), # Date of Manufacture + (0x0018, 0x1012), # Date of Secondary Capture + (0x0018, 0x1202), # DateTime of Last Calibration + (0x0018, 0x937F), # Decomposition Description + (0x0008, 0x2111), # Derivation Description + (0x0050, 0x0020), # Device Description + (0x0016, 0x004B), # Device Setting Description + (0xFFFA, 0xFFFA), # Digital Signatures Sequence + (0x0038, 0x0030), # Discharge Date + (0x0038, 0x0040), # Discharge Diagnosis Description + (0x0038, 0x0032), # Discharge Time + (0x300A, 0x079A), # Displacement Reference Label + (0x4008, 0x011A), # Distribution Address + (0x4008, 0x0119), # Distribution Name + (0x300A, 0x0016), # Dose Reference Description + (0x3010, 0x0037), # Entity Description + (0x3010, 0x0036), # Entity Name + (0x300A, 0x0676), # Equipment Frame of Reference Description + (0x0012, 0x0087), # Ethics Committee Approval Effectiveness End Date + (0x0012, 0x0086), # Ethics Committee Approval Effectiveness Start Date + (0x0010, 0x2160), # Ethnic Group + (0x0040, 0x4011), # Expected Completion DateTime + (0x003A, 0x032B), # Filter Lookup Table Description + (0x0040, 0xA023), # Findings Group Recording Date (Trial) + (0x0040, 0xA024), # Findings Group Recording Time (Trial) + (0x300A, 0x0196), # Fixation Device Description + (0x300A, 0x0072), # Fraction Group Description + (0x0020, 0x9158), # Frame Comments + (0x0016, 0x0076), # GPS Altitude + (0x0016, 0x0075), # GPS Altitude Ref + (0x0016, 0x008C), # GPS Area Information + (0x0016, 0x007B), # GPS DOP + (0x0016, 0x008D), # GPS Date Stamp + (0x0016, 0x0088), # GPS Dest Bearing + (0x0016, 0x008A), # GPS Dest Distance + (0x0016, 0x0089), # GPS Dest Distance Ref + (0x0016, 0x0086), # GPS Dest Longitude + (0x0016, 0x0085), # GPS Dest Longitude Ref + (0x0016, 0x0087), # GPS Dest Bearing Ref + (0x0016, 0x0084), # GPS Dest Latitude + (0x0016, 0x0083), # GPS Dest Latitude Ref + (0x0016, 0x008E), # GPS Differential + (0x0016, 0x0081), # GPS Img Direction + (0x0016, 0x0080), # GPS Img Direction Ref + (0x0016, 0x0072), # GPS Latitude + (0x0016, 0x0071), # GPS Latitude Ref + (0x0016, 0x0074), # GPS Longitude + (0x0016, 0x0073), # GPS Longitude Ref + (0x0016, 0x0082), # GPS Map Datum + (0x0016, 0x007A), # GPS Measure Mode + (0x0016, 0x008B), # GPS Processing Method + (0x0016, 0x0078), # GPS Satellites + (0x0016, 0x007D), # GPS Speed + (0x0016, 0x007C), # GPS Speed Ref + (0x0016, 0x0079), # GPS Status + (0x0016, 0x0077), # GPS Time Stamp + (0x0016, 0x007F), # GPS Track + (0x0016, 0x007E), # GPS Track Ref + (0x0016, 0x0070), # GPS Version ID + (0x0018, 0x1008), # Gantry ID + (0x0018, 0x1005), # Generator ID + (0x0040, 0xE004), # HL7 Document Effective Time + (0x0040, 0x4037), # Human Performer's Name + (0x0040, 0x4036), # Human Performer's Organization + (0x0088, 0x0200), # Icon Image Sequence + (0x0008, 0x4000), # Identifying Comments + (0x0020, 0x4000), # Image Comments + (0x0028, 0x4000), # Image Presentation Comments + (0x0040, 0x2400), # Imaging Service Request Comments + (0x4008, 0x0300), # Impressions + (0x0008, 0x0015), # Instance Coercion DateTime + (0x0400, 0x0600), # Instance Origin Status + (0x0008, 0x0081), # Institution Address + (0x0008, 0x1040), # Institutional Department Name + (0x0008, 0x1041), # Institutional Department Type Code Sequence + (0x0010, 0x1050), # Insurance Plan Identification + (0x3010, 0x0085), # Intended Fraction Start Time + (0x0040, 0x1011), # Intended Recipients of Results Identification Sequence + (0x4008, 0x0112), # Interpretation Approval Date + (0x4008, 0x0113), # Interpretation Approval Time + (0x4008, 0x0111), # Interpretation Approver Sequence + (0x4008, 0x010C), # Interpretation Author + (0x4008, 0x0115), # Interpretation Diagnosis Description + (0x4008, 0x0200), # Interpretation ID + (0x4008, 0x0202), # Interpretation ID Issuer + (0x4008, 0x0100), # Interpretation Recorded Date + (0x4008, 0x0101), # Interpretation Recorded Time + (0x4008, 0x0102), # Interpretation Recorder + (0x4008, 0x010B), # Interpretation Text + (0x4008, 0x010A), # Interpretation Transcriber + (0x4008, 0x0108), # Interpretation Transcription Date + (0x4008, 0x0109), # Interpretation Transcription Time + (0x0018, 0x0035), # Intervention Drug Start Time + (0x0018, 0x0027), # Intervention Drug Stop Time + (0x0040, 0x2004), # Issue Date of Imaging Service Request + (0x0040, 0x2005), # Issue Time of Imaging Service Request + (0x0038, 0x0011), # Issuer of Admission ID + (0x0038, 0x0014), # Issuer of Admission ID Sequence + (0x0010, 0x0021), # Issuer of Patient ID + (0x0038, 0x0061), # Issuer of Service Episode ID + (0x0038, 0x0064), # Issuer of Service Episode ID Sequence + (0x0010, 0x21D0), # Last Menstrual Date + (0x0016, 0x004F), # Lens Make + (0x0016, 0x0050), # Lens Model + (0x0016, 0x0051), # Lens Serial Number + (0x0016, 0x004E), # Lens Specification + (0x0050, 0x0021), # Long Device Description + (0x0400, 0x0404), # MAC + (0x0016, 0x002B), # Maker Note + (0x0010, 0x2000), # Medical Alerts + (0x0010, 0x1090), # Medical Record Locator + (0x0010, 0x1080), # Military Rank + (0x0400, 0x0550), # Modified Attributes Sequence + (0x0020, 0x3403), # Modified Image Date + (0x0020, 0x3406), # Modified Image Description + (0x0020, 0x3405), # Modified Image Time + (0x0020, 0x3401), # Modifying Device ID + (0x0018, 0x937B), # Multi-energy Acquisition Description + (0x0008, 0x1060), # Name of Physician(s) Reading Study + (0x0040, 0x1010), # Names of Intended Recipients of Results + (0x0008, 0x1000), # Network ID + (0x0400, 0x0552), # Nonconforming Data Element Value + (0x0400, 0x0551), # Nonconforming Modified Attributes Sequence + (0x0040, 0xA192), # Observation Date (Trial) + (0x0040, 0xA033), # Observation Start DateTime + (0x0040, 0xA193), # Observation Time (Trial) + (0x0010, 0x2180), # Occupation + (0x0040, 0x2010), # Order Callback Phone Number + (0x0040, 0x2011), # Order Callback Telecom Information + (0x0040, 0x2008), # Order Entered By + (0x0040, 0x2009), # Order Enterer's Location + (0x0400, 0x0561), # Original Attributes Sequence + (0x2100, 0x0070), # Originator + (0x0010, 0x1000), # Other Patient IDs + (0x0010, 0x1002), # Other Patient IDs Sequence + (0x0010, 0x1001), # Other Patient Names + (0x6000, 0x4000, 0xFF00, 0xFFFF), # Overlay Comments + (0x6000, 0x3000, 0xFF00, 0xFFFF), # Overlay Data + (0x0008, 0x0024), # Overlay Date + (0x0008, 0x0034), # Overlay Time + (0x0040, 0xA07A), # Participant Sequence + (0x0010, 0x4000), # Patient Comments + (0x300A, 0x0794), # Patient Setup Photo Description + (0x0038, 0x0500), # Patient State + (0x0040, 0x1004), # Patient Transport Arrangements + (0x300A, 0x0792), # Patient Treatment Preparation Method Description + (0x300A, 0x078E), # Patient Treatment Preparation Procedure Parameter Description + (0x0010, 0x1040), # Patient's Address + (0x0010, 0x1010), # Patient's Age + (0x0010, 0x1005), # Patient's Birth Name + (0x0010, 0x0032), # Patient's Birth Time + (0x0038, 0x0400), # Patient's Institution Residence + (0x0010, 0x0050), # Patient's Insurance Plan Code Sequence + (0x0010, 0x1060), # Patient's Mother's Birth Name + (0x0010, 0x0101), # Patient's Primary Language Code Sequence + (0x0010, 0x0102), # Patient's Primary Language Modifier Code Sequence + (0x0010, 0x21F0), # Patient's Religious Preference + (0x0010, 0x1020), # Patient's Size + (0x0010, 0x2155), # Patient's Telecom Information + (0x0010, 0x2154), # Patient's Telephone Numbers + (0x0010, 0x1030), # Patient's Weight + (0x0040, 0x0243), # Performed Location + (0x0040, 0x0254), # Performed Procedure Step Description + (0x0040, 0x0250), # Performed Procedure Step End Date + (0x0040, 0x4051), # Performed Procedure Step End DateTime + (0x0040, 0x0251), # Performed Procedure Step End Time + (0x0040, 0x0253), # Performed Procedure Step ID + (0x0040, 0x0244), # Performed Procedure Step Start Date + (0x0040, 0x4050), # Performed Procedure Step Start DateTime + (0x0040, 0x0245), # Performed Procedure Step Start Time + (0x0040, 0x0241), # Performed Station AE Title + (0x0040, 0x4030), # Performed Station Geographic Location Code Sequence + (0x0040, 0x0242), # Performed Station Name + (0x0040, 0x4028), # Performed Station Name Code Sequence + (0x0008, 0x1052), # Performing Physician Identification Sequence + (0x0008, 0x1050), # Performing Physician's Name + (0x0040, 0x1102), # Person's Address + (0x0040, 0x1104), # Person's Telecom Information + (0x0040, 0x1103), # Person's Telephone Numbers + (0x4008, 0x0114), # Physician Approving Interpretation + (0x0008, 0x1062), # Physician(s) Reading Study Identification Sequence + (0x0008, 0x1048), # Physician(s) of Record + (0x0008, 0x1049), # Physician(s) of Record Identification Sequence + (0x0018, 0x1004), # Plate ID + (0x3002, 0x0123), # Position Acquisition Template Description + (0x3002, 0x0121), # Position Acquisition Template Name + (0x0040, 0x0012), # Pre-Medication + (0x0010, 0x21C0), # Pregnancy Status + (0x300A, 0x000E), # Prescription Description + (0x0070, 0x0082), # Presentation Creation Date + (0x0070, 0x0083), # Presentation Creation Time + (0x3010, 0x0061), # Prior Treatment Dose Description + (0x0040, 0x4052), # Procedure Step Cancellation DateTime + (0x0044, 0x000B), # Product Expiration DateTime + (0x0008, 0x1088), # Pyramid Description + (0x0020, 0x0027), # Pyramid Label + (0x3006, 0x0028), # ROI Description + (0x3006, 0x0038), # ROI Generation Description + (0x3006, 0x0088), # ROI Observation Description + (0x3006, 0x0085), # ROI Observation Label + (0x300A, 0x0004), # RT Plan Description + (0x300A, 0x0003), # RT Plan Name + (0x0018, 0x1078), # Radiopharmaceutical Start DateTime + (0x0018, 0x1072), # Radiopharmaceutical Start Time + (0x0018, 0x1079), # Radiopharmaceutical Stop DateTime + (0x0018, 0x1073), # Radiopharmaceutical Stop Time + (0x300C, 0x0113), # Reason for Omission Description + (0x0040, 0x100A), # Reason for Requested Procedure Code Sequence + (0x0032, 0x1030), # Reason for Study + (0x0032, 0x1066), # Reason for Visit + (0x0032, 0x1067), # Reason for Visit Code Sequence + (0x0040, 0x2001), # Reason for the Imaging Service Request + (0x0040, 0x1002), # Reason for the Requested Procedure + (0x0074, 0x1234), # Receiving AE + (0x0400, 0x0402), # Referenced Digital Signature Sequence + (0x0038, 0x0004), # Referenced Patient Alias Sequence + (0x0010, 0x1100), # Referenced Patient Photo Sequence + (0x0008, 0x1120), # Referenced Patient Sequence + (0x0400, 0x0403), # Referenced SOP Instance MAC Sequence + (0x0008, 0x0096), # Referring Physician Identification Sequence + (0x0008, 0x0092), # Referring Physician's Address + (0x0008, 0x0094), # Referring Physician's Telephone Numbers + (0x0010, 0x2152), # Region of Residence + (0x0040, 0x0275), # Request Attributes Sequence + (0x0032, 0x1070), # Requested Contrast Agent + (0x0040, 0x1400), # Requested Procedure Comments + (0x0040, 0x1001), # Requested Procedure ID + (0x0040, 0x1005), # Requested Procedure Location + (0x0018, 0x9937), # Requested Series Description + (0x0074, 0x1236), # Requesting AE + (0x0032, 0x1032), # Requesting Physician + (0x0032, 0x1033), # Requesting Service + (0x0018, 0x9185), # Respiratory Motion Compensation Technique Description + (0x0010, 0x2299), # Responsible Organization + (0x0010, 0x2297), # Responsible Person + (0x4008, 0x4000), # Results Comments + (0x4008, 0x0118), # Results Distribution List Sequence + (0x4008, 0x0040), # Results ID + (0x4008, 0x0042), # Results ID Issuer + (0x0008, 0x0054), # Retrieve AE Title + (0x0100, 0x0420), # SOP Authorization DateTime + (0x0038, 0x001A), # Scheduled Admission Date + (0x0038, 0x001B), # Scheduled Admission Time + (0x0038, 0x001C), # Scheduled Discharge Date + (0x0038, 0x001D), # Scheduled Discharge Time + (0x0040, 0x4034), # Scheduled Human Performers Sequence + (0x0038, 0x001E), # Scheduled Patient Institution Residence + (0x0040, 0x000B), # Scheduled Performing Physician Identification Sequence + (0x0040, 0x0006), # Scheduled Performing Physician's Name + (0x0040, 0x0007), # Scheduled Procedure Step Description + (0x0040, 0x0004), # Scheduled Procedure Step End Date + (0x0040, 0x0005), # Scheduled Procedure Step End Time + (0x0040, 0x4008), # Scheduled Procedure Step Expiration DateTime + (0x0040, 0x0009), # Scheduled Procedure Step ID + (0x0040, 0x0011), # Scheduled Procedure Step Location + (0x0040, 0x4010), # Scheduled Procedure Step Modification DateTime + (0x0040, 0x0002), # Scheduled Procedure Step Start Date + (0x0040, 0x4005), # Scheduled Procedure Step Start DateTime + (0x0040, 0x0003), # Scheduled Procedure Step Start Time + (0x0040, 0x0001), # Scheduled Station AE Title + (0x0040, 0x4027), # Scheduled Station Geographic Location Code Sequence + (0x0040, 0x0010), # Scheduled Station Name + (0x0040, 0x4025), # Scheduled Station Name Code Sequence + (0x0032, 0x1020), # Scheduled Study Location + (0x0032, 0x1021), # Scheduled Study Location AE Title + (0x0032, 0x1000), # Scheduled Study Start Date + (0x0032, 0x1001), # Scheduled Study Start Time + (0x0032, 0x1010), # Scheduled Study Stop Date + (0x0032, 0x1011), # Scheduled Study Stop Time + (0x0008, 0x103E), # Series Description + (0x0038, 0x0062), # Service Episode Description + (0x0038, 0x0060), # Service Episode ID + (0x300A, 0x01B2), # Setup Technique Description + (0x300A, 0x01A6), # Shielding Device Description + (0x0040, 0x06FA), # Slide Identifier + (0x0010, 0x21A0), # Smoking Status + (0x300A, 0x0216), # Source Manufacturer + (0x0038, 0x0050), # Special Needs + (0x0040, 0x050A), # Specimen Accession Number + (0x0040, 0x0602), # Specimen Detailed Description + (0x0040, 0x0600), # Specimen Short Description + (0x0008, 0x0055), # Station AE Title + (0x3006, 0x0006), # Structure Set Description + (0x3006, 0x0004), # Structure Set Name + (0x0032, 0x1040), # Study Arrival Date + (0x0032, 0x1041), # Study Arrival Time + (0x0032, 0x4000), # Study Comments + (0x0032, 0x1050), # Study Completion Date + (0x0032, 0x1051), # Study Completion Time + (0x0008, 0x1030), # Study Description + (0x0032, 0x0012), # Study ID Issuer + (0x0032, 0x0034), # Study Read Date + (0x0032, 0x0035), # Study Read Time + (0x0032, 0x0032), # Study Verified Date + (0x0032, 0x0033), # Study Verified Time + (0x0044, 0x0010), # Substance Administration DateTime + (0x0040, 0xA354), # Telephone Number (Trial) + (0x0040, 0xDB07), # Template Local Version + (0x0040, 0xDB06), # Template Version + (0x4000, 0x4000), # Text Comments + (0x2030, 0x0020), # Text String + (0x0040, 0xA112), # Time of Document Creation or Verbal Transaction (Trial) + (0x0018, 0x1201), # Time of Last Calibration + (0x0018, 0x1014), # Time of Secondary Capture + (0x0008, 0x0201), # Timezone Offset From UTC + (0x0088, 0x0910), # Topic Author + (0x0088, 0x0912), # Topic Keywords + (0x0088, 0x0906), # Topic Subject + (0x0088, 0x0904), # Topic Title + (0x0018, 0x5011), # Transducer Identification Sequence + (0x300A, 0x000B), # Treatment Sites + (0x0018, 0x100A), # UDI Sequence + (0x0018, 0x1009), # Unique Device Identifier + (0x0040, 0xA352), # Verbal Source (Trial) + (0x0040, 0xA358), # Verbal Source Identifier Code Sequence (Trial) + (0x0038, 0x4000), # Visit Comments + (0x0018, 0x9373), # X-Ray Detector Label + (0x003A, 0x0329), # Waveform Filter Description ] # Replace UID U_TAGS = [ - (0x0008, 0x0017), # Acquisition UID - (0x0020, 0x9161), # Concatenation UID - (0x3010, 0x0006), # Conceptual Volume UID - (0x3010, 0x0013), # Constituent Conceptual Volume UID - (0x0018, 0x1002), # Device UID - (0x0400, 0x0100), # Digital Signature UID - (0x0020, 0x9164), # Dimension Organization UID - (0x300A, 0x0013), # Dose Reference UID - (0x3010, 0x006E), # Dosimetric Objective UID - (0x0008, 0x0058), # Failed SOP Instance UID List - (0x0070, 0x031A), # Fiducial UID - (0x0020, 0x0052), # Frame of Reference UID - (0x0008, 0x0014), # Instance Creator UID - (0x0008, 0x3010), # Irradiation Event UID - (0x0028, 0x1214), # Large Palette Color Lookup Table UID - (0x0018, 0x100B), # Manufacturer's Device Class UID - (0x0002, 0x0003), # Media Storage SOP Instance UID - (0x003A, 0x0310), # Multiplex Group UID - (0x0040, 0xA402), # Observation Subject UID (Trial) - (0x0040, 0xA171), # Observation UID - (0x0028, 0x1199), # Palette Color Lookup Table UID - (0x300A, 0x0650), # Patient Setup UID - (0x0070, 0x1101), # Presentation Display Collection UID - (0x0070, 0x1102), # Presentation Sequence Collection UID - (0x0008, 0x0019), # Pyramid UID - (0x3010, 0x003B), # RT Treatment Phase UID - (0x3010, 0x000B), # Referenced Conceptual Volume UID - (0x300A, 0x0083), # Referenced Dose Reference UID - (0x3010, 0x006F), # Referenced Dosimetric Objective UID - (0x3010, 0x0031), # Referenced Fiducials UID - (0x3006, 0x0024), # Referenced Frame of Reference UID - (0x0040, 0x4023), # Referenced General Purpose Scheduled Procedure Step Transaction UID - (0x0040, 0xA172), # Referenced Observation UID (Trial) - (0x0008, 0x1155), # Referenced SOP Instance UID - (0x0004, 0x1511), # Referenced SOP Instance UID in File - (0x300A, 0x0785), # Referenced Treatment Position Group UID - (0x3006, 0x00C2), # Related Frame of Reference UID - (0x0000, 0x1001), # Requested SOP Instance UID - (0x0008, 0x0018), # SOP Instance UID - (0x0020, 0x000E), # Series Instance UID - (0x3010, 0x0015), # Source Conceptual Volume UID - (0x0064, 0x0003), # Source Frame of Reference UID - (0x0040, 0x0554), # Specimen UID - (0x0088, 0x0140), # Storage Media File-set UID - (0x0020, 0x000D), # Study Instance UID - (0x0020, 0x0200), # Synchronization Frame of Reference UID - (0x0018, 0x2042), # Target UID - (0x0040, 0xDB0D), # Template Extension Creator UID - (0x0040, 0xDB0C), # Template Extension Organization UID - (0x0062, 0x0021), # Tracking UID - (0x0008, 0x1195), # Transaction UID - (0x300A, 0x0609), # Treatment Position Group UID - (0x300A, 0x0700), # Treatment Session UID - (0x0040, 0xA124), # UID + (0x0008, 0x0017), # Acquisition UID + (0x0020, 0x9161), # Concatenation UID + (0x3010, 0x0006), # Conceptual Volume UID + (0x3010, 0x0013), # Constituent Conceptual Volume UID + (0x0018, 0x1002), # Device UID + (0x0400, 0x0100), # Digital Signature UID + (0x0020, 0x9164), # Dimension Organization UID + (0x300A, 0x0013), # Dose Reference UID + (0x3010, 0x006E), # Dosimetric Objective UID + (0x0008, 0x0058), # Failed SOP Instance UID List + (0x0070, 0x031A), # Fiducial UID + (0x0020, 0x0052), # Frame of Reference UID + (0x0008, 0x0014), # Instance Creator UID + (0x0008, 0x3010), # Irradiation Event UID + (0x0028, 0x1214), # Large Palette Color Lookup Table UID + (0x0018, 0x100B), # Manufacturer's Device Class UID + (0x0002, 0x0003), # Media Storage SOP Instance UID + (0x003A, 0x0310), # Multiplex Group UID + (0x0040, 0xA402), # Observation Subject UID (Trial) + (0x0040, 0xA171), # Observation UID + (0x0028, 0x1199), # Palette Color Lookup Table UID + (0x300A, 0x0650), # Patient Setup UID + (0x0070, 0x1101), # Presentation Display Collection UID + (0x0070, 0x1102), # Presentation Sequence Collection UID + (0x0008, 0x0019), # Pyramid UID + (0x3010, 0x003B), # RT Treatment Phase UID + (0x3010, 0x000B), # Referenced Conceptual Volume UID + (0x300A, 0x0083), # Referenced Dose Reference UID + (0x3010, 0x006F), # Referenced Dosimetric Objective UID + (0x3010, 0x0031), # Referenced Fiducials UID + (0x3006, 0x0024), # Referenced Frame of Reference UID + ( + 0x0040, + 0x4023, + ), # Referenced General Purpose Scheduled Procedure Step Transaction UID + (0x0040, 0xA172), # Referenced Observation UID (Trial) + (0x0008, 0x1155), # Referenced SOP Instance UID + (0x0004, 0x1511), # Referenced SOP Instance UID in File + (0x300A, 0x0785), # Referenced Treatment Position Group UID + (0x3006, 0x00C2), # Related Frame of Reference UID + (0x0000, 0x1001), # Requested SOP Instance UID + (0x0008, 0x0018), # SOP Instance UID + (0x0020, 0x000E), # Series Instance UID + (0x3010, 0x0015), # Source Conceptual Volume UID + (0x0064, 0x0003), # Source Frame of Reference UID + (0x0040, 0x0554), # Specimen UID + (0x0088, 0x0140), # Storage Media File-set UID + (0x0020, 0x000D), # Study Instance UID + (0x0020, 0x0200), # Synchronization Frame of Reference UID + (0x0018, 0x2042), # Target UID + (0x0040, 0xDB0D), # Template Extension Creator UID + (0x0040, 0xDB0C), # Template Extension Organization UID + (0x0062, 0x0021), # Tracking UID + (0x0008, 0x1195), # Transaction UID + (0x300A, 0x0609), # Treatment Position Group UID + (0x300A, 0x0700), # Treatment Session UID + (0x0040, 0xA124), # UID ] # Replace element according to the VR Z_D_TAGS = [ - (0x0070, 0x0084), # Content Creator's Name - (0x0008, 0x0023), # Content Date - (0x0008, 0x0033), # Content Time - (0x0018, 0x0010), # Contrast/Bolus Agent - (0x0018, 0x9919), # Instruction Performed DateTime + (0x0070, 0x0084), # Content Creator's Name + (0x0008, 0x0023), # Content Date + (0x0008, 0x0033), # Content Time + (0x0018, 0x0010), # Contrast/Bolus Agent + (0x0018, 0x9919), # Instruction Performed DateTime ] # Set the value to empty according to the VR X_Z_TAGS = [ - (0x0040, 0x0555), # Acquisition Context Sequence - (0x0008, 0x0022), # Acquisition Date - (0x0008, 0x0032), # Acquisition Time - (0x2200, 0x0005), # Barcode Value - (0x2200, 0x0002), # Label Text - (0x0010, 0x2203), # Patient's Sex Neutered - (0x0008, 0x1110), # Referenced Study Sequence - (0x0032, 0x1060), # Requested Procedure Description - (0x300E, 0x0008), # Reviewer Name - (0x3008, 0x0105), # Source Serial Number - (0x300A, 0x00B2), # Treatment Machine Name + (0x0040, 0x0555), # Acquisition Context Sequence + (0x0008, 0x0022), # Acquisition Date + (0x0008, 0x0032), # Acquisition Time + (0x2200, 0x0005), # Barcode Value + (0x2200, 0x0002), # Label Text + (0x0010, 0x2203), # Patient's Sex Neutered + (0x0008, 0x1110), # Referenced Study Sequence + (0x0032, 0x1060), # Requested Procedure Description + (0x300E, 0x0008), # Reviewer Name + (0x3008, 0x0105), # Source Serial Number + (0x300A, 0x00B2), # Treatment Machine Name ] # Replace element according to the VR X_D_TAGS = [ - (0x0018, 0x1400), # Acquisition Device Processing Description - (0x0018, 0x700C), # Date of Last Detector Calibration - (0x0018, 0x700A), # Detector ID - (0x0018, 0x9517), # End Acquisition DateTime - (0x3008, 0x0054), # First Treatment Date - (0x0008, 0x0012), # Instance Creation Date - (0x3010, 0x004D), # Intended Phase End Date - (0x3010, 0x004C), # Intended Phase Start Date - (0x3008, 0x0056), # Most Recent Treatment Date - (0x0040, 0xA032), # Observation DateTime - (0x0008, 0x1072), # Operator Identification Sequence - (0x0018, 0x1030), # Protocol Name - (0x300A, 0x0006), # RT Plan Date - (0x300A, 0x0007), # RT Plan Time - (0x3010, 0x0056), # RT Treatment Approach Label - (0x0008, 0x0021), # Series Date - (0x0008, 0x0031), # Series Time - (0x0018, 0x9516), # Start Acquisition DateTime - (0x0018, 0x700E), # Time of Last Detector Calibration - (0x3008, 0x0250), # Treatment Date - (0x3010, 0x0077), # Treatment Site - (0x3008, 0x0251), # Treatment Time + (0x0018, 0x1400), # Acquisition Device Processing Description + (0x0018, 0x700C), # Date of Last Detector Calibration + (0x0018, 0x700A), # Detector ID + (0x0018, 0x9517), # End Acquisition DateTime + (0x3008, 0x0054), # First Treatment Date + (0x0008, 0x0012), # Instance Creation Date + (0x3010, 0x004D), # Intended Phase End Date + (0x3010, 0x004C), # Intended Phase Start Date + (0x3008, 0x0056), # Most Recent Treatment Date + (0x0040, 0xA032), # Observation DateTime + (0x0008, 0x1072), # Operator Identification Sequence + (0x0018, 0x1030), # Protocol Name + (0x300A, 0x0006), # RT Plan Date + (0x300A, 0x0007), # RT Plan Time + (0x3010, 0x0056), # RT Treatment Approach Label + (0x0008, 0x0021), # Series Date + (0x0008, 0x0031), # Series Time + (0x0018, 0x9516), # Start Acquisition DateTime + (0x0018, 0x700E), # Time of Last Detector Calibration + (0x3008, 0x0250), # Treatment Date + (0x3010, 0x0077), # Treatment Site + (0x3008, 0x0251), # Treatment Time ] # Replace element according to the VR X_Z_D_TAGS = [ - (0x0008, 0x002A), # Acquisition DateTime - (0x0018, 0x1000), # Device Serial Number - (0x0008, 0x0013), # Instance Creation Time - (0x0008, 0x0082), # Institution Code Sequence - (0x0008, 0x0080), # Institution Name - (0x0008, 0x1070), # Operators' Name - (0x0008, 0x1111), # Referenced Performed Procedure Step Sequence - (0x0008, 0x1010), # Station Name + (0x0008, 0x002A), # Acquisition DateTime + (0x0018, 0x1000), # Device Serial Number + (0x0008, 0x0013), # Instance Creation Time + (0x0008, 0x0082), # Institution Code Sequence + (0x0008, 0x0080), # Institution Name + (0x0008, 0x1070), # Operators' Name + (0x0008, 0x1111), # Referenced Performed Procedure Step Sequence + (0x0008, 0x1010), # Station Name ] # Replace element with UI as VR, else replace according to VR with empty values X_Z_U_STAR_TAGS = [ - (0x0008, 0x1140), # Referenced Image Sequence - (0x0008, 0x2112), # Source Image Sequence + (0x0008, 0x1140), # Referenced Image Sequence + (0x0008, 0x2112), # Source Image Sequence ] # Contains all previous tags into one array diff --git a/dicomanonymizer/format_tag.py b/dicomanonymizer/format_tag.py index cb7aee2..3d65792 100644 --- a/dicomanonymizer/format_tag.py +++ b/dicomanonymizer/format_tag.py @@ -14,7 +14,7 @@ def hex_to_string(x): left = x[:2] right = x[2:] num_zeroes = 4 - len(right) - return left + ('0' * num_zeroes) + right + return left + ("0" * num_zeroes) + right def tag_to_hex_strings(tag): diff --git a/dicomanonymizer/simpledicomanonymizer.py b/dicomanonymizer/simpledicomanonymizer.py index 74260d4..29bd310 100644 --- a/dicomanonymizer/simpledicomanonymizer.py +++ b/dicomanonymizer/simpledicomanonymizer.py @@ -6,7 +6,16 @@ from dataclasses import dataclass from dicomanonymizer.dicomfields import ( - D_TAGS, Z_TAGS, X_TAGS, U_TAGS, Z_D_TAGS, X_Z_TAGS, X_D_TAGS, X_Z_D_TAGS, X_Z_U_STAR_TAGS) + D_TAGS, + Z_TAGS, + X_TAGS, + U_TAGS, + Z_D_TAGS, + X_Z_TAGS, + X_D_TAGS, + X_Z_D_TAGS, + X_Z_U_STAR_TAGS, +) from dicomanonymizer.format_tag import tag_to_hex_strings @@ -15,6 +24,7 @@ # Regexp function + def regexp(options: Union[list, dict]): """ Apply a regexp method to the dataset @@ -31,8 +41,8 @@ def apply_regexp(dataset, tag): """ if isinstance(options, dict): try: - find = options['find'] - replace = options['replace'] + find = options["find"] + replace = options["replace"] except KeyError as e: raise ValueError(f"Missing field in tag dictionary {tag}: {e.args[0]}") else: @@ -53,10 +63,11 @@ def replace_with_value(options: Union[list, dict]): - value: the string used to replace the tag value If options is a list, value is expected to be the first value. """ + def apply_replace_with_value(dataset, tag): if isinstance(options, dict): try: - value = options['value'] + value = options["value"] except KeyError as e: raise ValueError(f"Missing field in tag dictionary {tag}: {e.args[0]}") else: @@ -71,11 +82,13 @@ def apply_replace_with_value(dataset, tag): # Default anonymization functions + def get_UID(old_uid: str) -> str: """ Lookup new UID in cached dictionary or create new one if none found """ from pydicom.uid import generate_uid + if old_uid not in dictionary: dictionary[old_uid] = generate_uid(None) return dictionary.get(old_uid) @@ -88,6 +101,7 @@ def replace_element_UID(element): apply the same replaced value if we have an other UID with the same value """ from pydicom.multival import MultiValue + if isinstance(element.value, MultiValue): # Example of multi-value UID situation: IrradiationEventUID, (0008,3010) for k, v in enumerate(element.value): @@ -102,11 +116,11 @@ def replace_date_time_element(element): Date and time elements are all handled in the same way, whether they are emptied or removed. """ - if element.VR == 'DA': + if element.VR == "DA": replace_element_date(element) - elif element.VR == 'DT': + elif element.VR == "DT": replace_element_date_time(element) - elif element.VR == 'TM': + elif element.VR == "TM": replace_element_time(element) @@ -114,21 +128,21 @@ def replace_element_date(element): """ Replace date element's value with '00010101' """ - element.value = '00010101' + element.value = "00010101" def replace_element_date_time(element): """ Replace date time element's value with '00010101010101.000000+0000' """ - element.value = '00010101010101.000000+0000' + element.value = "00010101010101.000000+0000" def replace_element_time(element): """ Replace time element's value with '000000.00' """ - element.value = '000000.00' + element.value = "000000.00" def replace_element(element): @@ -146,19 +160,19 @@ def replace_element(element): See https://laurelbridge.com/pdf/Dicom-Anonymization-Conformance-Statement.pdf """ - if element.VR in ('LO', 'LT', 'SH', 'PN', 'CS', 'ST', 'UT'): - element.value = 'ANONYMIZED' # CS VR accepts only uppercase characters - elif element.VR == 'UI': + if element.VR in ("LO", "LT", "SH", "PN", "CS", "ST", "UT"): + element.value = "ANONYMIZED" # CS VR accepts only uppercase characters + elif element.VR == "UI": replace_element_UID(element) - elif element.VR in ('DS', 'IS'): - element.value = '0' - elif element.VR in ('FD', 'FL', 'SS', 'US', 'SL', 'UL'): + elif element.VR in ("DS", "IS"): + element.value = "0" + elif element.VR in ("FD", "FL", "SS", "US", "SL", "UL"): element.value = 0 - elif element.VR in ('DT', 'DA', 'TM'): + elif element.VR in ("DT", "DA", "TM"): replace_date_time_element(element) - elif element.VR == 'UN': - element.value = b'Anonymized' - elif element.VR == 'SQ': + elif element.VR == "UN": + element.value = b"Anonymized" + elif element.VR == "SQ": for sub_dataset in element.value: for sub_element in sub_dataset.elements(): if isinstance(sub_element, pydicom.dataelem.RawDataElement): @@ -171,7 +185,9 @@ def replace_element(element): else: replace_element(sub_element) else: - raise NotImplementedError('Not anonymized. VR {} not yet implemented.'.format(element.VR)) + raise NotImplementedError( + "Not anonymized. VR {} not yet implemented.".format(element.VR) + ) def replace(dataset, tag): @@ -201,22 +217,24 @@ def empty_element(element): See: https://laurelbridge.com/pdf/Dicom-Anonymization-Conformance-Statement.pdf """ - if element.VR in ('SH', 'PN', 'UI', 'LO', 'LT', 'CS', 'AS', 'ST', 'UT'): - element.value = '' - elif element.VR in ('DT', 'DA', 'TM'): + if element.VR in ("SH", "PN", "UI", "LO", "LT", "CS", "AS", "ST", "UT"): + element.value = "" + elif element.VR in ("DT", "DA", "TM"): replace_date_time_element(element) - elif element.VR in ('UL', 'FL', 'FD', 'SL', 'SS', 'US'): + elif element.VR in ("UL", "FL", "FD", "SL", "SS", "US"): element.value = 0 - elif element.VR in ('DS', 'IS'): - element.value = '0' - elif element.VR == 'UN': - element.value = b'' - elif element.VR == 'SQ': + elif element.VR in ("DS", "IS"): + element.value = "0" + elif element.VR == "UN": + element.value = b"" + elif element.VR == "SQ": for sub_dataset in element.value: for sub_element in sub_dataset.elements(): empty_element(sub_element) else: - raise NotImplementedError('Not anonymized. VR {} not yet implemented.'.format(element.VR)) + raise NotImplementedError( + "Not anonymized. VR {} not yet implemented.".format(element.VR) + ) def empty(dataset, tag): @@ -234,9 +252,9 @@ def delete_element(dataset, element): Delete the element from the dataset. If VR's element is a date, then it will be replaced by 00010101 """ - if element.VR == 'DA': + if element.VR == "DA": replace_element_date(element) - elif element.VR == 'SQ' and element.value is type(pydicom.Sequence): + elif element.VR == "SQ" and element.value is type(pydicom.Sequence): for sub_dataset in element.value: for sub_element in sub_dataset.elements(): delete_element(sub_dataset, sub_element) @@ -296,7 +314,7 @@ def delete_or_empty_or_replace_UID(dataset, tag): """ element = dataset.get(tag) if element is not None: - if element.VR == 'UI': + if element.VR == "UI": replace_element_UID(element) else: empty_element(element) @@ -304,6 +322,7 @@ def delete_or_empty_or_replace_UID(dataset, tag): # Generation functions + @dataclass class Action: function: callable @@ -338,13 +357,21 @@ def initialize_actions() -> dict: anonymization_actions.update({tag: empty_or_replace for tag in Z_D_TAGS}) anonymization_actions.update({tag: delete_or_empty for tag in X_Z_TAGS}) anonymization_actions.update({tag: delete_or_replace for tag in X_D_TAGS}) - anonymization_actions.update({tag: delete_or_empty_or_replace for tag in X_Z_D_TAGS}) - anonymization_actions.update({tag: delete_or_empty_or_replace_UID for tag in X_Z_U_STAR_TAGS}) + anonymization_actions.update( + {tag: delete_or_empty_or_replace for tag in X_Z_D_TAGS} + ) + anonymization_actions.update( + {tag: delete_or_empty_or_replace_UID for tag in X_Z_U_STAR_TAGS} + ) return anonymization_actions -def anonymize_dicom_file(in_file: str, out_file: str, extra_anonymization_rules: dict = None, - delete_private_tags: bool = True) -> None: +def anonymize_dicom_file( + in_file: str, + out_file: str, + extra_anonymization_rules: dict = None, + delete_private_tags: bool = True, +) -> None: """ Anonymize a DICOM file by modifying personal tags @@ -377,10 +404,7 @@ def get_private_tag(dataset, tag): tag_group = element.tag.group # The element is a private creator if element_value in dataset.private_creators(tag_group): - creator = { - "tagGroup": tag_group, - "creatorName": element.value - } + creator = {"tagGroup": tag_group, "creatorName": element.value} private_element = None # The element is a private element with an associated private creator else: @@ -389,26 +413,19 @@ def get_private_tag(dataset, tag): create_tag_element = element.tag.element >> 8 create_tag = pydicom.tag.Tag(tag_group, create_tag_element) create_dataset = dataset.get(create_tag) - creator = { - "tagGroup": tag_group, - "creatorName": create_dataset.value - } + creator = {"tagGroup": tag_group, "creatorName": create_dataset.value} # Define which offset should be applied to the creator to find # this element # 0x0010 << 8 will give 0x1000 offset_from_creator = element.tag.element - (create_tag_element << 8) - private_element = { - "element": element, - "offset": offset_from_creator - } + private_element = {"element": element, "offset": offset_from_creator} - return { - "creator": creator, - "element": private_element - } + return {"creator": creator, "element": private_element} -def get_private_tags(anonymization_actions: dict, dataset: pydicom.Dataset) -> List[dict]: +def get_private_tags( + anonymization_actions: dict, dataset: pydicom.Dataset +) -> List[dict]: """ Extract private tag as a list of object with creator and element @@ -429,8 +446,11 @@ def get_private_tags(anonymization_actions: dict, dataset: pydicom.Dataset) -> L return private_tags -def anonymize_dataset(dataset: pydicom.Dataset, extra_anonymization_rules: dict = None, - delete_private_tags: bool = True) -> None: +def anonymize_dataset( + dataset: pydicom.Dataset, + extra_anonymization_rules: dict = None, + delete_private_tags: bool = True, +) -> None: """ Anonymize a pydicom Dataset by using anonymization rules which links an action to a tag @@ -448,7 +468,10 @@ def anonymize_dataset(dataset: pydicom.Dataset, extra_anonymization_rules: dict for tag, action in current_anonymization_actions.items(): def range_callback(dataset, data_element): - if data_element.tag.group & tag[2] == tag[0] and data_element.tag.element & tag[3] == tag[1]: + if ( + data_element.tag.group & tag[2] == tag[0] + and data_element.tag.element & tag[3] == tag[1] + ): action(dataset, (data_element.tag.group, data_element.tag.element)) element = None @@ -462,7 +485,7 @@ def range_callback(dataset, data_element): # The meta header information is located in the `file_meta` dataset # For tags with tag group `0x0002` we thus apply the action to the `file_meta` dataset if tag[0] == 0x0002: - if not hasattr(dataset, 'file_meta'): + if not hasattr(dataset, "file_meta"): continue # Apply rule to meta information header action(dataset.file_meta, tag) @@ -485,6 +508,10 @@ def range_callback(dataset, data_element): for privateTag in private_tags: creator = privateTag["creator"] element = privateTag["element"] - block = dataset.private_block(creator["tagGroup"], creator["creatorName"], create=True) + block = dataset.private_block( + creator["tagGroup"], creator["creatorName"], create=True + ) if element is not None: - block.add_new(element["offset"], element["element"].VR, element["element"].value) + block.add_new( + element["offset"], element["element"].VR, element["element"].value + ) diff --git a/examples/anonymize_extra_rules.py b/examples/anonymize_extra_rules.py index f90cc48..7687874 100644 --- a/examples/anonymize_extra_rules.py +++ b/examples/anonymize_extra_rules.py @@ -35,7 +35,9 @@ def set_date_to_year(dataset, tag): for i in ALL_TAGS: extra_anonymization_rules[i] = keep - extra_anonymization_rules[(0x0010, 0x0030)] = set_date_to_year # Patient's Birth Date + extra_anonymization_rules[(0x0010, 0x0030)] = ( + set_date_to_year # Patient's Birth Date + ) # Launch the anonymization anonymize( diff --git a/scripts/scrap_DICOM_fields.py b/scripts/scrap_DICOM_fields.py index 66f742f..000acad 100644 --- a/scripts/scrap_DICOM_fields.py +++ b/scripts/scrap_DICOM_fields.py @@ -35,41 +35,54 @@ def scrap_profiles(url): page = requests.get(url) soup = BeautifulSoup(page.content, "html.parser") - headers = [th.text for th in soup.find(attrs={'id': 'table_E.1-1'}).parent.find('table').find('thead').find_all('strong')] + headers = [ + th.text + for th in soup.find(attrs={"id": "table_E.1-1"}) + .parent.find("table") + .find("thead") + .find_all("strong") + ] data = [] - - for tr in soup.find(attrs={'id': 'table_E.1-1'}).parent.find('table').find('tbody').find_all('tr'): - tmp = {key: value.text.strip() for key, value in dict(zip(headers, tr.find_all('td'))).items() if key in ['Attribute Name', 'Tag', 'Basic Prof.']} - tmp2 = (tmp.get('Tag'), tmp.get('Attribute Name'), tmp.get('Basic Prof.')) + for tr in ( + soup.find(attrs={"id": "table_E.1-1"}) + .parent.find("table") + .find("tbody") + .find_all("tr") + ): + tmp = { + key: value.text.strip() + for key, value in dict(zip(headers, tr.find_all("td"))).items() + if key in ["Attribute Name", "Tag", "Basic Prof."] + } + tmp2 = (tmp.get("Tag"), tmp.get("Attribute Name"), tmp.get("Basic Prof.")) data.append(tmp2) data = sorted(data, key=lambda ele: (ele[2], ele[1])) - profiles = defaultdict(list) fields_to_skip = { - 'Private Attributes', + "Private Attributes", } for tag, name, profile in data: if name in fields_to_skip: continue - if name == 'Curve Data': - new_tag = '(0x5000, 0x0000, 0xFF00, 0x0000)' - elif name == 'Overlay Comments': - new_tag = '(0x6000, 0x4000, 0xFF00, 0xFFFF)' - elif name == 'Overlay Data': - new_tag = '(0x6000, 0x3000, 0xFF00, 0xFFFF)' + if name == "Curve Data": + new_tag = "(0x5000, 0x0000, 0xFF00, 0x0000)" + elif name == "Overlay Comments": + new_tag = "(0x6000, 0x4000, 0xFF00, 0xFFFF)" + elif name == "Overlay Data": + new_tag = "(0x6000, 0x3000, 0xFF00, 0xFFFF)" else: new_tag = list(tag) - new_tag.insert(6, '0x') - new_tag.insert(6, ' ') - new_tag.insert(1, '0x') - new_tag = ''.join(new_tag) + new_tag.insert(6, "0x") + new_tag.insert(6, " ") + new_tag.insert(1, "0x") + new_tag = "".join(new_tag) - name = name.replace('\u200b', '').replace('\n', '') - string = f'{new_tag}, # {name}' + name = name.replace("\u200b", "").replace("\n", "") + string = f"{new_tag}, # {name}" profiles[profile].append(string) return profiles @@ -78,34 +91,37 @@ def scrap_profiles(url): def create_DICOM_fields(profiles): dicom_fields = "" for tag, tag_list, comment in ( - ('D', 'D_TAGS', '# Replaced tags'), - ('Z', 'Z_TAGS', "# Replaced with empty values (0, '', ...)"), - ('X', 'X_TAGS', '# Deleted tags'), - ('U', 'U_TAGS', '# Replace UID'), - - ('Z/D', 'Z_D_TAGS', '# Replace element according to the VR'), - ('X/Z', 'X_Z_TAGS', '# Set the value to empty according to the VR'), - ('X/D', 'X_D_TAGS', "# Replace element according to the VR"), - - ('X/Z/D', 'X_Z_D_TAGS', '# Replace element according to the VR'), - ('X/Z/U*', 'X_Z_U_STAR_TAGS', - '# Replace element with UI as VR, else replace according to VR with empty values'), + ("D", "D_TAGS", "# Replaced tags"), + ("Z", "Z_TAGS", "# Replaced with empty values (0, '', ...)"), + ("X", "X_TAGS", "# Deleted tags"), + ("U", "U_TAGS", "# Replace UID"), + ("Z/D", "Z_D_TAGS", "# Replace element according to the VR"), + ("X/Z", "X_Z_TAGS", "# Set the value to empty according to the VR"), + ("X/D", "X_D_TAGS", "# Replace element according to the VR"), + ("X/Z/D", "X_Z_D_TAGS", "# Replace element according to the VR"), + ( + "X/Z/U*", + "X_Z_U_STAR_TAGS", + "# Replace element with UI as VR, else replace according to VR with empty values", + ), ): - dicom_fields += f'{comment}\n{tag_list} = [\n' + dicom_fields += f"{comment}\n{tag_list} = [\n" for profile in profiles.get(tag): - dicom_fields += f' {profile}\n' - dicom_fields += ']\n\n' + dicom_fields += f" {profile}\n" + dicom_fields += "]\n\n" return dicom_fields_header + dicom_fields + dicom_fields_footer def main( - url="https://dicom.nema.org/medical/dicom/current/output/chtml/part15/chapter_e.html", - output_path='dicomanonymizer/dicomfields.py'): + url="https://dicom.nema.org/medical/dicom/current/output/chtml/part15/chapter_e.html", + output_path="dicomanonymizer/dicomfields.py", +): profiles = scrap_profiles(url) file_content = create_DICOM_fields(profiles=profiles) - with open(output_path, 'w') as file: + with open(output_path, "w") as file: file.write(file_content) -if __name__ == '__main__': - fire.Fire(main) \ No newline at end of file + +if __name__ == "__main__": + fire.Fire(main) diff --git a/setup.py b/setup.py index 941e88e..4f56fec 100644 --- a/setup.py +++ b/setup.py @@ -8,10 +8,10 @@ from setuptools import setup, find_packages setup( - name='dicom_anonymizer', # Required - version='1.0.12', # Required - author='Laurenn Lam', - author_email='laurenn.lam@kitware.com', + name="dicom_anonymizer", # Required + version="1.0.12", # Required + author="Laurenn Lam", + author_email="laurenn.lam@kitware.com", description="Program to anonymize dicom files with default and custom rules", url="https://github.com/KitwareMedical/dicom-anonymizer", project_urls={ @@ -23,34 +23,21 @@ "Topic :: Software Development :: Build Tools", "License :: OSI Approved :: BSD License", "Natural Language :: English", - "Programming Language :: Python" + "Programming Language :: Python", ], keywords=["dicom", "anonymizer", "medical"], - python_requires='>=3.6', - + python_requires=">=3.6", packages=find_packages(), # Required - # Define an executable calls dicom-anonymizer from a specific file entry_points={ - 'console_scripts': [ - 'dicom-anonymizer = dicomanonymizer.anonymizer:main' - ] + "console_scripts": ["dicom-anonymizer = dicomanonymizer.anonymizer:main"] }, - # This field lists other packages that your project depends on to run. # Any package you put here will be installed by pip when your project is # installed, so they must be valid existing projects. # # For an analysis of "install_requires" vs pip's requirements files see: # https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/ - install_requires=['pydicom', 'tqdm'], # Optional - - extras_require={ - 'dev': [ - "pytest", - "bs4", - "fire", - "requests" - ] - } + install_requires=["pydicom", "tqdm"], # Optional + extras_require={"dev": ["pytest", "bs4", "fire", "requests", "pre-commit", "ruff"]}, ) diff --git a/tests/test_anon.py b/tests/test_anon.py index 704bd2a..569df03 100644 --- a/tests/test_anon.py +++ b/tests/test_anon.py @@ -41,7 +41,9 @@ def get_passing_files(): @pytest.fixture(scope="module", params=get_passing_files()) def orig_anon_dataset(request): orig_ds = dcmread(request.param) - orig_ds.filename = None # Non-None value causes warnings in copy(). Not needed for this testing + orig_ds.filename = ( + None # Non-None value causes warnings in copy(). Not needed for this testing + ) anon_ds = orig_ds.copy() anonymize_dataset(anon_ds) return (orig_ds, anon_ds) diff --git a/tests/test_cli.py b/tests/test_cli.py index 730c7ab..5b82090 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -22,22 +22,34 @@ def an_anonymize_mock(): def test_basic_cli(an_anonymize_mock): - sys.argv = ['cmd', 'input', 'output'] + sys.argv = ["cmd", "input", "output"] main() an_anonymize_mock.assert_called_once_with("input", "output", {}, True) def test_simple_tag_arguments(an_anonymize_mock): - sys.argv = ['cmd', 'input', 'output', '-t', '(0x0010, 0x0010)', 'empty'] + sys.argv = ["cmd", "input", "output", "-t", "(0x0010, 0x0010)", "empty"] main() - an_anonymize_mock.assert_called_once_with("input", "output", {(0x0010, 0x0010): empty}, True) + an_anonymize_mock.assert_called_once_with( + "input", "output", {(0x0010, 0x0010): empty}, True + ) def test_complex_tag_arguments(an_anonymize_mock, a_simple_dataset): - sys.argv = ['cmd', 'input', 'output', '-t', '(0x0010, 0x0010)', 'replace_with_value', 'Replaced'] + sys.argv = [ + "cmd", + "input", + "output", + "-t", + "(0x0010, 0x0010)", + "replace_with_value", + "Replaced", + ] main() # Call the function created by our arguments to make sure it works as expected - an_anonymize_mock.call_args.args[2][(0x0010, 0x0010)](a_simple_dataset, (0x0010, 0x0010)) + an_anonymize_mock.call_args.args[2][(0x0010, 0x0010)]( + a_simple_dataset, (0x0010, 0x0010) + ) assert a_simple_dataset[(0x0010, 0x0010)].value == "Replaced" @@ -48,27 +60,33 @@ def read(self): return '{"(0x0010, 0x0010)": "empty"}' an_open_mock.return_value.__enter__.return_value = FakeFile() - sys.argv = ['cmd', 'input', 'output', '--dictionary', 'whatever.json'] + sys.argv = ["cmd", "input", "output", "--dictionary", "whatever.json"] main() - an_anonymize_mock.assert_called_once_with("input", "output", {(0x0010, 0x0010): empty}, True) + an_anonymize_mock.assert_called_once_with( + "input", "output", {(0x0010, 0x0010): empty}, True + ) @patch("builtins.open") -def test_complex_dictionnary_argument(an_open_mock, an_anonymize_mock, a_simple_dataset): +def test_complex_dictionnary_argument( + an_open_mock, an_anonymize_mock, a_simple_dataset +): class FakeFile: def read(self): return '{"(0x0010, 0x0010)": {"action":"regexp", "find": "Name", "replace": "Replaced"}}' an_open_mock.return_value.__enter__.return_value = FakeFile() - sys.argv = ['cmd', 'input', 'output', '--dictionary', 'whatever.json'] + sys.argv = ["cmd", "input", "output", "--dictionary", "whatever.json"] main() # Call the function created by our arguments to make sure it works as expected - an_anonymize_mock.call_args.args[2][(0x0010, 0x0010)](a_simple_dataset, (0x0010, 0x0010)) + an_anonymize_mock.call_args.args[2][(0x0010, 0x0010)]( + a_simple_dataset, (0x0010, 0x0010) + ) assert a_simple_dataset[(0x0010, 0x0010)].value == "Test Replaced" def test_unrecognized_action_gives_helpful_error(): - sys.argv = ['cmd', 'input', 'output', '-t', '(0x0010, 0x0010)', 'wrong_action'] + sys.argv = ["cmd", "input", "output", "-t", "(0x0010, 0x0010)", "wrong_action"] try: main() except ValueError as e: @@ -76,7 +94,14 @@ def test_unrecognized_action_gives_helpful_error(): def test_wrong_number_of_arguments_gives_helpful_error(): - sys.argv = ['cmd', 'input', 'output', '-t', '(0x0010, 0x0010)', 'replace_with_value'] + sys.argv = [ + "cmd", + "input", + "output", + "-t", + "(0x0010, 0x0010)", + "replace_with_value", + ] try: main() except ValueError as e: