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