1
2 """Medication handling code.
3
4 license: GPL
5 """
6
7
8
9 __version__ = "$Revision: 1.20 $"
10 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
11
12 import sys, logging, csv, codecs, os, re as regex
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17 from Gnumed.pycommon import gmBusinessDBObject, gmPG2, gmShellAPI, gmTools, gmDateTime
18 from Gnumed.business import gmATC
19
20
21 _log = logging.getLogger('gm.meds')
22 _log.info(__version__)
23
24
25
26
27
28
29
30
32 """Iterator over a Gelbe Liste/MMI v8.2 CSV file."""
33
34 version = u'Gelbe Liste/MMI v8.2 CSV file interface'
35 default_transfer_file_windows = r"c:\rezept.txt"
36
37 default_encoding = 'cp1250'
38 csv_fieldnames = [
39 u'name',
40 u'packungsgroesse',
41 u'darreichungsform',
42 u'packungstyp',
43 u'festbetrag',
44 u'avp',
45 u'hersteller',
46 u'rezepttext',
47 u'pzn',
48 u'status_vertrieb',
49 u'status_rezeptpflicht',
50 u'status_fachinfo',
51 u'btm',
52 u'atc',
53 u'anzahl_packungen',
54 u'zuzahlung_pro_packung',
55 u'einheit',
56 u'schedule_morgens',
57 u'schedule_mittags',
58 u'schedule_abends',
59 u'schedule_nachts',
60 u'status_dauermedikament',
61 u'status_hausliste',
62 u'status_negativliste',
63 u'ik_nummer',
64 u'status_rabattvertrag',
65 u'wirkstoffe',
66 u'wirkstoffmenge',
67 u'wirkstoffeinheit',
68 u'wirkstoffmenge_bezug',
69 u'wirkstoffmenge_bezugseinheit',
70 u'status_import',
71 u'status_lifestyle',
72 u'status_ausnahmeliste',
73 u'packungsmenge',
74 u'apothekenpflicht',
75 u'status_billigere_packung',
76 u'rezepttyp',
77 u'besonderes_arzneimittel',
78 u't-rezept-pflicht',
79 u'erstattbares_medizinprodukt',
80 u'hilfsmittel',
81 u'hzv_rabattkennung',
82 u'hzv_preis'
83 ]
84 boolean_fields = [
85 u'status_rezeptpflicht',
86 u'status_fachinfo',
87 u'btm',
88 u'status_dauermedikament',
89 u'status_hausliste',
90 u'status_negativliste',
91 u'status_rabattvertrag',
92 u'status_import',
93 u'status_lifestyle',
94 u'status_ausnahmeliste',
95 u'apothekenpflicht',
96 u'status_billigere_packung',
97 u'besonderes_arzneimittel',
98 u't-rezept-pflicht',
99 u'erstattbares_medizinprodukt',
100 u'hilfsmittel'
101 ]
102
122
125
127 line = self.csv_lines.next()
128
129 for field in cGelbeListeCSVFile.boolean_fields:
130 line[field] = (line[field].strip() == u'T')
131
132
133 if line['wirkstoffe'].strip() == u'':
134 line['wirkstoffe'] = []
135 else:
136 line['wirkstoffe'] = [ wirkstoff.strip() for wirkstoff in line['wirkstoffe'].split(u';') ]
137
138 return line
139
140 - def close(self, truncate=True):
141 try: self.csv_file.close()
142 except: pass
143
144 if truncate:
145 try: os.open(self.filename, 'wb').close
146 except: pass
147
149
150
152 raise NotImplementedError
153
155 raise NotImplementedError
156
158 raise NotImplementedError
159
161 raise NotImplementedError
162
164 raise NotImplementedError
165
167 raise NotImplementedError
168
170 raise NotImplementedError
171
173 """Support v8.2 CSV file interface only."""
174
175 version = u'Gelbe Liste/MMI v8.2 interface'
176 default_encoding = 'cp1250'
177 bdt_line_template = u'%03d6210#%s\r\n'
178 bdt_line_base_length = 8
179
181 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version)
182
183 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
184 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
185
186 paths = gmTools.gmPaths()
187
188 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt')
189 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp')
190 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt')
191 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
192
193 self.data_date = None
194 self.online_update_date = None
195
196
197
199
200 open(self.data_date_filename, 'wb').close()
201
202 cmd = u'%s -DATADATE' % self.path_to_binary
203 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
204 _log.error('problem querying the MMI drug database for version information')
205 return {
206 'data': u'?',
207 'online_update': u'?'
208 }
209
210 version_file = open(self.data_date_filename, 'rU')
211 versions = {
212 'data': version_file.readline()[:10],
213 'online_update': version_file.readline()[:10]
214 }
215 version_file.close()
216
217 return versions
218
220 versions = self.get_data_source_version()
221
222 args = {
223 'lname': u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
224 'sname': u'GL/MMI',
225 'ver': u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
226 'src': u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
227 'lang': u'de'
228 }
229
230 cmd = u"""select pk from ref.data_source where name_long = %(lname)s and name_short = %(sname)s and version = %(ver)s"""
231 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
232 if len(rows) > 0:
233 return rows[0]['pk']
234
235 cmd = u"""
236 INSERT INTO ref.data_source (name_long, name_short, version, source, lang)
237 VALUES (
238 %(lname)s,
239 %(sname)s,
240 %(ver)s,
241 %(src)s,
242 %(lang)s
243 )
244 returning pk
245 """
246
247 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
248
249 return rows[0]['pk']
250
252
253
254 open(self.default_csv_filename, 'wb').close()
255
256 if cmd is None:
257 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
258
259 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
260 _log.error('problem switching to the MMI drug database')
261
262
263
264
265 return True
266
276
278
279 selected_drugs = self.select_drugs()
280 if selected_drugs is None:
281 return None
282
283 new_substances = []
284
285 for drug in selected_drugs:
286 atc = None
287 if len(drug['wirkstoffe']) == 1:
288 atc = drug['atc']
289 for wirkstoff in drug['wirkstoffe']:
290 new_substances.append(create_used_substance(substance = wirkstoff, atc = atc))
291
292 selected_drugs.close()
293
294 return new_substances
295
297
298 selected_drugs = self.select_drugs()
299 if selected_drugs is None:
300 return None
301
302 data_src_pk = self.create_data_source_entry()
303
304 new_drugs = []
305 new_substances = []
306
307 for entry in selected_drugs:
308
309 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
310
311 if entry[u'hilfsmittel']:
312 _log.debug('skipping Hilfsmittel')
313 continue
314
315 if entry[u'erstattbares_medizinprodukt']:
316 _log.debug('skipping sonstiges Medizinprodukt')
317 continue
318
319
320 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform'])
321 if drug is None:
322 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform'])
323 new_drugs.append(drug)
324
325
326 drug['is_fake'] = False
327 drug['atc_code'] = entry['atc']
328 drug['external_code'] = u'%s::%s' % ('DE-PZN', entry['pzn'])
329 drug['fk_data_source'] = data_src_pk
330 drug.save()
331
332
333 atc = None
334 if len(entry['wirkstoffe']) == 1:
335 atc = entry['atc']
336 for wirkstoff in entry['wirkstoffe']:
337 drug.add_component(substance = wirkstoff, atc = atc)
338
339
340 atc = None
341 if len(entry['wirkstoffe']) == 1:
342 atc = entry['atc']
343 for wirkstoff in entry['wirkstoffe']:
344 new_substances.append(create_used_substance(substance = wirkstoff, atc = atc))
345
346 return new_drugs, new_substances
347
377
398
400
402 cGelbeListeWindowsInterface.__init__(self)
403
404 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version)
405
406
407 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
408 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
409
410 paths = gmTools.gmPaths()
411
412 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
413 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
414 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
415 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
416
418 """empirical CSV interface"""
419
422
424
425 try:
426 csv_file = open(filename, 'rb')
427 except:
428 _log.exception('cannot access [%s]', filename)
429 csv_file = None
430
431 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
432
433 if csv_file is None:
434 return False
435
436 csv_lines = csv.DictReader (
437 csv_file,
438 fieldnames = field_names,
439 delimiter = ';'
440 )
441
442 for line in csv_lines:
443 print "--------------------------------------------------------------------"[:31]
444 for key in field_names:
445 tmp = ('%s ' % key)[:30]
446 print '%s: %s' % (tmp, line[key])
447
448 csv_file.close()
449
450
451
452
453
454
455
456
457
458
459
460
461 drug_data_source_interfaces = {
462 'Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
463 'Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface
464 }
465
466
467
469 cmd = u'select * from clin.consumed_substance order by description'
470 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
471 return rows
472
474 cmd = u'select * from clin.consumed_substance WHERE pk = %(pk)s'
475 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': pk}}])
476 if len(rows) == 0:
477 return None
478 return rows[0]
479
481
482 substance = substance.strip()
483
484 if atc is not None:
485 atc = atc.strip()
486
487 args = {'desc': substance, 'atc': atc}
488
489 cmd = u'select pk, atc_code, description from clin.consumed_substance where description = %(desc)s'
490 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
491
492 if len(rows) == 0:
493 cmd = u'insert into clin.consumed_substance (description, atc_code) values (%(desc)s, gm.nullify_empty_string(%(atc)s)) returning pk, atc_code, description'
494 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
495
496 gmATC.propagate_atc(substance = substance, atc = atc)
497
498 row = rows[0]
499
500
501 row[1] = args['atc']
502 return row
503
505 args = {'pk': substance}
506 cmd = u"""
507 delete from clin.consumed_substance
508 where
509 pk = %(pk)s and not exists (
510 select 1 from clin.substance_intake
511 where fk_substance = %(pk)s
512 )"""
513 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
514
515 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
516 """Represents a substance currently taken by a patient."""
517
518 _cmd_fetch_payload = u"select * from clin.v_pat_substance_intake where pk_substance_intake = %s"
519 _cmds_store_payload = [
520 u"""update clin.substance_intake set
521 clin_when = %(started)s,
522 strength = gm.nullify_empty_string(%(strength)s),
523 preparation = %(preparation)s,
524 schedule = gm.nullify_empty_string(%(schedule)s),
525 aim = gm.nullify_empty_string(%(aim)s),
526 narrative = gm.nullify_empty_string(%(notes)s),
527 intake_is_approved_of = %(intake_is_approved_of)s,
528
529 -- is_long_term = %(is_long_term)s,
530 is_long_term = (
531 case
532 when (
533 (%(is_long_term)s is False)
534 and
535 (gm.is_null_or_blank_string(%(duration)s) is True)
536 ) is True then null
537 else %(is_long_term)s
538 end
539 )::boolean,
540 duration = (
541 case
542 when %(is_long_term)s is True then null
543 else gm.nullify_empty_string(%(duration)s)
544 end
545 )::interval,
546
547 fk_brand = %(pk_brand)s,
548 fk_substance = %(pk_substance)s,
549 fk_episode = %(pk_episode)s
550 where
551 pk = %(pk_substance_intake)s and
552 xmin = %(xmin_substance_intake)s
553 returning
554 xmin as xmin_substance_intake
555 """
556 ]
557 _updatable_fields = [
558 u'started',
559 u'preparation',
560 u'strength',
561 u'intake_is_approved_of',
562 u'schedule',
563 u'duration',
564 u'aim',
565 u'is_long_term',
566 u'notes',
567 u'pk_brand',
568 u'pk_substance',
569 u'pk_episode'
570 ]
571
572 - def format(self, left_margin=0, date_format='%Y-%m-%d'):
573
574 if self._payload[self._idx['duration']] is None:
575 duration = gmTools.bool2subst (
576 self._payload[self._idx['is_long_term']],
577 _('long-term'),
578 _('short-term'),
579 _('?short-term')
580 )
581 else:
582 duration = gmDateTime.format_interval (
583 self._payload[self._idx['duration']],
584 accuracy_wanted = gmDateTime.acc_days
585 )
586
587 line = u'%s%s (%s %s): %s %s %s (%s)' % (
588 u' ' * left_margin,
589 self._payload[self._idx['started']].strftime(date_format),
590 gmTools.u_right_arrow,
591 duration,
592 self._payload[self._idx['substance']],
593 self._payload[self._idx['strength']],
594 self._payload[self._idx['preparation']],
595 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing'))
596 )
597
598 return line
599
601 drug = self.containing_drug
602
603 if drug is None:
604 return None
605
606 return drug.external_code
607
608 external_code = property(_get_external_code, lambda x:x)
609
611 if self._payload[self._idx['pk_brand']] is None:
612 return None
613
614 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
615
616 containing_drug = property(_get_containing_drug, lambda x:x)
617
619 tests = [
620
621 ' 1-1-1-1 ',
622
623 '1-1-1-1',
624 '22-1-1-1',
625 '1/3-1-1-1',
626 '/4-1-1-1'
627 ]
628 pattern = "^(\d\d|/\d|\d/\d|\d)[\s-]{1,5}\d{0,2}[\s-]{1,5}\d{0,2}[\s-]{1,5}\d{0,2}$"
629 for test in tests:
630 print test.strip(), ":", regex.match(pattern, test.strip())
631
633
634 args = {
635 'enc': encounter,
636 'epi': episode,
637 'prep': preparation,
638 'subst': create_used_substance(substance = substance, atc = atc)['pk']
639 }
640
641 cmd = u"""
642 insert into clin.substance_intake (
643 fk_encounter,
644 fk_episode,
645 fk_substance,
646 preparation,
647 intake_is_approved_of
648 ) values (
649 %(enc)s,
650 %(epi)s,
651 %(subst)s,
652 gm.nullify_empty_string(%(prep)s),
653 False
654 )
655 returning pk
656 """
657 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
658 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
659
661 cmd = u'delete from clin.substance_intake where pk = %(pk)s'
662 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
663
665 """Represents a drug as marketed by a manufacturer."""
666
667 _cmd_fetch_payload = u"select *, xmin from ref.branded_drug where pk = %s"
668 _cmds_store_payload = [
669 u"""update ref.branded_drug set
670 description = %(description)s,
671 preparation = %(preparation)s,
672 atc_code = gm.nullify_empty_string(%(atc_code)s),
673 external_code = gm.nullify_empty_string(%(external_code)s),
674 is_fake = %(is_fake)s,
675 fk_data_source = %(fk_data_source)s
676 where
677 pk = %(pk)s and
678 xmin = %(xmin)s
679 returning
680 xmin
681 """
682 ]
683 _updatable_fields = [
684 u'description',
685 u'preparation',
686 u'atc_code',
687 u'is_fake',
688 u'external_code',
689 u'fk_data_source'
690 ]
691
693 if self._payload[self._idx['external_code']] is None:
694 return None
695
696 if regex.match(u'.+::.+', self._payload[self._idx['external_code']], regex.UNICODE) is None:
697
698 return None
699
700 return regex.split(u'::', self._payload[self._idx['external_code']], 1)
701
702 external_code = property(_get_external_code, lambda x:x)
703
705 cmd = u'select * from ref.substance_in_brand where fk_brand = %(brand)s'
706 args = {'brand': self._payload[self._idx['pk']]}
707 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
708 return rows
709
710 components = property(_get_components, lambda x:x)
711
713
714
715 atc = gmATC.propagate_atc(substance = substance, atc = atc)
716
717 args = {
718 'brand': self.pk_obj,
719 'desc': substance,
720 'atc': atc
721 }
722
723
724 cmd = u"""
725 SELECT pk
726 FROM ref.substance_in_brand
727 WHERE
728 fk_brand = %(brand)s
729 AND
730 ((description = %(desc)s) OR ((atc_code = %(atc)s) IS TRUE))
731 """
732 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
733 if len(rows) > 0:
734 return
735
736
737 cmd = u"""
738 INSERT INTO ref.substance_in_brand (fk_brand, description, atc_code)
739 VALUES (%(brand)s, %(desc)s, %(atc)s)
740 """
741 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
742
745
747 cmd = u'SELECT * FROM ref.v_substance_in_brand ORDER BY brand, substance'
748 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
749 return rows
750
752
753 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description'
754 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
755
756 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
757
759 args = {'brand': brand_name, 'prep': preparation}
760
761 cmd = u'SELECT pk FROM ref.branded_drug WHERE description = %(brand)s AND preparation = %(prep)s'
762 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
763
764 if len(rows) == 0:
765 return None
766
767 return cBrandedDrug(aPK_obj = rows[0]['pk'])
768
770
771 if preparation is None:
772 preparation = _('units')
773
774 if preparation.strip() == u'':
775 preparation = _('units')
776
777 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation)
778
779 if drug is not None:
780 if return_existing:
781 return drug
782 return None
783
784 cmd = u'insert into ref.branded_drug (description, preparation) values (%(brand)s, %(prep)s) returning pk'
785 args = {'brand': brand_name, 'prep': preparation}
786 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
787
788 return cBrandedDrug(aPK_obj = rows[0]['pk'])
789
791 cmd = u'delete from ref.branded_drug where pk = %(pk)s'
792 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': brand}}])
793
795 cmd = u'delete from ref.substance_in_brand where fk_brand = %(brand)s and pk = %(comp)s'
796 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'brand': brand, 'comp': component}}])
797
798
799
800 if __name__ == "__main__":
801
802 from Gnumed.pycommon import gmLog2
803 from Gnumed.pycommon import gmI18N
804
805 gmI18N.activate_locale()
806
807
813
815 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
816 for drug in mmi_file:
817 print "-------------"
818 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
819 for stoff in drug['wirkstoffe']:
820 print " Wirkstoff:", stoff
821 print drug
822 mmi_file.close()
823
827
829 mmi = cGelbeListeWineInterface()
830 mmi_file = mmi.select_drugs()
831 for drug in mmi_file:
832 print "-------------"
833 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
834 for stoff in drug['wirkstoffe']:
835 print " Wirkstoff:", stoff
836 print drug
837 mmi_file.close()
838
842
844 mmi = cGelbeListeInterface()
845 print mmi
846 print "interface definition:", mmi.version
847
848 diclofenac = '7587712'
849 phenprocoumon = '4421744'
850 mmi.check_drug_interactions(pzn_list = [diclofenac, phenprocoumon])
851
853 drug = create_substance_intake (
854 substance = u'Whiskey',
855 atc = u'no ATC available',
856 encounter = 1,
857 episode = 1,
858 preparation = 'a nice glass'
859 )
860 print drug
861
866
867 if (len(sys.argv)) > 1 and (sys.argv[1] == 'test'):
868
869
870
871
872
873
874
875
876 test_show_components()
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965