# http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html import datetime from logger import get_logger import pydicom import os import base64 import math IGNORE_OB = os.getenv('IGNORE_OB', True) log = get_logger(__name__) def rep_string(elem): return serialize_sets(elem) def convert_DA(elem): # Convert DICOM DA to ISO format for datatype DATE compability try: if not elem.is_empty: # Only return YYYY-MM-DD, exclude TIMESTAMP # date = datetime.datetime.strptime(elem.value, '%Y%m%d').date() date = rep_string(elem) if isinstance(date, list): date_list = [] for item in date: date_list.append( datetime.datetime.strptime(item, '%Y%m%d').date()) return date_list return datetime.datetime.strptime(date, '%Y%m%d').date() return datetime.datetime.fromisoformat('1900-01-01').date() except Exception as e: log.error(e) raise def convert_OB(elem): try: if elem.is_empty: return elem.value if IGNORE_OB: return 'IGNORED' else: return base64.standard_b64encode(elem.value) except Exception as e: log.error(e) raise def convert_TM(elem): # return string, athena does not support TIME data type ex HHMMSS.FFFFFF return rep_string(elem) def generate_PN(elem): return { 'FamilyName': elem.family_name, 'GivenName': elem.given_name, 'Ideographic': elem.ideographic, 'MiddleName': elem.middle_name, 'NamePrefix': elem.name_prefix, 'NameSuffix': elem.name_suffix, 'Phonetic': elem.phonetic, } def convert_PN(elem): try: if (elem.is_empty): return { 'FamilyName': '', 'GivenName': '', 'Ideographic': '', 'MiddleName': '', 'NamePrefix': '', 'NameSuffix': '', 'Phonetic': '', } data = rep_string(elem) # if list of PN iterate to construct common data structure if isinstance(data, list): PN_LIST = [] for item in data: PN_LIST.append(generate_PN(item)) return PN_LIST else: return generate_PN(elem.value) except Exception as e: log.error(e) raise def serialize_sets(obj): try: # pydicom.multival.MultiValue type into serialize list # if isinstance(obj.value, pydicom.multival.MultiValue): # if len(obj.value._list) > 0: # # return list(map(str, obj.value._list)) # return obj.value._list # else: # return '' # if obj.VM > 1: # return obj.value # else: # return str(obj.value) return validate_vm(obj) except Exception as e: log.error(e) raise def validate_vm(obj): try: log.debug(f'Validing Tag {obj.keyword} VM : {obj.VM} VR: {obj.VR}') maxVM = pydicom._dicom_dict.DicomDictionary[obj.tag][1] split = maxVM.split('-') if len(split) > 1: min = split[0] max = split[1] if 'n' in max: max = math.inf else: max = int(max) else: min = int(split[0]) max = min if max > 1: if isinstance(obj.value, pydicom.multival.MultiValue): return obj.value._list elif isinstance(obj.value, list): return obj.value else: return [obj.value] else: return str(obj.value) except Exception as e: log.error(e) raise def convert_SQ(elem): try: sq = {} if (not elem.is_empty): # sq[elem.keyword] = {} for item in elem.value._list: # Check for empty pydicom dataset if item == pydicom.Dataset(): return None for i in item: sq[i.keyword] = vr_select(i)(i) return sq return rep_string(elem) except Exception as e: log.error(e) raise def return_integer(elem): try: if not elem.is_empty: return rep_string(elem) return int(0) except Exception as e: log.error(e) raise def return_lo(elem): return elem.value def return_float(elem): try: if not elem.is_empty: # if elem.VM == 1: # return [float(elem.value)] # return rep_string(elem) return rep_string(elem) return float(0) except Exception as e: log.error(e) raise def convert_DT(elem): try: if not elem.is_empty: # TODO properly evaluate timestamp format before applying datetime # date = datetime.datetime.strptime(elem.value, '%Y%m%d').date() date = rep_string(elem) if isinstance(date, list): date_list = [] for item in date: #date_list.append(datetime.datetime.strptime(item, '%Y%m%d%H%M%S.%f%z')) return elem.value return date_list # return datetime.datetime.strptime(elem.value, '%Y%m%d%H%M%S.%f%z') return elem.value # return datetime.datetime.strptime(elem.value, '%Y%m%d%H%M%S.%f%z') return elem.value except Exception as e: log.error(e) raise def vr_select(elem): return { 'AE': rep_string, 'AS': rep_string, 'AT': return_integer, # return integer 'CS': rep_string, # return string 'DA': convert_DA, # return datetime in YYYY-MM-DD format 'DS': rep_string, 'DT': convert_DT, # return Timestamp 'FD': rep_string, # return float 'FL': return_float, # return float 'IS': rep_string, 'LO': rep_string, # return string 'LT': rep_string, 'OB': rep_string, 'OD': rep_string, 'OF': rep_string, 'OL': rep_string, 'OW': rep_string, 'OV': rep_string, 'PN': convert_PN, # return string if empty or return dict, 'SH': rep_string, # return string 'SL': return_integer, 'SQ': convert_SQ, # return struct 'SS': return_integer, 'ST': rep_string, 'SV': rep_string, 'TM': convert_TM, # return string, TIME data type is not supported. 'UC': rep_string, 'UI': rep_string, # return string 'UL': return_integer, # return integer 'UN': rep_string, 'UR': rep_string, 'US': return_integer, # return integer 'UT': rep_string, 'UV': rep_string, 'OB': rep_string, 'OW': rep_string, 'US': rep_string, 'SS': rep_string, 'US': rep_string, 'OW': rep_string, 'US': rep_string, 'SS': rep_string, 'OW': rep_string, }.get(elem.VR, f'Invalid VR {elem.VR} tag')