1
2 """Medication handling code.
3
4 license: GPL
5 """
6
7 __version__ = "$Revision: 1.21 $"
8 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
9
10 import sys, logging, csv, codecs, os, re as regex, subprocess
11
12
13 if __name__ == '__main__':
14 sys.path.insert(0, '../../')
15 from Gnumed.pycommon import gmBusinessDBObject, gmPG2, gmShellAPI, gmTools
16 from Gnumed.pycommon import gmDispatcher, gmDateTime, gmHooks
17 from Gnumed.business import gmATC, gmAllergy
18
19
20 _log = logging.getLogger('gm.meds')
21 _log.info(__version__)
22
23
27
28 gmDispatcher.connect(_on_substance_intake_modified, u'substance_intake_mod_db')
29
30
32
33 if search_term is None:
34 return u'http://www.dosing.de'
35
36 terms = []
37 names = []
38
39 if isinstance(search_term, cBrandedDrug):
40 if search_term['atc_code'] is not None:
41 terms.append(search_term['atc_code'])
42
43 elif isinstance(search_term, cSubstanceIntakeEntry):
44 names.append(search_term['substance'])
45 if search_term['atc_brand'] is not None:
46 terms.append(search_term['atc_brand'])
47 if search_term['atc_substance'] is not None:
48 terms.append(search_term['atc_substance'])
49
50 elif search_term is not None:
51 names.append(u'%s' % search_term)
52 terms.extend(gmATC.text2atc(text = u'%s' % search_term, fuzzy = True))
53
54 for name in names:
55 if name.endswith('e'):
56 terms.append(name[:-1])
57 else:
58 terms.append(name)
59
60
61
62
63 url_template = u'http://www.google.de/search?hl=de&source=hp&q=site%%3Adosing.de+%s&btnG=Google-Suche'
64 url = url_template % u'+OR+'.join(terms)
65
66 _log.debug(u'renal insufficiency URL: %s', url)
67
68 return url
69
70
71 -def create_data_source(long_name=None, short_name=None, version=None, source=None, language=None):
72
73 args = {
74 'lname': long_name,
75 'sname': short_name,
76 'ver': version,
77 'src': source,
78 'lang': language
79 }
80
81 cmd = u"""select pk from ref.data_source where name_long = %(lname)s and name_short = %(sname)s and version = %(ver)s"""
82 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
83 if len(rows) > 0:
84 return rows[0]['pk']
85
86 cmd = u"""
87 INSERT INTO ref.data_source (name_long, name_short, version, source, lang)
88 VALUES (
89 %(lname)s,
90 %(sname)s,
91 %(ver)s,
92 %(src)s,
93 %(lang)s
94 )
95 returning pk
96 """
97 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
98
99 return rows[0]['pk']
100
101
102
103
104
105
106
108 """Iterator over a Gelbe Liste/MMI v8.2 CSV file."""
109
110 version = u'Gelbe Liste/MMI v8.2 CSV file interface'
111 default_transfer_file_windows = r"c:\rezept.txt"
112
113 default_encoding = 'cp1250'
114 csv_fieldnames = [
115 u'name',
116 u'packungsgroesse',
117 u'darreichungsform',
118 u'packungstyp',
119 u'festbetrag',
120 u'avp',
121 u'hersteller',
122 u'rezepttext',
123 u'pzn',
124 u'status_vertrieb',
125 u'status_rezeptpflicht',
126 u'status_fachinfo',
127 u'btm',
128 u'atc',
129 u'anzahl_packungen',
130 u'zuzahlung_pro_packung',
131 u'einheit',
132 u'schedule_morgens',
133 u'schedule_mittags',
134 u'schedule_abends',
135 u'schedule_nachts',
136 u'status_dauermedikament',
137 u'status_hausliste',
138 u'status_negativliste',
139 u'ik_nummer',
140 u'status_rabattvertrag',
141 u'wirkstoffe',
142 u'wirkstoffmenge',
143 u'wirkstoffeinheit',
144 u'wirkstoffmenge_bezug',
145 u'wirkstoffmenge_bezugseinheit',
146 u'status_import',
147 u'status_lifestyle',
148 u'status_ausnahmeliste',
149 u'packungsmenge',
150 u'apothekenpflicht',
151 u'status_billigere_packung',
152 u'rezepttyp',
153 u'besonderes_arzneimittel',
154 u't_rezept_pflicht',
155 u'erstattbares_medizinprodukt',
156 u'hilfsmittel',
157 u'hzv_rabattkennung',
158 u'hzv_preis'
159 ]
160 boolean_fields = [
161 u'status_rezeptpflicht',
162 u'status_fachinfo',
163 u'btm',
164 u'status_dauermedikament',
165 u'status_hausliste',
166 u'status_negativliste',
167 u'status_rabattvertrag',
168 u'status_import',
169 u'status_lifestyle',
170 u'status_ausnahmeliste',
171 u'apothekenpflicht',
172 u'status_billigere_packung',
173 u'besonderes_arzneimittel',
174 u't_rezept_pflicht',
175 u'erstattbares_medizinprodukt',
176 u'hilfsmittel'
177 ]
178
198
201
203 line = self.csv_lines.next()
204
205 for field in cGelbeListeCSVFile.boolean_fields:
206 line[field] = (line[field].strip() == u'T')
207
208
209 if line['wirkstoffe'].strip() == u'':
210 line['wirkstoffe'] = []
211 else:
212 line['wirkstoffe'] = [ wirkstoff.strip() for wirkstoff in line['wirkstoffe'].split(u';') ]
213
214 return line
215
216 - def close(self, truncate=True):
217 try: self.csv_file.close()
218 except: pass
219
220 if truncate:
221 try: os.open(self.filename, 'wb').close
222 except: pass
223
226
227 has_unknown_fields = property(_get_has_unknown_fields, lambda x:x)
228
230
231
233 self.patient = None
234 self.custom_path_to_binary = None
235
237 raise NotImplementedError
238
240 raise NotImplementedError
241
243 raise NotImplementedError
244
246 raise NotImplementedError
247
249 raise NotImplementedError
250
252 raise NotImplementedError
253
255 raise NotImplementedError
256
258 raise NotImplementedError
259
261
262 version = u'FreeDiams v0.5.0 interface'
263 default_encoding = 'utf8'
264 default_dob_format = '%Y/%m/%d'
265
266 map_gender2mf = {
267 'm': u'M',
268 'f': u'F',
269 'tf': u'H',
270 'tm': u'H',
271 'h': u'H'
272 }
273
285
287
288
289 if not self.__detect_binary():
290 return False
291
292 freediams = subprocess.Popen (
293 args = u'--version',
294 executable = self.path_to_binary,
295 stdout = subprocess.PIPE,
296 stderr = subprocess.PIPE,
297
298 universal_newlines = True
299 )
300 data, errors = freediams.communicate()
301 version = regex.search('FreeDiams\s\d.\d.\d', data).group().split()[1]
302 _log.debug('FreeDiams %s', version)
303
304 return version
305
307 return create_data_source (
308 long_name = u'"FreeDiams" Drug Database Frontend',
309 short_name = u'FreeDiams',
310 version = self.get_data_source_version(),
311 source = u'http://ericmaeker.fr/FreeMedForms/di-manual/index.html',
312 language = u'fr'
313 )
314
316 """http://ericmaeker.fr/FreeMedForms/di-manual/en/html/ligne_commandes.html"""
317
318 if not self.__detect_binary():
319 return False
320
321 self.__create_gm2fd_file()
322 open(self.__fd2gm_filename, 'wb').close()
323
324 args = u'--exchange-in="%s"' % (self.__gm2fd_filename)
325 cmd = r'%s %s' % (self.path_to_binary, args)
326 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
327 _log.error('problem switching to the FreeDiams drug database')
328 return False
329
330 return True
331
334
336 """FreeDiams ONLY use CIS.
337
338 CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel).
339 CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS.
340 AFSSAPS is the French FDA.
341
342 CIP stands for Unique Presentation Identifier (eg 30 pills plaq)
343 CIP if you want to specify the packaging of the drug (30 pills
344 thermoformed tablet...) -- actually not really usefull for french
345 doctors.
346 # .external_code_type: u'FR-CIS'
347 # .external_cod: the CIS value
348 """
349 self.switch_to_frontend(blocking = True)
350 self.import_fd2gm_file()
351
354
358
362
363
364
366
367 if self.path_to_binary is not None:
368 return True
369
370 found, cmd = gmShellAPI.find_first_binary(binaries = [
371 r'/usr/bin/freediams',
372 r'freediams',
373 r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams',
374 r'c:\programs\freediams\freediams.exe',
375 r'freediams.exe'
376 ])
377
378 if found:
379 self.path_to_binary = cmd
380 return True
381
382 try:
383 self.custom_path_to_binary
384 except AttributeError:
385 _log.error('cannot find FreeDiams binary, no custom path set')
386 return False
387
388 found, cmd = gmShellAPI.detect_external_binary(binary = self.custom_path_to_binary)
389 if found:
390 self.path_to_binary = cmd
391 return True
392
393 _log.error('cannot find FreeDiams binary')
394 return False
395
397
398 u"""<?xml version = "1.0" encoding = "UTF-8"?>
399
400 <FreeDiams>
401 <!-- <DrugsDatabaseName>FR_AFSSAPS</DrugsDatabaseName> -->
402 <FullPrescription version="0.4.0">
403 </FullPrescription>
404 </FreeDiams>
405 """
406
407 drug_snippet = u"""
408 <Prescription>
409 <OnlyForTest>True</OnlyForTest>
410 <Drug_UID>%s</Drug_UID>
411 </Prescription>
412 """
413
414 xml_file = codecs.open(self.__gm2fd_filename, 'wb', 'utf8')
415 if self.patient is None:
416 xml_file.close()
417 return
418
419
421
422 xml_file = codecs.open(self.__gm2fd_filename, 'wb', 'utf8')
423
424 xml = u"""<?xml version="1.0" encoding="UTF-8"?>
425
426 <!-- Eric says the order of same-level nodes does not matter. -->
427
428 <FreeDiams_In version="0.5.0">
429 <EMR name="GNUmed" uid="unused"/>
430 <ConfigFile value="%s"/>
431 <ExchangeOut value="%s" format="xml"/> <!-- should perhaps better be html_xml ? -->
432 <!-- <DrugsDatabase uid="can be set to a specific DB"/> -->
433 <Ui editmode="select-only" blockPatientDatas="1"/>
434 %%s
435 </FreeDiams_In>
436
437 <!--
438 # FIXME: search by LOINC code and add (as soon as supported by FreeDiams ...)
439 <Creatinine value="12" unit="mg/l or mmol/l"/>
440 <Weight value="70" unit="kg or pd" />
441 <Height value="170" unit="cm or "/>
442 <ICD10 value="J11.0;A22;Z23"/>
443 -->
444 """ % (
445 self.__fd4gm_config_file,
446 self.__fd2gm_filename
447 )
448
449 if self.patient is None:
450 xml_file.write(xml % u'')
451 xml_file.close()
452 return
453
454 name = self.patient.get_active_name()
455 if self.patient['dob'] is None:
456 dob = u''
457 else:
458 dob = self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format)
459
460 emr = self.patient.get_emr()
461 allgs = emr.get_allergies()
462 atc_allgs = [
463 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'allergy'))
464 ]
465 atc_sens = [
466 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'sensitivity'))
467 ]
468 inn_allgs = [ a['allergene'] for a in allgs if a['type'] == u'allergy' ]
469 inn_sens = [ a['allergene'] for a in allgs if a['type'] == u'sensitivity' ]
470
471
472 uid_allgs = [
473 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'allergy'))
474 ]
475 uid_sens = [
476 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'sensitivity'))
477 ]
478
479 patient_xml = u"""<Patient>
480 <Identity
481 lastnames="%s"
482 firstnames="%s"
483 uid="%s"
484 dob="%s"
485 gender="%s"
486 />
487 <ATCAllergies value="%s"/>
488 <ATCIntolerances value="%s"/>
489
490 <InnAllergies value="%s"/>
491 <InnIntolerances value="%s"/>
492
493 <DrugsUidAllergies value="%s"/>
494 <DrugsUidIntolerances value="%s"/>
495 </Patient>
496 """ % (
497 name['lastnames'],
498 name['firstnames'],
499 self.patient.ID,
500 dob,
501 cFreeDiamsInterface.map_gender2mf[self.patient['gender']],
502 u';'.join(atc_allgs),
503 u';'.join(atc_sens),
504 u';'.join(inn_allgs),
505 u';'.join(inn_sens),
506 u';'.join(uid_allgs),
507 u';'.join(uid_sens)
508 )
509
510 xml_file.write(xml % patient_xml)
511 xml_file.close()
512
514
515 from xml.etree import ElementTree as etree
516 fd2gm_xml = etree.ElementTree()
517 fd2gm_xml.parse(self.__fd2gm_filename)
518
519 data_src_pk = self.create_data_source_entry()
520
521 db_id = fd2gm_xml.find('DrugsDatabaseName').text.strip()
522 drugs = fd2gm_xml.findall('FullPrescription/Prescription')
523 for drug in drugs:
524 drug_name = drug.find('DrugName').text.replace(', )', ')').strip()
525 drug_uid = drug.find('Drug_UID').text.strip()
526 drug_form = drug.find('DrugForm').text.strip()
527
528
529 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True)
530
531 new_drug['is_fake_brand'] = False
532
533 new_drug['external_code_type'] = u'FreeDiams::%s' % db_id
534 new_drug['external_code'] = drug_uid
535 new_drug['pk_data_source'] = data_src_pk
536 new_drug.save()
537
538 components = drug.getiterator('Composition')
539 for comp in components:
540
541 subst = comp.attrib['molecularName'].strip()
542 inn = comp.attrib['inn'].strip()
543 if inn != u'':
544 create_consumable_substance(substance = inn, atc = None)
545 if subst == u'':
546 subst = inn
547
548 amount = regex.match(r'\d+[.,]{0,1}\d*', comp.attrib['strenght'].strip())
549 if amount is None:
550 amount = 99999
551 else:
552 amount = amount.group()
553
554 unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', comp.attrib['strenght'].strip()).strip()
555 if unit == u'':
556 unit = u'*?*'
557
558 new_drug.add_component(substance = subst, atc = None, amount = amount, unit = unit)
559
561 """Support v8.2 CSV file interface only."""
562
563 version = u'Gelbe Liste/MMI v8.2 interface'
564 default_encoding = 'cp1250'
565 bdt_line_template = u'%03d6210#%s\r\n'
566 bdt_line_base_length = 8
567
569
570 cDrugDataSourceInterface.__init__(self)
571
572 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version)
573
574 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
575 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
576
577 paths = gmTools.gmPaths()
578
579 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt')
580 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp')
581 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt')
582 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
583
584 self.__data_date = None
585 self.__online_update_date = None
586
587
588
590
591 if self.__data_date is not None:
592 if not force_reload:
593 return {
594 'data': self.__data_date,
595 'online_update': self.__online_update_date
596 }
597
598 open(self.data_date_filename, 'wb').close()
599
600 cmd = u'%s -DATADATE' % self.path_to_binary
601 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
602 _log.error('problem querying the MMI drug database for version information')
603 self.__data_date = None
604 self.__online_update_date = None
605 return {
606 'data': u'?',
607 'online_update': u'?'
608 }
609
610 try:
611 version_file = open(self.data_date_filename, 'rU')
612 except StandardError:
613 _log.error('problem querying the MMI drug database for version information')
614 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename)
615 self.__data_date = None
616 self.__online_update_date = None
617 return {
618 'data': u'?',
619 'online_update': u'?'
620 }
621
622 self.__data_date = version_file.readline()[:10]
623 self.__online_update_date = version_file.readline()[:10]
624 version_file.close()
625
626 return {
627 'data': self.__data_date,
628 'online_update': self.__online_update_date
629 }
630
632 versions = self.get_data_source_version()
633
634 return create_data_source (
635 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
636 short_name = u'GL/MMI',
637 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
638 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
639 language = u'de'
640 )
641
643
644
645 open(self.default_csv_filename, 'wb').close()
646
647 if cmd is None:
648 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
649
650 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
651 _log.error('problem switching to the MMI drug database')
652
653
654
655
656 return True
657
667
669
670 selected_drugs = self.select_drugs()
671 if selected_drugs is None:
672 return None
673
674 new_substances = []
675
676 for drug in selected_drugs:
677 atc = None
678 if len(drug['wirkstoffe']) == 1:
679 atc = drug['atc']
680 for wirkstoff in drug['wirkstoffe']:
681 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc))
682
683 selected_drugs.close()
684
685 return new_substances
686
688
689 selected_drugs = self.select_drugs()
690 if selected_drugs is None:
691 return None
692
693 data_src_pk = self.create_data_source_entry()
694
695 new_drugs = []
696 new_substances = []
697
698 for entry in selected_drugs:
699
700 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
701
702 if entry[u'hilfsmittel']:
703 _log.debug('skipping Hilfsmittel')
704 continue
705
706 if entry[u'erstattbares_medizinprodukt']:
707 _log.debug('skipping sonstiges Medizinprodukt')
708 continue
709
710
711 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform'])
712 if drug is None:
713 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform'])
714 new_drugs.append(drug)
715
716
717 drug['is_fake'] = False
718 drug['atc_code'] = entry['atc']
719 drug['external_code_type'] = u'DE-PZN'
720 drug['external_code'] = entry['pzn']
721 drug['fk_data_source'] = data_src_pk
722 drug.save()
723
724
725 atc = None
726 if len(entry['wirkstoffe']) == 1:
727 atc = entry['atc']
728 for wirkstoff in entry['wirkstoffe']:
729 drug.add_component(substance = wirkstoff, atc = atc)
730
731
732 atc = None
733 if len(entry['wirkstoffe']) == 1:
734 atc = entry['atc']
735 for wirkstoff in entry['wirkstoffe']:
736 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc))
737
738 return new_drugs, new_substances
739
768
771
790
792
794 cGelbeListeWindowsInterface.__init__(self)
795
796 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version)
797
798
799 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
800 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
801
802 paths = gmTools.gmPaths()
803
804 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
805 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
806 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
807 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
808
810 """empirical CSV interface"""
811
814
816
817 try:
818 csv_file = open(filename, 'rb')
819 except:
820 _log.exception('cannot access [%s]', filename)
821 csv_file = None
822
823 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
824
825 if csv_file is None:
826 return False
827
828 csv_lines = csv.DictReader (
829 csv_file,
830 fieldnames = field_names,
831 delimiter = ';'
832 )
833
834 for line in csv_lines:
835 print "--------------------------------------------------------------------"[:31]
836 for key in field_names:
837 tmp = ('%s ' % key)[:30]
838 print '%s: %s' % (tmp, line[key])
839
840 csv_file.close()
841
842
843
844
845
846
847
848
849
850
851
852
853 drug_data_source_interfaces = {
854 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
855 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface,
856 'FreeDiams (France, US, Canada)': cFreeDiamsInterface
857 }
858
859
860
861 _SQL_get_consumable_substance = u"""
862 SELECT *, xmin
863 FROM ref.consumable_substance
864 WHERE %s
865 """
866
868
869 _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s"
870 _cmds_store_payload = [
871 u"""UPDATE ref.consumable_substance SET
872 description = %(description)s,
873 atc_code = gm.nullify_empty_string(%(atc_code)s)
874 WHERE
875 pk = %(pk)s
876 AND
877 xmin = %(xmin)s
878 AND
879 -- must not currently be used with a patient directly
880 NOT EXISTS (
881 SELECT 1
882 FROM clin.substance_intake
883 WHERE
884 fk_drug_component IS NULL
885 AND
886 fk_substance = %(pk)s
887 LIMIT 1
888 )
889 AND
890 -- must not currently be used with a patient indirectly, either
891 NOT EXISTS (
892 SELECT 1
893 FROM clin.substance_intake
894 WHERE
895 fk_drug_component IS NOT NULL
896 AND
897 fk_drug_component = (
898 SELECT r_ls2b.pk
899 FROM ref.lnk_substance2brand r_ls2b
900 WHERE fk_substance = %(pk)s
901 )
902 LIMIT 1
903 )
904 -- -- must not currently be used with a branded drug
905 -- -- (but this would make it rather hard fixing branded drugs which contain only this substance)
906 -- NOT EXISTS (
907 -- SELECT 1
908 -- FROM ref.lnk_substance2brand
909 -- WHERE fk_substance = %(pk)s
910 -- LIMIT 1
911 -- )
912 RETURNING
913 xmin
914 """
915 ]
916 _updatable_fields = [
917 u'description',
918 u'atc_code'
919 ]
920
929
931
932 substance = substance.strip()
933 if atc is not None:
934 atc = atc.strip()
935 args = {'desc': substance, 'atc': atc}
936
937 cmd = u'SELECT pk FROM ref.consumable_substance WHERE lower(description) = lower(%(desc)s)'
938 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
939
940 if len(rows) == 0:
941 cmd = u"""
942 INSERT INTO ref.consumable_substance (description, atc_code) VALUES (
943 %(desc)s,
944 gm.nullify_empty_string(%(atc)s)
945 ) RETURNING pk"""
946 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = True)
947
948 gmATC.propagate_atc(substance = substance, atc = atc)
949
950 return cConsumableSubstance(aPK_obj = rows[0]['pk'])
951
953 args = {'pk': substance}
954 cmd = u"""
955 DELETE FROM ref.consumable_substance
956 WHERE
957 pk = %(pk)s
958 AND
959
960 -- must not currently be used with a patient
961 NOT EXISTS (
962 SELECT 1
963 FROM clin.v_pat_substance_intake
964 WHERE pk_substance = %(pk)s
965 LIMIT 1
966 )
967 AND
968
969 -- must not currently be used with a branded drug
970 NOT EXISTS (
971 SELECT 1
972 FROM ref.lnk_substance2brand
973 WHERE fk_substance = %(pk)s
974 LIMIT 1
975 )"""
976 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
977 return True
978
979 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
980 """Represents a substance currently taken by a patient."""
981
982 _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s"
983 _cmds_store_payload = [
984 u"""UPDATE clin.substance_intake SET
985 clin_when = %(started)s,
986 discontinued = %(discontinued)s,
987 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s),
988 amount = %(amount)s,
989 unit = %(unit)s,
990 preparation = %(preparation)s,
991 schedule = gm.nullify_empty_string(%(schedule)s),
992 aim = gm.nullify_empty_string(%(aim)s),
993 narrative = gm.nullify_empty_string(%(notes)s),
994 intake_is_approved_of = %(intake_is_approved_of)s,
995
996 -- is_long_term = %(is_long_term)s,
997 is_long_term = (
998 case
999 when (
1000 (%(is_long_term)s is False)
1001 and
1002 (%(duration)s is NULL)
1003 ) is True then null
1004 else %(is_long_term)s
1005 end
1006 )::boolean,
1007 duration = (
1008 case
1009 when %(is_long_term)s is True then null
1010 else %(duration)s
1011 end
1012 )::interval,
1013
1014 fk_drug_component = %(pk_drug_component)s,
1015 fk_substance = %(pk_substance)s,
1016 fk_episode = %(pk_episode)s
1017 WHERE
1018 pk = %(pk_substance_intake)s
1019 AND
1020 xmin = %(xmin_substance_intake)s
1021 RETURNING
1022 xmin as xmin_substance_intake
1023 """
1024 ]
1025 _updatable_fields = [
1026 u'started',
1027 u'discontinued',
1028 u'discontinue_reason',
1029 u'preparation',
1030 u'amount',
1031 u'unit',
1032 u'intake_is_approved_of',
1033 u'schedule',
1034 u'duration',
1035 u'aim',
1036 u'is_long_term',
1037 u'notes',
1038 u'pk_drug_component',
1039 u'pk_substance',
1040 u'pk_episode'
1041 ]
1042
1043 - def format(self, left_margin=0, date_format='%Y-%m-%d'):
1044
1045 if self._payload[self._idx['duration']] is None:
1046 duration = gmTools.bool2subst (
1047 self._payload[self._idx['is_long_term']],
1048 _('long-term'),
1049 _('short-term'),
1050 _('?short-term')
1051 )
1052 else:
1053 duration = gmDateTime.format_interval (
1054 self._payload[self._idx['duration']],
1055 accuracy_wanted = gmDateTime.acc_days
1056 )
1057
1058 line = u'%s%s (%s %s): %s %s%s %s (%s)' % (
1059 u' ' * left_margin,
1060 self._payload[self._idx['started']].strftime(date_format),
1061 gmTools.u_right_arrow,
1062 duration,
1063 self._payload[self._idx['substance']],
1064 self._payload[self._idx['amount']],
1065 self._payload[self._idx['unit']],
1066 self._payload[self._idx['preparation']],
1067 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing'))
1068 )
1069
1070 return line
1071
1072 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
1073 allg = gmAllergy.create_allergy (
1074 allergene = self._payload[self._idx['substance']],
1075 allg_type = allergy_type,
1076 episode_id = self._payload[self._idx['pk_episode']],
1077 encounter_id = encounter_id
1078 )
1079 allg['substance'] = gmTools.coalesce (
1080 self._payload[self._idx['brand']],
1081 self._payload[self._idx['substance']]
1082 )
1083 allg['reaction'] = self._payload[self._idx['discontinue_reason']]
1084 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']])
1085 if self._payload[self._idx['external_code_brand']] is not None:
1086 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']])
1087 allg['allergene'] = self._payload[self._idx['substance']]
1088 comps = [ c['description'] for c in self.containing_drug.components ]
1089 if len(comps) == 0:
1090 allg['generics'] = self._payload[self._idx['substance']]
1091 else:
1092 allg['generics'] = u'; '.join(comps)
1093
1094 allg.save()
1095 return allg
1096
1097
1098
1099 - def _get_ddd(self):
1100
1101 try: self.__ddd
1102 except AttributeError: self.__ddd = None
1103
1104 if self.__ddd is not None:
1105 return self.__ddd
1106
1107 if self._payload[self._idx['atc_substance']] is not None:
1108 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']])
1109 if len(ddd) != 0:
1110 self.__ddd = ddd[0]
1111 else:
1112 if self._payload[self._idx['atc_brand']] is not None:
1113 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']])
1114 if len(ddd) != 0:
1115 self.__ddd = ddd[0]
1116
1117 return self.__ddd
1118
1119 ddd = property(_get_ddd, lambda x:x)
1120
1122 drug = self.containing_drug
1123
1124 if drug is None:
1125 return None
1126
1127 return drug.external_code
1128
1129 external_code = property(_get_external_code, lambda x:x)
1130
1132 drug = self.containing_drug
1133
1134 if drug is None:
1135 return None
1136
1137 return drug.external_code_type
1138
1139 external_code_type = property(_get_external_code_type, lambda x:x)
1140
1142 if self._payload[self._idx['pk_brand']] is None:
1143 return None
1144
1145 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1146
1147 containing_drug = property(_get_containing_drug, lambda x:x)
1148
1150 tests = [
1151
1152 ' 1-1-1-1 ',
1153
1154 '1-1-1-1',
1155 '22-1-1-1',
1156 '1/3-1-1-1',
1157 '/4-1-1-1'
1158 ]
1159 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}$"
1160 for test in tests:
1161 print test.strip(), ":", regex.match(pattern, test.strip())
1162
1163 -def create_substance_intake(substance=None, drug_component=None, atc=None, encounter=None, episode=None, preparation=None):
1164
1165 args = {
1166 'enc': encounter,
1167 'epi': episode,
1168 'prep': preparation,
1169 'comp': drug_component,
1170 'subst': create_consumable_substance(substance = substance, atc = atc)['pk']
1171 }
1172
1173 if drug_component is None:
1174 cmd = u"""
1175 INSERT INTO clin.substance_intake (
1176 fk_encounter,
1177 fk_episode,
1178 fk_substance,
1179 preparation,
1180 intake_is_approved_of
1181 ) VALUES (
1182 %(enc)s,
1183 %(epi)s,
1184 %(subst)s,
1185 gm.nullify_empty_string(%(prep)s),
1186 False
1187 )
1188 RETURNING pk"""
1189 else:
1190 cmd = u"""
1191 INSERT INTO clin.substance_intake (
1192 fk_encounter,
1193 fk_episode,
1194 fk_drug_component,
1195 intake_is_approved_of
1196 ) VALUES (
1197 %(enc)s,
1198 %(epi)s,
1199 %(comp)s,
1200 False
1201 )
1202 RETURNING pk"""
1203
1204 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
1205 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
1206
1208 cmd = u'delete from clin.substance_intake where pk = %(pk)s'
1209 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
1210
1245
1246
1323
1324 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s'
1325
1327
1328 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s'
1329 _cmds_store_payload = [
1330 u"""UPDATE ref.lnk_substance2brand SET
1331 fk_brand = %(pk_brand)s,
1332 fk_substance = %(pk_consumable_substance)s,
1333 amount = %(amount)s,
1334 unit = gm.nullify_empty_string(%(unit)s)
1335 WHERE
1336 NOT EXISTS (
1337 SELECT 1
1338 FROM clin.substance_intake
1339 WHERE fk_drug_component = %(pk_component)s
1340 LIMIT 1
1341 )
1342 AND
1343 pk = %(pk_component)s
1344 AND
1345 xmin = %(xmin_lnk_substance2brand)s
1346 RETURNING
1347 xmin AS xmin_lnk_substance2brand
1348 """
1349 ]
1350 _updatable_fields = [
1351 u'pk_brand',
1352 u'pk_consumable_substance',
1353 u'amount',
1354 u'unit'
1355 ]
1356
1357
1358
1360 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1361
1362 containing_drug = property(_get_containing_drug, lambda x:x)
1363
1368
1370 """Represents a drug as marketed by a manufacturer."""
1371
1372 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s"
1373 _cmds_store_payload = [
1374 u"""UPDATE ref.branded_drug SET
1375 description = %(brand)s,
1376 preparation = %(preparation)s,
1377 atc_code = gm.nullify_empty_string(%(atc)s),
1378 external_code = gm.nullify_empty_string(%(external_code)s),
1379 external_code_type = gm.nullify_empty_string(%(external_code_type)s),
1380 is_fake = %(is_fake_brand)s,
1381 fk_data_source = %(pk_data_source)s
1382 WHERE
1383 pk = %(pk_brand)s
1384 AND
1385 xmin = %(xmin_branded_drug)s
1386 RETURNING
1387 xmin AS xmin_branded_drug
1388 """
1389 ]
1390 _updatable_fields = [
1391 u'brand',
1392 u'preparation',
1393 u'atc',
1394 u'is_fake_brand',
1395 u'external_code',
1396 u'external_code_type',
1397 u'pk_data_source'
1398 ]
1399
1400 - def add_component(self, substance=None, atc=None, amount=None, unit=None):
1401
1402 consumable = create_consumable_substance(substance = substance, atc = atc)
1403
1404 args = {
1405 'brand': self.pk_obj,
1406 'subst': consumable['description'],
1407 'atc': consumable['atc_code'],
1408 'pk_subst': consumable['pk'],
1409 'amount': amount,
1410 'unit': unit
1411 }
1412
1413
1414 cmd = u"""
1415 SELECT pk_component
1416 FROM ref.v_drug_components
1417 WHERE
1418 pk_brand = %(brand)s
1419 AND
1420 ((
1421 (lower(substance) = lower(%(subst)s))
1422 OR
1423 (atc_substance = %(atc)s)
1424 OR
1425 (pk_consumable_substance = %(pk_subst)s)
1426 ) IS TRUE)
1427 """
1428 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1429
1430 if len(rows) > 0:
1431 return
1432
1433
1434 cmd = u"""
1435 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance, amount, unit)
1436 VALUES (%(brand)s, %(pk_subst)s, %(amount)s, %(unit)s)
1437 """
1438 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1439
1441 if len(self._payload[self._idx['components']]) == 1:
1442 _log.error('cannot remove the only component of a drug')
1443 return False
1444
1445 args = {'brand': self.pk_obj, 'comp': substance}
1446 cmd = u"""
1447 DELETE FROM ref.lnk_substance2brand
1448 WHERE
1449 fk_brand = %(brand)s
1450 AND
1451 fk_substance = %(comp)s
1452 AND
1453 NOT EXISTS (
1454 SELECT 1
1455 FROM clin.substance_intake
1456 WHERE fk_drug_component = %(comp)s
1457 LIMIT 1
1458 )
1459 """
1460 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1461 return True
1462
1463
1464
1466 if self._payload[self._idx['external_code']] is None:
1467 return None
1468
1469 return self._payload[self._idx['external_code']]
1470
1471 external_code = property(_get_external_code, lambda x:x)
1472
1474
1475
1476 if self._payload[self._idx['external_code_type']] is None:
1477 return None
1478
1479 return self._payload[self._idx['external_code_type']]
1480
1481 external_code_type = property(_get_external_code_type, lambda x:x)
1482
1484 cmd = u'SELECT * FROM ref.v_drug_components WHERE pk_brand = %(brand)s'
1485 args = {'brand': self._payload[self._idx['pk_brand']]}
1486 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1487 return rows
1488
1489 components = property(_get_components, lambda x:x)
1490
1492 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)'
1493 args = {'fk_brand': self._payload[self._idx['pk_brand']]}
1494 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1495 return rows[0][0]
1496
1497 is_vaccine = property(_get_is_vaccine, lambda x:x)
1498
1500 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description'
1501 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
1502 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
1503
1505 args = {'brand': brand_name, 'prep': preparation}
1506
1507 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)'
1508 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1509
1510 if len(rows) == 0:
1511 return None
1512
1513 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1514
1516
1517 if preparation is None:
1518 preparation = _('units')
1519
1520 if preparation.strip() == u'':
1521 preparation = _('units')
1522
1523 if return_existing:
1524 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation)
1525 if drug is not None:
1526 return drug
1527
1528 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk'
1529 args = {'brand': brand_name, 'prep': preparation}
1530 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
1531
1532 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1533
1535 cmd = u"""
1536 DELETE FROM ref.branded_drug
1537 WHERE
1538 pk = %(pk)s
1539 AND
1540 NOT EXISTS (
1541 SELECT 1
1542 FROM clin.v_pat_substance_intake
1543 WHERE pk_brand = %(pk)s
1544 LIMIT 1
1545 )
1546 """
1547 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': brand}}])
1548
1549
1550
1551 if __name__ == "__main__":
1552
1553 if len(sys.argv) < 2:
1554 sys.exit()
1555
1556 if sys.argv[1] != 'test':
1557 sys.exit()
1558
1559 from Gnumed.pycommon import gmLog2
1560 from Gnumed.pycommon import gmI18N
1561 from Gnumed.business import gmPerson
1562
1563 gmI18N.activate_locale()
1564
1565
1571
1573 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
1574 for drug in mmi_file:
1575 print "-------------"
1576 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
1577 for stoff in drug['wirkstoffe']:
1578 print " Wirkstoff:", stoff
1579 raw_input()
1580 if mmi_file.has_unknown_fields is not None:
1581 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key
1582 for key in mmi_file.csv_fieldnames:
1583 print key, '->', drug[key]
1584 raw_input()
1585 mmi_file.close()
1586
1590
1592 mmi = cGelbeListeWineInterface()
1593 mmi_file = mmi.select_drugs()
1594 for drug in mmi_file:
1595 print "-------------"
1596 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
1597 for stoff in drug['wirkstoffe']:
1598 print " Wirkstoff:", stoff
1599 print drug
1600 mmi_file.close()
1601
1605
1607 mmi = cGelbeListeInterface()
1608 print mmi
1609 print "interface definition:", mmi.version
1610
1611 diclofenac = '7587712'
1612 phenprocoumon = '4421744'
1613 mmi.check_drug_interactions(drug_ids_list = [diclofenac, phenprocoumon])
1614
1615
1616
1623
1624
1625
1627 drug = create_substance_intake (
1628 substance = u'Whiskey',
1629 atc = u'no ATC available',
1630 encounter = 1,
1631 episode = 1,
1632 preparation = 'a nice glass'
1633 )
1634 print drug
1635
1640
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654 test_fd_switch_to()
1655
1656
1657
1658
1659
1660
1661
1662