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
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 identity.add_external_id (
155 type_name = insurance_number_external_id_type_egk,
156 value = self.insuree_number,
157 issuer = issuer_template % (self.insurance_company, self.insurance_number),
158 comment = u'Nummer (eGK) des Versicherten bei der Krankenkasse',
159 context = u'p'
160 )
161
162 street = self.street
163 number = regex.findall(' \d+.*', street)
164 if len(number) == 0:
165 number = None
166 else:
167 street = street.replace(number[0], '')
168 number = number[0].strip()
169 identity.link_address (
170 number = number,
171 street = street,
172 postcode = self.zip,
173 urb = self.urb,
174 state = u'??',
175 country = u'DE'
176 )
177
178
180 try:
181 os.remove(self.filename)
182 self.filename = None
183 except:
184 _log.exception('cannot delete kvkd file [%s]' % self.filename, verbose = False)
185
186
187
189
190 _log.debug('parsing eGK data in [%s]', self.filename)
191
192 egk_file = codecs.open(filename = self.filename, mode = 'rU', encoding = 'utf8')
193
194 card_type_seen = False
195 for line in egk_file:
196 line = line.replace('\n', '').replace('\r', '')
197 tag, content = line.split(':', 1)
198 content = content.strip()
199
200 if tag == 'Kartentyp':
201 card_type_seen = True
202 if content != cDTO_eGK.kvkd_card_id_string:
203 _log.error('parsing wrong card type')
204 _log.error('found : %s', content)
205 _log.error('expected: %s', cDTO_KVK.kvkd_card_id_string)
206 if strict:
207 raise ValueError('wrong card type: %s, expected %s', content, cDTO_KVK.kvkd_card_id_string)
208 else:
209 _log.debug('trying to parse anyway')
210
211 if tag == 'Geburtsdatum':
212 tmp = time.strptime(content, self.dob_format)
213 content = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)
214
215 try:
216 setattr(self, map_kvkd_tags2dto[tag], content)
217 except KeyError:
218 _log.exception('unknown KVKd eGK file key [%s]' % tag)
219
220
221 ts = time.strptime (
222 '%s20%s' % (self.valid_since[:4], self.valid_since[4:]),
223 self.valid_since_format
224 )
225
226
227 ts = time.strptime (
228 '%s %s' % (self.last_read_date, self.last_read_time),
229 '%s %s' % (self.last_read_date_format, self.last_read_time_format)
230 )
231 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)
232
233
234 self.gender = gmTools.coalesce(gmPerson.map_firstnames2gender(firstnames=self.firstnames), 'f')
235
236 if not card_type_seen:
237 _log.warning('no line with card type found, unable to verify')
238
240
241 kvkd_card_id_string = u'Krankenversichertenkarte'
242
243 - def __init__(self, filename=None, strict=True):
244 self.dto_type = 'KVK'
245 self.dob_format = '%d%m%Y'
246 self.valid_until_format = '%d%m%Y'
247 self.last_read_time_format = '%H:%M:%S'
248 self.last_read_date_format = '%d.%m.%Y'
249 self.filename = filename
250
251 self.__parse_kvk_file(strict = strict)
252
253
254
255
256
257
258
260 old_idents = gmPerson.cDTO_person.get_candidate_identities(self, can_create = can_create)
261
262 cmd = u"""
263 select pk_identity from dem.v_external_ids4identity where
264 value = %(val)s and
265 name = %(name)s and
266 issuer = %(kk)s
267 """
268 args = {
269 'val': self.insuree_number,
270 'name': insurance_number_external_id_type,
271 'kk': issuer_template % (self.insurance_company, self.insurance_number)
272 }
273 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
274
275
276 new_idents = []
277 for r in rows:
278 for oid in old_idents:
279 if r[0] == oid.ID:
280 break
281 new_idents.append(gmPerson.cIdentity(aPK_obj = r['pk_identity']))
282
283 old_idents.extend(new_idents)
284
285 return old_idents
286
288
289 identity.add_external_id (
290 type_name = insurance_number_external_id_type,
291 value = self.insuree_number,
292 issuer = issuer_template % (self.insurance_company, self.insurance_number),
293 comment = u'Nummer des Versicherten bei der Krankenkasse',
294 context = u'p'
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