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 )
160
161 street = self.street
162 number = regex.findall(' \d+.*', street)
163 if len(number) == 0:
164 number = None
165 else:
166 street = street.replace(number[0], '')
167 number = number[0].strip()
168 identity.link_address (
169 number = number,
170 street = street,
171 postcode = self.zip,
172 urb = self.urb,
173 state = u'??',
174 country = u'DE'
175 )
176
177
179 try:
180 os.remove(self.filename)
181 self.filename = None
182 except:
183 _log.exception('cannot delete kvkd file [%s]' % self.filename, verbose = False)
184
185
186
188
189 _log.debug('parsing eGK data in [%s]', self.filename)
190
191 egk_file = codecs.open(filename = self.filename, mode = 'rU', encoding = 'utf8')
192
193 card_type_seen = False
194 for line in egk_file:
195 line = line.replace('\n', '').replace('\r', '')
196 tag, content = line.split(':', 1)
197 content = content.strip()
198
199 if tag == 'Kartentyp':
200 card_type_seen = True
201 if content != cDTO_eGK.kvkd_card_id_string:
202 _log.error('parsing wrong card type')
203 _log.error('found : %s', content)
204 _log.error('expected: %s', cDTO_KVK.kvkd_card_id_string)
205 if strict:
206 raise ValueError('wrong card type: %s, expected %s', content, cDTO_KVK.kvkd_card_id_string)
207 else:
208 _log.debug('trying to parse anyway')
209
210 if tag == 'Geburtsdatum':
211 tmp = time.strptime(content, self.dob_format)
212 content = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)
213
214 try:
215 setattr(self, map_kvkd_tags2dto[tag], content)
216 except KeyError:
217 _log.exception('unknown KVKd eGK file key [%s]' % tag)
218
219
220 ts = time.strptime (
221 '%s20%s' % (self.valid_since[:4], self.valid_since[4:]),
222 self.valid_since_format
223 )
224
225
226 ts = time.strptime (
227 '%s %s' % (self.last_read_date, self.last_read_time),
228 '%s %s' % (self.last_read_date_format, self.last_read_time_format)
229 )
230 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)
231
232
233 self.gender = gmTools.coalesce(gmPerson.map_firstnames2gender(firstnames=self.firstnames), 'f')
234
235 if not card_type_seen:
236 _log.warning('no line with card type found, unable to verify')
237
239
240 kvkd_card_id_string = u'Krankenversichertenkarte'
241
242 - def __init__(self, filename=None, strict=True):
243 self.dto_type = 'KVK'
244 self.dob_format = '%d%m%Y'
245 self.valid_until_format = '%d%m%Y'
246 self.last_read_time_format = '%H:%M:%S'
247 self.last_read_date_format = '%d.%m.%Y'
248 self.filename = filename
249
250 self.__parse_kvk_file(strict = strict)
251
252
253
254
255
256
257
259 old_idents = gmPerson.cDTO_person.get_candidate_identities(self, can_create = can_create)
260
261 cmd = u"""
262 select pk_identity from dem.v_external_ids4identity where
263 value = %(val)s and
264 name = %(name)s and
265 issuer = %(kk)s
266 """
267 args = {
268 'val': self.insuree_number,
269 'name': insurance_number_external_id_type,
270 'kk': issuer_template % (self.insurance_company, self.insurance_number)
271 }
272 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
273
274
275 new_idents = []
276 for r in rows:
277 for oid in old_idents:
278 if r[0] == oid.ID:
279 break
280 new_idents.append(gmPerson.cIdentity(aPK_obj = r['pk_identity']))
281
282 old_idents.extend(new_idents)
283
284 return old_idents
285
287
288 identity.add_external_id (
289 type_name = insurance_number_external_id_type,
290 value = self.insuree_number,
291 issuer = issuer_template % (self.insurance_company, self.insurance_number),
292 comment = u'Nummer des Versicherten bei der Krankenkasse'
293 )
294
295 street = self.street
296 number = regex.findall(' \d+.*', street)
297 if len(number) == 0:
298 number = None
299 else:
300 street = street.replace(number[0], '')
301 number = number[0].strip()
302 identity.link_address (
303 number = number,
304 street = street,
305 postcode = self.zip,
306 urb = self.urb,
307 state = u'??',
308 country = u'DE'
309 )
310
311
313 try:
314 os.remove(self.filename)
315 self.filename = None
316 except:
317 _log.exception('cannot delete kvkd file [%s]' % self.filename, verbose = False)
318
319
320
322
323 _log.debug('parsing KVK data in [%s]', self.filename)
324
325 kvk_file = codecs.open(filename = self.filename, mode = 'rU', encoding = 'utf8')
326
327 card_type_seen = False
328 for line in kvk_file:
329 line = line.replace('\n', '').replace('\r', '')
330 tag, content = line.split(':', 1)
331 content = content.strip()
332
333 if tag == 'Kartentyp':
334 card_type_seen = True
335 if content != cDTO_KVK.kvkd_card_id_string:
336 _log.error('parsing wrong card type')
337 _log.error('found : %s', content)
338 _log.error('expected: %s', cDTO_KVK.kvkd_card_id_string)
339 if strict:
340 raise ValueError('wrong card type: %s, expected %s', content, cDTO_KVK.kvkd_card_id_string)
341 else:
342 _log.debug('trying to parse anyway')
343
344 if tag == 'Geburtsdatum':
345 tmp = time.strptime(content, self.dob_format)
346 content = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)
347
348 try:
349 setattr(self, map_kvkd_tags2dto[tag], content)
350 except KeyError:
351 _log.exception('unknown KVKd kvk file key [%s]' % tag)
352
353
354 ts = time.strptime (
355 '28%s20%s' % (self.valid_until[:2], self.valid_until[2:]),
356 self.valid_until_format
357 )
358
359
360 ts = time.strptime (
361 '%s %s' % (self.last_read_date, self.last_read_time),
362 '%s %s' % (self.last_read_date_format, self.last_read_time_format)
363 )
364 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)
365
366
367 self.gender = gmTools.coalesce(gmPerson.map_firstnames2gender(firstnames=self.firstnames), 'f')
368
369 if not card_type_seen:
370 _log.warning('no line with card type found, unable to verify')
371
373
374 data_file = codecs.open(filename = card_file, mode = 'rU', encoding = 'utf8')
375
376 for line in kvk_file:
377 line = line.replace('\n', '').replace('\r', '')
378 tag, content = line.split(':', 1)
379 content = content.strip()
380
381 if tag == 'Kartentyp':
382 pass
383
385
386 kvk_files = glob.glob(os.path.join(spool_dir, 'KVK-*.dat'))
387 dtos = []
388 for kvk_file in kvk_files:
389 try:
390 dto = cDTO_KVK(filename = kvk_file)
391 except:
392 _log.exception('probably not a KVKd KVK file: [%s]' % kvk_file)
393 continue
394 dtos.append(dto)
395
396 return dtos
397
399
400 egk_files = glob.glob(os.path.join(spool_dir, 'eGK-*.dat'))
401 dtos = []
402 for egk_file in egk_files:
403 try:
404 dto = cDTO_eGK(filename = egk_file)
405 except:
406 _log.exception('probably not a KVKd eGK file: [%s]' % egk_file)
407 continue
408 dtos.append(dto)
409
410 return dtos
411
419
420
421
422 if __name__ == "__main__":
423
424 from Gnumed.pycommon import gmI18N
425
426 gmI18N.activate_locale()
427 gmDateTime.init()
428
430
431 kvkd_file = sys.argv[2]
432 print "reading eGK data from KVKd file", kvkd_file
433 dto = cDTO_eGK(filename = kvkd_file, strict = False)
434 print dto
435 for attr in true_egk_fields:
436 print getattr(dto, attr)
437
439
440 kvkd_file = sys.argv[2]
441 print "reading KVK data from KVKd file", kvkd_file
442 dto = cDTO_KVK(filename = kvkd_file, strict = False)
443 print dto
444 for attr in true_kvk_fields:
445 print getattr(dto, attr)
446
451
452 if (len(sys.argv)) > 1 and (sys.argv[1] == 'test'):
453 if len(sys.argv) < 3:
454 print "give name of KVKd file as first argument"
455 sys.exit(-1)
456 test_egk_dto()
457
458
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