def __PID2dto(HL7=None): pat_lname = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__lastname) pat_fname = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__firstname) pat_mname = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__middlename) if pat_mname is not None: pat_fname += ' ' pat_fname += pat_mname _log.debug('patient data from PID segment: first=%s (middle=%s) last=%s', pat_fname, pat_mname, pat_lname) dto = gmPerson.cDTO_person() dto.firstnames = pat_fname dto.lastnames = pat_lname dto.gender = HL7_GENDERS[HL7.extract_field('PID', segment_num = 1, field_num = PID_field__gender)] hl7_dob = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__dob) if hl7_dob is not None: tmp = time.strptime(hl7_dob, '%Y%m%d') dto.dob = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone) idents = dto.get_candidate_identities() if len(idents) == 0: _log.warning('no match candidate, not auto-importing') _log.debug(dto) return [] if len(idents) > 1: _log.warning('more than one match candidate, not auto-importing') _log.debug(dto) return idents return [gmPerson.cPatient(idents[0].ID)]
def __PID2dto(HL7=None): pat_lname = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__lastname) pat_fname = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__firstname) pat_mname = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__middlename) if pat_mname is not None: pat_fname += u' ' pat_fname += pat_mname _log.debug(u'patient data from PID segment: first=%s (middle=%s) last=%s', pat_fname, pat_mname, pat_lname) dto = gmPerson.cDTO_person() dto.firstnames = pat_fname dto.lastnames = pat_lname dto.gender = HL7_GENDERS[HL7.extract_field('PID', segment_num = 1, field_num = PID_field__gender)] hl7_dob = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__dob) if hl7_dob is not None: tmp = time.strptime(hl7_dob, '%Y%m%d') dto.dob = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone) idents = dto.get_candidate_identities() if len(idents) == 0: _log.warning('no match candidate, not auto-importing') _log.debug(dto) return [] if len(idents) > 1: _log.warning('more than one match candidate, not auto-importing') _log.debug(dto) return idents return [gmPerson.cPatient(idents[0].ID)]
def parse_xml_linuxmednews(xml_text=None, filename=None): dob_format = '%Y-%m-%d' try: if xml_text is None: _log.debug('parsing XML in [%s]', filename) pat = etree.parse(filename) else: pat = etree.fromstring(xml_text) except etree.ParseError: _log.exception('Cannot parse, is this really XML ?') return None dto = gmPerson.cDTO_person() dto.firstnames = pat.find('firstname').text dto.lastnames = pat.find('lastname').text dto.title = pat.find('name_prefix').text dto.gender = pat.find('gender').text dob = pyDT.datetime.strptime(pat.find('DOB').text, dob_format) dto.dob = dob.replace(tzinfo = gmDateTime.pydt_now_here().tzinfo) dto.dob_is_estimated = False dto.source = 'LinuxMedNews XML' #dto.remember_comm_channel(channel=None, url=None): #dto.remember_address(number=None, street=None, urb=None, region_code=None, zip=None, country_code=None, adr_type=None, subunit=None) return dto
def parse_xml_linuxmednews(xml_text=None, filename=None): dob_format = '%Y-%m-%d' try: if xml_text is None: _log.debug('parsing XML in [%s]', filename) pat = etree.parse(filename) else: pat = etree.fromstring(xml_text) except etree.ParseError: _log.exception('Cannot parse, is this really XML ?') return None dto = gmPerson.cDTO_person() dto.firstnames = pat.find('firstname').text dto.lastnames = pat.find('lastname').text dto.title = pat.find('name_prefix').text dto.gender = pat.find('gender').text dob = pyDT.datetime.strptime(pat.find('DOB').text, dob_format) dto.dob = dob.replace(tzinfo=gmDateTime.pydt_now_here().tzinfo) dto.dob_is_estimated = False dto.source = 'LinuxMedNews XML' #dto.remember_comm_channel(channel=None, url=None): #dto.remember_address(number=None, street=None, urb=None, region_code=None, zip=None, country_code=None, adr_type=None, subunit=None) return dto
def read_persons_from_pracsoft_file(filename=None, encoding='ascii'): pats_file = io.open(filename, mode='rt', encoding=encoding) dtos = [] for line in pats_file: if len(line) < PATIENTS_IN_line_len: continue # perhaps raise Exception ? dto = gmPerson.cDTO_person() dto.external_ids = [{ 'PracSoft No.': line[0:9].strip(), 'issuer': 'AU PracSoft application' }, { 'CRN': line[166:180].replace(' ', ''), 'issuer': 'Centrelink (AU)' }, { 'DVA': line[180:194].replace(' ', ''), 'issuer': "Department of Veteran's Affairs (AU)" }, { 'AU-Medicare': line[153:166].replace(' ', ''), 'issuer': 'HIC (AU)' }] dto.title = gmTools.capitalize(line[9:14].strip(), gmTools.CAPS_FIRST) dto.firstnames = gmTools.capitalize(line[44:74].strip(), gmTools.CAPS_NAMES) dto.lastnames = gmTools.capitalize(line[14:44].strip(), gmTools.CAPS_NAMES) dto.gender = line[223].lower() dob = time.strptime(line[143:153].strip(), PATIENTS_IN_dob_format) dto.dob = pyDT.datetime(dob.tm_year, dob.tm_mon, dob.tm_mday, tzinfo=gmDateTime.gmCurrentLocalTimezone) # this is the home address dto.street = gmTools.capitalize(line[74:114].strip(), gmTools.CAPS_FIRST) dto.zip = line[139:143].strip() dto.urb = line[114:139].strip() dto.comms = [ # types must correspond to GNUmed database comm type { 'homephone': line[194:208].replace(' ', '') }, { 'workphone': line[208:222].replace(' ', '') } ] dto.pracsoft_billing_flag = line[222] # P=pensioner R=repatriation dtos.append(dto) return dtos
def test_search_by_dto(): dto = gmPerson.cDTO_person() dto.firstnames = 'Sigrid' dto.lastnames = 'Kiesewetter' dto.gender = 'female' # dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone) dto.dob = datetime.datetime(1939,6,24,23,0,0,0,gmDateTime.gmCurrentLocalTimezone) print(dto) searcher = cPatientSearcher_SQL() pats = searcher.get_patients(dto = dto) print(pats)
def test_search_by_dto(): dto = gmPerson.cDTO_person() dto.firstnames = 'Sigrid' dto.lastnames = 'Kiesewetter' dto.gender = 'female' # dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone) dto.dob = datetime.datetime(1939,6,24,23,0,0,0,gmDateTime.gmCurrentLocalTimezone) print dto searcher = cPatientSearcher_SQL() pats = searcher.get_patients(dto = dto) print pats
def read_persons_from_pracsoft_file(filename=None, encoding='ascii'): pats_file = io.open(filename, mode = 'rt', encoding = encoding) dtos = [] for line in pats_file: if len(line) < PATIENTS_IN_line_len: continue # perhaps raise Exception ? dto = gmPerson.cDTO_person() dto.external_ids = [ {'PracSoft No.': line[0:9].strip(), 'issuer': 'AU PracSoft application'}, {'CRN': line[166:180].replace(' ', ''), 'issuer': 'Centrelink (AU)'}, {'DVA': line[180:194].replace(' ', ''), 'issuer': "Department of Veteran's Affairs (AU)"}, {'AU-Medicare': line[153:166].replace(' ', ''), 'issuer': 'HIC (AU)'} ] dto.title = gmTools.capitalize(line[9:14].strip(), gmTools.CAPS_FIRST) dto.firstnames = gmTools.capitalize(line[44:74].strip(), gmTools.CAPS_NAMES) dto.lastnames = gmTools.capitalize(line[14:44].strip(), gmTools.CAPS_NAMES) dto.gender = line[223].lower() dob = time.strptime(line[143:153].strip(), PATIENTS_IN_dob_format) dto.dob = pyDT.datetime(dob.tm_year, dob.tm_mon, dob.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone) # this is the home address dto.street = gmTools.capitalize(line[74:114].strip(), gmTools.CAPS_FIRST) dto.zip = line[139:143].strip() dto.urb = line[114:139].strip() dto.comms = [ # types must correspond to GNUmed database comm type {'homephone': line[194:208].replace(' ', '')}, {'workphone': line[208:222].replace(' ', '')} ] dto.pracsoft_billing_flag = line[222] # P=pensioner R=repatriation dtos.append(dto) return dtos
def parse_vcard2dto(vc_text=None, filename=None): import vobject if vc_text is None: _log.info('trying to parse vCard from [%s]', filename) for encoding in ['utf8', 'Windows-1252']: try: vcf = io.open(filename, mode = u'rt', encoding = encoding) vc_text = vcf.read() vcf.close() break except UnicodeDecodeError: _log.exception('vCard not encoded as [%s]', encoding) if vc_text is None: return None dob_format = '%Y%m%d' try: vc = vobject.readOne(vc_text) except vobject.base.ParseError: _log.exception('cannot parse, really a vcf ?') return None try: if vc.kind.value.strip() != u'individual': _log.warning('not a vCard for a single person (vCard.KIND=%s)', vc.kind.value) return None except AttributeError: _log.debug('vCard.KIND attribute not available') dto = gmPerson.cDTO_person() dto.firstnames = vc.n.value.given.strip() dto.lastnames = vc.n.value.family.strip() if vc.title: dto.title = vc.title.value.strip() try: gender = vc.gender.value.strip().lower() if gender != u'': dto.gender = gender except AttributeError: _log.debug('vCard.GENDER attribute not available') try: dob = pyDT.datetime.strptime(vc.bday.value.strip(), dob_format) dto.dob = dob.replace(tzinfo = gmDateTime.pydt_now_here().tzinfo) dto.dob_is_estimated = False except AttributeError: _log.debug('vCard.BDAY attribute not available') dto.source = u'vCard %s' % vc.version.value.strip() adr = None try: adr = vc.adr.value except AttributeError: _log.debug('vCard.ADR attribute not available') if adr is not None: region_code = None region = adr.region.strip() if region == u'': region = None # deduce country country_code = None country = adr.country.strip() if country == u'': country = None if country is None: country_row = gmDemographicRecord.map_urb_zip_region2country(urb = adr.city, zip = adr.code, region = region) if country_row is not None: country = country_row['country'] country_code = country_row['code_country'] else: country_code = gmDemographicRecord.map_country2code(country = country) if None in [country, country_code]: _log.error('unknown vCard.ADR.country (%s), skipping address', adr.country) else: # deduce region if region is None: region_row = gmDemographicRecord.map_urb_zip_country2region(urb = adr.city, zip = adr.code, country_code = country_code) if region_row is not None: region = region_row['state'] region_code = region_row['code_state'] else: region_code = gmDemographicRecord.map_region2code(region = region, country_code = country_code) if region_code is None: _log.warning('unknown vCard.ADR.region (%s), using default region', adr.region) dto.remember_address ( number = u'?', street = adr.street, urb = adr.city, region_code = region_code, zip = adr.code, country_code = country_code, adr_type = 'home', subunit = None ) tel = None try: tel = vc.tel.value except AttributeError: _log.debug('vCard.TEL attribute not available') if tel is not None: if u'TYPE' in vc.tel.params: channel = (vc.tel.params[u'TYPE'][0]).lower() if not channel.endswith('phone'): channel += 'phone' else: channel = u'homephone' dto.remember_comm_channel(channel = channel, url = tel) email = None try: email = vc.email.value except AttributeError: _log.debug('vCard.EMAIL attribute not available') if email is not None: dto.remember_comm_channel(channel = 'email', url = email) url = None try: url = vc.url.value except AttributeError: _log.debug('vCard.URL attribute not available') if url is not None: dto.remember_comm_channel(channel = 'web', url = url) return dto
def parse_vcard2dto(vc_text=None, filename=None): import vobject if vc_text is None: _log.info('trying to parse vCard from [%s]', filename) for encoding in ['utf8', 'Windows-1252']: try: vcf = io.open(filename, mode='rt', encoding=encoding) vc_text = vcf.read() vcf.close() break except UnicodeDecodeError: _log.exception('vCard not encoded as [%s]', encoding) if vc_text is None: return None vcf_lines = [] found_first = False for line in vc_text.split('\n'): if not found_first: if line.strip() == 'BEGIN:VCARD': found_first = True vcf_lines.append(line) continue vcf_lines.append(line) if line.strip() == 'END:VCARD': break vc_text = '\n'.join(vcf_lines) dob_format = '%Y%m%d' try: vc = vobject.readOne(vc_text) except vobject.base.ParseError: _log.exception('cannot parse, really a vcf ?') return None try: if vc.kind.value.strip() != 'individual': _log.warning('not a vCard for a single person (vCard.KIND=%s)', vc.kind.value) return None except AttributeError: _log.debug('vCard.KIND attribute not available') dto = gmPerson.cDTO_person() dto.firstnames = vc.n.value.given.strip() dto.lastnames = vc.n.value.family.strip() try: dto.title = vc.title.value.strip() except AttributeError: _log.debug('vCard.TITLE attribute not available') try: gender = vc.gender.value.strip().lower() if gender != '': dto.gender = gender except AttributeError: _log.debug('vCard.GENDER attribute not available') try: dob = pyDT.datetime.strptime(vc.bday.value.strip(), dob_format) dto.dob = dob.replace(tzinfo=gmDateTime.pydt_now_here().tzinfo) dto.dob_is_estimated = False except AttributeError: _log.debug('vCard.BDAY attribute not available') dto.source = 'vCard %s' % vc.version.value.strip() adr = None try: adr = vc.adr.value except AttributeError: _log.debug('vCard.ADR attribute not available') if adr is not None: region_code = None region = adr.region.strip() if region == '': region = None # deduce country country_code = None country = adr.country.strip() if country == '': country = None if country is None: country_row = gmDemographicRecord.map_urb_zip_region2country( urb=adr.city, zip=adr.code, region=region) if country_row is not None: country = country_row['country'] country_code = country_row['code_country'] else: country_code = gmDemographicRecord.map_country2code( country=country) if None in [country, country_code]: _log.error('unknown vCard.ADR.country (%s), skipping address', adr.country) else: # deduce region if region is None: region_row = gmDemographicRecord.map_urb_zip_country2region( urb=adr.city, zip=adr.code, country_code=country_code) if region_row is not None: region = region_row['region'] region_code = region_row['code_region'] else: region_code = gmDemographicRecord.map_region2code( region=region, country_code=country_code) if region_code is None: _log.warning( 'unknown vCard.ADR.region (%s), using default region', adr.region) dto.remember_address(number='?', street=adr.street, urb=adr.city, region_code=region_code, zip=adr.code, country_code=country_code, adr_type='home', subunit=None) tel = None try: tel = vc.tel.value except AttributeError: _log.debug('vCard.TEL attribute not available') if tel is not None: if 'TYPE' in vc.tel.params: channel = (vc.tel.params['TYPE'][0]).lower() if not channel.endswith('phone'): channel += 'phone' else: channel = 'homephone' dto.remember_comm_channel(channel=channel, url=tel) email = None try: email = vc.email.value except AttributeError: _log.debug('vCard.EMAIL attribute not available') if email is not None: dto.remember_comm_channel(channel='email', url=email) url = None try: url = vc.url.value except AttributeError: _log.debug('vCard.URL attribute not available') if url is not None: dto.remember_comm_channel(channel='web', url=url) return dto
def read_person_from_xdt(filename=None, encoding=None, dob_format=None): _map_id2name = { '3101': 'lastnames', '3102': 'firstnames', '3103': 'dob', '3110': 'gender', '3106': 'zipurb', '3107': 'street', '3112': 'zip', '3113': 'urb', '8316': 'source' } needed_fields = ('3101', '3102') interesting_fields = list(_map_id2name) data = {} # try to find encoding if not given if encoding is None: encoding = determine_xdt_encoding(filename=filename) xdt_file = open(filename, mode='rt', encoding=encoding) for line in xdt_file: # # can't use more than what's interesting ... ;-) # if len(data) == len(interesting_fields): # break line = line.replace('\015', '') line = line.replace('\012', '') # xDT line format: aaabbbbcccccccccccCRLF where aaa = length, bbbb = record type, cccc... = content field = line[3:7] # do we care about this line ? if field in interesting_fields: try: data[_map_id2name[field]] break except KeyError: data[_map_id2name[field]] = line[7:] xdt_file.close() # found enough data ? if len(data) < len(needed_fields): raise ValueError( 'insufficient patient data in XDT file [%s], found only: %s' % (filename, data)) from Gnumed.business import gmPerson dto = gmPerson.cDTO_person() dto.firstnames = data['firstnames'] dto.lastnames = data['lastnames'] # CAVE: different data orders are possible, so configuration may be needed # FIXME: detect xDT version and use default from the standard when dob_format is None try: dob = time.strptime(data['dob'], gmTools.coalesce(dob_format, '%d%m%Y')) dto.dob = pyDT.datetime(dob.tm_year, dob.tm_mon, dob.tm_mday, tzinfo=gmDateTime.gmCurrentLocalTimezone) except KeyError: dto.dob = None try: dto.gender = gmXdtMappings.map_gender_xdt2gm[data['gender'].casefold()] except KeyError: dto.gender = None dto.zip = None try: dto.zip = regex.match('\d{5}', data['zipurb']).group() except KeyError: pass try: dto.zip = data['zip'] except KeyError: pass dto.urb = None try: dto.urb = regex.sub('\d{5} ', '', data['zipurb']) except KeyError: pass try: dto.urb = data['urb'] except KeyError: pass try: dto.street = data['street'] except KeyError: dto.street = None try: dto.source = data['source'] except KeyError: dto.source = None return dto
def read_person_from_xdt(filename=None, encoding=None, dob_format=None): _map_id2name = { '3101': 'lastnames', '3102': 'firstnames', '3103': 'dob', '3110': 'gender', '3106': 'zipurb', '3107': 'street', '3112': 'zip', '3113': 'urb', '8316': 'source' } needed_fields = ( '3101', '3102' ) interesting_fields = _map_id2name.keys() data = {} # try to find encoding if not given if encoding is None: encoding = determine_xdt_encoding(filename=filename) xdt_file = io.open(filename, mode = 'rt', encoding = encoding) for line in xdt_file: # # can't use more than what's interesting ... ;-) # if len(data) == len(interesting_fields): # break line = line.replace('\015','') line = line.replace('\012','') # xDT line format: aaabbbbcccccccccccCRLF where aaa = length, bbbb = record type, cccc... = content field = line[3:7] # do we care about this line ? if field in interesting_fields: try: already_seen = data[_map_id2name[field]] break except KeyError: data[_map_id2name[field]] = line[7:] xdt_file.close() # found enough data ? if len(data) < len(needed_fields): raise ValueError('insufficient patient data in XDT file [%s], found only: %s' % (filename, data)) from Gnumed.business import gmPerson dto = gmPerson.cDTO_person() dto.firstnames = data['firstnames'] dto.lastnames = data['lastnames'] # CAVE: different data orders are possible, so configuration may be needed # FIXME: detect xDT version and use default from the standard when dob_format is None try: dob = time.strptime(data['dob'], gmTools.coalesce(dob_format, '%d%m%Y')) dto.dob = pyDT.datetime(dob.tm_year, dob.tm_mon, dob.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone) except KeyError: dto.dob = None try: dto.gender = gmXdtMappings.map_gender_xdt2gm[data['gender'].lower()] except KeyError: dto.gender = None dto.zip = None try: dto.zip = regex.match('\d{5}', data['zipurb']).group() except KeyError: pass try: dto.zip = data['zip'] except KeyError: pass dto.urb = None try: dto.urb = regex.sub('\d{5} ', '', data['zipurb']) except KeyError: pass try: dto.urb = data['urb'] except KeyError: pass try: dto.street = data['street'] except KeyError: dto.street = None try: dto.source = data['source'] except KeyError: dto.source = None return dto
def read_persons_from_msva_file(filename=None, encoding=None): if encoding is None: encoding = MSVA_encoding pats_file = io.open(filename, mode = 'rt', encoding = encoding) dtos = [] for line in pats_file: if len(line) < MSVA_line_len: continue # perhaps raise Exception ? dto = gmPerson.cDTO_person() dto.source = 'Med.Manager/CA' dto.firstnames = '%s %s' % ( gmTools.capitalize(line[:20].strip(), gmTools.CAPS_FIRST_ONLY), # should be _NAMES gmTools.capitalize(line[20:22].strip(), gmTools.CAPS_FIRST_ONLY) # should be _NAMES ) dto.lastnames = gmTools.capitalize(line[22:47].strip(), gmTools.CAPS_FIRST_ONLY) # should be _NAMES region = line[59:61] dto.remember_external_id ( name = 'PHN (%s.CA)' % region, value = line[47:57], issuer = 'MOH (%s.CA)' % region ) dob = time.strptime(line[65:73].strip(), MSVA_dob_format) dto.dob = pyDT.datetime(dob.tm_year, dob.tm_mon, dob.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone) dto.gender = line[83].lower() dto.remember_external_id ( name = 'MM (CA) Chart #', value = line[84:92], issuer = 'Medical Manager (CA) application' ) # this is the home address street = '%s // %s' % ( gmTools.capitalize(line[92:117].strip(), gmTools.CAPS_FIRST), gmTools.capitalize(line[117:142].strip(), gmTools.CAPS_FIRST) ) dto.remember_address ( number = '?', street = street, urb = line[142:167], region_code = line[167:169], zip = line[169:178], country_code = 'CA' ) # channel types must correspond to GNUmed database comm type dto.remember_comm_channel(channel = 'homephone', url = line[178:188]) dto.remember_comm_channel(channel = 'workphone', url = line[188:198]) dto.remember_external_id ( name = 'Social Insurance Number', value = line[198:207], issuer = 'Canada' ) dtos.append(dto) pats_file.close() return dtos
def read_persons_from_msva_file(filename=None, encoding=None): if encoding is None: encoding = MSVA_encoding pats_file = io.open(filename, mode='rt', encoding=encoding) dtos = [] for line in pats_file: if len(line) < MSVA_line_len: continue # perhaps raise Exception ? dto = gmPerson.cDTO_person() dto.source = 'Med.Manager/CA' dto.firstnames = '%s %s' % ( gmTools.capitalize(line[:20].strip(), gmTools.CAPS_FIRST_ONLY), # should be _NAMES gmTools.capitalize(line[20:22].strip(), gmTools.CAPS_FIRST_ONLY) # should be _NAMES ) dto.lastnames = gmTools.capitalize( line[22:47].strip(), gmTools.CAPS_FIRST_ONLY) # should be _NAMES region = line[59:61] dto.remember_external_id(name='PHN (%s.CA)' % region, value=line[47:57], issuer='MOH (%s.CA)' % region) dob = time.strptime(line[65:73].strip(), MSVA_dob_format) dto.dob = pyDT.datetime(dob.tm_year, dob.tm_mon, dob.tm_mday, tzinfo=gmDateTime.gmCurrentLocalTimezone) dto.gender = line[83].casefold() dto.remember_external_id(name='MM (CA) Chart #', value=line[84:92], issuer='Medical Manager (CA) application') # this is the home address street = '%s // %s' % ( gmTools.capitalize(line[92:117].strip(), gmTools.CAPS_FIRST), gmTools.capitalize(line[117:142].strip(), gmTools.CAPS_FIRST)) dto.remember_address(number='?', street=street, urb=line[142:167], region_code=line[167:169], zip=line[169:178], country_code='CA') # channel types must correspond to GNUmed database comm type dto.remember_comm_channel(channel='homephone', url=line[178:188]) dto.remember_comm_channel(channel='workphone', url=line[188:198]) dto.remember_external_id(name='Social Insurance Number', value=line[198:207], issuer='Canada') dtos.append(dto) pats_file.close() return dtos