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_code'] is not None:
62 terms.append(search_term['atc_code'])
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.de/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 = [ a['allergene'] for a in allgs if a['type'] == u'allergy' ]
675 inn_sens = [ a['allergene'] for a in allgs if a['type'] == u'sensitivity' ]
676
677
678 uid_allgs = [
679 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'allergy'))
680 ]
681 uid_sens = [
682 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'sensitivity'))
683 ]
684
685 patient_xml = u"""<Patient>
686 <Identity
687 lastnames="%s"
688 firstnames="%s"
689 uid="%s"
690 dob="%s"
691 gender="%s"
692 />
693 <ATCAllergies value="%s"/>
694 <ATCIntolerances value="%s"/>
695
696 <InnAllergies value="%s"/>
697 <InnIntolerances value="%s"/>
698
699 <DrugsUidAllergies value="%s"/>
700 <DrugsUidIntolerances value="%s"/>
701 </Patient>
702 """ % (
703 gmTools.xml_escape_string(text = name['lastnames']),
704 gmTools.xml_escape_string(text = name['firstnames']),
705 self.patient.ID,
706 dob,
707 cFreeDiamsInterface.map_gender2mf[self.patient['gender']],
708 gmTools.xml_escape_string(text = u';'.join(atc_allgs)),
709 gmTools.xml_escape_string(text = u';'.join(atc_sens)),
710 gmTools.xml_escape_string(text = u';'.join(inn_allgs)),
711 gmTools.xml_escape_string(text = u';'.join(inn_sens)),
712 gmTools.xml_escape_string(text = u';'.join(uid_allgs)),
713 gmTools.xml_escape_string(text = u';'.join(uid_sens))
714 )
715
716 xml_file.write(xml % patient_xml)
717 xml_file.close()
718
771
773 """
774 If returning textual prescriptions (say, drugs which FreeDiams
775 did not know) then "IsTextual" will be True and UID will be -1.
776 """
777 if filename is None:
778 filename = self.__fd2gm_filename
779
780
781
782 fd2gm_xml = etree.ElementTree()
783 fd2gm_xml.parse(filename)
784
785 data_src_pk = self.create_data_source_entry()
786
787 db_def = fd2gm_xml.find('DrugsDatabaseName')
788 db_id = db_def.text.strip()
789 drug_id_name = db_def.attrib['drugUidName']
790 fd_xml_drug_entries = fd2gm_xml.findall('FullPrescription/Prescription')
791
792 self.__imported_drugs = []
793 for fd_xml_drug in fd_xml_drug_entries:
794 drug_uid = fd_xml_drug.find('Drug_UID').text.strip()
795 if drug_uid == u'-1':
796 _log.debug('skipping textual drug')
797 continue
798 drug_name = fd_xml_drug.find('DrugName').text.replace(', )', ')').strip()
799 drug_form = fd_xml_drug.find('DrugForm').text.strip()
800 drug_atc = fd_xml_drug.find('DrugATC')
801 if drug_atc is None:
802 drug_atc = u''
803 else:
804 if drug_atc.text is None:
805 drug_atc = u''
806 else:
807 drug_atc = drug_atc.text.strip()
808
809
810 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True)
811 self.__imported_drugs.append(new_drug)
812 new_drug['is_fake_brand'] = False
813 new_drug['atc'] = drug_atc
814 new_drug['external_code_type'] = u'FreeDiams::%s::%s' % (db_id, drug_id_name)
815 new_drug['external_code'] = drug_uid
816 new_drug['pk_data_source'] = data_src_pk
817 new_drug.save()
818
819
820 fd_xml_components = fd_xml_drug.getiterator('Composition')
821 comp_data = {}
822 for fd_xml_comp in fd_xml_components:
823
824 data = {}
825
826 amount = regex.match(r'\d+[.,]{0,1}\d*', fd_xml_comp.attrib['strenght'].strip())
827 if amount is None:
828 amount = 99999
829 else:
830 amount = amount.group()
831 data['amount'] = amount
832
833 unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', fd_xml_comp.attrib['strenght'].strip()).strip()
834 if unit == u'':
835 unit = u'*?*'
836 data['unit'] = unit
837
838 molecule_name = fd_xml_comp.attrib['molecularName'].strip()
839 if molecule_name != u'':
840 create_consumable_substance(substance = molecule_name, atc = None, amount = amount, unit = unit)
841 data['molecule_name'] = molecule_name
842
843 inn_name = fd_xml_comp.attrib['inn'].strip()
844 if inn_name != u'':
845 create_consumable_substance(substance = inn_name, atc = None, amount = amount, unit = unit)
846 data['inn_name'] = molecule_name
847
848 if molecule_name == u'':
849 data['substance'] = inn_name
850 _log.info('linking INN [%s] rather than molecularName as component', inn_name)
851 else:
852 data['substance'] = molecule_name
853
854 data['nature'] = fd_xml_comp.attrib['nature'].strip()
855 data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
856
857
858 try:
859 old_data = comp_data[data['nature_ID']]
860
861 if old_data['inn_name'] == u'':
862 old_data['inn_name'] = data['inn_name']
863 if data['inn_name'] == u'':
864 data['inn_name'] = old_data['inn_name']
865
866 if old_data['molecule_name'] == u'':
867 old_data['molecule_name'] = data['molecule_name']
868 if data['molecule_name'] == u'':
869 data['molecule_name'] = old_data['molecule_name']
870
871
872
873
874
875 if data['nature'] == u'FT':
876 comp_data[data['nature_ID']] = data
877 else:
878 comp_data[data['nature_ID']] = old_data
879
880
881 except KeyError:
882 comp_data[data['nature_ID']] = data
883
884
885 for key, data in comp_data.items():
886 new_drug.add_component (
887 substance = data['substance'],
888 atc = None,
889 amount = data['amount'],
890 unit = data['unit']
891 )
892
894 """Support v8.2 CSV file interface only."""
895
896 version = u'Gelbe Liste/MMI v8.2 interface'
897 default_encoding = 'cp1250'
898 bdt_line_template = u'%03d6210#%s\r\n'
899 bdt_line_base_length = 8
900
902
903 cDrugDataSourceInterface.__init__(self)
904
905 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version)
906
907 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
908 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
909
910 paths = gmTools.gmPaths()
911
912 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt')
913 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp')
914 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt')
915 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
916
917 self.__data_date = None
918 self.__online_update_date = None
919
920
921
923
924 if self.__data_date is not None:
925 if not force_reload:
926 return {
927 'data': self.__data_date,
928 'online_update': self.__online_update_date
929 }
930 try:
931 open(self.data_date_filename, 'wb').close()
932 except StandardError:
933 _log.error('problem querying the MMI drug database for version information')
934 _log.exception('cannot create MMI drug database version file [%s]', self.data_date_filename)
935 self.__data_date = None
936 self.__online_update_date = None
937 return {
938 'data': u'?',
939 'online_update': u'?'
940 }
941
942 cmd = u'%s -DATADATE' % self.path_to_binary
943 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
944 _log.error('problem querying the MMI drug database for version information')
945 self.__data_date = None
946 self.__online_update_date = None
947 return {
948 'data': u'?',
949 'online_update': u'?'
950 }
951
952 try:
953 version_file = open(self.data_date_filename, 'rU')
954 except StandardError:
955 _log.error('problem querying the MMI drug database for version information')
956 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename)
957 self.__data_date = None
958 self.__online_update_date = None
959 return {
960 'data': u'?',
961 'online_update': u'?'
962 }
963
964 self.__data_date = version_file.readline()[:10]
965 self.__online_update_date = version_file.readline()[:10]
966 version_file.close()
967
968 return {
969 'data': self.__data_date,
970 'online_update': self.__online_update_date
971 }
972
974 versions = self.get_data_source_version()
975
976 return create_data_source (
977 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
978 short_name = u'GL/MMI',
979 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
980 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
981 language = u'de'
982 )
983
985
986 try:
987
988 open(self.default_csv_filename, 'wb').close()
989 except IOError:
990 _log.exception('problem creating GL/MMI <-> GNUmed exchange file')
991 return False
992
993 if cmd is None:
994 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
995
996 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
997 _log.error('problem switching to the MMI drug database')
998
999
1000
1001
1002 return True
1003
1013
1015
1016 selected_drugs = self.__let_user_select_drugs()
1017 if selected_drugs is None:
1018 return None
1019
1020 new_substances = []
1021
1022 for drug in selected_drugs:
1023 atc = None
1024 if len(drug['wirkstoffe']) == 1:
1025 atc = drug['atc']
1026 for wirkstoff in drug['wirkstoffe']:
1027 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1028
1029 selected_drugs.close()
1030
1031 return new_substances
1032
1034
1035 selected_drugs = self.__let_user_select_drugs()
1036 if selected_drugs is None:
1037 return None
1038
1039 data_src_pk = self.create_data_source_entry()
1040
1041 new_drugs = []
1042 new_substances = []
1043
1044 for entry in selected_drugs:
1045
1046 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
1047
1048 if entry[u'hilfsmittel']:
1049 _log.debug('skipping Hilfsmittel')
1050 continue
1051
1052 if entry[u'erstattbares_medizinprodukt']:
1053 _log.debug('skipping sonstiges Medizinprodukt')
1054 continue
1055
1056
1057 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform'])
1058 if drug is None:
1059 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform'])
1060 new_drugs.append(drug)
1061
1062
1063 drug['is_fake'] = False
1064 drug['atc_code'] = entry['atc']
1065 drug['external_code_type'] = u'DE-PZN'
1066 drug['external_code'] = entry['pzn']
1067 drug['fk_data_source'] = data_src_pk
1068 drug.save()
1069
1070
1071 atc = None
1072 if len(entry['wirkstoffe']) == 1:
1073 atc = entry['atc']
1074 for wirkstoff in entry['wirkstoffe']:
1075 drug.add_component(substance = wirkstoff, atc = atc)
1076
1077
1078 atc = None
1079 if len(entry['wirkstoffe']) == 1:
1080 atc = entry['atc']
1081 for wirkstoff in entry['wirkstoffe']:
1082 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1083
1084 return new_drugs, new_substances
1085
1114
1117
1136
1138
1140 cGelbeListeWindowsInterface.__init__(self)
1141
1142 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version)
1143
1144
1145 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
1146 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
1147
1148 paths = gmTools.gmPaths()
1149
1150 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
1151 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
1152 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
1153 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
1154
1156 """empirical CSV interface"""
1157
1160
1162
1163 try:
1164 csv_file = open(filename, 'rb')
1165 except:
1166 _log.exception('cannot access [%s]', filename)
1167 csv_file = None
1168
1169 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
1170
1171 if csv_file is None:
1172 return False
1173
1174 csv_lines = csv.DictReader (
1175 csv_file,
1176 fieldnames = field_names,
1177 delimiter = ';'
1178 )
1179
1180 for line in csv_lines:
1181 print "--------------------------------------------------------------------"[:31]
1182 for key in field_names:
1183 tmp = ('%s ' % key)[:30]
1184 print '%s: %s' % (tmp, line[key])
1185
1186 csv_file.close()
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199 drug_data_source_interfaces = {
1200 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
1201 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface,
1202 'FreeDiams (FR, US, CA, ZA)': cFreeDiamsInterface
1203 }
1204
1205
1206
1207
1208
1209 _SQL_get_consumable_substance = u"""
1210 SELECT *, xmin
1211 FROM ref.consumable_substance
1212 WHERE %s
1213 """
1214
1216
1217 _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s"
1218 _cmds_store_payload = [
1219 u"""UPDATE ref.consumable_substance SET
1220 description = %(description)s,
1221 atc_code = gm.nullify_empty_string(%(atc_code)s),
1222 amount = %(amount)s,
1223 unit = gm.nullify_empty_string(%(unit)s)
1224 WHERE
1225 pk = %(pk)s
1226 AND
1227 xmin = %(xmin)s
1228 AND
1229 -- must not currently be used with a patient directly
1230 NOT EXISTS (
1231 SELECT 1
1232 FROM clin.substance_intake
1233 WHERE
1234 fk_drug_component IS NULL
1235 AND
1236 fk_substance = %(pk)s
1237 LIMIT 1
1238 )
1239 AND
1240 -- must not currently be used with a patient indirectly, either
1241 NOT EXISTS (
1242 SELECT 1
1243 FROM clin.substance_intake
1244 WHERE
1245 fk_drug_component IS NOT NULL
1246 AND
1247 fk_drug_component = (
1248 SELECT r_ls2b.pk
1249 FROM ref.lnk_substance2brand r_ls2b
1250 WHERE fk_substance = %(pk)s
1251 )
1252 LIMIT 1
1253 )
1254 -- -- must not currently be used with a branded drug
1255 -- -- (but this would make it rather hard fixing branded drugs which contain only this substance)
1256 -- NOT EXISTS (
1257 -- SELECT 1
1258 -- FROM ref.lnk_substance2brand
1259 -- WHERE fk_substance = %(pk)s
1260 -- LIMIT 1
1261 -- )
1262 RETURNING
1263 xmin
1264 """
1265 ]
1266 _updatable_fields = [
1267 u'description',
1268 u'atc_code',
1269 u'amount',
1270 u'unit'
1271 ]
1272
1274 success, data = super(self.__class__, self).save_payload(conn = conn)
1275
1276 if not success:
1277 return (success, data)
1278
1279 if self._payload[self._idx['atc_code']] is not None:
1280 atc = self._payload[self._idx['atc_code']].strip()
1281 if atc != u'':
1282 gmATC.propagate_atc (
1283 substance = self._payload[self._idx['description']].strip(),
1284 atc = atc
1285 )
1286
1287 return (success, data)
1288
1289
1290
1292 cmd = u"""
1293 SELECT
1294 EXISTS (
1295 SELECT 1
1296 FROM clin.substance_intake
1297 WHERE
1298 fk_drug_component IS NULL
1299 AND
1300 fk_substance = %(pk)s
1301 LIMIT 1
1302 ) OR EXISTS (
1303 SELECT 1
1304 FROM clin.substance_intake
1305 WHERE
1306 fk_drug_component IS NOT NULL
1307 AND
1308 fk_drug_component = (
1309 SELECT r_ls2b.pk
1310 FROM ref.lnk_substance2brand r_ls2b
1311 WHERE fk_substance = %(pk)s
1312 )
1313 LIMIT 1
1314 )"""
1315 args = {'pk': self.pk_obj}
1316
1317 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1318 return rows[0][0]
1319
1320 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
1321
1323 cmd = u"""
1324 SELECT EXISTS (
1325 SELECT 1
1326 FROM ref.lnk_substance2brand
1327 WHERE fk_substance = %(pk)s
1328 LIMIT 1
1329 )"""
1330 args = {'pk': self.pk_obj}
1331
1332 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1333 return rows[0][0]
1334
1335 is_drug_component = property(_get_is_drug_component, lambda x:x)
1336
1345
1347
1348 substance = substance
1349 if atc is not None:
1350 atc = atc.strip()
1351
1352 converted, amount = gmTools.input2decimal(amount)
1353 if not converted:
1354 raise ValueError('<amount> must be a number: %s (%s)', amount, type(amount))
1355
1356 args = {
1357 'desc': substance.strip(),
1358 'amount': amount,
1359 'unit': unit.strip(),
1360 'atc': atc
1361 }
1362 cmd = u"""
1363 SELECT pk FROM ref.consumable_substance
1364 WHERE
1365 lower(description) = lower(%(desc)s)
1366 AND
1367 amount = %(amount)s
1368 AND
1369 unit = %(unit)s
1370 """
1371 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1372
1373 if len(rows) == 0:
1374 cmd = u"""
1375 INSERT INTO ref.consumable_substance (description, atc_code, amount, unit) VALUES (
1376 %(desc)s,
1377 gm.nullify_empty_string(%(atc)s),
1378 %(amount)s,
1379 gm.nullify_empty_string(%(unit)s)
1380 ) RETURNING pk"""
1381 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
1382
1383 gmATC.propagate_atc(substance = substance, atc = atc)
1384
1385 return cConsumableSubstance(aPK_obj = rows[0]['pk'])
1386
1388 args = {'pk': substance}
1389 cmd = u"""
1390 DELETE FROM ref.consumable_substance
1391 WHERE
1392 pk = %(pk)s
1393 AND
1394
1395 -- must not currently be used with a patient
1396 NOT EXISTS (
1397 SELECT 1
1398 FROM clin.v_pat_substance_intake
1399 WHERE pk_substance = %(pk)s
1400 LIMIT 1
1401 )
1402 AND
1403
1404 -- must not currently be used with a branded drug
1405 NOT EXISTS (
1406 SELECT 1
1407 FROM ref.lnk_substance2brand
1408 WHERE fk_substance = %(pk)s
1409 LIMIT 1
1410 )"""
1411 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1412 return True
1413
1415
1416 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
1417 _query1 = u"""
1418 SELECT
1419 pk::text,
1420 (description || ' ' || amount || unit) as subst
1421 FROM ref.consumable_substance
1422 WHERE description %(fragment_condition)s
1423 ORDER BY subst
1424 LIMIT 50"""
1425 _query2 = u"""
1426 SELECT
1427 pk::text,
1428 (description || ' ' || amount || unit) as subst
1429 FROM ref.consumable_substance
1430 WHERE
1431 %(fragment_condition)s
1432 ORDER BY subst
1433 LIMIT 50"""
1434
1435
1437 """Return matches for aFragment at start of phrases."""
1438
1439 if cSubstanceMatchProvider._pattern.match(aFragment):
1440 self._queries = [cSubstanceMatchProvider._query2]
1441 fragment_condition = """description ILIKE %(desc)s
1442 AND
1443 amount::text ILIKE %(amount)s"""
1444 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1445 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1446 else:
1447 self._queries = [cSubstanceMatchProvider._query1]
1448 fragment_condition = u"ILIKE %(fragment)s"
1449 self._args['fragment'] = u"%s%%" % aFragment
1450
1451 return self._find_matches(fragment_condition)
1452
1454 """Return matches for aFragment at start of words inside phrases."""
1455
1456 if cSubstanceMatchProvider._pattern.match(aFragment):
1457 self._queries = [cSubstanceMatchProvider._query2]
1458
1459 desc = regex.sub(r'\s*\d+$', u'', aFragment)
1460 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
1461
1462 fragment_condition = """description ~* %(desc)s
1463 AND
1464 amount::text ILIKE %(amount)s"""
1465
1466 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
1467 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1468 else:
1469 self._queries = [cSubstanceMatchProvider._query1]
1470 fragment_condition = u"~* %(fragment)s"
1471 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
1472 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
1473
1474 return self._find_matches(fragment_condition)
1475
1477 """Return matches for aFragment as a true substring."""
1478
1479 if cSubstanceMatchProvider._pattern.match(aFragment):
1480 self._queries = [cSubstanceMatchProvider._query2]
1481 fragment_condition = """description ILIKE %(desc)s
1482 AND
1483 amount::text ILIKE %(amount)s"""
1484 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1485 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1486 else:
1487 self._queries = [cSubstanceMatchProvider._query1]
1488 fragment_condition = u"ILIKE %(fragment)s"
1489 self._args['fragment'] = u"%%%s%%" % aFragment
1490
1491 return self._find_matches(fragment_condition)
1492
1493 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
1494 """Represents a substance currently taken by a patient."""
1495
1496 _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s"
1497 _cmds_store_payload = [
1498 u"""UPDATE clin.substance_intake SET
1499 clin_when = %(started)s,
1500 discontinued = %(discontinued)s,
1501 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s),
1502 schedule = gm.nullify_empty_string(%(schedule)s),
1503 aim = gm.nullify_empty_string(%(aim)s),
1504 narrative = gm.nullify_empty_string(%(notes)s),
1505 intake_is_approved_of = %(intake_is_approved_of)s,
1506 fk_episode = %(pk_episode)s,
1507
1508 preparation = (
1509 case
1510 when %(pk_brand)s is NULL then %(preparation)s
1511 else NULL
1512 end
1513 )::text,
1514
1515 is_long_term = (
1516 case
1517 when (
1518 (%(is_long_term)s is False)
1519 and
1520 (%(duration)s is NULL)
1521 ) is True then null
1522 else %(is_long_term)s
1523 end
1524 )::boolean,
1525
1526 duration = (
1527 case
1528 when %(is_long_term)s is True then null
1529 else %(duration)s
1530 end
1531 )::interval
1532 WHERE
1533 pk = %(pk_substance_intake)s
1534 AND
1535 xmin = %(xmin_substance_intake)s
1536 RETURNING
1537 xmin as xmin_substance_intake
1538 """
1539 ]
1540 _updatable_fields = [
1541 u'started',
1542 u'discontinued',
1543 u'discontinue_reason',
1544 u'preparation',
1545 u'intake_is_approved_of',
1546 u'schedule',
1547 u'duration',
1548 u'aim',
1549 u'is_long_term',
1550 u'notes',
1551 u'pk_episode'
1552 ]
1553
1554 - def format(self, left_margin=0, date_format='%Y-%m-%d'):
1555
1556 if self._payload[self._idx['duration']] is None:
1557 duration = gmTools.bool2subst (
1558 self._payload[self._idx['is_long_term']],
1559 _('long-term'),
1560 _('short-term'),
1561 _('?short-term')
1562 )
1563 else:
1564 duration = gmDateTime.format_interval (
1565 self._payload[self._idx['duration']],
1566 accuracy_wanted = gmDateTime.acc_days
1567 )
1568
1569 line = u'%s%s (%s %s): %s %s%s %s (%s)' % (
1570 u' ' * left_margin,
1571 self._payload[self._idx['started']].strftime(date_format),
1572 gmTools.u_right_arrow,
1573 duration,
1574 self._payload[self._idx['substance']],
1575 self._payload[self._idx['amount']],
1576 self._payload[self._idx['unit']],
1577 self._payload[self._idx['preparation']],
1578 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing'))
1579 )
1580
1581 return line
1582
1583 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
1584 allg = gmAllergy.create_allergy (
1585 allergene = self._payload[self._idx['substance']],
1586 allg_type = allergy_type,
1587 episode_id = self._payload[self._idx['pk_episode']],
1588 encounter_id = encounter_id
1589 )
1590 allg['substance'] = gmTools.coalesce (
1591 self._payload[self._idx['brand']],
1592 self._payload[self._idx['substance']]
1593 )
1594 allg['reaction'] = self._payload[self._idx['discontinue_reason']]
1595 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']])
1596 if self._payload[self._idx['external_code_brand']] is not None:
1597 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']])
1598
1599 if self._payload[self._idx['pk_brand']] is None:
1600 allg['generics'] = self._payload[self._idx['substance']]
1601 else:
1602 comps = [ c['substance'] for c in self.containing_drug.components ]
1603 if len(comps) == 0:
1604 allg['generics'] = self._payload[self._idx['substance']]
1605 else:
1606 allg['generics'] = u'; '.join(comps)
1607
1608 allg.save()
1609 return allg
1610
1611
1612
1613 - def _get_ddd(self):
1614
1615 try: self.__ddd
1616 except AttributeError: self.__ddd = None
1617
1618 if self.__ddd is not None:
1619 return self.__ddd
1620
1621 if self._payload[self._idx['atc_substance']] is not None:
1622 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']])
1623 if len(ddd) != 0:
1624 self.__ddd = ddd[0]
1625 else:
1626 if self._payload[self._idx['atc_brand']] is not None:
1627 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']])
1628 if len(ddd) != 0:
1629 self.__ddd = ddd[0]
1630
1631 return self.__ddd
1632
1633 ddd = property(_get_ddd, lambda x:x)
1634
1636 drug = self.containing_drug
1637
1638 if drug is None:
1639 return None
1640
1641 return drug.external_code
1642
1643 external_code = property(_get_external_code, lambda x:x)
1644
1646 drug = self.containing_drug
1647
1648 if drug is None:
1649 return None
1650
1651 return drug.external_code_type
1652
1653 external_code_type = property(_get_external_code_type, lambda x:x)
1654
1656 if self._payload[self._idx['pk_brand']] is None:
1657 return None
1658
1659 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1660
1661 containing_drug = property(_get_containing_drug, lambda x:x)
1662
1664 tests = [
1665
1666 ' 1-1-1-1 ',
1667
1668 '1-1-1-1',
1669 '22-1-1-1',
1670 '1/3-1-1-1',
1671 '/4-1-1-1'
1672 ]
1673 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}$"
1674 for test in tests:
1675 print test.strip(), ":", regex.match(pattern, test.strip())
1676
1677 -def create_substance_intake(pk_substance=None, pk_component=None, preparation=None, encounter=None, episode=None):
1678
1679 args = {
1680 'enc': encounter,
1681 'epi': episode,
1682 'comp': pk_component,
1683 'subst': pk_substance,
1684 'prep': preparation
1685 }
1686
1687 if pk_component is None:
1688 cmd = u"""
1689 INSERT INTO clin.substance_intake (
1690 fk_encounter,
1691 fk_episode,
1692 intake_is_approved_of,
1693 fk_substance,
1694 preparation
1695 ) VALUES (
1696 %(enc)s,
1697 %(epi)s,
1698 False,
1699 %(subst)s,
1700 %(prep)s
1701 )
1702 RETURNING pk"""
1703 else:
1704 cmd = u"""
1705 INSERT INTO clin.substance_intake (
1706 fk_encounter,
1707 fk_episode,
1708 intake_is_approved_of,
1709 fk_drug_component
1710 ) VALUES (
1711 %(enc)s,
1712 %(epi)s,
1713 False,
1714 %(comp)s
1715 )
1716 RETURNING pk"""
1717
1718 try:
1719 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
1720 except gmPG2.dbapi.InternalError, e:
1721 if e.pgerror is None:
1722 raise
1723 if 'prevent_duplicate_component' in e.pgerror:
1724 _log.exception('will not create duplicate substance intake entry')
1725 _log.error(e.pgerror)
1726 return None
1727 raise
1728
1729 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
1730
1734
1769
1770
1847
1848 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s'
1849
1851
1852 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s'
1853 _cmds_store_payload = [
1854 u"""UPDATE ref.lnk_substance2brand SET
1855 fk_brand = %(pk_brand)s,
1856 fk_substance = %(pk_consumable_substance)s
1857 WHERE
1858 NOT EXISTS (
1859 SELECT 1
1860 FROM clin.substance_intake
1861 WHERE fk_drug_component = %(pk_component)s
1862 LIMIT 1
1863 )
1864 AND
1865 pk = %(pk_component)s
1866 AND
1867 xmin = %(xmin_lnk_substance2brand)s
1868 RETURNING
1869 xmin AS xmin_lnk_substance2brand
1870 """
1871 ]
1872 _updatable_fields = [
1873 u'pk_brand',
1874 u'pk_consumable_substance'
1875 ]
1876
1877
1878
1880 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1881
1882 containing_drug = property(_get_containing_drug, lambda x:x)
1883
1885 return self._payload[self._idx['is_in_use']]
1886
1887 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
1888
1891
1892 substance = property(_get_substance, lambda x:x)
1893
1898
1900
1901 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
1902 _query_desc_only = u"""
1903 SELECT DISTINCT ON (component)
1904 pk_component,
1905 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
1906 AS component
1907 FROM ref.v_drug_components
1908 WHERE
1909 substance %(fragment_condition)s
1910 OR
1911 brand %(fragment_condition)s
1912 ORDER BY component
1913 LIMIT 50"""
1914 _query_desc_and_amount = u"""
1915
1916 SELECT DISTINCT ON (component)
1917 pk_component,
1918 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
1919 AS component
1920 FROM ref.v_drug_components
1921 WHERE
1922 %(fragment_condition)s
1923 ORDER BY component
1924 LIMIT 50"""
1925
1927 """Return matches for aFragment at start of phrases."""
1928
1929 if cDrugComponentMatchProvider._pattern.match(aFragment):
1930 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
1931 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
1932 AND
1933 amount::text ILIKE %(amount)s"""
1934 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1935 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1936 else:
1937 self._queries = [cDrugComponentMatchProvider._query_desc_only]
1938 fragment_condition = u"ILIKE %(fragment)s"
1939 self._args['fragment'] = u"%s%%" % aFragment
1940
1941 return self._find_matches(fragment_condition)
1942
1944 """Return matches for aFragment at start of words inside phrases."""
1945
1946 if cDrugComponentMatchProvider._pattern.match(aFragment):
1947 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
1948
1949 desc = regex.sub(r'\s*\d+$', u'', aFragment)
1950 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
1951
1952 fragment_condition = """(substance ~* %(desc)s OR brand ~* %(desc)s)
1953 AND
1954 amount::text ILIKE %(amount)s"""
1955
1956 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
1957 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1958 else:
1959 self._queries = [cDrugComponentMatchProvider._query_desc_only]
1960 fragment_condition = u"~* %(fragment)s"
1961 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
1962 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
1963
1964 return self._find_matches(fragment_condition)
1965
1967 """Return matches for aFragment as a true substring."""
1968
1969 if cDrugComponentMatchProvider._pattern.match(aFragment):
1970 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
1971 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
1972 AND
1973 amount::text ILIKE %(amount)s"""
1974 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1975 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1976 else:
1977 self._queries = [cDrugComponentMatchProvider._query_desc_only]
1978 fragment_condition = u"ILIKE %(fragment)s"
1979 self._args['fragment'] = u"%%%s%%" % aFragment
1980
1981 return self._find_matches(fragment_condition)
1982
1983
1985 """Represents a drug as marketed by a manufacturer."""
1986
1987 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s"
1988 _cmds_store_payload = [
1989 u"""UPDATE ref.branded_drug SET
1990 description = %(brand)s,
1991 preparation = %(preparation)s,
1992 atc_code = gm.nullify_empty_string(%(atc)s),
1993 external_code = gm.nullify_empty_string(%(external_code)s),
1994 external_code_type = gm.nullify_empty_string(%(external_code_type)s),
1995 is_fake = %(is_fake_brand)s,
1996 fk_data_source = %(pk_data_source)s
1997 WHERE
1998 pk = %(pk_brand)s
1999 AND
2000 xmin = %(xmin_branded_drug)s
2001 RETURNING
2002 xmin AS xmin_branded_drug
2003 """
2004 ]
2005 _updatable_fields = [
2006 u'brand',
2007 u'preparation',
2008 u'atc',
2009 u'is_fake_brand',
2010 u'external_code',
2011 u'external_code_type',
2012 u'pk_data_source'
2013 ]
2014
2016 success, data = super(self.__class__, self).save_payload(conn = conn)
2017
2018 if not success:
2019 return (success, data)
2020
2021 if self._payload[self._idx['atc']] is not None:
2022 atc = self._payload[self._idx['atc']].strip()
2023 if atc != u'':
2024 gmATC.propagate_atc (
2025 substance = self._payload[self._idx['brand']].strip(),
2026 atc = atc
2027 )
2028
2029 return (success, data)
2030
2032
2033 if self._payload[self._idx['is_in_use']]:
2034 return False
2035
2036 args = {'brand': self._payload[self._idx['pk_brand']]}
2037
2038 queries = [{'cmd': u"DELETE FROM ref.lnk_substance2brand WHERE fk_brand = %(brand)s", 'args': args}]
2039 cmd = u'INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) VALUES (%%(brand)s, %s)'
2040 for s in substances:
2041 queries.append({'cmd': cmd % s['pk'], 'args': args})
2042
2043 gmPG2.run_rw_queries(queries = queries)
2044 self.refetch_payload()
2045
2046 return True
2047
2048 - def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
2049
2050 args = {
2051 'brand': self.pk_obj,
2052 'subst': substance,
2053 'atc': atc,
2054 'pk_subst': pk_substance
2055 }
2056
2057 if pk_substance is None:
2058 consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit)
2059 args['pk_subst'] = consumable['pk']
2060
2061
2062 cmd = u"""
2063 SELECT pk_component
2064 FROM ref.v_drug_components
2065 WHERE
2066 pk_brand = %(brand)s
2067 AND
2068 ((
2069 (lower(substance) = lower(%(subst)s))
2070 OR
2071 (lower(atc_substance) = lower(%(atc)s))
2072 OR
2073 (pk_consumable_substance = %(pk_subst)s)
2074 ) IS TRUE)
2075 """
2076 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2077
2078 if len(rows) > 0:
2079 return
2080
2081
2082 cmd = u"""
2083 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance)
2084 VALUES (%(brand)s, %(pk_subst)s)
2085 """
2086 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2087 self.refetch_payload()
2088
2090 if len(self._payload[self._idx['components']]) == 1:
2091 _log.error('cannot remove the only component of a drug')
2092 return False
2093
2094 args = {'brand': self.pk_obj, 'comp': substance}
2095 cmd = u"""
2096 DELETE FROM ref.lnk_substance2brand
2097 WHERE
2098 fk_brand = %(brand)s
2099 AND
2100 fk_substance = %(comp)s
2101 AND
2102 NOT EXISTS (
2103 SELECT 1
2104 FROM clin.substance_intake
2105 WHERE fk_drug_component = %(comp)s
2106 LIMIT 1
2107 )
2108 """
2109 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2110 self.refetch_payload()
2111
2112 return True
2113
2114
2115
2117 if self._payload[self._idx['external_code']] is None:
2118 return None
2119
2120 return self._payload[self._idx['external_code']]
2121
2122 external_code = property(_get_external_code, lambda x:x)
2123
2125
2126
2127 if self._payload[self._idx['external_code_type']] is None:
2128 return None
2129
2130 return self._payload[self._idx['external_code_type']]
2131
2132 external_code_type = property(_get_external_code_type, lambda x:x)
2133
2135 cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s'
2136 args = {'brand': self._payload[self._idx['pk_brand']]}
2137 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2138 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
2139
2140 components = property(_get_components, lambda x:x)
2141
2143 if self._payload[self._idx['pk_substances']] is None:
2144 return []
2145 cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s'
2146 args = {'pks': tuple(self._payload[self._idx['pk_substances']])}
2147 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2148 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
2149
2150 components_as_substances = property(_get_components_as_substances, lambda x:x)
2151
2153 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)'
2154 args = {'fk_brand': self._payload[self._idx['pk_brand']]}
2155 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2156 return rows[0][0]
2157
2158 is_vaccine = property(_get_is_vaccine, lambda x:x)
2159
2161 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description'
2162 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
2163 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
2164
2166 args = {'brand': brand_name, 'prep': preparation}
2167
2168 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)'
2169 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2170
2171 if len(rows) == 0:
2172 return None
2173
2174 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2175
2177
2178 if preparation is None:
2179 preparation = _('units')
2180
2181 if preparation.strip() == u'':
2182 preparation = _('units')
2183
2184 if return_existing:
2185 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation)
2186 if drug is not None:
2187 return drug
2188
2189 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk'
2190 args = {'brand': brand_name, 'prep': preparation}
2191 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
2192
2193 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2194
2196 queries = []
2197 args = {'pk': brand}
2198
2199
2200 cmd = u"""
2201 DELETE FROM ref.lnk_substance2brand
2202 WHERE
2203 fk_brand = %(pk)s
2204 AND
2205 NOT EXISTS (
2206 SELECT 1
2207 FROM clin.v_pat_substance_intake
2208 WHERE pk_brand = %(pk)s
2209 LIMIT 1
2210 )
2211 """
2212 queries.append({'cmd': cmd, 'args': args})
2213
2214
2215 cmd = u"""
2216 DELETE FROM ref.branded_drug
2217 WHERE
2218 pk = %(pk)s
2219 AND
2220 NOT EXISTS (
2221 SELECT 1
2222 FROM clin.v_pat_substance_intake
2223 WHERE pk_brand = %(pk)s
2224 LIMIT 1
2225 )
2226 """
2227 queries.append({'cmd': cmd, 'args': args})
2228
2229 gmPG2.run_rw_queries(queries = queries)
2230
2231
2232
2233 if __name__ == "__main__":
2234
2235 if len(sys.argv) < 2:
2236 sys.exit()
2237
2238 if sys.argv[1] != 'test':
2239 sys.exit()
2240
2241 from Gnumed.pycommon import gmLog2
2242 from Gnumed.pycommon import gmI18N
2243 from Gnumed.business import gmPerson
2244
2245 gmI18N.activate_locale()
2246
2247
2253
2255 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
2256 for drug in mmi_file:
2257 print "-------------"
2258 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2259 for stoff in drug['wirkstoffe']:
2260 print " Wirkstoff:", stoff
2261 raw_input()
2262 if mmi_file.has_unknown_fields is not None:
2263 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key
2264 for key in mmi_file.csv_fieldnames:
2265 print key, '->', drug[key]
2266 raw_input()
2267 mmi_file.close()
2268
2272
2274 mmi = cGelbeListeWineInterface()
2275 mmi_file = mmi.__let_user_select_drugs()
2276 for drug in mmi_file:
2277 print "-------------"
2278 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2279 for stoff in drug['wirkstoffe']:
2280 print " Wirkstoff:", stoff
2281 print drug
2282 mmi_file.close()
2283
2287
2289 mmi = cGelbeListeInterface()
2290 print mmi
2291 print "interface definition:", mmi.version
2292
2293 diclofenac = '7587712'
2294 phenprocoumon = '4421744'
2295 mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
2296
2297
2298
2305
2311
2312
2313
2315 drug = create_substance_intake (
2316 pk_component = 2,
2317 encounter = 1,
2318 episode = 1
2319 )
2320 print drug
2321
2326
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341 test_fd_switch_to()
2342
2343
2344
2345
2346
2347
2348
2349
2350