1
2 """Medication handling code.
3
4 license: GPL v2 or later
5 """
6
7 __version__ = "$Revision: 1.21 $"
8 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
9
10 import sys
11 import logging
12 import csv
13 import codecs
14 import os
15 import re as regex
16 import subprocess
17 import decimal
18 from xml.etree import ElementTree as etree
19
20
21 if __name__ == '__main__':
22 sys.path.insert(0, '../../')
23 _ = lambda x:x
24 from Gnumed.pycommon import gmBusinessDBObject
25 from Gnumed.pycommon import gmTools
26 from Gnumed.pycommon import gmShellAPI
27 from Gnumed.pycommon import gmPG2
28 from Gnumed.pycommon import gmDispatcher
29 from Gnumed.pycommon import gmMatchProvider
30 from Gnumed.pycommon import gmHooks
31 from Gnumed.pycommon import gmDateTime
32
33 from Gnumed.business import gmATC
34 from Gnumed.business import gmAllergy
35 from Gnumed.business.gmDocuments import DOCUMENT_TYPE_PRESCRIPTION
36 from Gnumed.business.gmDocuments import create_document_type
37
38
39 _log = logging.getLogger('gm.meds')
40 _log.info(__version__)
41
42
43 DEFAULT_MEDICATION_HISTORY_EPISODE = _('Medication history')
44
48
49 gmDispatcher.connect(_on_substance_intake_modified, u'substance_intake_mod_db')
50
51
53
54 if search_term is None:
55 return u'http://www.dosing.de'
56
57 terms = []
58 names = []
59
60 if isinstance(search_term, cBrandedDrug):
61 if search_term['atc'] is not None:
62 terms.append(search_term['atc'])
63
64 elif isinstance(search_term, cSubstanceIntakeEntry):
65 names.append(search_term['substance'])
66 if search_term['atc_brand'] is not None:
67 terms.append(search_term['atc_brand'])
68 if search_term['atc_substance'] is not None:
69 terms.append(search_term['atc_substance'])
70
71 elif search_term is not None:
72 names.append(u'%s' % search_term)
73 terms.extend(gmATC.text2atc(text = u'%s' % search_term, fuzzy = True))
74
75 for name in names:
76 if name.endswith('e'):
77 terms.append(name[:-1])
78 else:
79 terms.append(name)
80
81
82
83
84 url_template = u'http://www.google.com/search?hl=de&source=hp&q=site%%3Adosing.de+%s&btnG=Google-Suche'
85 url = url_template % u'+OR+'.join(terms)
86
87 _log.debug(u'renal insufficiency URL: %s', url)
88
89 return url
90
91
92 -def create_data_source(long_name=None, short_name=None, version=None, source=None, language=None):
93
94 args = {
95 'lname': long_name,
96 'sname': short_name,
97 'ver': version,
98 'src': source,
99 'lang': language
100 }
101
102 cmd = u"""select pk from ref.data_source where name_long = %(lname)s and name_short = %(sname)s and version = %(ver)s"""
103 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
104 if len(rows) > 0:
105 return rows[0]['pk']
106
107 cmd = u"""
108 INSERT INTO ref.data_source (name_long, name_short, version, source, lang)
109 VALUES (
110 %(lname)s,
111 %(sname)s,
112 %(ver)s,
113 %(src)s,
114 %(lang)s
115 )
116 returning pk
117 """
118 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
119
120 return rows[0]['pk']
121
122
123
124
125
126
127
129 """Iterator over a Gelbe Liste/MMI v8.2 CSV file."""
130
131 version = u'Gelbe Liste/MMI v8.2 CSV file interface'
132 default_transfer_file_windows = r"c:\rezept.txt"
133
134 default_encoding = 'cp1250'
135 csv_fieldnames = [
136 u'name',
137 u'packungsgroesse',
138 u'darreichungsform',
139 u'packungstyp',
140 u'festbetrag',
141 u'avp',
142 u'hersteller',
143 u'rezepttext',
144 u'pzn',
145 u'status_vertrieb',
146 u'status_rezeptpflicht',
147 u'status_fachinfo',
148 u'btm',
149 u'atc',
150 u'anzahl_packungen',
151 u'zuzahlung_pro_packung',
152 u'einheit',
153 u'schedule_morgens',
154 u'schedule_mittags',
155 u'schedule_abends',
156 u'schedule_nachts',
157 u'status_dauermedikament',
158 u'status_hausliste',
159 u'status_negativliste',
160 u'ik_nummer',
161 u'status_rabattvertrag',
162 u'wirkstoffe',
163 u'wirkstoffmenge',
164 u'wirkstoffeinheit',
165 u'wirkstoffmenge_bezug',
166 u'wirkstoffmenge_bezugseinheit',
167 u'status_import',
168 u'status_lifestyle',
169 u'status_ausnahmeliste',
170 u'packungsmenge',
171 u'apothekenpflicht',
172 u'status_billigere_packung',
173 u'rezepttyp',
174 u'besonderes_arzneimittel',
175 u't_rezept_pflicht',
176 u'erstattbares_medizinprodukt',
177 u'hilfsmittel',
178 u'hzv_rabattkennung',
179 u'hzv_preis'
180 ]
181 boolean_fields = [
182 u'status_rezeptpflicht',
183 u'status_fachinfo',
184 u'btm',
185 u'status_dauermedikament',
186 u'status_hausliste',
187 u'status_negativliste',
188 u'status_rabattvertrag',
189 u'status_import',
190 u'status_lifestyle',
191 u'status_ausnahmeliste',
192 u'apothekenpflicht',
193 u'status_billigere_packung',
194 u'besonderes_arzneimittel',
195 u't_rezept_pflicht',
196 u'erstattbares_medizinprodukt',
197 u'hilfsmittel'
198 ]
199
219
222
224 line = self.csv_lines.next()
225
226 for field in cGelbeListeCSVFile.boolean_fields:
227 line[field] = (line[field].strip() == u'T')
228
229
230 if line['wirkstoffe'].strip() == u'':
231 line['wirkstoffe'] = []
232 else:
233 line['wirkstoffe'] = [ wirkstoff.strip() for wirkstoff in line['wirkstoffe'].split(u';') ]
234
235 return line
236
237 - def close(self, truncate=True):
238 try: self.csv_file.close()
239 except: pass
240
241 if truncate:
242 try: os.open(self.filename, 'wb').close
243 except: pass
244
247
248 has_unknown_fields = property(_get_has_unknown_fields, lambda x:x)
249
251
252
254 self.patient = None
255 self.reviewer = None
256 self.custom_path_to_binary = None
257
259 raise NotImplementedError
260
262 raise NotImplementedError
263
265 raise NotImplementedError
266
269
272
275
278
279 - def prescribe(self, substance_intakes=None):
282
284
285 version = u'FreeDiams v0.5.4 interface'
286 default_encoding = 'utf8'
287 default_dob_format = '%Y/%m/%d'
288
289 map_gender2mf = {
290 'm': u'M',
291 'f': u'F',
292 'tf': u'H',
293 'tm': u'H',
294 'h': u'H'
295 }
296
312
314
315
316 if not self.__detect_binary():
317 return False
318
319 freediams = subprocess.Popen (
320 args = u'--version',
321 executable = self.path_to_binary,
322 stdout = subprocess.PIPE,
323 stderr = subprocess.PIPE,
324
325 universal_newlines = True
326 )
327 data, errors = freediams.communicate()
328 version = regex.search('FreeDiams\s\d.\d.\d', data).group().split()[1]
329 _log.debug('FreeDiams %s', version)
330
331 return version
332
334 return create_data_source (
335 long_name = u'"FreeDiams" Drug Database Frontend',
336 short_name = u'FreeDiams',
337 version = self.get_data_source_version(),
338 source = u'http://ericmaeker.fr/FreeMedForms/di-manual/index.html',
339 language = u'fr'
340 )
341
343 """http://ericmaeker.fr/FreeMedForms/di-manual/en/html/ligne_commandes.html"""
344
345 _log.debug('calling FreeDiams in [%s] mode', mode)
346
347 self.__imported_drugs = []
348
349 if not self.__detect_binary():
350 return False
351
352 self.__create_gm2fd_file(mode = mode)
353
354 args = u'--exchange-in="%s"' % (self.__gm2fd_filename)
355 cmd = r'%s %s' % (self.path_to_binary, args)
356 if os.name == 'nt':
357 blocking = True
358 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
359 _log.error('problem switching to the FreeDiams drug database')
360 return False
361
362 if blocking == True:
363 self.import_fd2gm_file_as_drugs()
364
365 return True
366
369
371 if substance_intakes is None:
372 return
373 if len(substance_intakes) < 2:
374 return
375
376 self.__create_prescription_file(substance_intakes = substance_intakes)
377 self.switch_to_frontend(mode = 'interactions', blocking = False)
378
380 if substance_intake is None:
381 return
382
383 self.__create_prescription_file(substance_intakes = [substance_intake])
384 self.switch_to_frontend(mode = 'interactions', blocking = False)
385
388
389 - def prescribe(self, substance_intakes=None):
390 if substance_intakes is None:
391 if not self.__export_latest_prescription():
392 self.__create_prescription_file()
393 else:
394 self.__create_prescription_file(substance_intakes = substance_intakes)
395
396 self.switch_to_frontend(mode = 'prescription', blocking = True)
397 self.import_fd2gm_file_as_prescription()
398
399 return self.__imported_drugs
400
401
402
404
405 if self.path_to_binary is not None:
406 return True
407
408 found, cmd = gmShellAPI.find_first_binary(binaries = [
409 r'/usr/bin/freediams',
410 r'freediams',
411 r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams',
412 r'C:\Program Files (x86)\FreeDiams\freediams.exe',
413 r'C:\Program Files\FreeDiams\freediams.exe',
414 r'c:\programs\freediams\freediams.exe',
415 r'freediams.exe'
416 ])
417
418 if found:
419 self.path_to_binary = cmd
420 return True
421
422 try:
423 self.custom_path_to_binary
424 except AttributeError:
425 _log.error('cannot find FreeDiams binary, no custom path set')
426 return False
427
428 if self.custom_path_to_binary is None:
429 _log.error('cannot find FreeDiams binary')
430 return False
431
432 found, cmd = gmShellAPI.detect_external_binary(binary = self.custom_path_to_binary)
433 if found:
434 self.path_to_binary = cmd
435 return True
436
437 _log.error('cannot find FreeDiams binary')
438 return False
439
441
442 if self.patient is None:
443 _log.debug('cannot export latest FreeDiams prescriptions w/o patient')
444 return False
445
446 docs = self.patient.get_document_folder()
447 prescription = docs.get_latest_freediams_prescription()
448 if prescription is None:
449 _log.debug('no FreeDiams prescription available')
450 return False
451
452 for part in prescription.parts:
453 if part['filename'] == u'freediams-prescription.xml':
454 if part.export_to_file(filename = self.__fd2gm_filename) is not None:
455 return True
456
457 _log.error('cannot export latest FreeDiams prescription to XML file')
458
459 return False
460
462 """FreeDiams calls this exchange-out or prescription file.
463
464 CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel).
465 CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS.
466 AFSSAPS is the French FDA.
467
468 CIP stands for Unique Presentation Identifier (eg 30 pills plaq)
469 CIP if you want to specify the packaging of the drug (30 pills
470 thermoformed tablet...) -- actually not really usefull for french
471 doctors.
472 # .external_code_type: u'FR-CIS'
473 # .external_cod: the CIS value
474
475 OnlyForTest:
476 OnlyForTest drugs will be processed by the IA Engine but
477 not printed (regardless of FreeDiams mode). They are shown
478 in gray in the prescription view.
479
480 Select-only is a mode where FreeDiams creates a list of drugs
481 not a full prescription. In this list, users can add ForTestOnly
482 drug if they want to
483 1. print the list without some drugs
484 2. but including these drugs in the IA engine calculation
485
486 Select-Only mode does not have any relation with the ForTestOnly drugs.
487
488 IsTextual:
489 What is the use and significance of the
490 <IsTextual>true/false</IsTextual>
491 flag when both <DrugName> and <TextualDrugName> exist ?
492
493 This tag must be setted even if it sounds like a duplicated
494 data. This tag is needed inside FreeDiams code.
495
496 INN:
497 GNUmed will pass the substance in <TextualDrugName
498 and will also pass <INN>True</INN>.
499
500 Eric: Nop, this is not usefull because pure textual drugs
501 are not processed but just shown.
502 """
503
504 open(self.__fd2gm_filename, 'wb').close()
505
506
507 if substance_intakes is None:
508 if self.patient is None:
509 _log.warning('cannot create prescription file because there is neither a patient nor a substance intake list')
510
511 return False
512 emr = self.patient.get_emr()
513 substance_intakes = emr.get_current_substance_intake (
514 include_inactive = False,
515 include_unapproved = True
516 )
517
518 drug_snippets = []
519
520
521 fd_intakes = [ i for i in substance_intakes if (
522 (i['intake_is_approved_of'] is True)
523 and
524 (i['external_code_type_brand'] is not None)
525 and
526 (i['external_code_type_brand'].startswith(u'FreeDiams::'))
527 )]
528
529 intakes_pooled_by_brand = {}
530 for intake in fd_intakes:
531
532
533 intakes_pooled_by_brand[intake['brand']] = intake
534 del fd_intakes
535
536 drug_snippet = u"""<Prescription>
537 <IsTextual>False</IsTextual>
538 <DrugName>%s</DrugName>
539 <Drug_UID>%s</Drug_UID>
540 <Drug_UID_type>%s</Drug_UID_type> <!-- not yet supported by FreeDiams -->
541 </Prescription>"""
542
543 last_db_id = u'CA_HCDPD'
544 for intake in intakes_pooled_by_brand.values():
545 last_db_id = gmTools.xml_escape_string(text = intake['external_code_type_brand'].replace(u'FreeDiams::', u'').split(u'::')[0])
546 drug_snippets.append(drug_snippet % (
547 gmTools.xml_escape_string(text = intake['brand'].strip()),
548 gmTools.xml_escape_string(text = intake['external_code_brand'].strip()),
549 last_db_id
550 ))
551
552
553 non_fd_intakes = [ i for i in substance_intakes if (
554 (i['intake_is_approved_of'] is True)
555 and (
556 (i['external_code_type_brand'] is None)
557 or
558 (not i['external_code_type_brand'].startswith(u'FreeDiams::'))
559 )
560 )]
561
562 non_fd_brand_intakes = [ i for i in non_fd_intakes if i['brand'] is not None ]
563 non_fd_substance_intakes = [ i for i in non_fd_intakes if i['brand'] is None ]
564 del non_fd_intakes
565
566 drug_snippet = u"""<Prescription>
567 <IsTextual>True</IsTextual>
568 <TextualDrugName>%s</TextualDrugName>
569 </Prescription>"""
570
571 for intake in non_fd_substance_intakes:
572 drug_name = u'%s %s%s (%s)%s' % (
573 intake['substance'],
574 intake['amount'],
575 intake['unit'],
576 intake['preparation'],
577 gmTools.coalesce(intake['schedule'], u'', _('\n Take: %s'))
578 )
579 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
580
581 intakes_pooled_by_brand = {}
582 for intake in non_fd_brand_intakes:
583 brand = u'%s %s' % (intake['brand'], intake['preparation'])
584 try:
585 intakes_pooled_by_brand[brand].append(intake)
586 except KeyError:
587 intakes_pooled_by_brand[brand] = [intake]
588
589 for brand, comps in intakes_pooled_by_brand.iteritems():
590 drug_name = u'%s\n' % brand
591 for comp in comps:
592 drug_name += u' %s %s%s\n' % (
593 comp['substance'],
594 comp['amount'],
595 comp['unit']
596 )
597 if comps[0]['schedule'] is not None:
598 drug_name += gmTools.coalesce(comps[0]['schedule'], u'', _('Take: %s'))
599 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
600
601
602 xml = u"""<?xml version = "1.0" encoding = "UTF-8"?>
603
604 <FreeDiams>
605 <DrugsDatabaseName>%s</DrugsDatabaseName>
606 <FullPrescription version="0.5.0">
607
608 %s
609
610 </FullPrescription>
611 </FreeDiams>
612 """
613
614 xml_file = codecs.open(self.__fd2gm_filename, 'wb', 'utf8')
615 xml_file.write(xml % (
616 last_db_id,
617 u'\n\t\t'.join(drug_snippets)
618 ))
619 xml_file.close()
620
621 return True
622
624
625 if mode == 'interactions':
626 mode = u'select-only'
627 elif mode == 'prescription':
628 mode = u'prescriber'
629 else:
630 mode = u'select-only'
631
632 xml_file = codecs.open(self.__gm2fd_filename, 'wb', 'utf8')
633
634 xml = u"""<?xml version="1.0" encoding="UTF-8"?>
635
636 <FreeDiams_In version="0.5.0">
637 <EMR name="GNUmed" uid="unused"/>
638 <ConfigFile value="%s"/>
639 <ExchangeOut value="%s" format="xml"/>
640 <!-- <DrugsDatabase uid="can be set to a specific DB"/> -->
641 <Ui editmode="%s" blockPatientDatas="1"/>
642 %%s
643 </FreeDiams_In>
644
645 <!--
646 # FIXME: search by LOINC code and add (as soon as supported by FreeDiams ...)
647 <Creatinine value="12" unit="mg/l or mmol/l"/>
648 <Weight value="70" unit="kg or pd" />
649 <Height value="170" unit="cm or "/>
650 <ICD10 value="J11.0;A22;Z23"/>
651 -->
652 """ % (
653 self.__fd4gm_config_file,
654 self.__fd2gm_filename,
655 mode
656 )
657
658 if self.patient is None:
659 xml_file.write(xml % u'')
660 xml_file.close()
661 return
662
663 name = self.patient.get_active_name()
664 if self.patient['dob'] is None:
665 dob = u''
666 else:
667 dob = self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format)
668
669 emr = self.patient.get_emr()
670 allgs = emr.get_allergies()
671 atc_allgs = [
672 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'allergy'))
673 ]
674 atc_sens = [
675 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'sensitivity'))
676 ]
677 inn_allgs = [
678 a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'allergy'))
679 ]
680 inn_sens = [
681 a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'sensitivity'))
682 ]
683
684
685
686 uid_allgs = [
687 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'allergy'))
688 ]
689 uid_sens = [
690 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'sensitivity'))
691 ]
692
693 patient_xml = u"""<Patient>
694 <Identity
695 lastnames="%s"
696 firstnames="%s"
697 uid="%s"
698 dob="%s"
699 gender="%s"
700 />
701 <ATCAllergies value="%s"/>
702 <ATCIntolerances value="%s"/>
703
704 <InnAllergies value="%s"/>
705 <InnIntolerances value="%s"/>
706
707 <DrugsUidAllergies value="%s"/>
708 <DrugsUidIntolerances value="%s"/>
709 </Patient>
710 """ % (
711 gmTools.xml_escape_string(text = name['lastnames']),
712 gmTools.xml_escape_string(text = name['firstnames']),
713 self.patient.ID,
714 dob,
715 cFreeDiamsInterface.map_gender2mf[self.patient['gender']],
716 gmTools.xml_escape_string(text = u';'.join(atc_allgs)),
717 gmTools.xml_escape_string(text = u';'.join(atc_sens)),
718 gmTools.xml_escape_string(text = u';'.join(inn_allgs)),
719 gmTools.xml_escape_string(text = u';'.join(inn_sens)),
720 gmTools.xml_escape_string(text = u';'.join(uid_allgs)),
721 gmTools.xml_escape_string(text = u';'.join(uid_sens))
722 )
723
724 xml_file.write(xml % patient_xml)
725 xml_file.close()
726
779
781 """
782 If returning textual prescriptions (say, drugs which FreeDiams
783 did not know) then "IsTextual" will be True and UID will be -1.
784 """
785 if filename is None:
786 filename = self.__fd2gm_filename
787
788
789
790 fd2gm_xml = etree.ElementTree()
791 fd2gm_xml.parse(filename)
792
793 data_src_pk = self.create_data_source_entry()
794
795 db_def = fd2gm_xml.find('DrugsDatabaseName')
796 db_id = db_def.text.strip()
797 drug_id_name = db_def.attrib['drugUidName']
798 fd_xml_drug_entries = fd2gm_xml.findall('FullPrescription/Prescription')
799
800 self.__imported_drugs = []
801 for fd_xml_drug in fd_xml_drug_entries:
802 drug_uid = fd_xml_drug.find('Drug_UID').text.strip()
803 if drug_uid == u'-1':
804 _log.debug('skipping textual drug')
805 continue
806 drug_name = fd_xml_drug.find('DrugName').text.replace(', )', ')').strip()
807 drug_form = fd_xml_drug.find('DrugForm').text.strip()
808 drug_atc = fd_xml_drug.find('DrugATC')
809 if drug_atc is None:
810 drug_atc = u''
811 else:
812 if drug_atc.text is None:
813 drug_atc = u''
814 else:
815 drug_atc = drug_atc.text.strip()
816
817
818 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True)
819 self.__imported_drugs.append(new_drug)
820 new_drug['is_fake_brand'] = False
821 new_drug['atc'] = drug_atc
822 new_drug['external_code_type'] = u'FreeDiams::%s::%s' % (db_id, drug_id_name)
823 new_drug['external_code'] = drug_uid
824 new_drug['pk_data_source'] = data_src_pk
825 new_drug.save()
826
827
828 fd_xml_components = fd_xml_drug.getiterator('Composition')
829 comp_data = {}
830 for fd_xml_comp in fd_xml_components:
831
832 data = {}
833
834 amount = regex.match(r'\d+[.,]{0,1}\d*', fd_xml_comp.attrib['strenght'].strip())
835 if amount is None:
836 amount = 99999
837 else:
838 amount = amount.group()
839 data['amount'] = amount
840
841 unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', fd_xml_comp.attrib['strenght'].strip()).strip()
842 if unit == u'':
843 unit = u'*?*'
844 data['unit'] = unit
845
846 molecule_name = fd_xml_comp.attrib['molecularName'].strip()
847 if molecule_name != u'':
848 create_consumable_substance(substance = molecule_name, atc = None, amount = amount, unit = unit)
849 data['molecule_name'] = molecule_name
850
851 inn_name = fd_xml_comp.attrib['inn'].strip()
852 if inn_name != u'':
853 create_consumable_substance(substance = inn_name, atc = None, amount = amount, unit = unit)
854 data['inn_name'] = molecule_name
855
856 if molecule_name == u'':
857 data['substance'] = inn_name
858 _log.info('linking INN [%s] rather than molecularName as component', inn_name)
859 else:
860 data['substance'] = molecule_name
861
862 data['nature'] = fd_xml_comp.attrib['nature'].strip()
863 data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
864
865
866 try:
867 old_data = comp_data[data['nature_ID']]
868
869 if old_data['inn_name'] == u'':
870 old_data['inn_name'] = data['inn_name']
871 if data['inn_name'] == u'':
872 data['inn_name'] = old_data['inn_name']
873
874 if old_data['molecule_name'] == u'':
875 old_data['molecule_name'] = data['molecule_name']
876 if data['molecule_name'] == u'':
877 data['molecule_name'] = old_data['molecule_name']
878
879
880
881
882
883 if data['nature'] == u'FT':
884 comp_data[data['nature_ID']] = data
885 else:
886 comp_data[data['nature_ID']] = old_data
887
888
889 except KeyError:
890 comp_data[data['nature_ID']] = data
891
892
893 for key, data in comp_data.items():
894 new_drug.add_component (
895 substance = data['substance'],
896 atc = None,
897 amount = data['amount'],
898 unit = data['unit']
899 )
900
902 """Support v8.2 CSV file interface only."""
903
904 version = u'Gelbe Liste/MMI v8.2 interface'
905 default_encoding = 'cp1250'
906 bdt_line_template = u'%03d6210#%s\r\n'
907 bdt_line_base_length = 8
908
910
911 cDrugDataSourceInterface.__init__(self)
912
913 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version)
914
915 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
916 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
917
918 paths = gmTools.gmPaths()
919
920 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt')
921 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp')
922 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt')
923 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
924
925 self.__data_date = None
926 self.__online_update_date = None
927
928
929
931
932 if self.__data_date is not None:
933 if not force_reload:
934 return {
935 'data': self.__data_date,
936 'online_update': self.__online_update_date
937 }
938 try:
939 open(self.data_date_filename, 'wb').close()
940 except StandardError:
941 _log.error('problem querying the MMI drug database for version information')
942 _log.exception('cannot create MMI drug database version file [%s]', self.data_date_filename)
943 self.__data_date = None
944 self.__online_update_date = None
945 return {
946 'data': u'?',
947 'online_update': u'?'
948 }
949
950 cmd = u'%s -DATADATE' % self.path_to_binary
951 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
952 _log.error('problem querying the MMI drug database for version information')
953 self.__data_date = None
954 self.__online_update_date = None
955 return {
956 'data': u'?',
957 'online_update': u'?'
958 }
959
960 try:
961 version_file = open(self.data_date_filename, 'rU')
962 except StandardError:
963 _log.error('problem querying the MMI drug database for version information')
964 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename)
965 self.__data_date = None
966 self.__online_update_date = None
967 return {
968 'data': u'?',
969 'online_update': u'?'
970 }
971
972 self.__data_date = version_file.readline()[:10]
973 self.__online_update_date = version_file.readline()[:10]
974 version_file.close()
975
976 return {
977 'data': self.__data_date,
978 'online_update': self.__online_update_date
979 }
980
982 versions = self.get_data_source_version()
983
984 return create_data_source (
985 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
986 short_name = u'GL/MMI',
987 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
988 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
989 language = u'de'
990 )
991
993
994 try:
995
996 open(self.default_csv_filename, 'wb').close()
997 except IOError:
998 _log.exception('problem creating GL/MMI <-> GNUmed exchange file')
999 return False
1000
1001 if cmd is None:
1002 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
1003
1004 if os.name == 'nt':
1005 blocking = True
1006 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
1007 _log.error('problem switching to the MMI drug database')
1008
1009
1010
1011
1012 return True
1013
1023
1025
1026 selected_drugs = self.__let_user_select_drugs()
1027 if selected_drugs is None:
1028 return None
1029
1030 new_substances = []
1031
1032 for drug in selected_drugs:
1033 atc = None
1034 if len(drug['wirkstoffe']) == 1:
1035 atc = drug['atc']
1036 for wirkstoff in drug['wirkstoffe']:
1037 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1038
1039 selected_drugs.close()
1040
1041 return new_substances
1042
1044
1045 selected_drugs = self.__let_user_select_drugs()
1046 if selected_drugs is None:
1047 return None
1048
1049 data_src_pk = self.create_data_source_entry()
1050
1051 new_drugs = []
1052 new_substances = []
1053
1054 for entry in selected_drugs:
1055
1056 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
1057
1058 if entry[u'hilfsmittel']:
1059 _log.debug('skipping Hilfsmittel')
1060 continue
1061
1062 if entry[u'erstattbares_medizinprodukt']:
1063 _log.debug('skipping sonstiges Medizinprodukt')
1064 continue
1065
1066
1067 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform'])
1068 if drug is None:
1069 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform'])
1070 new_drugs.append(drug)
1071
1072
1073 drug['is_fake_brand'] = False
1074 drug['atc'] = entry['atc']
1075 drug['external_code_type'] = u'DE-PZN'
1076 drug['external_code'] = entry['pzn']
1077 drug['fk_data_source'] = data_src_pk
1078 drug.save()
1079
1080
1081 atc = None
1082 if len(entry['wirkstoffe']) == 1:
1083 atc = entry['atc']
1084 for wirkstoff in entry['wirkstoffe']:
1085 drug.add_component(substance = wirkstoff, atc = atc)
1086
1087
1088 atc = None
1089 if len(entry['wirkstoffe']) == 1:
1090 atc = entry['atc']
1091 for wirkstoff in entry['wirkstoffe']:
1092 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1093
1094 return new_drugs, new_substances
1095
1124
1127
1146
1148
1150 cGelbeListeWindowsInterface.__init__(self)
1151
1152 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version)
1153
1154
1155 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
1156 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
1157
1158 paths = gmTools.gmPaths()
1159
1160 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
1161 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
1162 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
1163 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
1164
1166 """empirical CSV interface"""
1167
1170
1172
1173 try:
1174 csv_file = open(filename, 'rb')
1175 except:
1176 _log.exception('cannot access [%s]', filename)
1177 csv_file = None
1178
1179 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
1180
1181 if csv_file is None:
1182 return False
1183
1184 csv_lines = csv.DictReader (
1185 csv_file,
1186 fieldnames = field_names,
1187 delimiter = ';'
1188 )
1189
1190 for line in csv_lines:
1191 print "--------------------------------------------------------------------"[:31]
1192 for key in field_names:
1193 tmp = ('%s ' % key)[:30]
1194 print '%s: %s' % (tmp, line[key])
1195
1196 csv_file.close()
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209 drug_data_source_interfaces = {
1210 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
1211 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface,
1212 'FreeDiams (FR, US, CA, ZA)': cFreeDiamsInterface
1213 }
1214
1215
1216
1217
1218
1219 _SQL_get_consumable_substance = u"""
1220 SELECT *, xmin
1221 FROM ref.consumable_substance
1222 WHERE %s
1223 """
1224
1226
1227 _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s"
1228 _cmds_store_payload = [
1229 u"""UPDATE ref.consumable_substance SET
1230 description = %(description)s,
1231 atc_code = gm.nullify_empty_string(%(atc_code)s),
1232 amount = %(amount)s,
1233 unit = gm.nullify_empty_string(%(unit)s)
1234 WHERE
1235 pk = %(pk)s
1236 AND
1237 xmin = %(xmin)s
1238 AND
1239 -- must not currently be used with a patient directly
1240 NOT EXISTS (
1241 SELECT 1
1242 FROM clin.substance_intake
1243 WHERE
1244 fk_drug_component IS NULL
1245 AND
1246 fk_substance = %(pk)s
1247 LIMIT 1
1248 )
1249 AND
1250 -- must not currently be used with a patient indirectly, either
1251 NOT EXISTS (
1252 SELECT 1
1253 FROM clin.substance_intake
1254 WHERE
1255 fk_drug_component IS NOT NULL
1256 AND
1257 fk_drug_component = (
1258 SELECT r_ls2b.pk
1259 FROM ref.lnk_substance2brand r_ls2b
1260 WHERE fk_substance = %(pk)s
1261 )
1262 LIMIT 1
1263 )
1264 -- -- must not currently be used with a branded drug
1265 -- -- (but this would make it rather hard fixing branded drugs which contain only this substance)
1266 -- NOT EXISTS (
1267 -- SELECT 1
1268 -- FROM ref.lnk_substance2brand
1269 -- WHERE fk_substance = %(pk)s
1270 -- LIMIT 1
1271 -- )
1272 RETURNING
1273 xmin
1274 """
1275 ]
1276 _updatable_fields = [
1277 u'description',
1278 u'atc_code',
1279 u'amount',
1280 u'unit'
1281 ]
1282
1284 success, data = super(self.__class__, self).save_payload(conn = conn)
1285
1286 if not success:
1287 return (success, data)
1288
1289 if self._payload[self._idx['atc_code']] is not None:
1290 atc = self._payload[self._idx['atc_code']].strip()
1291 if atc != u'':
1292 gmATC.propagate_atc (
1293 substance = self._payload[self._idx['description']].strip(),
1294 atc = atc
1295 )
1296
1297 return (success, data)
1298
1299
1300
1302 cmd = u"""
1303 SELECT
1304 EXISTS (
1305 SELECT 1
1306 FROM clin.substance_intake
1307 WHERE
1308 fk_drug_component IS NULL
1309 AND
1310 fk_substance = %(pk)s
1311 LIMIT 1
1312 ) OR EXISTS (
1313 SELECT 1
1314 FROM clin.substance_intake
1315 WHERE
1316 fk_drug_component IS NOT NULL
1317 AND
1318 fk_drug_component IN (
1319 SELECT r_ls2b.pk
1320 FROM ref.lnk_substance2brand r_ls2b
1321 WHERE fk_substance = %(pk)s
1322 )
1323 LIMIT 1
1324 )"""
1325 args = {'pk': self.pk_obj}
1326
1327 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1328 return rows[0][0]
1329
1330 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
1331
1333 cmd = u"""
1334 SELECT EXISTS (
1335 SELECT 1
1336 FROM ref.lnk_substance2brand
1337 WHERE fk_substance = %(pk)s
1338 LIMIT 1
1339 )"""
1340 args = {'pk': self.pk_obj}
1341
1342 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1343 return rows[0][0]
1344
1345 is_drug_component = property(_get_is_drug_component, lambda x:x)
1346
1355
1357
1358 substance = substance
1359 if atc is not None:
1360 atc = atc.strip()
1361
1362 converted, amount = gmTools.input2decimal(amount)
1363 if not converted:
1364 raise ValueError('<amount> must be a number: %s (%s)', amount, type(amount))
1365
1366 args = {
1367 'desc': substance.strip(),
1368 'amount': amount,
1369 'unit': unit.strip(),
1370 'atc': atc
1371 }
1372 cmd = u"""
1373 SELECT pk FROM ref.consumable_substance
1374 WHERE
1375 lower(description) = lower(%(desc)s)
1376 AND
1377 amount = %(amount)s
1378 AND
1379 unit = %(unit)s
1380 """
1381 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1382
1383 if len(rows) == 0:
1384 cmd = u"""
1385 INSERT INTO ref.consumable_substance (description, atc_code, amount, unit) VALUES (
1386 %(desc)s,
1387 gm.nullify_empty_string(%(atc)s),
1388 %(amount)s,
1389 gm.nullify_empty_string(%(unit)s)
1390 ) RETURNING pk"""
1391 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
1392
1393 gmATC.propagate_atc(substance = substance, atc = atc)
1394
1395 return cConsumableSubstance(aPK_obj = rows[0]['pk'])
1396
1398 args = {'pk': substance}
1399 cmd = u"""
1400 DELETE FROM ref.consumable_substance
1401 WHERE
1402 pk = %(pk)s
1403 AND
1404
1405 -- must not currently be used with a patient
1406 NOT EXISTS (
1407 SELECT 1
1408 FROM clin.v_pat_substance_intake
1409 WHERE pk_substance = %(pk)s
1410 LIMIT 1
1411 )
1412 AND
1413
1414 -- must not currently be used with a branded drug
1415 NOT EXISTS (
1416 SELECT 1
1417 FROM ref.lnk_substance2brand
1418 WHERE fk_substance = %(pk)s
1419 LIMIT 1
1420 )"""
1421 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1422 return True
1423
1425
1426 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
1427 _query1 = u"""
1428 SELECT
1429 pk::text,
1430 (description || ' ' || amount || ' ' || unit) as subst
1431 FROM ref.consumable_substance
1432 WHERE description %(fragment_condition)s
1433 ORDER BY subst
1434 LIMIT 50"""
1435 _query2 = u"""
1436 SELECT
1437 pk::text,
1438 (description || ' ' || amount || ' ' || unit) as subst
1439 FROM ref.consumable_substance
1440 WHERE
1441 %(fragment_condition)s
1442 ORDER BY subst
1443 LIMIT 50"""
1444
1445
1447 """Return matches for aFragment at start of phrases."""
1448
1449 if cSubstanceMatchProvider._pattern.match(aFragment):
1450 self._queries = [cSubstanceMatchProvider._query2]
1451 fragment_condition = """description ILIKE %(desc)s
1452 AND
1453 amount::text ILIKE %(amount)s"""
1454 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1455 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1456 else:
1457 self._queries = [cSubstanceMatchProvider._query1]
1458 fragment_condition = u"ILIKE %(fragment)s"
1459 self._args['fragment'] = u"%s%%" % aFragment
1460
1461 return self._find_matches(fragment_condition)
1462
1464 """Return matches for aFragment at start of words inside phrases."""
1465
1466 if cSubstanceMatchProvider._pattern.match(aFragment):
1467 self._queries = [cSubstanceMatchProvider._query2]
1468
1469 desc = regex.sub(r'\s*\d+$', u'', aFragment)
1470 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
1471
1472 fragment_condition = """description ~* %(desc)s
1473 AND
1474 amount::text ILIKE %(amount)s"""
1475
1476 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
1477 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1478 else:
1479 self._queries = [cSubstanceMatchProvider._query1]
1480 fragment_condition = u"~* %(fragment)s"
1481 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
1482 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
1483
1484 return self._find_matches(fragment_condition)
1485
1487 """Return matches for aFragment as a true substring."""
1488
1489 if cSubstanceMatchProvider._pattern.match(aFragment):
1490 self._queries = [cSubstanceMatchProvider._query2]
1491 fragment_condition = """description ILIKE %(desc)s
1492 AND
1493 amount::text ILIKE %(amount)s"""
1494 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1495 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1496 else:
1497 self._queries = [cSubstanceMatchProvider._query1]
1498 fragment_condition = u"ILIKE %(fragment)s"
1499 self._args['fragment'] = u"%%%s%%" % aFragment
1500
1501 return self._find_matches(fragment_condition)
1502
1503 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
1504 """Represents a substance currently taken by a patient."""
1505
1506 _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s"
1507 _cmds_store_payload = [
1508 u"""UPDATE clin.substance_intake SET
1509 clin_when = %(started)s,
1510 discontinued = %(discontinued)s,
1511 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s),
1512 schedule = gm.nullify_empty_string(%(schedule)s),
1513 aim = gm.nullify_empty_string(%(aim)s),
1514 narrative = gm.nullify_empty_string(%(notes)s),
1515 intake_is_approved_of = %(intake_is_approved_of)s,
1516 fk_episode = %(pk_episode)s,
1517
1518 preparation = (
1519 case
1520 when %(pk_brand)s is NULL then %(preparation)s
1521 else NULL
1522 end
1523 )::text,
1524
1525 is_long_term = (
1526 case
1527 when (
1528 (%(is_long_term)s is False)
1529 and
1530 (%(duration)s is NULL)
1531 ) is True then null
1532 else %(is_long_term)s
1533 end
1534 )::boolean,
1535
1536 duration = (
1537 case
1538 when %(is_long_term)s is True then null
1539 else %(duration)s
1540 end
1541 )::interval
1542 WHERE
1543 pk = %(pk_substance_intake)s
1544 AND
1545 xmin = %(xmin_substance_intake)s
1546 RETURNING
1547 xmin as xmin_substance_intake
1548 """
1549 ]
1550 _updatable_fields = [
1551 u'started',
1552 u'discontinued',
1553 u'discontinue_reason',
1554 u'preparation',
1555 u'intake_is_approved_of',
1556 u'schedule',
1557 u'duration',
1558 u'aim',
1559 u'is_long_term',
1560 u'notes',
1561 u'pk_episode'
1562 ]
1563
1564 - def format(self, left_margin=0, date_format='%Y %B %d', one_line=True, allergy=None):
1565 if one_line:
1566 return self.format_as_one_line(left_margin = left_margin, date_format = date_format)
1567
1568 return self.format_as_multiple_lines(left_margin = left_margin, date_format = date_format, allergy = allergy)
1569
1571
1572 if self._payload[self._idx['duration']] is None:
1573 duration = gmTools.bool2subst (
1574 self._payload[self._idx['is_long_term']],
1575 _('long-term'),
1576 _('short-term'),
1577 _('?short-term')
1578 )
1579 else:
1580 duration = gmDateTime.format_interval (
1581 self._payload[self._idx['duration']],
1582 accuracy_wanted = gmDateTime.acc_days
1583 )
1584
1585 line = u'%s%s (%s %s): %s %s%s %s (%s)' % (
1586 u' ' * left_margin,
1587 self._payload[self._idx['started']].strftime(date_format),
1588 gmTools.u_right_arrow,
1589 duration,
1590 self._payload[self._idx['substance']],
1591 self._payload[self._idx['amount']],
1592 self._payload[self._idx['unit']],
1593 self._payload[self._idx['preparation']],
1594 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing'))
1595 )
1596
1597 return line
1598
1600
1601 txt = _('Substance intake entry (%s, %s) [#%s] \n') % (
1602 gmTools.bool2subst (
1603 boolean = self._payload[self._idx['is_currently_active']],
1604 true_return = gmTools.bool2subst (
1605 boolean = self._payload[self._idx['seems_inactive']],
1606 true_return = _('active, needs check'),
1607 false_return = _('active'),
1608 none_return = _('assumed active')
1609 ),
1610 false_return = _('inactive')
1611 ),
1612 gmTools.bool2subst (
1613 boolean = self._payload[self._idx['intake_is_approved_of']],
1614 true_return = _('approved'),
1615 false_return = _('unapproved')
1616 ),
1617 self._payload[self._idx['pk_substance_intake']]
1618 )
1619
1620 if allergy is not None:
1621 certainty = gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'))
1622 txt += u'\n'
1623 txt += u' !! ---- Cave ---- !!\n'
1624 txt += u' %s (%s): %s (%s)\n' % (
1625 allergy['l10n_type'],
1626 certainty,
1627 allergy['descriptor'],
1628 gmTools.coalesce(allergy['reaction'], u'')[:40]
1629 )
1630 txt += u'\n'
1631
1632 txt += u' ' + _('Substance: %s [#%s]\n') % (self._payload[self._idx['substance']], self._payload[self._idx['pk_substance']])
1633 txt += u' ' + _('Preparation: %s\n') % self._payload[self._idx['preparation']]
1634 txt += u' ' + _('Amount per dose: %s %s') % (self._payload[self._idx['amount']], self._payload[self._idx['unit']])
1635 if self.ddd is not None:
1636 txt += u' (DDD: %s %s)' % (self.ddd['ddd'], self.ddd['unit'])
1637 txt += u'\n'
1638 txt += gmTools.coalesce(self._payload[self._idx['atc_substance']], u'', _(' ATC (substance): %s\n'))
1639
1640 txt += u'\n'
1641
1642 txt += gmTools.coalesce (
1643 self._payload[self._idx['brand']],
1644 u'',
1645 _(' Brand name: %%s [#%s]\n') % self._payload[self._idx['pk_brand']]
1646 )
1647 txt += gmTools.coalesce(self._payload[self._idx['atc_brand']], u'', _(' ATC (brand): %s\n'))
1648
1649 txt += u'\n'
1650
1651 txt += gmTools.coalesce(self._payload[self._idx['schedule']], u'', _(' Regimen: %s\n'))
1652
1653 if self._payload[self._idx['is_long_term']]:
1654 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity)
1655 else:
1656 if self._payload[self._idx['duration']] is None:
1657 duration = u''
1658 else:
1659 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(self._payload[self._idx['duration']], gmDateTime.acc_days))
1660
1661 txt += _(' Started %s%s%s\n') % (
1662 gmDateTime.pydt_strftime (
1663 self._payload[self._idx['started']],
1664 format = date_format,
1665 accuracy = gmDateTime.acc_days
1666 ),
1667 duration,
1668 gmTools.bool2subst(self._payload[self._idx['is_long_term']], _(' (long-term)'), _(' (short-term)'), u'')
1669 )
1670
1671 if self._payload[self._idx['discontinued']] is not None:
1672 txt += _(' Discontinued %s\n') % (
1673 gmDateTime.pydt_strftime (
1674 self._payload[self._idx['discontinued']],
1675 format = date_format,
1676 accuracy = gmDateTime.acc_days
1677 )
1678 )
1679 txt += _(' Reason: %s\n') % self._payload[self._idx['discontinue_reason']]
1680
1681 txt += u'\n'
1682
1683 txt += gmTools.coalesce(self._payload[self._idx['aim']], u'', _(' Aim: %s\n'))
1684 txt += gmTools.coalesce(self._payload[self._idx['episode']], u'', _(' Episode: %s\n'))
1685 txt += gmTools.coalesce(self._payload[self._idx['health_issue']], u'', _(' Health issue: %s\n'))
1686 txt += gmTools.coalesce(self._payload[self._idx['notes']], u'', _(' Advice: %s\n'))
1687
1688 txt += u'\n'
1689
1690 txt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % {
1691 'row_ver': self._payload[self._idx['row_version']],
1692 'mod_when': gmDateTime.pydt_strftime(self._payload[self._idx['modified_when']]),
1693 'mod_by': self._payload[self._idx['modified_by']]
1694 }
1695
1696 return txt
1697
1698 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
1699 allg = gmAllergy.create_allergy (
1700 allergene = self._payload[self._idx['substance']],
1701 allg_type = allergy_type,
1702 episode_id = self._payload[self._idx['pk_episode']],
1703 encounter_id = encounter_id
1704 )
1705 allg['substance'] = gmTools.coalesce (
1706 self._payload[self._idx['brand']],
1707 self._payload[self._idx['substance']]
1708 )
1709 allg['reaction'] = self._payload[self._idx['discontinue_reason']]
1710 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']])
1711 if self._payload[self._idx['external_code_brand']] is not None:
1712 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']])
1713
1714 if self._payload[self._idx['pk_brand']] is None:
1715 allg['generics'] = self._payload[self._idx['substance']]
1716 else:
1717 comps = [ c['substance'] for c in self.containing_drug.components ]
1718 if len(comps) == 0:
1719 allg['generics'] = self._payload[self._idx['substance']]
1720 else:
1721 allg['generics'] = u'; '.join(comps)
1722
1723 allg.save()
1724 return allg
1725
1726
1727
1728 - def _get_ddd(self):
1729
1730 try: self.__ddd
1731 except AttributeError: self.__ddd = None
1732
1733 if self.__ddd is not None:
1734 return self.__ddd
1735
1736 if self._payload[self._idx['atc_substance']] is not None:
1737 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']])
1738 if len(ddd) != 0:
1739 self.__ddd = ddd[0]
1740 else:
1741 if self._payload[self._idx['atc_brand']] is not None:
1742 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']])
1743 if len(ddd) != 0:
1744 self.__ddd = ddd[0]
1745
1746 return self.__ddd
1747
1748 ddd = property(_get_ddd, lambda x:x)
1749
1751 drug = self.containing_drug
1752
1753 if drug is None:
1754 return None
1755
1756 return drug.external_code
1757
1758 external_code = property(_get_external_code, lambda x:x)
1759
1761 drug = self.containing_drug
1762
1763 if drug is None:
1764 return None
1765
1766 return drug.external_code_type
1767
1768 external_code_type = property(_get_external_code_type, lambda x:x)
1769
1771 if self._payload[self._idx['pk_brand']] is None:
1772 return None
1773
1774 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1775
1776 containing_drug = property(_get_containing_drug, lambda x:x)
1777
1779 tests = [
1780
1781 ' 1-1-1-1 ',
1782
1783 '1-1-1-1',
1784 '22-1-1-1',
1785 '1/3-1-1-1',
1786 '/4-1-1-1'
1787 ]
1788 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}$"
1789 for test in tests:
1790 print test.strip(), ":", regex.match(pattern, test.strip())
1791
1793 args = {'comp': pk_component, 'subst': pk_substance, 'pat': pk_identity}
1794
1795 where_clause = u"""
1796 fk_encounter IN (
1797 SELECT pk FROM clin.encounter WHERE fk_patient = %(pat)s
1798 )
1799 AND
1800 """
1801
1802 if pk_substance is not None:
1803 where_clause += u'fk_substance = %(subst)s'
1804 if pk_component is not None:
1805 where_clause += u'fk_drug_component = %(comp)s'
1806
1807 cmd = u"""SELECT exists (
1808 SELECT 1 FROM clin.substance_intake
1809 WHERE
1810 %s
1811 LIMIT 1
1812 )""" % where_clause
1813
1814 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1815 return rows[0][0]
1816
1817 -def create_substance_intake(pk_substance=None, pk_component=None, preparation=None, encounter=None, episode=None):
1818
1819 args = {
1820 'enc': encounter,
1821 'epi': episode,
1822 'comp': pk_component,
1823 'subst': pk_substance,
1824 'prep': preparation
1825 }
1826
1827 if pk_component is None:
1828 cmd = u"""
1829 INSERT INTO clin.substance_intake (
1830 fk_encounter,
1831 fk_episode,
1832 intake_is_approved_of,
1833 fk_substance,
1834 preparation
1835 ) VALUES (
1836 %(enc)s,
1837 %(epi)s,
1838 False,
1839 %(subst)s,
1840 %(prep)s
1841 )
1842 RETURNING pk"""
1843 else:
1844 cmd = u"""
1845 INSERT INTO clin.substance_intake (
1846 fk_encounter,
1847 fk_episode,
1848 intake_is_approved_of,
1849 fk_drug_component
1850 ) VALUES (
1851 %(enc)s,
1852 %(epi)s,
1853 False,
1854 %(comp)s
1855 )
1856 RETURNING pk"""
1857
1858 try:
1859 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
1860 except gmPG2.dbapi.InternalError, e:
1861 if e.pgerror is None:
1862 raise
1863 if 'prevent_duplicate_component' in e.pgerror:
1864 _log.exception('will not create duplicate substance intake entry')
1865 _log.error(e.pgerror)
1866 return None
1867 raise
1868
1869 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
1870
1874
1908
1909
1986
1987 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s'
1988
1990
1991 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s'
1992 _cmds_store_payload = [
1993 u"""UPDATE ref.lnk_substance2brand SET
1994 fk_brand = %(pk_brand)s,
1995 fk_substance = %(pk_consumable_substance)s
1996 WHERE
1997 NOT EXISTS (
1998 SELECT 1
1999 FROM clin.substance_intake
2000 WHERE fk_drug_component = %(pk_component)s
2001 LIMIT 1
2002 )
2003 AND
2004 pk = %(pk_component)s
2005 AND
2006 xmin = %(xmin_lnk_substance2brand)s
2007 RETURNING
2008 xmin AS xmin_lnk_substance2brand
2009 """
2010 ]
2011 _updatable_fields = [
2012 u'pk_brand',
2013 u'pk_consumable_substance'
2014 ]
2015
2016
2017
2019 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
2020
2021 containing_drug = property(_get_containing_drug, lambda x:x)
2022
2024 return self._payload[self._idx['is_in_use']]
2025
2026 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
2027
2030
2031 substance = property(_get_substance, lambda x:x)
2032
2037
2039
2040 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
2041 _query_desc_only = u"""
2042 SELECT DISTINCT ON (component)
2043 pk_component,
2044 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
2045 AS component
2046 FROM ref.v_drug_components
2047 WHERE
2048 substance %(fragment_condition)s
2049 OR
2050 brand %(fragment_condition)s
2051 ORDER BY component
2052 LIMIT 50"""
2053 _query_desc_and_amount = u"""
2054
2055 SELECT DISTINCT ON (component)
2056 pk_component,
2057 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
2058 AS component
2059 FROM ref.v_drug_components
2060 WHERE
2061 %(fragment_condition)s
2062 ORDER BY component
2063 LIMIT 50"""
2064
2066 """Return matches for aFragment at start of phrases."""
2067
2068 if cDrugComponentMatchProvider._pattern.match(aFragment):
2069 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2070 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
2071 AND
2072 amount::text ILIKE %(amount)s"""
2073 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
2074 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2075 else:
2076 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2077 fragment_condition = u"ILIKE %(fragment)s"
2078 self._args['fragment'] = u"%s%%" % aFragment
2079
2080 return self._find_matches(fragment_condition)
2081
2083 """Return matches for aFragment at start of words inside phrases."""
2084
2085 if cDrugComponentMatchProvider._pattern.match(aFragment):
2086 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2087
2088 desc = regex.sub(r'\s*\d+$', u'', aFragment)
2089 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
2090
2091 fragment_condition = """(substance ~* %(desc)s OR brand ~* %(desc)s)
2092 AND
2093 amount::text ILIKE %(amount)s"""
2094
2095 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
2096 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2097 else:
2098 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2099 fragment_condition = u"~* %(fragment)s"
2100 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
2101 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
2102
2103 return self._find_matches(fragment_condition)
2104
2106 """Return matches for aFragment as a true substring."""
2107
2108 if cDrugComponentMatchProvider._pattern.match(aFragment):
2109 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2110 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
2111 AND
2112 amount::text ILIKE %(amount)s"""
2113 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
2114 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2115 else:
2116 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2117 fragment_condition = u"ILIKE %(fragment)s"
2118 self._args['fragment'] = u"%%%s%%" % aFragment
2119
2120 return self._find_matches(fragment_condition)
2121
2122
2124 """Represents a drug as marketed by a manufacturer."""
2125
2126 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s"
2127 _cmds_store_payload = [
2128 u"""UPDATE ref.branded_drug SET
2129 description = %(brand)s,
2130 preparation = %(preparation)s,
2131 atc_code = gm.nullify_empty_string(%(atc)s),
2132 external_code = gm.nullify_empty_string(%(external_code)s),
2133 external_code_type = gm.nullify_empty_string(%(external_code_type)s),
2134 is_fake = %(is_fake_brand)s,
2135 fk_data_source = %(pk_data_source)s
2136 WHERE
2137 pk = %(pk_brand)s
2138 AND
2139 xmin = %(xmin_branded_drug)s
2140 RETURNING
2141 xmin AS xmin_branded_drug
2142 """
2143 ]
2144 _updatable_fields = [
2145 u'brand',
2146 u'preparation',
2147 u'atc',
2148 u'is_fake_brand',
2149 u'external_code',
2150 u'external_code_type',
2151 u'pk_data_source'
2152 ]
2153
2155 success, data = super(self.__class__, self).save_payload(conn = conn)
2156
2157 if not success:
2158 return (success, data)
2159
2160 if self._payload[self._idx['atc']] is not None:
2161 atc = self._payload[self._idx['atc']].strip()
2162 if atc != u'':
2163 gmATC.propagate_atc (
2164 substance = self._payload[self._idx['brand']].strip(),
2165 atc = atc
2166 )
2167
2168 return (success, data)
2169
2171
2172 if self.is_in_use_by_patients:
2173 return False
2174
2175 args = {'brand': self._payload[self._idx['pk_brand']]}
2176
2177 queries = [{'cmd': u"DELETE FROM ref.lnk_substance2brand WHERE fk_brand = %(brand)s", 'args': args}]
2178 cmd = u'INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) VALUES (%%(brand)s, %s)'
2179 for s in substances:
2180 queries.append({'cmd': cmd % s['pk'], 'args': args})
2181
2182 gmPG2.run_rw_queries(queries = queries)
2183 self.refetch_payload()
2184
2185 return True
2186
2187 - def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
2188
2189 args = {
2190 'brand': self.pk_obj,
2191 'subst': substance,
2192 'atc': atc,
2193 'pk_subst': pk_substance
2194 }
2195
2196 if pk_substance is None:
2197 consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit)
2198 args['pk_subst'] = consumable['pk']
2199
2200
2201 cmd = u"""
2202 SELECT pk_component
2203 FROM ref.v_drug_components
2204 WHERE
2205 pk_brand = %(brand)s
2206 AND
2207 ((
2208 (lower(substance) = lower(%(subst)s))
2209 OR
2210 (lower(atc_substance) = lower(%(atc)s))
2211 OR
2212 (pk_consumable_substance = %(pk_subst)s)
2213 ) IS TRUE)
2214 """
2215 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2216
2217 if len(rows) > 0:
2218 return
2219
2220
2221 cmd = u"""
2222 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance)
2223 VALUES (%(brand)s, %(pk_subst)s)
2224 """
2225 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2226 self.refetch_payload()
2227
2229 if len(self._payload[self._idx['components']]) == 1:
2230 _log.error('cannot remove the only component of a drug')
2231 return False
2232
2233 args = {'brand': self.pk_obj, 'comp': substance}
2234 cmd = u"""
2235 DELETE FROM ref.lnk_substance2brand
2236 WHERE
2237 fk_brand = %(brand)s
2238 AND
2239 fk_substance = %(comp)s
2240 AND
2241 NOT EXISTS (
2242 SELECT 1
2243 FROM clin.substance_intake
2244 WHERE fk_drug_component = %(comp)s
2245 LIMIT 1
2246 )
2247 """
2248 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2249 self.refetch_payload()
2250
2251 return True
2252
2253
2254
2256 if self._payload[self._idx['external_code']] is None:
2257 return None
2258
2259 return self._payload[self._idx['external_code']]
2260
2261 external_code = property(_get_external_code, lambda x:x)
2262
2264
2265
2266 if self._payload[self._idx['external_code_type']] is None:
2267 return None
2268
2269 return self._payload[self._idx['external_code_type']]
2270
2271 external_code_type = property(_get_external_code_type, lambda x:x)
2272
2274 cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s'
2275 args = {'brand': self._payload[self._idx['pk_brand']]}
2276 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2277 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
2278
2279 components = property(_get_components, lambda x:x)
2280
2282 if self._payload[self._idx['pk_substances']] is None:
2283 return []
2284 cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s'
2285 args = {'pks': tuple(self._payload[self._idx['pk_substances']])}
2286 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2287 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
2288
2289 components_as_substances = property(_get_components_as_substances, lambda x:x)
2290
2292 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)'
2293 args = {'fk_brand': self._payload[self._idx['pk_brand']]}
2294 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2295 return rows[0][0]
2296
2297 is_vaccine = property(_get_is_vaccine, lambda x:x)
2298
2300 cmd = u"""
2301 SELECT EXISTS (
2302 SELECT 1
2303 FROM clin.substance_intake
2304 WHERE
2305 fk_drug_component IS NOT NULL
2306 AND
2307 fk_drug_component IN (
2308 SELECT r_ls2b.pk
2309 FROM ref.lnk_substance2brand r_ls2b
2310 WHERE fk_brand = %(pk)s
2311 )
2312 LIMIT 1
2313 )"""
2314 args = {'pk': self.pk_obj}
2315
2316 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2317 return rows[0][0]
2318
2319 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
2320
2322 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description'
2323 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
2324 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
2325
2327 args = {'brand': brand_name, 'prep': preparation}
2328
2329 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)'
2330 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2331
2332 if len(rows) == 0:
2333 return None
2334
2335 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2336
2338
2339 if preparation is None:
2340 preparation = _('units')
2341
2342 if preparation.strip() == u'':
2343 preparation = _('units')
2344
2345 if return_existing:
2346 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation)
2347 if drug is not None:
2348 return drug
2349
2350 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk'
2351 args = {'brand': brand_name, 'prep': preparation}
2352 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
2353
2354 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2355
2357 queries = []
2358 args = {'pk': brand}
2359
2360
2361 cmd = u"""
2362 DELETE FROM ref.lnk_substance2brand
2363 WHERE
2364 fk_brand = %(pk)s
2365 AND
2366 NOT EXISTS (
2367 SELECT 1
2368 FROM clin.v_pat_substance_intake
2369 WHERE pk_brand = %(pk)s
2370 LIMIT 1
2371 )
2372 """
2373 queries.append({'cmd': cmd, 'args': args})
2374
2375
2376 cmd = u"""
2377 DELETE FROM ref.branded_drug
2378 WHERE
2379 pk = %(pk)s
2380 AND
2381 NOT EXISTS (
2382 SELECT 1
2383 FROM clin.v_pat_substance_intake
2384 WHERE pk_brand = %(pk)s
2385 LIMIT 1
2386 )
2387 """
2388 queries.append({'cmd': cmd, 'args': args})
2389
2390 gmPG2.run_rw_queries(queries = queries)
2391
2392
2393
2394 if __name__ == "__main__":
2395
2396 if len(sys.argv) < 2:
2397 sys.exit()
2398
2399 if sys.argv[1] != 'test':
2400 sys.exit()
2401
2402 from Gnumed.pycommon import gmLog2
2403 from Gnumed.pycommon import gmI18N
2404 from Gnumed.business import gmPerson
2405
2406 gmI18N.activate_locale()
2407
2408
2414
2416 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
2417 for drug in mmi_file:
2418 print "-------------"
2419 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2420 for stoff in drug['wirkstoffe']:
2421 print " Wirkstoff:", stoff
2422 raw_input()
2423 if mmi_file.has_unknown_fields is not None:
2424 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key
2425 for key in mmi_file.csv_fieldnames:
2426 print key, '->', drug[key]
2427 raw_input()
2428 mmi_file.close()
2429
2433
2435 mmi = cGelbeListeWineInterface()
2436 mmi_file = mmi.__let_user_select_drugs()
2437 for drug in mmi_file:
2438 print "-------------"
2439 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2440 for stoff in drug['wirkstoffe']:
2441 print " Wirkstoff:", stoff
2442 print drug
2443 mmi_file.close()
2444
2448
2450 mmi = cGelbeListeInterface()
2451 print mmi
2452 print "interface definition:", mmi.version
2453
2454 diclofenac = '7587712'
2455 phenprocoumon = '4421744'
2456 mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
2457
2458
2459
2466
2472
2473
2474
2476 drug = create_substance_intake (
2477 pk_component = 2,
2478 encounter = 1,
2479 episode = 1
2480 )
2481 print drug
2482
2487
2491
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513 test_drug2renal_insufficiency_url()
2514
2515