diff --git a/dicom_standard/extract_conf_profile_attributes.py b/dicom_standard/extract_conf_profile_attributes.py index 3a5ac19..f0bdeca 100644 --- a/dicom_standard/extract_conf_profile_attributes.py +++ b/dicom_standard/extract_conf_profile_attributes.py @@ -2,6 +2,7 @@ Extract the listing of all attributes given in table E.1-1 from part 15 of the DICOM Standard. ''' from typing import cast, Dict, List, Union +import re import sys from bs4 import BeautifulSoup @@ -18,6 +19,8 @@ RETIREMENT_MISMATCH_ATTRIBUTES = ['Referenced Patient Alias Sequence'] +TAG_PATTERN = re.compile(r'\(\w{4},\w{4}\)') + AttrTableType = List[Dict[str, Union[str, bool]]] @@ -39,11 +42,26 @@ def get_conf_profile_table(standard: BeautifulSoup) -> List[TableDictType]: return table_to_dict(list_table, COLUMN_TITLES, omit_empty=True) +def extract_tag(raw_tag: str) -> str: + """Standard workaround: isolate the tag from a cell that qualifies it with prose + The "Private Attributes" row of Table E.1-1 gives its tag as + "(gggg,eeee) where gggg is odd". Without this, the trailing prose ends up in the + attribute's tag and ID, giving that one row a shape no other row has. + Args: + raw_tag (str): the contents of the table's tag cell + Returns: + str: the tag alone, or the original cell if it contains no tag + """ + match = TAG_PATTERN.search(raw_tag) + return match.group(0) if match else raw_tag + + def table_to_json(table: List[TableDictType]) -> List[TableDictType]: attributes = [] for attr in table: - attr['id'] = pl.create_slug(attr['tag']) - attr['tag'] = attr['tag'].upper() + tag = extract_tag(attr['tag']) + attr['id'] = pl.create_slug(tag) + attr['tag'] = tag.upper() attributes.append(attr) return attributes diff --git a/tests/extract_conf_profile_attributes_test.py b/tests/extract_conf_profile_attributes_test.py new file mode 100644 index 0000000..2d9217a --- /dev/null +++ b/tests/extract_conf_profile_attributes_test.py @@ -0,0 +1,32 @@ +''' +Unit tests covering functions in `extract_conf_profile_attributes.py`. +''' +import pytest + +from dicom_standard.extract_conf_profile_attributes import extract_tag, table_to_json + + +@pytest.mark.parametrize('raw_tag,expected', [ + ('(0008,0050)', '(0008,0050)'), + ('(50xx,xxxx)', '(50xx,xxxx)'), + ('(gggg,eeee) where gggg is odd', '(gggg,eeee)'), +]) +def test_extract_tag(raw_tag, expected): + assert extract_tag(raw_tag) == expected + + +def test_extract_tag_passes_through_cell_without_a_tag(): + assert extract_tag('No tag here') == 'No tag here' + + +def test_table_to_json_derives_id_from_the_tag_alone(): + table = [ + {'name': 'Accession Number', 'tag': '(0008,0050)'}, + {'name': 'Curve Data', 'tag': '(50xx,xxxx)'}, + {'name': 'Private Attributes', 'tag': '(gggg,eeee) where gggg is odd'}, + ] + attributes = table_to_json(table) + assert [attr['tag'] for attr in attributes] == [ + '(0008,0050)', '(50XX,XXXX)', '(GGGG,EEEE)'] + assert [attr['id'] for attr in attributes] == [ + '00080050', '50xxxxxx', 'ggggeeee']