1
2 """GNUmed German KVK/eGK objects.
3
4 These objects handle German patient cards (KVK and eGK).
5
6 KVK: http://www.kbv.de/ita/register_G.html
7 eGK: http://www.gematik.de/upload/gematik_Qop_eGK_Spezifikation_Teil1_V1_1_0_Kommentare_4_1652.pdf
8
9 license: GPL v2 or later
10 """
11
12
13
14 __version__ = "$Revision: 1.22 $"
15 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
16
17
18 import sys, os, os.path, fileinput, codecs, time, datetime as pyDT, glob, re as regex, logging
19
20
21
22 if __name__ == '__main__':
23 sys.path.insert(0, '../../')
24 from Gnumed.business import gmPerson
25 from Gnumed.pycommon import gmExceptions, gmDateTime, gmTools, gmPG2
26
27
28 _log = logging.getLogger('gm.kvk')
29 _log.info(__version__)
30
31 true_egk_fields = [
32 'insurance_company',
33 'insurance_number',
34 'insuree_number',
35 'insuree_status',
36 'insuree_status_detail',
37 'insuree_status_comment',
38 'title',
39 'firstnames',
40 'lastnames',
41 'dob',
42 'street',
43 'zip',
44 'urb',
45 'valid_since',
46 ]
47
48
49 true_kvk_fields = [
50 'insurance_company',
51 'insurance_number',
52 'insurance_number_vknr',
53 'insuree_number',
54 'insuree_status',
55 'insuree_status_detail',
56 'insuree_status_comment',
57 'title',
58 'firstnames',
59 'name_affix',
60 'lastnames',
61 'dob',
62 'street',
63 'urb_region_code',
64 'zip',
65 'urb',
66 'valid_until'
67 ]
68
69
70 map_kvkd_tags2dto = {
71 'Version': 'libchipcard_version',
72 'Datum': 'last_read_date',
73 'Zeit': 'last_read_time',
74 'Lesertyp': 'reader_type',
75 'Kartentyp': 'card_type',
76 'KK-Name': 'insurance_company',
77 'KK-Nummer': 'insurance_number',
78 'KVK-Nummer': 'insurance_number_vknr',
79 'VKNR': 'insurance_number_vknr',
80 'V-Nummer': 'insuree_number',
81 'V-Status': 'insuree_status',
82 'V-Statusergaenzung': 'insuree_status_detail',
83 'V-Status-Erlaeuterung': 'insuree_status_comment',
84 'Titel': 'title',
85 'Vorname': 'firstnames',
86 'Namenszusatz': 'name_affix',
87 'Familienname': 'lastnames',
88 'Geburtsdatum': 'dob',
89 'Strasse': 'street',
90 'Laendercode': 'urb_region_code',
91 'PLZ': 'zip',
92 'Ort': 'urb',
93 'gueltig-seit': 'valid_since',
94 'gueltig-bis': 'valid_until',
95 'Pruefsumme-gueltig': 'crc_valid',
96 'Kommentar': 'comment'
97 }
98
99 issuer_template = u'%s (%s)'
100 insurance_number_external_id_type = u'Versichertennummer'
101 insurance_number_external_id_type_egk = u'Versichertennummer (eGK)'
102
103
105
106 kvkd_card_id_string = u'Elektronische Gesundheitskarte'
107
108 - def __init__(self, filename=None, strict=True):
109 self.dto_type = 'eGK'
110 self.dob_format = '%d%m%Y'
111 self.valid_since_format = '%d%m%Y'
112 self.last_read_time_format = '%H:%M:%S'
113 self.last_read_date_format = '%d.%m.%Y'
114 self.filename = filename
115
116 self.__parse_egk_file(strict = strict)
117
118
119
120
121
122
123
125 old_idents = gmPerson.cDTO_person.get_candidate_identities(self, can_create = can_create)
126
127 cmd = u"""
128 select pk_identity from dem.v_external_ids4identity where
129 value = %(val)s and
130 name = %(name)s and
131 issuer = %(kk)s
132 """
133 args = {
134 'val': self.insuree_number,
135 'name': insurance_number_external_id_type,
136 'kk': issuer_template % (self.insurance_company, self.insurance_number)
137 }
138 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
139
140
141 new_idents = []
142 for r in rows:
143 for oid in old_idents:
144 if r[0] == oid.ID:
145 break
146 new_idents.append(gmPerson.cIdentity(aPK_obj = r['pk_identity']))
147
148 old_idents.extend(new_idents)
149
150 return old_idents
151
153
154
155
156 identity.add_external_id (
157 type_name = insurance_number_external_id_type_egk,
158 value = self.insuree_number,
159 issuer = issuer_template % (self.insurance_company, self.insurance_number),
160 comment = u'Nummer (eGK) des Versicherten bei der Krankenkasse'
161 )
162
163 street = self.street
164 number = regex.findall(' \d+.*', street)
165 if len(number) == 0:
166 number = None
167 else:
168 street = street.replace(number[0], '')
169 number = number[0].strip()
170 identity.link_address (
171 number = number,
172 street = street,
173 postcode = self.zip,
174 urb = self.urb,
175 state = u'??',
176 country = u'DE'
177 )
178
179
181 try:
182 os.remove(self.filename)
183 self.filename = None
184 except:
185 _log.exception('cannot delete kvkd file [%s]' % self.filename, verbose = False)
186
187
188
190
191 _log.debug('parsing eGK data in [%s]', self.filename)
192
193 egk_file = codecs.open(filename = self.filename, mode = 'rU', encoding = 'utf8')
194
195 card_type_seen = False
196 for line in egk_file:
197 line = line.replace('\n', '').replace('\r', '')
198 tag, content = line.split(':', 1)
199 content = content.strip()
200
201 if tag == 'Kartentyp':
202 card_type_seen = True
203 if content != cDTO_eGK.kvkd_card_id_string:
204 _log.error('parsing wrong card type')
205 _log.error('found : %s', content)
206 _log.error('expected: %s', cDTO_KVK.kvkd_card_id_string)
207 if strict:
208 raise ValueError('wrong card type: %s, expected %s', content, cDTO_KVK.kvkd_card_id_string)
209 else:
210 _log.debug('trying to parse anyway')
211
212 if tag == 'Geburtsdatum':
213 tmp = time.strptime(content, self.dob_format)
214 content = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)
215
216 try:
217 setattr(self, map_kvkd_tags2dto[tag], content)
218 except KeyError:
219 _log.exception('unknown KVKd eGK file key [%s]' % tag)
220
221
222 ts = time.strptime (
223 '%s20%s' % (self.valid_since[:4], self.valid_since[4:]),
224 self.valid_since_format
225 )
226
227
228 ts = time.strptime (
229 '%s %s' % (self.last_read_date, self.last_read_time),
230 '%s %s' % (self.last_read_date_format, self.last_read_time_format)
231 )
232 self.last_read_timestamp = pyDT.datetime(ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, tzinfo = gmDateTime.gmCurrentLocalTimezone)
233
234
235 self.gender = gmTools.coalesce(gmPerson.map_firstnames2gender(firstnames=self.firstnames), 'f')
236
237 if not card_type_seen:
238 _log.warning('no line with card type found, unable to verify')
239
241
242 kvkd_card_id_string = u'Krankenversichertenkarte'
243
244 - def __init__(self, filename=None, strict=True):
245 self.dto_type = 'KVK'
246 self.dob_format = '%d%m%Y'
247 self.valid_until_format = '%d%m%Y'
248 self.last_read_time_format = '%H:%M:%S'
249 self.last_read_date_format = '%d.%m.%Y'
250 self.filename = filename
251
252 self.__parse_kvk_file(strict = strict)
253
254
255
256
257
258
259
261 old_idents = gmPerson.cDTO_person.get_candidate_identities(self, can_create = can_create)
262
263 cmd = u"""
264 select pk_identity from dem.v_external_ids4identity where
265 value = %(val)s and
266 name = %(name)s and
267 issuer = %(kk)s
268 """
269 args = {
270 'val': self.insuree_number,
271 'name': insurance_number_external_id_type,
272 'kk': issuer_template % (self.insurance_company, self.insurance_number)
273 }
274 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
275
276
277 new_idents = []
278 for r in rows:
279 for oid in old_idents:
280 if r[0] == oid.ID:
281 break
282 new_idents.append(gmPerson.cIdentity(aPK_obj = r['pk_identity']))
283
284 old_idents.extend(new_idents)
285
286 return old_idents
287
289
290 identity.add_external_id (
291 type_name = insurance_number_external_id_type,
292 value = self.insuree_number,
293 issuer = issuer_template % (self.insurance_company, self.insurance_number),
294 comment = u'Nummer des Versicherten bei der Krankenkasse'
295 )
296
297 street = self.street
298 number = regex.findall(' \d+.*', street)
299 if len(number) == 0:
300 number = None
301 else:
302 street = street.replace(number[0], '')
303 number = number[0].strip()
304 identity.link_address (
305 number = number,
306 street = street,
307 postcode = self.zip,
308 urb = self.urb,
309 state = u'??',
310 country = u'DE'
311 )
312
313
315 try:
316 os.remove(self.filename)
317 self.filename = None
318 except:
319 _log.exception('cannot delete kvkd file [%s]' % self.filename, verbose = False)
320
321
322
324
325 _log.debug('parsing KVK data in [%s]', self.filename)
326
327 kvk_file = codecs.open(filename = self.filename, mode = 'rU', encoding = 'utf8')
328
329 card_type_seen = False
330 for line in kvk_file:
331 line = line.replace('\n', '').replace('\r', '')
332 tag, content = line.split(':', 1)
333 content = content.strip()
334
335 if tag == 'Kartentyp':
336 card_type_seen = True
337 if content != cDTO_KVK.kvkd_card_id_string:
338 _log.error('parsing wrong card type')
339 _log.error('found : %s', content)
340 _log.error('expected: %s', cDTO_KVK.kvkd_card_id_string)
341 if strict:
342 raise ValueError('wrong card type: %s, expected %s', content, cDTO_KVK.kvkd_card_id_string)
343 else:
344 _log.debug('trying to parse anyway')
345
346 if tag == 'Geburtsdatum':
347 tmp = time.strptime(content, self.dob_format)
348 content = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)
349
350 try:
351 setattr(self, map_kvkd_tags2dto[tag], content)
352 except KeyError:
353 _log.exception('unknown KVKd kvk file key [%s]' % tag)
354
355
356 ts = time.strptime (
357 '28%s20%s' % (self.valid_until[:2], self.valid_until[2:]),
358 self.valid_until_format
359 )
360
361
362 ts = time.strptime (
363 '%s %s' % (self.last_read_date, self.last_read_time),
364 '%s %s' % (self.last_read_date_format, self.last_read_time_format)
365 )
366 self.last_read_timestamp = pyDT.datetime(ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, tzinfo = gmDateTime.gmCurrentLocalTimezone)
367
368
369 self.gender = gmTools.coalesce(gmPerson.map_firstnames2gender(firstnames=self.firstnames), 'f')
370
371 if not card_type_seen:
372 _log.warning('no line with card type found, unable to verify')
373
375
376 data_file = codecs.open(filename = card_file, mode = 'rU', encoding = 'utf8')
377
378 for line in kvk_file:
379 line = line.replace('\n', '').replace('\r', '')
380 tag, content = line.split(':', 1)
381 content = content.strip()
382
383 if tag == 'Kartentyp':
384 pass
385
387
388 kvk_files = glob.glob(os.path.join(spool_dir, 'KVK-*.dat'))
389 dtos = []
390 for kvk_file in kvk_files:
391 try:
392 dto = cDTO_KVK(filename = kvk_file)
393 except:
394 _log.exception('probably not a KVKd KVK file: [%s]' % kvk_file)
395 continue
396 dtos.append(dto)
397
398 return dtos
399
401
402 egk_files = glob.glob(os.path.join(spool_dir, 'eGK-*.dat'))
403 dtos = []
404 for egk_file in egk_files:
405 try:
406 dto = cDTO_eGK(filename = egk_file)
407 except:
408 _log.exception('probably not a KVKd eGK file: [%s]' % egk_file)
409 continue
410 dtos.append(dto)
411
412 return dtos
413
421
422
423
424 if __name__ == "__main__":
425
426 from Gnumed.pycommon import gmI18N
427
428 gmI18N.activate_locale()
429 gmDateTime.init()
430
432
433 kvkd_file = sys.argv[2]
434 print "reading eGK data from KVKd file", kvkd_file
435 dto = cDTO_eGK(filename = kvkd_file, strict = False)
436 print dto
437 for attr in true_egk_fields:
438 print getattr(dto, attr)
439
441
442 kvkd_file = sys.argv[2]
443 print "reading KVK data from KVKd file", kvkd_file
444 dto = cDTO_KVK(filename = kvkd_file, strict = False)
445 print dto
446 for attr in true_kvk_fields:
447 print getattr(dto, attr)
448
453
454 if (len(sys.argv)) > 1 and (sys.argv[1] == 'test'):
455 if len(sys.argv) < 3:
456 print "give name of KVKd file as first argument"
457 sys.exit(-1)
458 test_egk_dto()
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571