def test_field_expansion(self): print("Test deid.dicom.fields expand_field_expression") from deid.dicom.fields import expand_field_expression dicom = get_dicom(self.dataset) contenders = dicom.dir() print("Testing that field expansion works for basic tags") expand_field_expression(dicom=dicom, field="endswith:Time", contenders=contenders) print("Testing that field expansion works including private tags") contenders += [e.tag for e in get_private(dicom)] expand_field_expression(dicom=dicom, field="endswith:Time", contenders=contenders) print("Testing that we can also search private tags based on numbers.") fields = expand_field_expression(dicom=dicom, field="contains:0019", contenders=contenders) # We should have a tag object in the list now! assert isinstance(fields[0], BaseTag) print("Testing nested private tags") dataset = get_dataset("animals") # includes nested private tags dicom = get_dicom(dataset)
def perform_action(self, field, value, action): """perform action takes an action (dictionary with field, action, value) and performs the action on the loaded dicom. Parameters ========== fields: if provided, a filtered list of fields for expand action: the action from the parsed deid to take "field" (eg, PatientID) the header field to process "action" (eg, REPLACE) what to do with the field "value": if needed, the field from the response to replace with """ # Validate the action if action not in valid_actions: bot.warning("%s in not a valid choice. Defaulting to blanked." % action) action = "BLANK" # A values list returns fields with the value (can be private tags if not removed) if re.search("^values", field): values = self.lookup.get(re.sub("^values:", "", field), []) fields = self.find_by_values(values=values) # A fields list is used vertabim # In expand_field_expression below, the stripped_tag is being passed in to field. At this point, # expanders for %fields lists have already been processed and each of the contenders is an # identified, unique field. It is important to use stripped_tag at this point instead of # element.keyword as private tags will not have a keyword and can only be identified by tag number. elif re.search("^fields", field): listing = {} for uid, contender in self.lookup.get( re.sub("^fields:", "", field), {}).items(): listing.update( expand_field_expression( field=contender.stripped_tag, dicom=self.dicom, contenders=self.fields, )) fields = listing else: # If there is an expander applied to field, we iterate over fields = expand_field_expression(field=field, dicom=self.dicom, contenders=self.fields) # If it's an addition, we might not have fields if action == "ADD": self.add_field(field, value) # Otherwise, these are operations on existing fields else: """clone the fields dictionary. delete actions must also delete from the fields dictionary. performing the clone and iterating on the clone allows the deletions while preventing a runtime error - "dictionary changed size during iterations" """ temp_fields = deepcopy(fields) for uid, field in temp_fields.items(): self._run_action(field=field, action=action, value=value)
def test_field_expansion(self): print("Test deid.dicom.fields expand_field_expression") from deid.dicom.fields import expand_field_expression dicom = get_dicom(self.dataset) contenders = get_fields(dicom) print("Testing that field expansion works for basic tags") fields = expand_field_expression(dicom=dicom, field="endswith:Time", contenders=contenders) # The fields returned should end in time for uid, field in fields.items(): assert field.name.endswith("Time") print("Testing that we can also search private tags based on numbers.") fields = expand_field_expression(dicom=dicom, field="contains:0019", contenders=contenders) # The fields returned should include tag group or element 0019 for uid, field in fields.items(): assert "0019" in uid print("Testing nested private tags") dataset = get_dataset("animals") # includes nested private tags dicom = get_dicom(dataset)
def test_expand_field_expression(self): from deid.dicom.fields import expand_field_expression dicom = get_dicom(self.dataset) dicom.AcquisitionDateTime = '20131210081530' dicom.data_element("AcquisitionDateTime").VR = 'DT' print("Case 1: Test startswith") fields = expand_field_expression("startswith:Patient", dicom) expected = ['PatientBirthDate', 'PatientID', 'PatientName', 'PatientOrientation', 'PatientSex'] self.assertEqual(fields, expected) print("Case 2: Test endswith") fields = expand_field_expression("endswith:Date", dicom) expected = ['PatientBirthDate', 'StudyDate'] self.assertEqual(fields, expected) print("Case 3: Test contains") fields = expand_field_expression("contains:Date", dicom) expected = ['AcquisitionDateTime', 'PatientBirthDate', 'StudyDate'] self.assertEqual(fields, expected) print("Case 4: Test except") fields = expand_field_expression("except:Patient", dicom) expected = ['AccessionNumber', 'AcquisitionDateTime', 'BitsAllocated', 'BitsStored', 'Columns', 'ConversionType', 'HighBit', 'ImageComments', 'InstanceNumber', 'InstitutionName', 'LossyImageCompression', 'LossyImageCompressionMethod', 'NameOfPhysiciansReadingStudy', 'OperatorsName', 'PhotometricInterpretation', 'PixelData', 'PixelRepresentation', 'PlanarConfiguration', 'ReferringPhysicianName', 'Rows', 'SOPClassUID', 'SOPInstanceUID', 'SamplesPerPixel', 'SeriesInstanceUID', 'SeriesNumber', 'SpecificCharacterSet', 'StudyDate', 'StudyID', 'StudyInstanceUID', 'StudyTime'] self.assertEqual(fields, expected) print("Case 5: Test all") fields = expand_field_expression("all", dicom) expected = ['AccessionNumber', 'AcquisitionDateTime', 'BitsAllocated', 'BitsStored', 'Columns', 'ConversionType', 'HighBit', 'ImageComments', 'InstanceNumber', 'InstitutionName', 'LossyImageCompression', 'LossyImageCompressionMethod', 'NameOfPhysiciansReadingStudy', 'OperatorsName', 'PatientBirthDate', 'PatientID', 'PatientName', 'PatientOrientation', 'PatientSex', 'PhotometricInterpretation', 'PixelData', 'PixelRepresentation', 'PlanarConfiguration', 'ReferringPhysicianName', 'Rows', 'SOPClassUID', 'SOPInstanceUID', 'SamplesPerPixel', 'SeriesInstanceUID', 'SeriesNumber', 'SpecificCharacterSet', 'StudyDate', 'StudyID', 'StudyInstanceUID', 'StudyTime'] self.assertEqual(fields, expected)
def perform_action(self, field, value, action): """perform action takes an action (dictionary with field, action, value) and performs the action on the loaded dicom. Parameters ========== fields: if provided, a filtered list of fields for expand action: the action from the parsed deid to take "field" (eg, PatientID) the header field to process "action" (eg, REPLACE) what to do with the field "value": if needed, the field from the response to replace with """ # Validate the action if action not in valid_actions: bot.warning("%s in not a valid choice. Defaulting to blanked." % action) action = "BLANK" # A values list returns fields with the value (can be private tags if not removed) if re.search("^values", field): values = self.lookup.get(re.sub("^values:", "", field), []) fields = self.find_by_values(values=values) # A fields list is used vertabim elif re.search("^fields", field): listing = {} for uid, contender in self.lookup.get( re.sub("^fields:", "", field), {}).items(): listing.update( expand_field_expression( field=contender.element.keyword, dicom=self.dicom, contenders=self.fields, )) fields = listing else: # If there is an expander applied to field, we iterate over fields = expand_field_expression(field=field, dicom=self.dicom, contenders=self.fields) # If it's an addition, we might not have fields if action == "ADD": self.add_field(field, value) # Otherwise, these are operations on existing fields else: for uid, field in fields.items(): self._run_action(field=field, action=action, value=value)
def perform_action(dicom, action, item=None, fields=None, return_seen=False): """perform action takes Parameters ========== dicom: a loaded dicom file (pydicom read_file) item: a dictionary with keys as fields, values as values fields: if provided, a filtered list of fields for expand action: the action from the parsed deid to take "deid" (eg, PatientID) the header field to process "action" (eg, REPLACE) what to do with the field "value": if needed, the field from the response to replace with """ field = action.get( "field") # e.g: PatientID, endswith:ID, values:name, fields:name value = action.get("value") # "suid" or "var:field" action = action.get("action") # "REPLACE" # Validate the action if action not in valid_actions: bot.warning("%s in not a valid choice. Defaulting to blanked." % action) action = "BLANK" # If values or fields is provided, ids is required if re.search("^(values|fields)", field): if not item: bot.exit( "An item lookup must be provided to reference a list of values or fields." ) # A values list returns fields with the value (can be private tags if not removed) if re.search("^values", field): values = item.get(re.sub("^values:", "", field), []) fields = find_by_values(values=values, dicom=dicom) # A fields list is used vertabim elif re.search("^fields", field): listing = [] for contender in item.get(re.sub("^fields:", "", field), []): listing += expand_field_expression(field=contender, dicom=dicom, contenders=fields) fields = listing else: # If there is an expander applied to field, we iterate over fields = expand_field_expression(field=field, dicom=dicom, contenders=fields) # Keep track of fields we have seen seen = [] # An expanded field must END with that field expanded_regexp = "__%s$" % field for field in fields: # This key can be for a string or tag seen.append(field) # Handle top level field, this can be a key (string) or tag _perform_action(dicom=dicom, field=field, item=item, action=action, value=value) # Expand sequences if item: expanded_fields = [ x for x in item if re.search(expanded_regexp, str(x)) ] # FieldA__FieldB for expanded_field in expanded_fields: _perform_expanded_action( dicom=dicom, expanded_field=expanded_field, item=item, action=action, value=value, ) if return_seen: return dicom, seen return dicom