| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: latin-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 # $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/business/gmKVK.py,v $
13 # $Id: gmKVK.py,v 1.22 2010-01-08 13:49:43 ncq Exp $
14 __version__ = "$Revision: 1.22 $"
15 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
16
17 # access our modules
18 import sys, os, os.path, fileinput, codecs, time, datetime as pyDT, glob, re as regex, logging
19
20
21 # our modules
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
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 # if we need to interpret KBV requirements by the
119 # letter we have to delete the file right here
120 #self.delete_from_source()
121 #--------------------------------------------------------
122 # external API
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 # weed out duplicates
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 # Versicherungsnummer
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 # address
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' # actually: map urb_region_code
175 )
176 # FIXME: eGK itself
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 # internal helpers
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 # valid_since -> valid_since_timestamp
220 ts = time.strptime (
221 '%s20%s' % (self.valid_since[:4], self.valid_since[4:]),
222 self.valid_since_format
223 )
224
225 # last_read_date and last_read_time -> last_read_timestamp
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 # guess gender from firstname
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
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 # if we need to interpret KBV requirements by the
253 # letter we have to delete the file right here
254 #self.delete_from_source()
255 #--------------------------------------------------------
256 # external API
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 # weed out duplicates
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 # Versicherungsnummer
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 # address
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' # actually: map urb_region_code
309 )
310 # FIXME: kvk itself
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 # internal helpers
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 # valid_until -> valid_until_timestamp
354 ts = time.strptime (
355 '28%s20%s' % (self.valid_until[:2], self.valid_until[2:]),
356 self.valid_until_format
357 )
358
359 # last_read_date and last_read_time -> last_read_timestamp
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 # guess gender from firstname
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 #------------------------------------------------------------
413
414 dtos = []
415 dtos.extend(get_available_kvks_as_dtos(spool_dir = spool_dir))
416 dtos.extend(get_available_egks_as_dtos(spool_dir = spool_dir))
417
418 return dtos
419 #============================================================
420 # main
421 #------------------------------------------------------------
422 if __name__ == "__main__":
423
424 from Gnumed.pycommon import gmI18N
425
426 gmI18N.activate_locale()
427 gmDateTime.init()
428
430 # test cKVKd_file object
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 # test cKVKd_file object
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 #test_kvk_dto()
458 #test_get_available_kvks_as_dto()
459
460 #============================================================
461 # docs
462 #------------------------------------------------------------
463 # name | mandat | type | length | format
464 # --------------------------------------------
465 # Name Kasse | x | str | 2-28
466 # Nr. Kasse | x | int | 7
467 # VKNR | | int | 5 # MUST be derived from Stammdaten-file, not from KVK
468 # Nr. Pat | x | int | 6-12
469 # Status Pat | x | str | 1 or 4
470 # Statuserg. | | str | 1-3
471 # Titel Pat | | str | 3-15
472 # Vorname | | str | 2-28
473 # Adelspraed.| | str | 1-15
474 # Nachname | x | str | 2-28
475 # geboren | x | int | 8 | DDMMYYYY
476 # StraÃe | | str | 1-28
477 # Ländercode | | str | 1-3
478 # PLZ | x | int | 4-7
479 # Ort | x | str | 2-23
480 # gültig bis | | int | 4 | MMYY
481
482 #============================================================
483 # $Log: gmKVK.py,v $
484 # Revision 1.22 2010-01-08 13:49:43 ncq
485 # - adjust to add-external-id() changes
486 #
487 # Revision 1.21 2009/04/03 09:31:37 ncq
488 # - improved docs
489 #
490 # Revision 1.20 2008/08/28 18:30:28 ncq
491 # - region_code -> urb_region_code
492 # - support eGK now that libchipcard can read it :-)
493 # - improved testing
494 #
495 # Revision 1.19 2008/02/25 17:31:41 ncq
496 # - logging cleanup
497 #
498 # Revision 1.18 2008/01/30 13:34:50 ncq
499 # - switch to std lib logging
500 #
501 # Revision 1.17 2007/12/26 12:35:30 ncq
502 # - import_extra_data(..., *args, **kwargs)
503 #
504 # Revision 1.16 2007/11/12 22:54:26 ncq
505 # - fix longstanding semantic bug ! KVK-Nummmer really is VKNR
506 # - delete KVKd file after importing it
507 # - improve get_candidate_identities()
508 # - improve import_extra_data()
509 # - implement delete_from_source()
510 # - cleanup, improve docs
511 #
512 # Revision 1.15 2007/11/02 10:55:37 ncq
513 # - syntax error fix
514 #
515 # Revision 1.14 2007/10/31 22:06:17 ncq
516 # - teach about more fields in file
517 # - start find_me_sql property
518 #
519 # Revision 1.13 2007/10/31 11:27:02 ncq
520 # - fix it again
521 # - test suite
522 #
523 # Revision 1.12 2007/05/11 14:10:19 ncq
524 # - latin1 -> utf8
525 #
526 # Revision 1.11 2007/02/17 13:55:26 ncq
527 # - consolidate, remove bitrot
528 #
529 # Revision 1.10 2007/02/15 14:54:47 ncq
530 # - fix test suite
531 # - true_kvk_fields list
532 # - map_kvkd_tags2dto
533 # - cDTO_KVK()
534 # - get_available_kvks_as_dtos()
535 #
536 # Revision 1.9 2006/01/01 20:37:22 ncq
537 # - cleanup
538 #
539 # Revision 1.8 2005/11/01 08:49:49 ncq
540 # - naming fix
541 #
542 # Revision 1.7 2005/03/06 14:48:23 ncq
543 # - patient pick list now works with 'field name' not 'data idx'
544 #
545 # Revision 1.6 2004/03/04 19:46:53 ncq
546 # - switch to package based import: from Gnumed.foo import bar
547 #
548 # Revision 1.5 2004/03/02 10:21:10 ihaywood
549 # gmDemographics now supports comm channels, occupation,
550 # country of birth and martial status
551 #
552 # Revision 1.4 2004/02/25 09:46:20 ncq
553 # - import from pycommon now, not python-common
554 #
555 # Revision 1.3 2003/11/17 10:56:34 sjtan
556 #
557 # synced and commiting.
558 #
559 # Revision 1.1 2003/10/23 06:02:38 sjtan
560 #
561 # manual edit areas modelled after r.terry's specs.
562 #
563 # Revision 1.2 2003/04/19 22:53:46 ncq
564 # - missing parameter for %s
565 #
566 # Revision 1.1 2003/04/09 16:15:24 ncq
567 # - KVK classes and helpers
568 #
569
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Oct 1 04:06:22 2010 | http://epydoc.sourceforge.net |