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 not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
357 _log.error('problem switching to the FreeDiams drug database')
358 return False
359
360 if blocking == True:
361 self.import_fd2gm_file_as_drugs()
362
363 return True
364
367
369 if substance_intakes is None:
370 return
371 if len(substance_intakes) < 2:
372 return
373
374 self.__create_prescription_file(substance_intakes = substance_intakes)
375 self.switch_to_frontend(mode = 'interactions', blocking = False)
376
378 if substance_intake is None:
379 return
380
381 self.__create_prescription_file(substance_intakes = [substance_intake])
382 self.switch_to_frontend(mode = 'interactions', blocking = False)
383
386
387 - def prescribe(self, substance_intakes=None):
388 if substance_intakes is None:
389 if not self.__export_latest_prescription():
390 self.__create_prescription_file()
391 else:
392 self.__create_prescription_file(substance_intakes = substance_intakes)
393
394 self.switch_to_frontend(mode = 'prescription', blocking = True)
395 self.import_fd2gm_file_as_prescription()
396
397 return self.__imported_drugs
398
399
400
402
403 if self.path_to_binary is not None:
404 return True
405
406 found, cmd = gmShellAPI.find_first_binary(binaries = [
407 r'/usr/bin/freediams',
408 r'freediams',
409 r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams',
410 r'C:\Program Files\FreeDiams\freediams.exe',
411 r'c:\programs\freediams\freediams.exe',
412 r'freediams.exe'
413 ])
414
415 if found:
416 self.path_to_binary = cmd
417 return True
418
419 try:
420 self.custom_path_to_binary
421 except AttributeError:
422 _log.error('cannot find FreeDiams binary, no custom path set')
423 return False
424
425 if self.custom_path_to_binary is None:
426 _log.error('cannot find FreeDiams binary')
427 return False
428
429 found, cmd = gmShellAPI.detect_external_binary(binary = self.custom_path_to_binary)
430 if found:
431 self.path_to_binary = cmd
432 return True
433
434 _log.error('cannot find FreeDiams binary')
435 return False
436
438
439 if self.patient is None:
440 _log.debug('cannot export latest FreeDiams prescriptions w/o patient')
441 return False
442
443 docs = self.patient.get_document_folder()
444 prescription = docs.get_latest_freediams_prescription()
445 if prescription is None:
446 _log.debug('no FreeDiams prescription available')
447 return False
448
449 for part in prescription.parts:
450 if part['filename'] == u'freediams-prescription.xml':
451 if part.export_to_file(filename = self.__fd2gm_filename) is not None:
452 return True
453
454 _log.error('cannot export latest FreeDiams prescription to XML file')
455
456 return False
457
459 """FreeDiams calls this exchange-out or prescription file.
460
461 CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel).
462 CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS.
463 AFSSAPS is the French FDA.
464
465 CIP stands for Unique Presentation Identifier (eg 30 pills plaq)
466 CIP if you want to specify the packaging of the drug (30 pills
467 thermoformed tablet...) -- actually not really usefull for french
468 doctors.
469 # .external_code_type: u'FR-CIS'
470 # .external_cod: the CIS value
471
472 OnlyForTest:
473 OnlyForTest drugs will be processed by the IA Engine but
474 not printed (regardless of FreeDiams mode). They are shown
475 in gray in the prescription view.
476
477 Select-only is a mode where FreeDiams creates a list of drugs
478 not a full prescription. In this list, users can add ForTestOnly
479 drug if they want to
480 1. print the list without some drugs
481 2. but including these drugs in the IA engine calculation
482
483 Select-Only mode does not have any relation with the ForTestOnly drugs.
484
485 IsTextual:
486 What is the use and significance of the
487 <IsTextual>true/false</IsTextual>
488 flag when both <DrugName> and <TextualDrugName> exist ?
489
490 This tag must be setted even if it sounds like a duplicated
491 data. This tag is needed inside FreeDiams code.
492
493 INN:
494 GNUmed will pass the substance in <TextualDrugName
495 and will also pass <INN>True</INN>.
496
497 Eric: Nop, this is not usefull because pure textual drugs
498 are not processed but just shown.
499 """
500
501 open(self.__fd2gm_filename, 'wb').close()
502
503
504 if substance_intakes is None:
505 if self.patient is None:
506 _log.warning('cannot create prescription file because there is neither a patient nor a substance intake list')
507
508 return False
509 emr = self.patient.get_emr()
510 substance_intakes = emr.get_current_substance_intake (
511 include_inactive = False,
512 include_unapproved = True
513 )
514
515 drug_snippets = []
516
517
518 fd_intakes = [ i for i in substance_intakes if (
519 (i['intake_is_approved_of'] is True)
520 and
521 (i['external_code_type_brand'] is not None)
522 and
523 (i['external_code_type_brand'].startswith(u'FreeDiams::'))
524 )]
525
526 intakes_pooled_by_brand = {}
527 for intake in fd_intakes:
528
529
530 intakes_pooled_by_brand[intake['brand']] = intake
531 del fd_intakes
532
533 drug_snippet = u"""<Prescription>
534 <IsTextual>False</IsTextual>
535 <DrugName>%s</DrugName>
536 <Drug_UID>%s</Drug_UID>
537 <Drug_UID_type>%s</Drug_UID_type> <!-- not yet supported by FreeDiams -->
538 </Prescription>"""
539
540 last_db_id = u'CA_HCDPD'
541 for intake in intakes_pooled_by_brand.values():
542 last_db_id = gmTools.xml_escape_string(text = intake['external_code_type_brand'].replace(u'FreeDiams::', u'').split(u'::')[0])
543 drug_snippets.append(drug_snippet % (
544 gmTools.xml_escape_string(text = intake['brand'].strip()),
545 gmTools.xml_escape_string(text = intake['external_code_brand'].strip()),
546 last_db_id
547 ))
548
549
550 non_fd_intakes = [ i for i in substance_intakes if (
551 (i['intake_is_approved_of'] is True)
552 and (
553 (i['external_code_type_brand'] is None)
554 or
555 (not i['external_code_type_brand'].startswith(u'FreeDiams::'))
556 )
557 )]
558
559 non_fd_brand_intakes = [ i for i in non_fd_intakes if i['brand'] is not None ]
560 non_fd_substance_intakes = [ i for i in non_fd_intakes if i['brand'] is None ]
561 del non_fd_intakes
562
563 drug_snippet = u"""<Prescription>
564 <IsTextual>True</IsTextual>
565 <TextualDrugName>%s</TextualDrugName>
566 </Prescription>"""
567
568 for intake in non_fd_substance_intakes:
569 drug_name = u'%s %s%s (%s)%s' % (
570 intake['substance'],
571 intake['amount'],
572 intake['unit'],
573 intake['preparation'],
574 gmTools.coalesce(intake['schedule'], u'', _('\n Take: %s'))
575 )
576 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
577
578 intakes_pooled_by_brand = {}
579 for intake in non_fd_brand_intakes:
580 brand = u'%s %s' % (intake['brand'], intake['preparation'])
581 try:
582 intakes_pooled_by_brand[brand].append(intake)
583 except KeyError:
584 intakes_pooled_by_brand[brand] = [intake]
585
586 for brand, comps in intakes_pooled_by_brand.iteritems():
587 drug_name = u'%s\n' % brand
588 for comp in comps:
589 drug_name += u' %s %s%s\n' % (
590 comp['substance'],
591 comp['amount'],
592 comp['unit']
593 )
594 if comps[0]['schedule'] is not None:
595 drug_name += gmTools.coalesce(comps[0]['schedule'], u'', _('Take: %s'))
596 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
597
598
599 xml = u"""<?xml version = "1.0" encoding = "UTF-8"?>
600
601 <FreeDiams>
602 <DrugsDatabaseName>%s</DrugsDatabaseName>
603 <FullPrescription version="0.5.0">
604
605 %s
606
607 </FullPrescription>
608 </FreeDiams>
609 """
610
611 xml_file = codecs.open(self.__fd2gm_filename, 'wb', 'utf8')
612 xml_file.write(xml % (
613 last_db_id,
614 u'\n\t\t'.join(drug_snippets)
615 ))
616 xml_file.close()
617
618 return True
619
621
622 if mode == 'interactions':
623 mode = u'select-only'
624 elif mode == 'prescription':
625 mode = u'prescriber'
626 else:
627 mode = u'select-only'
628
629 xml_file = codecs.open(self.__gm2fd_filename, 'wb', 'utf8')
630
631 xml = u"""<?xml version="1.0" encoding="UTF-8"?>
632
633 <FreeDiams_In version="0.5.0">
634 <EMR name="GNUmed" uid="unused"/>
635 <ConfigFile value="%s"/>
636 <ExchangeOut value="%s" format="xml"/>
637 <!-- <DrugsDatabase uid="can be set to a specific DB"/> -->
638 <Ui editmode="%s" blockPatientDatas="1"/>
639 %%s
640 </FreeDiams_In>
641
642 <!--
643 # FIXME: search by LOINC code and add (as soon as supported by FreeDiams ...)
644 <Creatinine value="12" unit="mg/l or mmol/l"/>
645 <Weight value="70" unit="kg or pd" />
646 <Height value="170" unit="cm or "/>
647 <ICD10 value="J11.0;A22;Z23"/>
648 -->
649 """ % (
650 self.__fd4gm_config_file,
651 self.__fd2gm_filename,
652 mode
653 )
654
655 if self.patient is None:
656 xml_file.write(xml % u'')
657 xml_file.close()
658 return
659
660 name = self.patient.get_active_name()
661 if self.patient['dob'] is None:
662 dob = u''
663 else:
664 dob = self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format)
665
666 emr = self.patient.get_emr()
667 allgs = emr.get_allergies()
668 atc_allgs = [
669 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'allergy'))
670 ]
671 atc_sens = [
672 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'sensitivity'))
673 ]
674 inn_allgs = [
675 a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'allergy'))
676 ]
677 inn_sens = [
678 a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'sensitivity'))
679 ]
680
681
682
683 uid_allgs = [
684 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'allergy'))
685 ]
686 uid_sens = [
687 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'sensitivity'))
688 ]
689
690 patient_xml = u"""<Patient>
691 <Identity
692 lastnames="%s"
693 firstnames="%s"
694 uid="%s"
695 dob="%s"
696 gender="%s"
697 />
698 <ATCAllergies value="%s"/>
699 <ATCIntolerances value="%s"/>
700
701 <InnAllergies value="%s"/>
702 <InnIntolerances value="%s"/>
703
704 <DrugsUidAllergies value="%s"/>
705 <DrugsUidIntolerances value="%s"/>
706 </Patient>
707 """ % (
708 gmTools.xml_escape_string(text = name['lastnames']),
709 gmTools.xml_escape_string(text = name['firstnames']),
710 self.patient.ID,
711 dob,
712 cFreeDiamsInterface.map_gender2mf[self.patient['gender']],
713 gmTools.xml_escape_string(text = u';'.join(atc_allgs)),
714 gmTools.xml_escape_string(text = u';'.join(atc_sens)),
715 gmTools.xml_escape_string(text = u';'.join(inn_allgs)),
716 gmTools.xml_escape_string(text = u';'.join(inn_sens)),
717 gmTools.xml_escape_string(text = u';'.join(uid_allgs)),
718 gmTools.xml_escape_string(text = u';'.join(uid_sens))
719 )
720
721 xml_file.write(xml % patient_xml)
722 xml_file.close()
723
776
778 """
779 If returning textual prescriptions (say, drugs which FreeDiams
780 did not know) then "IsTextual" will be True and UID will be -1.
781 """
782 if filename is None:
783 filename = self.__fd2gm_filename
784
785
786
787 fd2gm_xml = etree.ElementTree()
788 fd2gm_xml.parse(filename)
789
790 data_src_pk = self.create_data_source_entry()
791
792 db_def = fd2gm_xml.find('DrugsDatabaseName')
793 db_id = db_def.text.strip()
794 drug_id_name = db_def.attrib['drugUidName']
795 fd_xml_drug_entries = fd2gm_xml.findall('FullPrescription/Prescription')
796
797 self.__imported_drugs = []
798 for fd_xml_drug in fd_xml_drug_entries:
799 drug_uid = fd_xml_drug.find('Drug_UID').text.strip()
800 if drug_uid == u'-1':
801 _log.debug('skipping textual drug')
802 continue
803 drug_name = fd_xml_drug.find('DrugName').text.replace(', )', ')').strip()
804 drug_form = fd_xml_drug.find('DrugForm').text.strip()
805 drug_atc = fd_xml_drug.find('DrugATC')
806 if drug_atc is None:
807 drug_atc = u''
808 else:
809 if drug_atc.text is None:
810 drug_atc = u''
811 else:
812 drug_atc = drug_atc.text.strip()
813
814
815 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True)
816 self.__imported_drugs.append(new_drug)
817 new_drug['is_fake_brand'] = False
818 new_drug['atc'] = drug_atc
819 new_drug['external_code_type'] = u'FreeDiams::%s::%s' % (db_id, drug_id_name)
820 new_drug['external_code'] = drug_uid
821 new_drug['pk_data_source'] = data_src_pk
822 new_drug.save()
823
824
825 fd_xml_components = fd_xml_drug.getiterator('Composition')
826 comp_data = {}
827 for fd_xml_comp in fd_xml_components:
828
829 data = {}
830
831 amount = regex.match(r'\d+[.,]{0,1}\d*', fd_xml_comp.attrib['strenght'].strip())
832 if amount is None:
833 amount = 99999
834 else:
835 amount = amount.group()
836 data['amount'] = amount
837
838 unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', fd_xml_comp.attrib['strenght'].strip()).strip()
839 if unit == u'':
840 unit = u'*?*'
841 data['unit'] = unit
842
843 molecule_name = fd_xml_comp.attrib['molecularName'].strip()
844 if molecule_name != u'':
845 create_consumable_substance(substance = molecule_name, atc = None, amount = amount, unit = unit)
846 data['molecule_name'] = molecule_name
847
848 inn_name = fd_xml_comp.attrib['inn'].strip()
849 if inn_name != u'':
850 create_consumable_substance(substance = inn_name, atc = None, amount = amount, unit = unit)
851 data['inn_name'] = molecule_name
852
853 if molecule_name == u'':
854 data['substance'] = inn_name
855 _log.info('linking INN [%s] rather than molecularName as component', inn_name)
856 else:
857 data['substance'] = molecule_name
858
859 data['nature'] = fd_xml_comp.attrib['nature'].strip()
860 data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
861
862
863 try:
864 old_data = comp_data[data['nature_ID']]
865
866 if old_data['inn_name'] == u'':
867 old_data['inn_name'] = data['inn_name']
868 if data['inn_name'] == u'':
869 data['inn_name'] = old_data['inn_name']
870
871 if old_data['molecule_name'] == u'':
872 old_data['molecule_name'] = data['molecule_name']
873 if data['molecule_name'] == u'':
874 data['molecule_name'] = old_data['molecule_name']
875
876
877
878
879
880 if data['nature'] == u'FT':
881 comp_data[data['nature_ID']] = data
882 else:
883 comp_data[data['nature_ID']] = old_data
884
885
886 except KeyError:
887 comp_data[data['nature_ID']] = data
888
889
890 for key, data in comp_data.items():
891 new_drug.add_component (
892 substance = data['substance'],
893 atc = None,
894 amount = data['amount'],
895 unit = data['unit']
896 )
897
899 """Support v8.2 CSV file interface only."""
900
901 version = u'Gelbe Liste/MMI v8.2 interface'
902 default_encoding = 'cp1250'
903 bdt_line_template = u'%03d6210#%s\r\n'
904 bdt_line_base_length = 8
905
907
908 cDrugDataSourceInterface.__init__(self)
909
910 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version)
911
912 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
913 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
914
915 paths = gmTools.gmPaths()
916
917 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt')
918 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp')
919 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt')
920 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
921
922 self.__data_date = None
923 self.__online_update_date = None
924
925
926
928
929 if self.__data_date is not None:
930 if not force_reload:
931 return {
932 'data': self.__data_date,
933 'online_update': self.__online_update_date
934 }
935 try:
936 open(self.data_date_filename, 'wb').close()
937 except StandardError:
938 _log.error('problem querying the MMI drug database for version information')
939 _log.exception('cannot create MMI drug database version file [%s]', self.data_date_filename)
940 self.__data_date = None
941 self.__online_update_date = None
942 return {
943 'data': u'?',
944 'online_update': u'?'
945 }
946
947 cmd = u'%s -DATADATE' % self.path_to_binary
948 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
949 _log.error('problem querying the MMI drug database for version information')
950 self.__data_date = None
951 self.__online_update_date = None
952 return {
953 'data': u'?',
954 'online_update': u'?'
955 }
956
957 try:
958 version_file = open(self.data_date_filename, 'rU')
959 except StandardError:
960 _log.error('problem querying the MMI drug database for version information')
961 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename)
962 self.__data_date = None
963 self.__online_update_date = None
964 return {
965 'data': u'?',
966 'online_update': u'?'
967 }
968
969 self.__data_date = version_file.readline()[:10]
970 self.__online_update_date = version_file.readline()[:10]
971 version_file.close()
972
973 return {
974 'data': self.__data_date,
975 'online_update': self.__online_update_date
976 }
977
979 versions = self.get_data_source_version()
980
981 return create_data_source (
982 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
983 short_name = u'GL/MMI',
984 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
985 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
986 language = u'de'
987 )
988
990
991 try:
992
993 open(self.default_csv_filename, 'wb').close()
994 except IOError:
995 _log.exception('problem creating GL/MMI <-> GNUmed exchange file')
996 return False
997
998 if cmd is None:
999 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
1000
1001 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
1002 _log.error('problem switching to the MMI drug database')
1003
1004
1005
1006
1007 return True
1008
1018
1020
1021 selected_drugs = self.__let_user_select_drugs()
1022 if selected_drugs is None:
1023 return None
1024
1025 new_substances = []
1026
1027 for drug in selected_drugs:
1028 atc = None
1029 if len(drug['wirkstoffe']) == 1:
1030 atc = drug['atc']
1031 for wirkstoff in drug['wirkstoffe']:
1032 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1033
1034 selected_drugs.close()
1035
1036 return new_substances
1037
1039
1040 selected_drugs = self.__let_user_select_drugs()
1041 if selected_drugs is None:
1042 return None
1043
1044 data_src_pk = self.create_data_source_entry()
1045
1046 new_drugs = []
1047 new_substances = []
1048
1049 for entry in selected_drugs:
1050
1051 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
1052
1053 if entry[u'hilfsmittel']:
1054 _log.debug('skipping Hilfsmittel')
1055 continue
1056
1057 if entry[u'erstattbares_medizinprodukt']:
1058 _log.debug('skipping sonstiges Medizinprodukt')
1059 continue
1060
1061
1062 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform'])
1063 if drug is None:
1064 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform'])
1065 new_drugs.append(drug)
1066
1067
1068 drug['is_fake_brand'] = False
1069 drug['atc'] = entry['atc']
1070 drug['external_code_type'] = u'DE-PZN'
1071 drug['external_code'] = entry['pzn']
1072 drug['fk_data_source'] = data_src_pk
1073 drug.save()
1074
1075
1076 atc = None
1077 if len(entry['wirkstoffe']) == 1:
1078 atc = entry['atc']
1079 for wirkstoff in entry['wirkstoffe']:
1080 drug.add_component(substance = wirkstoff, atc = atc)
1081
1082
1083 atc = None
1084 if len(entry['wirkstoffe']) == 1:
1085 atc = entry['atc']
1086 for wirkstoff in entry['wirkstoffe']:
1087 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1088
1089 return new_drugs, new_substances
1090
1119
1122
1141
1143
1145 cGelbeListeWindowsInterface.__init__(self)
1146
1147 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version)
1148
1149
1150 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
1151 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
1152
1153 paths = gmTools.gmPaths()
1154
1155 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
1156 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
1157 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
1158 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
1159
1161 """empirical CSV interface"""
1162
1165
1167
1168 try:
1169 csv_file = open(filename, 'rb')
1170 except:
1171 _log.exception('cannot access [%s]', filename)
1172 csv_file = None
1173
1174 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
1175
1176 if csv_file is None:
1177 return False
1178
1179 csv_lines = csv.DictReader (
1180 csv_file,
1181 fieldnames = field_names,
1182 delimiter = ';'
1183 )
1184
1185 for line in csv_lines:
1186 print "--------------------------------------------------------------------"[:31]
1187 for key in field_names:
1188 tmp = ('%s ' % key)[:30]
1189 print '%s: %s' % (tmp, line[key])
1190
1191 csv_file.close()
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204 drug_data_source_interfaces = {
1205 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
1206 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface,
1207 'FreeDiams (FR, US, CA, ZA)': cFreeDiamsInterface
1208 }
1209
1210
1211
1212
1213
1214 _SQL_get_consumable_substance = u"""
1215 SELECT *, xmin
1216 FROM ref.consumable_substance
1217 WHERE %s
1218 """
1219
1221
1222 _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s"
1223 _cmds_store_payload = [
1224 u"""UPDATE ref.consumable_substance SET
1225 description = %(description)s,
1226 atc_code = gm.nullify_empty_string(%(atc_code)s),
1227 amount = %(amount)s,
1228 unit = gm.nullify_empty_string(%(unit)s)
1229 WHERE
1230 pk = %(pk)s
1231 AND
1232 xmin = %(xmin)s
1233 AND
1234 -- must not currently be used with a patient directly
1235 NOT EXISTS (
1236 SELECT 1
1237 FROM clin.substance_intake
1238 WHERE
1239 fk_drug_component IS NULL
1240 AND
1241 fk_substance = %(pk)s
1242 LIMIT 1
1243 )
1244 AND
1245 -- must not currently be used with a patient indirectly, either
1246 NOT EXISTS (
1247 SELECT 1
1248 FROM clin.substance_intake
1249 WHERE
1250 fk_drug_component IS NOT NULL
1251 AND
1252 fk_drug_component = (
1253 SELECT r_ls2b.pk
1254 FROM ref.lnk_substance2brand r_ls2b
1255 WHERE fk_substance = %(pk)s
1256 )
1257 LIMIT 1
1258 )
1259 -- -- must not currently be used with a branded drug
1260 -- -- (but this would make it rather hard fixing branded drugs which contain only this substance)
1261 -- NOT EXISTS (
1262 -- SELECT 1
1263 -- FROM ref.lnk_substance2brand
1264 -- WHERE fk_substance = %(pk)s
1265 -- LIMIT 1
1266 -- )
1267 RETURNING
1268 xmin
1269 """
1270 ]
1271 _updatable_fields = [
1272 u'description',
1273 u'atc_code',
1274 u'amount',
1275 u'unit'
1276 ]
1277
1279 success, data = super(self.__class__, self).save_payload(conn = conn)
1280
1281 if not success:
1282 return (success, data)
1283
1284 if self._payload[self._idx['atc_code']] is not None:
1285 atc = self._payload[self._idx['atc_code']].strip()
1286 if atc != u'':
1287 gmATC.propagate_atc (
1288 substance = self._payload[self._idx['description']].strip(),
1289 atc = atc
1290 )
1291
1292 return (success, data)
1293
1294
1295
1297 cmd = u"""
1298 SELECT
1299 EXISTS (
1300 SELECT 1
1301 FROM clin.substance_intake
1302 WHERE
1303 fk_drug_component IS NULL
1304 AND
1305 fk_substance = %(pk)s
1306 LIMIT 1
1307 ) OR EXISTS (
1308 SELECT 1
1309 FROM clin.substance_intake
1310 WHERE
1311 fk_drug_component IS NOT NULL
1312 AND
1313 fk_drug_component IN (
1314 SELECT r_ls2b.pk
1315 FROM ref.lnk_substance2brand r_ls2b
1316 WHERE fk_substance = %(pk)s
1317 )
1318 LIMIT 1
1319 )"""
1320 args = {'pk': self.pk_obj}
1321
1322 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1323 return rows[0][0]
1324
1325 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
1326
1328 cmd = u"""
1329 SELECT EXISTS (
1330 SELECT 1
1331 FROM ref.lnk_substance2brand
1332 WHERE fk_substance = %(pk)s
1333 LIMIT 1
1334 )"""
1335 args = {'pk': self.pk_obj}
1336
1337 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1338 return rows[0][0]
1339
1340 is_drug_component = property(_get_is_drug_component, lambda x:x)
1341
1350
1352
1353 substance = substance
1354 if atc is not None:
1355 atc = atc.strip()
1356
1357 converted, amount = gmTools.input2decimal(amount)
1358 if not converted:
1359 raise ValueError('<amount> must be a number: %s (%s)', amount, type(amount))
1360
1361 args = {
1362 'desc': substance.strip(),
1363 'amount': amount,
1364 'unit': unit.strip(),
1365 'atc': atc
1366 }
1367 cmd = u"""
1368 SELECT pk FROM ref.consumable_substance
1369 WHERE
1370 lower(description) = lower(%(desc)s)
1371 AND
1372 amount = %(amount)s
1373 AND
1374 unit = %(unit)s
1375 """
1376 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1377
1378 if len(rows) == 0:
1379 cmd = u"""
1380 INSERT INTO ref.consumable_substance (description, atc_code, amount, unit) VALUES (
1381 %(desc)s,
1382 gm.nullify_empty_string(%(atc)s),
1383 %(amount)s,
1384 gm.nullify_empty_string(%(unit)s)
1385 ) RETURNING pk"""
1386 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
1387
1388 gmATC.propagate_atc(substance = substance, atc = atc)
1389
1390 return cConsumableSubstance(aPK_obj = rows[0]['pk'])
1391
1393 args = {'pk': substance}
1394 cmd = u"""
1395 DELETE FROM ref.consumable_substance
1396 WHERE
1397 pk = %(pk)s
1398 AND
1399
1400 -- must not currently be used with a patient
1401 NOT EXISTS (
1402 SELECT 1
1403 FROM clin.v_pat_substance_intake
1404 WHERE pk_substance = %(pk)s
1405 LIMIT 1
1406 )
1407 AND
1408
1409 -- must not currently be used with a branded drug
1410 NOT EXISTS (
1411 SELECT 1
1412 FROM ref.lnk_substance2brand
1413 WHERE fk_substance = %(pk)s
1414 LIMIT 1
1415 )"""
1416 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1417 return True
1418
1420
1421 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
1422 _query1 = u"""
1423 SELECT
1424 pk::text,
1425 (description || ' ' || amount || ' ' || unit) as subst
1426 FROM ref.consumable_substance
1427 WHERE description %(fragment_condition)s
1428 ORDER BY subst
1429 LIMIT 50"""
1430 _query2 = u"""
1431 SELECT
1432 pk::text,
1433 (description || ' ' || amount || ' ' || unit) as subst
1434 FROM ref.consumable_substance
1435 WHERE
1436 %(fragment_condition)s
1437 ORDER BY subst
1438 LIMIT 50"""
1439
1440
1442 """Return matches for aFragment at start of phrases."""
1443
1444 if cSubstanceMatchProvider._pattern.match(aFragment):
1445 self._queries = [cSubstanceMatchProvider._query2]
1446 fragment_condition = """description ILIKE %(desc)s
1447 AND
1448 amount::text ILIKE %(amount)s"""
1449 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1450 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1451 else:
1452 self._queries = [cSubstanceMatchProvider._query1]
1453 fragment_condition = u"ILIKE %(fragment)s"
1454 self._args['fragment'] = u"%s%%" % aFragment
1455
1456 return self._find_matches(fragment_condition)
1457
1459 """Return matches for aFragment at start of words inside phrases."""
1460
1461 if cSubstanceMatchProvider._pattern.match(aFragment):
1462 self._queries = [cSubstanceMatchProvider._query2]
1463
1464 desc = regex.sub(r'\s*\d+$', u'', aFragment)
1465 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
1466
1467 fragment_condition = """description ~* %(desc)s
1468 AND
1469 amount::text ILIKE %(amount)s"""
1470
1471 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
1472 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1473 else:
1474 self._queries = [cSubstanceMatchProvider._query1]
1475 fragment_condition = u"~* %(fragment)s"
1476 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
1477 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
1478
1479 return self._find_matches(fragment_condition)
1480
1482 """Return matches for aFragment as a true substring."""
1483
1484 if cSubstanceMatchProvider._pattern.match(aFragment):
1485 self._queries = [cSubstanceMatchProvider._query2]
1486 fragment_condition = """description ILIKE %(desc)s
1487 AND
1488 amount::text ILIKE %(amount)s"""
1489 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1490 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1491 else:
1492 self._queries = [cSubstanceMatchProvider._query1]
1493 fragment_condition = u"ILIKE %(fragment)s"
1494 self._args['fragment'] = u"%%%s%%" % aFragment
1495
1496 return self._find_matches(fragment_condition)
1497
1498 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
1499 """Represents a substance currently taken by a patient."""
1500
1501 _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s"
1502 _cmds_store_payload = [
1503 u"""UPDATE clin.substance_intake SET
1504 clin_when = %(started)s,
1505 discontinued = %(discontinued)s,
1506 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s),
1507 schedule = gm.nullify_empty_string(%(schedule)s),
1508 aim = gm.nullify_empty_string(%(aim)s),
1509 narrative = gm.nullify_empty_string(%(notes)s),
1510 intake_is_approved_of = %(intake_is_approved_of)s,
1511 fk_episode = %(pk_episode)s,
1512
1513 preparation = (
1514 case
1515 when %(pk_brand)s is NULL then %(preparation)s
1516 else NULL
1517 end
1518 )::text,
1519
1520 is_long_term = (
1521 case
1522 when (
1523 (%(is_long_term)s is False)
1524 and
1525 (%(duration)s is NULL)
1526 ) is True then null
1527 else %(is_long_term)s
1528 end
1529 )::boolean,
1530
1531 duration = (
1532 case
1533 when %(is_long_term)s is True then null
1534 else %(duration)s
1535 end
1536 )::interval
1537 WHERE
1538 pk = %(pk_substance_intake)s
1539 AND
1540 xmin = %(xmin_substance_intake)s
1541 RETURNING
1542 xmin as xmin_substance_intake
1543 """
1544 ]
1545 _updatable_fields = [
1546 u'started',
1547 u'discontinued',
1548 u'discontinue_reason',
1549 u'preparation',
1550 u'intake_is_approved_of',
1551 u'schedule',
1552 u'duration',
1553 u'aim',
1554 u'is_long_term',
1555 u'notes',
1556 u'pk_episode'
1557 ]
1558
1559 - def format(self, left_margin=0, date_format='%Y %B %d', one_line=True, allergy=None):
1560 if one_line:
1561 return self.format_as_one_line(left_margin = left_margin, date_format = date_format)
1562
1563 return self.format_as_multiple_lines(left_margin = left_margin, date_format = date_format, allergy = allergy)
1564
1566
1567 if self._payload[self._idx['duration']] is None:
1568 duration = gmTools.bool2subst (
1569 self._payload[self._idx['is_long_term']],
1570 _('long-term'),
1571 _('short-term'),
1572 _('?short-term')
1573 )
1574 else:
1575 duration = gmDateTime.format_interval (
1576 self._payload[self._idx['duration']],
1577 accuracy_wanted = gmDateTime.acc_days
1578 )
1579
1580 line = u'%s%s (%s %s): %s %s%s %s (%s)' % (
1581 u' ' * left_margin,
1582 self._payload[self._idx['started']].strftime(date_format),
1583 gmTools.u_right_arrow,
1584 duration,
1585 self._payload[self._idx['substance']],
1586 self._payload[self._idx['amount']],
1587 self._payload[self._idx['unit']],
1588 self._payload[self._idx['preparation']],
1589 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing'))
1590 )
1591
1592 return line
1593
1595
1596 txt = _('Substance intake entry (%s, %s) [#%s] \n') % (
1597 gmTools.bool2subst (
1598 boolean = self._payload[self._idx['is_currently_active']],
1599 true_return = gmTools.bool2subst (
1600 boolean = self._payload[self._idx['seems_inactive']],
1601 true_return = _('active, needs check'),
1602 false_return = _('active'),
1603 none_return = _('assumed active')
1604 ),
1605 false_return = _('inactive')
1606 ),
1607 gmTools.bool2subst (
1608 boolean = self._payload[self._idx['intake_is_approved_of']],
1609 true_return = _('approved'),
1610 false_return = _('unapproved')
1611 ),
1612 self._payload[self._idx['pk_substance_intake']]
1613 )
1614
1615 if allergy is not None:
1616 certainty = gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'))
1617 txt += u'\n'
1618 txt += u' !! ---- Cave ---- !!\n'
1619 txt += u' %s (%s): %s (%s)\n' % (
1620 allergy['l10n_type'],
1621 certainty,
1622 allergy['descriptor'],
1623 gmTools.coalesce(allergy['reaction'], u'')[:40]
1624 )
1625 txt += u'\n'
1626
1627 txt += u' ' + _('Substance: %s [#%s]\n') % (self._payload[self._idx['substance']], self._payload[self._idx['pk_substance']])
1628 txt += u' ' + _('Preparation: %s\n') % self._payload[self._idx['preparation']]
1629 txt += u' ' + _('Amount per dose: %s %s') % (self._payload[self._idx['amount']], self._payload[self._idx['unit']])
1630 if self.ddd is not None:
1631 txt += u' (DDD: %s %s)' % (self.ddd['ddd'], self.ddd['unit'])
1632 txt += u'\n'
1633 txt += gmTools.coalesce(self._payload[self._idx['atc_substance']], u'', _(' ATC (substance): %s\n'))
1634
1635 txt += u'\n'
1636
1637 txt += gmTools.coalesce (
1638 self._payload[self._idx['brand']],
1639 u'',
1640 _(' Brand name: %%s [#%s]\n') % self._payload[self._idx['pk_brand']]
1641 )
1642 txt += gmTools.coalesce(self._payload[self._idx['atc_brand']], u'', _(' ATC (brand): %s\n'))
1643
1644 txt += u'\n'
1645
1646 txt += gmTools.coalesce(self._payload[self._idx['schedule']], u'', _(' Regimen: %s\n'))
1647
1648 if self._payload[self._idx['is_long_term']]:
1649 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity)
1650 else:
1651 if self._payload[self._idx['duration']] is None:
1652 duration = u''
1653 else:
1654 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(self._payload[self._idx['duration']], gmDateTime.acc_days))
1655
1656 txt += _(' Started %s%s%s\n') % (
1657 gmDateTime.pydt_strftime (
1658 self._payload[self._idx['started']],
1659 format = date_format,
1660 accuracy = gmDateTime.acc_days
1661 ),
1662 duration,
1663 gmTools.bool2subst(self._payload[self._idx['is_long_term']], _(' (long-term)'), _(' (short-term)'), u'')
1664 )
1665
1666 if self._payload[self._idx['discontinued']] is not None:
1667 txt += _(' Discontinued %s\n') % (
1668 gmDateTime.pydt_strftime (
1669 self._payload[self._idx['discontinued']],
1670 format = date_format,
1671 accuracy = gmDateTime.acc_days
1672 )
1673 )
1674 txt += _(' Reason: %s\n') % self._payload[self._idx['discontinue_reason']]
1675
1676 txt += u'\n'
1677
1678 txt += gmTools.coalesce(self._payload[self._idx['aim']], u'', _(' Aim: %s\n'))
1679 txt += gmTools.coalesce(self._payload[self._idx['episode']], u'', _(' Episode: %s\n'))
1680 txt += gmTools.coalesce(self._payload[self._idx['notes']], u'', _(' Advice: %s\n'))
1681
1682 txt += u'\n'
1683
1684 txt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % {
1685 'row_ver': self._payload[self._idx['row_version']],
1686 'mod_when': gmDateTime.pydt_strftime(self._payload[self._idx['modified_when']]),
1687 'mod_by': self._payload[self._idx['modified_by']]
1688 }
1689
1690 return txt
1691
1692 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
1693 allg = gmAllergy.create_allergy (
1694 allergene = self._payload[self._idx['substance']],
1695 allg_type = allergy_type,
1696 episode_id = self._payload[self._idx['pk_episode']],
1697 encounter_id = encounter_id
1698 )
1699 allg['substance'] = gmTools.coalesce (
1700 self._payload[self._idx['brand']],
1701 self._payload[self._idx['substance']]
1702 )
1703 allg['reaction'] = self._payload[self._idx['discontinue_reason']]
1704 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']])
1705 if self._payload[self._idx['external_code_brand']] is not None:
1706 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']])
1707
1708 if self._payload[self._idx['pk_brand']] is None:
1709 allg['generics'] = self._payload[self._idx['substance']]
1710 else:
1711 comps = [ c['substance'] for c in self.containing_drug.components ]
1712 if len(comps) == 0:
1713 allg['generics'] = self._payload[self._idx['substance']]
1714 else:
1715 allg['generics'] = u'; '.join(comps)
1716
1717 allg.save()
1718 return allg
1719
1720
1721
1722 - def _get_ddd(self):
1723
1724 try: self.__ddd
1725 except AttributeError: self.__ddd = None
1726
1727 if self.__ddd is not None:
1728 return self.__ddd
1729
1730 if self._payload[self._idx['atc_substance']] is not None:
1731 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']])
1732 if len(ddd) != 0:
1733 self.__ddd = ddd[0]
1734 else:
1735 if self._payload[self._idx['atc_brand']] is not None:
1736 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']])
1737 if len(ddd) != 0:
1738 self.__ddd = ddd[0]
1739
1740 return self.__ddd
1741
1742 ddd = property(_get_ddd, lambda x:x)
1743
1745 drug = self.containing_drug
1746
1747 if drug is None:
1748 return None
1749
1750 return drug.external_code
1751
1752 external_code = property(_get_external_code, lambda x:x)
1753
1755 drug = self.containing_drug
1756
1757 if drug is None:
1758 return None
1759
1760 return drug.external_code_type
1761
1762 external_code_type = property(_get_external_code_type, lambda x:x)
1763
1765 if self._payload[self._idx['pk_brand']] is None:
1766 return None
1767
1768 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1769
1770 containing_drug = property(_get_containing_drug, lambda x:x)
1771
1773 tests = [
1774
1775 ' 1-1-1-1 ',
1776
1777 '1-1-1-1',
1778 '22-1-1-1',
1779 '1/3-1-1-1',
1780 '/4-1-1-1'
1781 ]
1782 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}$"
1783 for test in tests:
1784 print test.strip(), ":", regex.match(pattern, test.strip())
1785
1787 args = {'comp': pk_component, 'subst': pk_substance, 'pat': pk_identity}
1788
1789 where_clause = u"""
1790 fk_encounter IN (
1791 SELECT pk FROM clin.encounter WHERE fk_patient = %(pat)s
1792 )
1793 AND
1794 """
1795
1796 if pk_substance is not None:
1797 where_clause += u'fk_substance = %(subst)s'
1798 if pk_component is not None:
1799 where_clause += u'fk_drug_component = %(comp)s'
1800
1801 cmd = u"""SELECT exists (
1802 SELECT 1 FROM clin.substance_intake
1803 WHERE
1804 %s
1805 LIMIT 1
1806 )""" % where_clause
1807
1808 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1809 return rows[0][0]
1810
1811 -def create_substance_intake(pk_substance=None, pk_component=None, preparation=None, encounter=None, episode=None):
1812
1813 args = {
1814 'enc': encounter,
1815 'epi': episode,
1816 'comp': pk_component,
1817 'subst': pk_substance,
1818 'prep': preparation
1819 }
1820
1821 if pk_component is None:
1822 cmd = u"""
1823 INSERT INTO clin.substance_intake (
1824 fk_encounter,
1825 fk_episode,
1826 intake_is_approved_of,
1827 fk_substance,
1828 preparation
1829 ) VALUES (
1830 %(enc)s,
1831 %(epi)s,
1832 False,
1833 %(subst)s,
1834 %(prep)s
1835 )
1836 RETURNING pk"""
1837 else:
1838 cmd = u"""
1839 INSERT INTO clin.substance_intake (
1840 fk_encounter,
1841 fk_episode,
1842 intake_is_approved_of,
1843 fk_drug_component
1844 ) VALUES (
1845 %(enc)s,
1846 %(epi)s,
1847 False,
1848 %(comp)s
1849 )
1850 RETURNING pk"""
1851
1852 try:
1853 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
1854 except gmPG2.dbapi.InternalError, e:
1855 if e.pgerror is None:
1856 raise
1857 if 'prevent_duplicate_component' in e.pgerror:
1858 _log.exception('will not create duplicate substance intake entry')
1859 _log.error(e.pgerror)
1860 return None
1861 raise
1862
1863 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
1864
1868
1902
1903
1980
1981 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s'
1982
1984
1985 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s'
1986 _cmds_store_payload = [
1987 u"""UPDATE ref.lnk_substance2brand SET
1988 fk_brand = %(pk_brand)s,
1989 fk_substance = %(pk_consumable_substance)s
1990 WHERE
1991 NOT EXISTS (
1992 SELECT 1
1993 FROM clin.substance_intake
1994 WHERE fk_drug_component = %(pk_component)s
1995 LIMIT 1
1996 )
1997 AND
1998 pk = %(pk_component)s
1999 AND
2000 xmin = %(xmin_lnk_substance2brand)s
2001 RETURNING
2002 xmin AS xmin_lnk_substance2brand
2003 """
2004 ]
2005 _updatable_fields = [
2006 u'pk_brand',
2007 u'pk_consumable_substance'
2008 ]
2009
2010
2011
2013 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
2014
2015 containing_drug = property(_get_containing_drug, lambda x:x)
2016
2018 return self._payload[self._idx['is_in_use']]
2019
2020 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
2021
2024
2025 substance = property(_get_substance, lambda x:x)
2026
2031
2033
2034 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
2035 _query_desc_only = u"""
2036 SELECT DISTINCT ON (component)
2037 pk_component,
2038 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
2039 AS component
2040 FROM ref.v_drug_components
2041 WHERE
2042 substance %(fragment_condition)s
2043 OR
2044 brand %(fragment_condition)s
2045 ORDER BY component
2046 LIMIT 50"""
2047 _query_desc_and_amount = u"""
2048
2049 SELECT DISTINCT ON (component)
2050 pk_component,
2051 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
2052 AS component
2053 FROM ref.v_drug_components
2054 WHERE
2055 %(fragment_condition)s
2056 ORDER BY component
2057 LIMIT 50"""
2058
2060 """Return matches for aFragment at start of phrases."""
2061
2062 if cDrugComponentMatchProvider._pattern.match(aFragment):
2063 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2064 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
2065 AND
2066 amount::text ILIKE %(amount)s"""
2067 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
2068 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2069 else:
2070 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2071 fragment_condition = u"ILIKE %(fragment)s"
2072 self._args['fragment'] = u"%s%%" % aFragment
2073
2074 return self._find_matches(fragment_condition)
2075
2077 """Return matches for aFragment at start of words inside phrases."""
2078
2079 if cDrugComponentMatchProvider._pattern.match(aFragment):
2080 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2081
2082 desc = regex.sub(r'\s*\d+$', u'', aFragment)
2083 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
2084
2085 fragment_condition = """(substance ~* %(desc)s OR brand ~* %(desc)s)
2086 AND
2087 amount::text ILIKE %(amount)s"""
2088
2089 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
2090 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2091 else:
2092 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2093 fragment_condition = u"~* %(fragment)s"
2094 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
2095 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
2096
2097 return self._find_matches(fragment_condition)
2098
2100 """Return matches for aFragment as a true substring."""
2101
2102 if cDrugComponentMatchProvider._pattern.match(aFragment):
2103 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2104 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
2105 AND
2106 amount::text ILIKE %(amount)s"""
2107 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
2108 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2109 else:
2110 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2111 fragment_condition = u"ILIKE %(fragment)s"
2112 self._args['fragment'] = u"%%%s%%" % aFragment
2113
2114 return self._find_matches(fragment_condition)
2115
2116
2118 """Represents a drug as marketed by a manufacturer."""
2119
2120 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s"
2121 _cmds_store_payload = [
2122 u"""UPDATE ref.branded_drug SET
2123 description = %(brand)s,
2124 preparation = %(preparation)s,
2125 atc_code = gm.nullify_empty_string(%(atc)s),
2126 external_code = gm.nullify_empty_string(%(external_code)s),
2127 external_code_type = gm.nullify_empty_string(%(external_code_type)s),
2128 is_fake = %(is_fake_brand)s,
2129 fk_data_source = %(pk_data_source)s
2130 WHERE
2131 pk = %(pk_brand)s
2132 AND
2133 xmin = %(xmin_branded_drug)s
2134 RETURNING
2135 xmin AS xmin_branded_drug
2136 """
2137 ]
2138 _updatable_fields = [
2139 u'brand',
2140 u'preparation',
2141 u'atc',
2142 u'is_fake_brand',
2143 u'external_code',
2144 u'external_code_type',
2145 u'pk_data_source'
2146 ]
2147
2149 success, data = super(self.__class__, self).save_payload(conn = conn)
2150
2151 if not success:
2152 return (success, data)
2153
2154 if self._payload[self._idx['atc']] is not None:
2155 atc = self._payload[self._idx['atc']].strip()
2156 if atc != u'':
2157 gmATC.propagate_atc (
2158 substance = self._payload[self._idx['brand']].strip(),
2159 atc = atc
2160 )
2161
2162 return (success, data)
2163
2165
2166 if self.is_in_use_by_patients:
2167 return False
2168
2169 args = {'brand': self._payload[self._idx['pk_brand']]}
2170
2171 queries = [{'cmd': u"DELETE FROM ref.lnk_substance2brand WHERE fk_brand = %(brand)s", 'args': args}]
2172 cmd = u'INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) VALUES (%%(brand)s, %s)'
2173 for s in substances:
2174 queries.append({'cmd': cmd % s['pk'], 'args': args})
2175
2176 gmPG2.run_rw_queries(queries = queries)
2177 self.refetch_payload()
2178
2179 return True
2180
2181 - def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
2182
2183 args = {
2184 'brand': self.pk_obj,
2185 'subst': substance,
2186 'atc': atc,
2187 'pk_subst': pk_substance
2188 }
2189
2190 if pk_substance is None:
2191 consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit)
2192 args['pk_subst'] = consumable['pk']
2193
2194
2195 cmd = u"""
2196 SELECT pk_component
2197 FROM ref.v_drug_components
2198 WHERE
2199 pk_brand = %(brand)s
2200 AND
2201 ((
2202 (lower(substance) = lower(%(subst)s))
2203 OR
2204 (lower(atc_substance) = lower(%(atc)s))
2205 OR
2206 (pk_consumable_substance = %(pk_subst)s)
2207 ) IS TRUE)
2208 """
2209 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2210
2211 if len(rows) > 0:
2212 return
2213
2214
2215 cmd = u"""
2216 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance)
2217 VALUES (%(brand)s, %(pk_subst)s)
2218 """
2219 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2220 self.refetch_payload()
2221
2223 if len(self._payload[self._idx['components']]) == 1:
2224 _log.error('cannot remove the only component of a drug')
2225 return False
2226
2227 args = {'brand': self.pk_obj, 'comp': substance}
2228 cmd = u"""
2229 DELETE FROM ref.lnk_substance2brand
2230 WHERE
2231 fk_brand = %(brand)s
2232 AND
2233 fk_substance = %(comp)s
2234 AND
2235 NOT EXISTS (
2236 SELECT 1
2237 FROM clin.substance_intake
2238 WHERE fk_drug_component = %(comp)s
2239 LIMIT 1
2240 )
2241 """
2242 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2243 self.refetch_payload()
2244
2245 return True
2246
2247
2248
2250 if self._payload[self._idx['external_code']] is None:
2251 return None
2252
2253 return self._payload[self._idx['external_code']]
2254
2255 external_code = property(_get_external_code, lambda x:x)
2256
2258
2259
2260 if self._payload[self._idx['external_code_type']] is None:
2261 return None
2262
2263 return self._payload[self._idx['external_code_type']]
2264
2265 external_code_type = property(_get_external_code_type, lambda x:x)
2266
2268 cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s'
2269 args = {'brand': self._payload[self._idx['pk_brand']]}
2270 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2271 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
2272
2273 components = property(_get_components, lambda x:x)
2274
2276 if self._payload[self._idx['pk_substances']] is None:
2277 return []
2278 cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s'
2279 args = {'pks': tuple(self._payload[self._idx['pk_substances']])}
2280 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2281 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
2282
2283 components_as_substances = property(_get_components_as_substances, lambda x:x)
2284
2286 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)'
2287 args = {'fk_brand': self._payload[self._idx['pk_brand']]}
2288 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2289 return rows[0][0]
2290
2291 is_vaccine = property(_get_is_vaccine, lambda x:x)
2292
2294 cmd = u"""
2295 SELECT EXISTS (
2296 SELECT 1
2297 FROM clin.substance_intake
2298 WHERE
2299 fk_drug_component IS NOT NULL
2300 AND
2301 fk_drug_component IN (
2302 SELECT r_ls2b.pk
2303 FROM ref.lnk_substance2brand r_ls2b
2304 WHERE fk_brand = %(pk)s
2305 )
2306 LIMIT 1
2307 )"""
2308 args = {'pk': self.pk_obj}
2309
2310 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2311 return rows[0][0]
2312
2313 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
2314
2316 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description'
2317 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
2318 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
2319
2321 args = {'brand': brand_name, 'prep': preparation}
2322
2323 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)'
2324 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2325
2326 if len(rows) == 0:
2327 return None
2328
2329 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2330
2332
2333 if preparation is None:
2334 preparation = _('units')
2335
2336 if preparation.strip() == u'':
2337 preparation = _('units')
2338
2339 if return_existing:
2340 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation)
2341 if drug is not None:
2342 return drug
2343
2344 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk'
2345 args = {'brand': brand_name, 'prep': preparation}
2346 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
2347
2348 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2349
2351 queries = []
2352 args = {'pk': brand}
2353
2354
2355 cmd = u"""
2356 DELETE FROM ref.lnk_substance2brand
2357 WHERE
2358 fk_brand = %(pk)s
2359 AND
2360 NOT EXISTS (
2361 SELECT 1
2362 FROM clin.v_pat_substance_intake
2363 WHERE pk_brand = %(pk)s
2364 LIMIT 1
2365 )
2366 """
2367 queries.append({'cmd': cmd, 'args': args})
2368
2369
2370 cmd = u"""
2371 DELETE FROM ref.branded_drug
2372 WHERE
2373 pk = %(pk)s
2374 AND
2375 NOT EXISTS (
2376 SELECT 1
2377 FROM clin.v_pat_substance_intake
2378 WHERE pk_brand = %(pk)s
2379 LIMIT 1
2380 )
2381 """
2382 queries.append({'cmd': cmd, 'args': args})
2383
2384 gmPG2.run_rw_queries(queries = queries)
2385
2386
2387
2388 if __name__ == "__main__":
2389
2390 if len(sys.argv) < 2:
2391 sys.exit()
2392
2393 if sys.argv[1] != 'test':
2394 sys.exit()
2395
2396 from Gnumed.pycommon import gmLog2
2397 from Gnumed.pycommon import gmI18N
2398 from Gnumed.business import gmPerson
2399
2400 gmI18N.activate_locale()
2401
2402
2408
2410 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
2411 for drug in mmi_file:
2412 print "-------------"
2413 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2414 for stoff in drug['wirkstoffe']:
2415 print " Wirkstoff:", stoff
2416 raw_input()
2417 if mmi_file.has_unknown_fields is not None:
2418 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key
2419 for key in mmi_file.csv_fieldnames:
2420 print key, '->', drug[key]
2421 raw_input()
2422 mmi_file.close()
2423
2427
2429 mmi = cGelbeListeWineInterface()
2430 mmi_file = mmi.__let_user_select_drugs()
2431 for drug in mmi_file:
2432 print "-------------"
2433 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2434 for stoff in drug['wirkstoffe']:
2435 print " Wirkstoff:", stoff
2436 print drug
2437 mmi_file.close()
2438
2442
2444 mmi = cGelbeListeInterface()
2445 print mmi
2446 print "interface definition:", mmi.version
2447
2448 diclofenac = '7587712'
2449 phenprocoumon = '4421744'
2450 mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
2451
2452
2453
2460
2466
2467
2468
2470 drug = create_substance_intake (
2471 pk_component = 2,
2472 encounter = 1,
2473 episode = 1
2474 )
2475 print drug
2476
2481
2485
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507 test_drug2renal_insufficiency_url()
2508
2509