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 if isinstance(search_term, basestring):
58 if search_term.strip() == u'':
59 return u'http://www.dosing.de'
60
61 terms = []
62 names = []
63
64 if isinstance(search_term, cBrandedDrug):
65 if search_term['atc'] is not None:
66 terms.append(search_term['atc'])
67
68 elif isinstance(search_term, cSubstanceIntakeEntry):
69 names.append(search_term['substance'])
70 if search_term['atc_brand'] is not None:
71 terms.append(search_term['atc_brand'])
72 if search_term['atc_substance'] is not None:
73 terms.append(search_term['atc_substance'])
74
75 elif isinstance(search_term, cDrugComponent):
76 names.append(search_term['substance'])
77 if search_term['atc_brand'] is not None:
78 terms.append(search_term['atc_brand'])
79 if search_term['atc_substance'] is not None:
80 terms.append(search_term['atc_substance'])
81
82 elif isinstance(search_term, cConsumableSubstance):
83 names.append(search_term['description'])
84 if search_term['atc_code'] is not None:
85 terms.append(search_term['atc_code'])
86
87 elif search_term is not None:
88 names.append(u'%s' % search_term)
89 terms.extend(gmATC.text2atc(text = u'%s' % search_term, fuzzy = True))
90
91 for name in names:
92 if name.endswith('e'):
93 terms.append(name[:-1])
94 else:
95 terms.append(name)
96
97
98
99
100 url_template = u'http://www.google.com/search?hl=de&source=hp&q=site%%3Adosing.de+%s&btnG=Google-Suche'
101 url = url_template % u'+OR+'.join(terms)
102
103 _log.debug(u'renal insufficiency URL: %s', url)
104
105 return url
106
107
108 -def create_data_source(long_name=None, short_name=None, version=None, source=None, language=None):
109
110 args = {
111 'lname': long_name,
112 'sname': short_name,
113 'ver': version,
114 'src': source,
115 'lang': language
116 }
117
118 cmd = u"""select pk from ref.data_source where name_long = %(lname)s and name_short = %(sname)s and version = %(ver)s"""
119 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
120 if len(rows) > 0:
121 return rows[0]['pk']
122
123 cmd = u"""
124 INSERT INTO ref.data_source (name_long, name_short, version, source, lang)
125 VALUES (
126 %(lname)s,
127 %(sname)s,
128 %(ver)s,
129 %(src)s,
130 %(lang)s
131 )
132 returning pk
133 """
134 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
135
136 return rows[0]['pk']
137
138
139
140
141
142
143
145 """Iterator over a Gelbe Liste/MMI v8.2 CSV file."""
146
147 version = u'Gelbe Liste/MMI v8.2 CSV file interface'
148 default_transfer_file_windows = r"c:\rezept.txt"
149
150 default_encoding = 'cp1250'
151 csv_fieldnames = [
152 u'name',
153 u'packungsgroesse',
154 u'darreichungsform',
155 u'packungstyp',
156 u'festbetrag',
157 u'avp',
158 u'hersteller',
159 u'rezepttext',
160 u'pzn',
161 u'status_vertrieb',
162 u'status_rezeptpflicht',
163 u'status_fachinfo',
164 u'btm',
165 u'atc',
166 u'anzahl_packungen',
167 u'zuzahlung_pro_packung',
168 u'einheit',
169 u'schedule_morgens',
170 u'schedule_mittags',
171 u'schedule_abends',
172 u'schedule_nachts',
173 u'status_dauermedikament',
174 u'status_hausliste',
175 u'status_negativliste',
176 u'ik_nummer',
177 u'status_rabattvertrag',
178 u'wirkstoffe',
179 u'wirkstoffmenge',
180 u'wirkstoffeinheit',
181 u'wirkstoffmenge_bezug',
182 u'wirkstoffmenge_bezugseinheit',
183 u'status_import',
184 u'status_lifestyle',
185 u'status_ausnahmeliste',
186 u'packungsmenge',
187 u'apothekenpflicht',
188 u'status_billigere_packung',
189 u'rezepttyp',
190 u'besonderes_arzneimittel',
191 u't_rezept_pflicht',
192 u'erstattbares_medizinprodukt',
193 u'hilfsmittel',
194 u'hzv_rabattkennung',
195 u'hzv_preis'
196 ]
197 boolean_fields = [
198 u'status_rezeptpflicht',
199 u'status_fachinfo',
200 u'btm',
201 u'status_dauermedikament',
202 u'status_hausliste',
203 u'status_negativliste',
204 u'status_rabattvertrag',
205 u'status_import',
206 u'status_lifestyle',
207 u'status_ausnahmeliste',
208 u'apothekenpflicht',
209 u'status_billigere_packung',
210 u'besonderes_arzneimittel',
211 u't_rezept_pflicht',
212 u'erstattbares_medizinprodukt',
213 u'hilfsmittel'
214 ]
215
235
238
240 line = self.csv_lines.next()
241
242 for field in cGelbeListeCSVFile.boolean_fields:
243 line[field] = (line[field].strip() == u'T')
244
245
246 if line['wirkstoffe'].strip() == u'':
247 line['wirkstoffe'] = []
248 else:
249 line['wirkstoffe'] = [ wirkstoff.strip() for wirkstoff in line['wirkstoffe'].split(u';') ]
250
251 return line
252
253 - def close(self, truncate=True):
254 try: self.csv_file.close()
255 except: pass
256
257 if truncate:
258 try: os.open(self.filename, 'wb').close
259 except: pass
260
263
264 has_unknown_fields = property(_get_has_unknown_fields, lambda x:x)
265
267
268
270 self.patient = None
271 self.reviewer = None
272 self.custom_path_to_binary = None
273
275 raise NotImplementedError
276
278 raise NotImplementedError
279
281 raise NotImplementedError
282
285
288
291
294
295 - def prescribe(self, substance_intakes=None):
298
300
301 version = u'FreeDiams interface'
302 default_encoding = 'utf8'
303 default_dob_format = '%Y/%m/%d'
304
305 map_gender2mf = {
306 'm': u'M',
307 'f': u'F',
308 'tf': u'H',
309 'tm': u'H',
310 'h': u'H'
311 }
312
328
330
331
332 if not self.__detect_binary():
333 return False
334
335 freediams = subprocess.Popen (
336 args = u'--version',
337 executable = self.path_to_binary,
338 stdout = subprocess.PIPE,
339 stderr = subprocess.PIPE,
340
341 universal_newlines = True
342 )
343 data, errors = freediams.communicate()
344 version = regex.search('FreeDiams\s\d.\d.\d', data).group().split()[1]
345 _log.debug('FreeDiams %s', version)
346
347 return version
348
350 return create_data_source (
351 long_name = u'"FreeDiams" Drug Database Frontend',
352 short_name = u'FreeDiams',
353 version = self.get_data_source_version(),
354 source = u'http://ericmaeker.fr/FreeMedForms/di-manual/index.html',
355 language = u'fr'
356 )
357
359 """http://ericmaeker.fr/FreeMedForms/di-manual/en/html/ligne_commandes.html"""
360
361 _log.debug('calling FreeDiams in [%s] mode', mode)
362
363 self.__imported_drugs = []
364
365 if not self.__detect_binary():
366 return False
367
368 self.__create_gm2fd_file(mode = mode)
369
370 args = u'--exchange-in="%s"' % (self.__gm2fd_filename)
371 cmd = r'%s %s' % (self.path_to_binary, args)
372 if os.name == 'nt':
373 blocking = True
374 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
375 _log.error('problem switching to the FreeDiams drug database')
376 return False
377
378 if blocking == True:
379 self.import_fd2gm_file_as_drugs()
380
381 return True
382
385
387 if substance_intakes is None:
388 return
389 if len(substance_intakes) < 2:
390 return
391
392 self.__create_prescription_file(substance_intakes = substance_intakes)
393 self.switch_to_frontend(mode = 'interactions', blocking = False)
394
396 if substance_intake is None:
397 return
398
399 self.__create_prescription_file(substance_intakes = [substance_intake])
400 self.switch_to_frontend(mode = 'interactions', blocking = False)
401
404
405 - def prescribe(self, substance_intakes=None):
406 if substance_intakes is None:
407 if not self.__export_latest_prescription():
408 self.__create_prescription_file()
409 else:
410 self.__create_prescription_file(substance_intakes = substance_intakes)
411
412 self.switch_to_frontend(mode = 'prescription', blocking = True)
413 self.import_fd2gm_file_as_prescription()
414
415 return self.__imported_drugs
416
417
418
420
421 if self.path_to_binary is not None:
422 return True
423
424 found, cmd = gmShellAPI.find_first_binary(binaries = [
425 r'/usr/bin/freediams',
426 r'freediams',
427 r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams',
428 r'C:\Program Files (x86)\FreeDiams\freediams.exe',
429 r'C:\Program Files\FreeDiams\freediams.exe',
430 r'c:\programs\freediams\freediams.exe',
431 r'freediams.exe'
432 ])
433
434 if found:
435 self.path_to_binary = cmd
436 return True
437
438 try:
439 self.custom_path_to_binary
440 except AttributeError:
441 _log.error('cannot find FreeDiams binary, no custom path set')
442 return False
443
444 if self.custom_path_to_binary is None:
445 _log.error('cannot find FreeDiams binary')
446 return False
447
448 found, cmd = gmShellAPI.detect_external_binary(binary = self.custom_path_to_binary)
449 if found:
450 self.path_to_binary = cmd
451 return True
452
453 _log.error('cannot find FreeDiams binary')
454 return False
455
457
458 if self.patient is None:
459 _log.debug('cannot export latest FreeDiams prescriptions w/o patient')
460 return False
461
462 docs = self.patient.get_document_folder()
463 prescription = docs.get_latest_freediams_prescription()
464 if prescription is None:
465 _log.debug('no FreeDiams prescription available')
466 return False
467
468 for part in prescription.parts:
469 if part['filename'] == u'freediams-prescription.xml':
470 if part.export_to_file(filename = self.__fd2gm_filename) is not None:
471 return True
472
473 _log.error('cannot export latest FreeDiams prescription to XML file')
474
475 return False
476
478 """FreeDiams calls this exchange-out or prescription file.
479
480 CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel).
481 CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS.
482 AFSSAPS is the French FDA.
483
484 CIP stands for Unique Presentation Identifier (eg 30 pills plaq)
485 CIP if you want to specify the packaging of the drug (30 pills
486 thermoformed tablet...) -- actually not really usefull for french
487 doctors.
488 # .external_code_type: u'FR-CIS'
489 # .external_cod: the CIS value
490
491 OnlyForTest:
492 OnlyForTest drugs will be processed by the IA Engine but
493 not printed (regardless of FreeDiams mode). They are shown
494 in gray in the prescription view.
495
496 Select-only is a mode where FreeDiams creates a list of drugs
497 not a full prescription. In this list, users can add ForTestOnly
498 drug if they want to
499 1. print the list without some drugs
500 2. but including these drugs in the IA engine calculation
501
502 Select-Only mode does not have any relation with the ForTestOnly drugs.
503
504 IsTextual:
505 What is the use and significance of the
506 <IsTextual>true/false</IsTextual>
507 flag when both <DrugName> and <TextualDrugName> exist ?
508
509 This tag must be setted even if it sounds like a duplicated
510 data. This tag is needed inside FreeDiams code.
511
512 INN:
513 GNUmed will pass the substance in <TextualDrugName
514 and will also pass <INN>True</INN>.
515
516 Eric: Nop, this is not usefull because pure textual drugs
517 are not processed but just shown.
518 """
519
520 open(self.__fd2gm_filename, 'wb').close()
521
522
523 if substance_intakes is None:
524 if self.patient is None:
525 _log.warning('cannot create prescription file because there is neither a patient nor a substance intake list')
526
527 return False
528 emr = self.patient.get_emr()
529 substance_intakes = emr.get_current_substance_intake (
530 include_inactive = False,
531 include_unapproved = True
532 )
533
534 drug_snippets = []
535
536
537 fd_intakes = [ i for i in substance_intakes if (
538 (i['intake_is_approved_of'] is True)
539 and
540 (i['external_code_type_brand'] is not None)
541 and
542 (i['external_code_type_brand'].startswith(u'FreeDiams::'))
543 )]
544
545 intakes_pooled_by_brand = {}
546 for intake in fd_intakes:
547
548
549 intakes_pooled_by_brand[intake['brand']] = intake
550 del fd_intakes
551
552 drug_snippet = u"""<Prescription>
553 <IsTextual>False</IsTextual>
554 <DrugName>%s</DrugName>
555 <Drug_UID>%s</Drug_UID>
556 <Drug_UID_type>%s</Drug_UID_type> <!-- not yet supported by FreeDiams -->
557 </Prescription>"""
558
559 last_db_id = u'CA_HCDPD'
560 for intake in intakes_pooled_by_brand.values():
561 last_db_id = gmTools.xml_escape_string(text = intake['external_code_type_brand'].replace(u'FreeDiams::', u'').split(u'::')[0])
562 drug_snippets.append(drug_snippet % (
563 gmTools.xml_escape_string(text = intake['brand'].strip()),
564 gmTools.xml_escape_string(text = intake['external_code_brand'].strip()),
565 last_db_id
566 ))
567
568
569 non_fd_intakes = [ i for i in substance_intakes if (
570 (i['intake_is_approved_of'] is True)
571 and (
572 (i['external_code_type_brand'] is None)
573 or
574 (not i['external_code_type_brand'].startswith(u'FreeDiams::'))
575 )
576 )]
577
578 non_fd_brand_intakes = [ i for i in non_fd_intakes if i['brand'] is not None ]
579 non_fd_substance_intakes = [ i for i in non_fd_intakes if i['brand'] is None ]
580 del non_fd_intakes
581
582 drug_snippet = u"""<Prescription>
583 <IsTextual>True</IsTextual>
584 <TextualDrugName>%s</TextualDrugName>
585 </Prescription>"""
586
587 for intake in non_fd_substance_intakes:
588 drug_name = u'%s %s%s (%s)%s' % (
589 intake['substance'],
590 intake['amount'],
591 intake['unit'],
592 intake['preparation'],
593 gmTools.coalesce(intake['schedule'], u'', _('\n Take: %s'))
594 )
595 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
596
597 intakes_pooled_by_brand = {}
598 for intake in non_fd_brand_intakes:
599 brand = u'%s %s' % (intake['brand'], intake['preparation'])
600 try:
601 intakes_pooled_by_brand[brand].append(intake)
602 except KeyError:
603 intakes_pooled_by_brand[brand] = [intake]
604
605 for brand, comps in intakes_pooled_by_brand.iteritems():
606 drug_name = u'%s\n' % brand
607 for comp in comps:
608 drug_name += u' %s %s%s\n' % (
609 comp['substance'],
610 comp['amount'],
611 comp['unit']
612 )
613 if comps[0]['schedule'] is not None:
614 drug_name += gmTools.coalesce(comps[0]['schedule'], u'', _('Take: %s'))
615 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
616
617
618 xml = u"""<?xml version = "1.0" encoding = "UTF-8"?>
619
620 <FreeDiams>
621 <DrugsDatabaseName>%s</DrugsDatabaseName>
622 <FullPrescription version="0.5.0">
623
624 %s
625
626 </FullPrescription>
627 </FreeDiams>
628 """
629
630 xml_file = codecs.open(self.__fd2gm_filename, 'wb', 'utf8')
631 xml_file.write(xml % (
632 last_db_id,
633 u'\n\t\t'.join(drug_snippets)
634 ))
635 xml_file.close()
636
637 return True
638
640
641 if mode == 'interactions':
642 mode = u'select-only'
643 elif mode == 'prescription':
644 mode = u'prescriber'
645 else:
646 mode = u'select-only'
647
648 xml_file = codecs.open(self.__gm2fd_filename, 'wb', 'utf8')
649
650 xml = u"""<?xml version="1.0" encoding="UTF-8"?>
651
652 <FreeDiams_In version="0.5.0">
653 <EMR name="GNUmed" uid="unused"/>
654 <ConfigFile value="%s"/>
655 <ExchangeOut value="%s" format="xml"/>
656 <!-- <DrugsDatabase uid="can be set to a specific DB"/> -->
657 <Ui editmode="%s" blockPatientDatas="1"/>
658 %%s
659 </FreeDiams_In>
660
661 <!--
662 # FIXME: search by LOINC code and add (as soon as supported by FreeDiams ...)
663 <Creatinine value="12" unit="mg/l or mmol/l"/>
664 <Weight value="70" unit="kg or pd" />
665 <Height value="170" unit="cm or "/>
666 <ICD10 value="J11.0;A22;Z23"/>
667 -->
668 """ % (
669 self.__fd4gm_config_file,
670 self.__fd2gm_filename,
671 mode
672 )
673
674 if self.patient is None:
675 xml_file.write(xml % u'')
676 xml_file.close()
677 return
678
679 name = self.patient.get_active_name()
680 if self.patient['dob'] is None:
681 dob = u''
682 else:
683 dob = self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format)
684
685 emr = self.patient.get_emr()
686 allgs = emr.get_allergies()
687 atc_allgs = [
688 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'allergy'))
689 ]
690 atc_sens = [
691 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'sensitivity'))
692 ]
693 inn_allgs = [
694 a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'allergy'))
695 ]
696 inn_sens = [
697 a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'sensitivity'))
698 ]
699
700
701
702 uid_allgs = [
703 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'allergy'))
704 ]
705 uid_sens = [
706 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'sensitivity'))
707 ]
708
709 patient_xml = u"""<Patient>
710 <Identity
711 lastnames="%s"
712 firstnames="%s"
713 uid="%s"
714 dob="%s"
715 gender="%s"
716 />
717 <ATCAllergies value="%s"/>
718 <ATCIntolerances value="%s"/>
719
720 <InnAllergies value="%s"/>
721 <InnIntolerances value="%s"/>
722
723 <DrugsUidAllergies value="%s"/>
724 <DrugsUidIntolerances value="%s"/>
725 </Patient>
726 """ % (
727 gmTools.xml_escape_string(text = name['lastnames']),
728 gmTools.xml_escape_string(text = name['firstnames']),
729 self.patient.ID,
730 dob,
731 cFreeDiamsInterface.map_gender2mf[self.patient['gender']],
732 gmTools.xml_escape_string(text = u';'.join(atc_allgs)),
733 gmTools.xml_escape_string(text = u';'.join(atc_sens)),
734 gmTools.xml_escape_string(text = u';'.join(inn_allgs)),
735 gmTools.xml_escape_string(text = u';'.join(inn_sens)),
736 gmTools.xml_escape_string(text = u';'.join(uid_allgs)),
737 gmTools.xml_escape_string(text = u';'.join(uid_sens))
738 )
739
740 xml_file.write(xml % patient_xml)
741 xml_file.close()
742
795
797 """
798 If returning textual prescriptions (say, drugs which FreeDiams
799 did not know) then "IsTextual" will be True and UID will be -1.
800 """
801 if filename is None:
802 filename = self.__fd2gm_filename
803
804
805
806 fd2gm_xml = etree.ElementTree()
807 fd2gm_xml.parse(filename)
808
809 data_src_pk = self.create_data_source_entry()
810
811 xml_version = fd2gm_xml.find('FullPrescription').attrib['version']
812 _log.debug('fd2gm file version: %s', xml_version)
813
814 if xml_version == '0.6.0':
815 return self.__import_fd2gm_file_as_drugs_0_6_0(fd2gm_xml = fd2gm_xml, pk_data_source = data_src_pk)
816
817 return self.__import_fd2gm_file_as_drugs_0_5(fd2gm_xml = fd2gm_xml, pk_data_source = data_src_pk)
818
820
821
822 fd_xml_prescriptions = fd2gm_xml.findall('FullPrescription/Prescription')
823
824 self.__imported_drugs = []
825 for fd_xml_prescription in fd_xml_prescriptions:
826 drug_db = fd_xml_prescription.find('Drug').attrib['db'].strip()
827 drug_uid = fd_xml_prescription.find('Drug').attrib['u1'].strip()
828
829 drug_uid_name = u'<%s>' % drug_db
830 if drug_uid == u'-1':
831 _log.debug('skipping textual drug')
832 continue
833 drug_name = fd_xml_prescription.find('Drug/DrugName').text.replace(', )', ')').strip()
834 drug_form = fd_xml_prescription.find('Drug/DrugForm').text.strip()
835
836
837
838
839
840
841
842
843
844
845 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True)
846 self.__imported_drugs.append(new_drug)
847 new_drug['is_fake_brand'] = False
848
849 new_drug['external_code_type'] = u'FreeDiams::%s::%s' % (drug_db, drug_uid_name)
850 new_drug['external_code'] = drug_uid
851 new_drug['pk_data_source'] = pk_data_source
852 new_drug.save()
853
854
855 fd_xml_components = fd_xml_prescription.getiterator('Composition')
856 comp_data = {}
857 for fd_xml_comp in fd_xml_components:
858
859 data = {}
860
861 xml_strength = fd_xml_comp.attrib['strength'].strip()
862 amount = regex.match(r'^\d+[.,]{0,1}\d*', xml_strength)
863 if amount is None:
864 amount = 99999
865 else:
866 amount = amount.group()
867 data['amount'] = amount
868
869
870 unit = (xml_strength[len(amount):]).strip()
871 if unit == u'':
872 unit = u'*?*'
873 data['unit'] = unit
874
875
876 atc = regex.match(r'[A-Za-z]\d\d[A-Za-z]{2}\d\d', fd_xml_comp.attrib['atc'].strip())
877 if atc is None:
878 data['atc'] = None
879 else:
880 atc = atc.group()
881 data['atc'] = atc
882
883 molecule_name = fd_xml_comp.attrib['molecularName'].strip()
884 if molecule_name != u'':
885 create_consumable_substance(substance = molecule_name, atc = atc, amount = amount, unit = unit)
886 data['molecule_name'] = molecule_name
887
888 inn_name = fd_xml_comp.attrib['inn'].strip()
889 if inn_name != u'':
890 create_consumable_substance(substance = inn_name, atc = atc, amount = amount, unit = unit)
891
892 data['inn_name'] = inn_name
893
894 if molecule_name == u'':
895 data['substance'] = inn_name
896 _log.info('linking INN [%s] rather than molecularName as component', inn_name)
897 else:
898 data['substance'] = molecule_name
899
900 data['nature'] = fd_xml_comp.attrib['nature'].strip()
901 data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
902
903
904 try:
905 old_data = comp_data[data['nature_ID']]
906
907 if old_data['inn_name'] == u'':
908 old_data['inn_name'] = data['inn_name']
909 if data['inn_name'] == u'':
910 data['inn_name'] = old_data['inn_name']
911
912 if old_data['molecule_name'] == u'':
913 old_data['molecule_name'] = data['molecule_name']
914 if data['molecule_name'] == u'':
915 data['molecule_name'] = old_data['molecule_name']
916
917 if old_data['atc'] == u'':
918 old_data['atc'] = data['atc']
919 if data['atc'] == u'':
920 data['atc'] = old_data['atc']
921
922
923
924
925
926
927 if data['nature'] == u'FT':
928 comp_data[data['nature_ID']] = data
929 else:
930 comp_data[data['nature_ID']] = old_data
931
932
933 except KeyError:
934 comp_data[data['nature_ID']] = data
935
936
937 for key, data in comp_data.items():
938 new_drug.add_component (
939 substance = data['substance'],
940 atc = data['atc'],
941 amount = data['amount'],
942 unit = data['unit']
943 )
944
946
947 db_def = fd2gm_xml.find('DrugsDatabaseName')
948 db_id = db_def.text.strip()
949 drug_id_name = db_def.attrib['drugUidName']
950 fd_xml_drug_entries = fd2gm_xml.findall('FullPrescription/Prescription')
951
952 self.__imported_drugs = []
953 for fd_xml_drug in fd_xml_drug_entries:
954 drug_uid = fd_xml_drug.find('Drug_UID').text.strip()
955 if drug_uid == u'-1':
956 _log.debug('skipping textual drug')
957 continue
958 drug_name = fd_xml_drug.find('DrugName').text.replace(', )', ')').strip()
959 drug_form = fd_xml_drug.find('DrugForm').text.strip()
960 drug_atc = fd_xml_drug.find('DrugATC')
961 if drug_atc is None:
962 drug_atc = u''
963 else:
964 if drug_atc.text is None:
965 drug_atc = u''
966 else:
967 drug_atc = drug_atc.text.strip()
968
969
970 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True)
971 self.__imported_drugs.append(new_drug)
972 new_drug['is_fake_brand'] = False
973 new_drug['atc'] = drug_atc
974 new_drug['external_code_type'] = u'FreeDiams::%s::%s' % (db_id, drug_id_name)
975 new_drug['external_code'] = drug_uid
976 new_drug['pk_data_source'] = pk_data_source
977 new_drug.save()
978
979
980 fd_xml_components = fd_xml_drug.getiterator('Composition')
981 comp_data = {}
982 for fd_xml_comp in fd_xml_components:
983
984 data = {}
985
986 amount = regex.match(r'\d+[.,]{0,1}\d*', fd_xml_comp.attrib['strenght'].strip())
987 if amount is None:
988 amount = 99999
989 else:
990 amount = amount.group()
991 data['amount'] = amount
992
993 unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', fd_xml_comp.attrib['strenght'].strip()).strip()
994 if unit == u'':
995 unit = u'*?*'
996 data['unit'] = unit
997
998 molecule_name = fd_xml_comp.attrib['molecularName'].strip()
999 if molecule_name != u'':
1000 create_consumable_substance(substance = molecule_name, atc = None, amount = amount, unit = unit)
1001 data['molecule_name'] = molecule_name
1002
1003 inn_name = fd_xml_comp.attrib['inn'].strip()
1004 if inn_name != u'':
1005 create_consumable_substance(substance = inn_name, atc = None, amount = amount, unit = unit)
1006 data['inn_name'] = molecule_name
1007
1008 if molecule_name == u'':
1009 data['substance'] = inn_name
1010 _log.info('linking INN [%s] rather than molecularName as component', inn_name)
1011 else:
1012 data['substance'] = molecule_name
1013
1014 data['nature'] = fd_xml_comp.attrib['nature'].strip()
1015 data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
1016
1017
1018 try:
1019 old_data = comp_data[data['nature_ID']]
1020
1021 if old_data['inn_name'] == u'':
1022 old_data['inn_name'] = data['inn_name']
1023 if data['inn_name'] == u'':
1024 data['inn_name'] = old_data['inn_name']
1025
1026 if old_data['molecule_name'] == u'':
1027 old_data['molecule_name'] = data['molecule_name']
1028 if data['molecule_name'] == u'':
1029 data['molecule_name'] = old_data['molecule_name']
1030
1031
1032
1033
1034
1035 if data['nature'] == u'FT':
1036 comp_data[data['nature_ID']] = data
1037 else:
1038 comp_data[data['nature_ID']] = old_data
1039
1040
1041 except KeyError:
1042 comp_data[data['nature_ID']] = data
1043
1044
1045 for key, data in comp_data.items():
1046 new_drug.add_component (
1047 substance = data['substance'],
1048 atc = None,
1049 amount = data['amount'],
1050 unit = data['unit']
1051 )
1052
1054 """Support v8.2 CSV file interface only."""
1055
1056 version = u'Gelbe Liste/MMI v8.2 interface'
1057 default_encoding = 'cp1250'
1058 bdt_line_template = u'%03d6210#%s\r\n'
1059 bdt_line_base_length = 8
1060
1062
1063 cDrugDataSourceInterface.__init__(self)
1064
1065 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version)
1066
1067 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
1068 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
1069
1070 paths = gmTools.gmPaths()
1071
1072 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt')
1073 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp')
1074 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt')
1075 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
1076
1077 self.__data_date = None
1078 self.__online_update_date = None
1079
1080
1081
1083
1084 if self.__data_date is not None:
1085 if not force_reload:
1086 return {
1087 'data': self.__data_date,
1088 'online_update': self.__online_update_date
1089 }
1090 try:
1091 open(self.data_date_filename, 'wb').close()
1092 except StandardError:
1093 _log.error('problem querying the MMI drug database for version information')
1094 _log.exception('cannot create MMI drug database version file [%s]', self.data_date_filename)
1095 self.__data_date = None
1096 self.__online_update_date = None
1097 return {
1098 'data': u'?',
1099 'online_update': u'?'
1100 }
1101
1102 cmd = u'%s -DATADATE' % self.path_to_binary
1103 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
1104 _log.error('problem querying the MMI drug database for version information')
1105 self.__data_date = None
1106 self.__online_update_date = None
1107 return {
1108 'data': u'?',
1109 'online_update': u'?'
1110 }
1111
1112 try:
1113 version_file = open(self.data_date_filename, 'rU')
1114 except StandardError:
1115 _log.error('problem querying the MMI drug database for version information')
1116 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename)
1117 self.__data_date = None
1118 self.__online_update_date = None
1119 return {
1120 'data': u'?',
1121 'online_update': u'?'
1122 }
1123
1124 self.__data_date = version_file.readline()[:10]
1125 self.__online_update_date = version_file.readline()[:10]
1126 version_file.close()
1127
1128 return {
1129 'data': self.__data_date,
1130 'online_update': self.__online_update_date
1131 }
1132
1134 versions = self.get_data_source_version()
1135
1136 return create_data_source (
1137 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
1138 short_name = u'GL/MMI',
1139 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
1140 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
1141 language = u'de'
1142 )
1143
1145
1146 try:
1147
1148 open(self.default_csv_filename, 'wb').close()
1149 except IOError:
1150 _log.exception('problem creating GL/MMI <-> GNUmed exchange file')
1151 return False
1152
1153 if cmd is None:
1154 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
1155
1156 if os.name == 'nt':
1157 blocking = True
1158 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
1159 _log.error('problem switching to the MMI drug database')
1160
1161
1162
1163
1164 return True
1165
1175
1177
1178 selected_drugs = self.__let_user_select_drugs()
1179 if selected_drugs is None:
1180 return None
1181
1182 new_substances = []
1183
1184 for drug in selected_drugs:
1185 atc = None
1186 if len(drug['wirkstoffe']) == 1:
1187 atc = drug['atc']
1188 for wirkstoff in drug['wirkstoffe']:
1189 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1190
1191 selected_drugs.close()
1192
1193 return new_substances
1194
1196
1197 selected_drugs = self.__let_user_select_drugs()
1198 if selected_drugs is None:
1199 return None
1200
1201 data_src_pk = self.create_data_source_entry()
1202
1203 new_drugs = []
1204 new_substances = []
1205
1206 for entry in selected_drugs:
1207
1208 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
1209
1210 if entry[u'hilfsmittel']:
1211 _log.debug('skipping Hilfsmittel')
1212 continue
1213
1214 if entry[u'erstattbares_medizinprodukt']:
1215 _log.debug('skipping sonstiges Medizinprodukt')
1216 continue
1217
1218
1219 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform'])
1220 if drug is None:
1221 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform'])
1222 new_drugs.append(drug)
1223
1224
1225 drug['is_fake_brand'] = False
1226 drug['atc'] = entry['atc']
1227 drug['external_code_type'] = u'DE-PZN'
1228 drug['external_code'] = entry['pzn']
1229 drug['fk_data_source'] = data_src_pk
1230 drug.save()
1231
1232
1233 atc = None
1234 if len(entry['wirkstoffe']) == 1:
1235 atc = entry['atc']
1236 for wirkstoff in entry['wirkstoffe']:
1237 drug.add_component(substance = wirkstoff, atc = atc)
1238
1239
1240 atc = None
1241 if len(entry['wirkstoffe']) == 1:
1242 atc = entry['atc']
1243 for wirkstoff in entry['wirkstoffe']:
1244 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1245
1246 return new_drugs, new_substances
1247
1276
1279
1298
1300
1302 cGelbeListeWindowsInterface.__init__(self)
1303
1304 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version)
1305
1306
1307 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
1308 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
1309
1310 paths = gmTools.gmPaths()
1311
1312 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
1313 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
1314 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
1315 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
1316
1318 """empirical CSV interface"""
1319
1322
1324
1325 try:
1326 csv_file = open(filename, 'rb')
1327 except:
1328 _log.exception('cannot access [%s]', filename)
1329 csv_file = None
1330
1331 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
1332
1333 if csv_file is None:
1334 return False
1335
1336 csv_lines = csv.DictReader (
1337 csv_file,
1338 fieldnames = field_names,
1339 delimiter = ';'
1340 )
1341
1342 for line in csv_lines:
1343 print "--------------------------------------------------------------------"[:31]
1344 for key in field_names:
1345 tmp = ('%s ' % key)[:30]
1346 print '%s: %s' % (tmp, line[key])
1347
1348 csv_file.close()
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361 drug_data_source_interfaces = {
1362 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
1363 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface,
1364 'FreeDiams (FR, US, CA, ZA)': cFreeDiamsInterface
1365 }
1366
1367
1368
1369
1370
1371 _SQL_get_consumable_substance = u"""
1372 SELECT *, xmin
1373 FROM ref.consumable_substance
1374 WHERE %s
1375 """
1376
1378
1379 _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s"
1380 _cmds_store_payload = [
1381 u"""UPDATE ref.consumable_substance SET
1382 description = %(description)s,
1383 atc_code = gm.nullify_empty_string(%(atc_code)s),
1384 amount = %(amount)s,
1385 unit = gm.nullify_empty_string(%(unit)s)
1386 WHERE
1387 pk = %(pk)s
1388 AND
1389 xmin = %(xmin)s
1390 AND
1391 -- must not currently be used with a patient directly
1392 NOT EXISTS (
1393 SELECT 1
1394 FROM clin.substance_intake
1395 WHERE
1396 fk_drug_component IS NULL
1397 AND
1398 fk_substance = %(pk)s
1399 LIMIT 1
1400 )
1401 AND
1402 -- must not currently be used with a patient indirectly, either
1403 NOT EXISTS (
1404 SELECT 1
1405 FROM clin.substance_intake
1406 WHERE
1407 fk_drug_component IS NOT NULL
1408 AND
1409 fk_drug_component = (
1410 SELECT r_ls2b.pk
1411 FROM ref.lnk_substance2brand r_ls2b
1412 WHERE fk_substance = %(pk)s
1413 )
1414 LIMIT 1
1415 )
1416 -- -- must not currently be used with a branded drug
1417 -- -- (but this would make it rather hard fixing branded drugs which contain only this substance)
1418 -- NOT EXISTS (
1419 -- SELECT 1
1420 -- FROM ref.lnk_substance2brand
1421 -- WHERE fk_substance = %(pk)s
1422 -- LIMIT 1
1423 -- )
1424 RETURNING
1425 xmin
1426 """
1427 ]
1428 _updatable_fields = [
1429 u'description',
1430 u'atc_code',
1431 u'amount',
1432 u'unit'
1433 ]
1434
1436 success, data = super(self.__class__, self).save_payload(conn = conn)
1437
1438 if not success:
1439 return (success, data)
1440
1441 if self._payload[self._idx['atc_code']] is not None:
1442 atc = self._payload[self._idx['atc_code']].strip()
1443 if atc != u'':
1444 gmATC.propagate_atc (
1445 substance = self._payload[self._idx['description']].strip(),
1446 atc = atc
1447 )
1448
1449 return (success, data)
1450
1451
1452
1454 cmd = u"""
1455 SELECT
1456 EXISTS (
1457 SELECT 1
1458 FROM clin.substance_intake
1459 WHERE
1460 fk_drug_component IS NULL
1461 AND
1462 fk_substance = %(pk)s
1463 LIMIT 1
1464 ) OR EXISTS (
1465 SELECT 1
1466 FROM clin.substance_intake
1467 WHERE
1468 fk_drug_component IS NOT NULL
1469 AND
1470 fk_drug_component IN (
1471 SELECT r_ls2b.pk
1472 FROM ref.lnk_substance2brand r_ls2b
1473 WHERE fk_substance = %(pk)s
1474 )
1475 LIMIT 1
1476 )"""
1477 args = {'pk': self.pk_obj}
1478
1479 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1480 return rows[0][0]
1481
1482 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
1483
1485 cmd = u"""
1486 SELECT EXISTS (
1487 SELECT 1
1488 FROM ref.lnk_substance2brand
1489 WHERE fk_substance = %(pk)s
1490 LIMIT 1
1491 )"""
1492 args = {'pk': self.pk_obj}
1493
1494 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1495 return rows[0][0]
1496
1497 is_drug_component = property(_get_is_drug_component, lambda x:x)
1498
1507
1509
1510 substance = substance
1511 if atc is not None:
1512 atc = atc.strip()
1513
1514 converted, amount = gmTools.input2decimal(amount)
1515 if not converted:
1516 raise ValueError('<amount> must be a number: %s (%s)', amount, type(amount))
1517
1518 args = {
1519 'desc': substance.strip(),
1520 'amount': amount,
1521 'unit': unit.strip(),
1522 'atc': atc
1523 }
1524 cmd = u"""
1525 SELECT pk FROM ref.consumable_substance
1526 WHERE
1527 lower(description) = lower(%(desc)s)
1528 AND
1529 amount = %(amount)s
1530 AND
1531 unit = %(unit)s
1532 """
1533 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1534
1535 if len(rows) == 0:
1536 cmd = u"""
1537 INSERT INTO ref.consumable_substance (description, atc_code, amount, unit) VALUES (
1538 %(desc)s,
1539 gm.nullify_empty_string(%(atc)s),
1540 %(amount)s,
1541 gm.nullify_empty_string(%(unit)s)
1542 ) RETURNING pk"""
1543 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
1544
1545 gmATC.propagate_atc(substance = substance, atc = atc)
1546
1547 return cConsumableSubstance(aPK_obj = rows[0]['pk'])
1548
1550 args = {'pk': substance}
1551 cmd = u"""
1552 DELETE FROM ref.consumable_substance
1553 WHERE
1554 pk = %(pk)s
1555 AND
1556
1557 -- must not currently be used with a patient
1558 NOT EXISTS (
1559 SELECT 1
1560 FROM clin.v_pat_substance_intake
1561 WHERE pk_substance = %(pk)s
1562 LIMIT 1
1563 )
1564 AND
1565
1566 -- must not currently be used with a branded drug
1567 NOT EXISTS (
1568 SELECT 1
1569 FROM ref.lnk_substance2brand
1570 WHERE fk_substance = %(pk)s
1571 LIMIT 1
1572 )"""
1573 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1574 return True
1575
1577
1578 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
1579 _query1 = u"""
1580 SELECT
1581 pk::text,
1582 (description || ' ' || amount || ' ' || unit) as subst
1583 FROM ref.consumable_substance
1584 WHERE description %(fragment_condition)s
1585 ORDER BY subst
1586 LIMIT 50"""
1587 _query2 = u"""
1588 SELECT
1589 pk::text,
1590 (description || ' ' || amount || ' ' || unit) as subst
1591 FROM ref.consumable_substance
1592 WHERE
1593 %(fragment_condition)s
1594 ORDER BY subst
1595 LIMIT 50"""
1596
1597
1599 """Return matches for aFragment at start of phrases."""
1600
1601 if cSubstanceMatchProvider._pattern.match(aFragment):
1602 self._queries = [cSubstanceMatchProvider._query2]
1603 fragment_condition = """description ILIKE %(desc)s
1604 AND
1605 amount::text ILIKE %(amount)s"""
1606 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1607 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1608 else:
1609 self._queries = [cSubstanceMatchProvider._query1]
1610 fragment_condition = u"ILIKE %(fragment)s"
1611 self._args['fragment'] = u"%s%%" % aFragment
1612
1613 return self._find_matches(fragment_condition)
1614
1616 """Return matches for aFragment at start of words inside phrases."""
1617
1618 if cSubstanceMatchProvider._pattern.match(aFragment):
1619 self._queries = [cSubstanceMatchProvider._query2]
1620
1621 desc = regex.sub(r'\s*\d+$', u'', aFragment)
1622 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
1623
1624 fragment_condition = """description ~* %(desc)s
1625 AND
1626 amount::text ILIKE %(amount)s"""
1627
1628 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
1629 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1630 else:
1631 self._queries = [cSubstanceMatchProvider._query1]
1632 fragment_condition = u"~* %(fragment)s"
1633 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
1634 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
1635
1636 return self._find_matches(fragment_condition)
1637
1639 """Return matches for aFragment as a true substring."""
1640
1641 if cSubstanceMatchProvider._pattern.match(aFragment):
1642 self._queries = [cSubstanceMatchProvider._query2]
1643 fragment_condition = """description ILIKE %(desc)s
1644 AND
1645 amount::text ILIKE %(amount)s"""
1646 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1647 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1648 else:
1649 self._queries = [cSubstanceMatchProvider._query1]
1650 fragment_condition = u"ILIKE %(fragment)s"
1651 self._args['fragment'] = u"%%%s%%" % aFragment
1652
1653 return self._find_matches(fragment_condition)
1654
1655 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
1656 """Represents a substance currently taken by a patient."""
1657
1658 _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s"
1659 _cmds_store_payload = [
1660 u"""UPDATE clin.substance_intake SET
1661 clin_when = %(started)s,
1662 discontinued = %(discontinued)s,
1663 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s),
1664 schedule = gm.nullify_empty_string(%(schedule)s),
1665 aim = gm.nullify_empty_string(%(aim)s),
1666 narrative = gm.nullify_empty_string(%(notes)s),
1667 intake_is_approved_of = %(intake_is_approved_of)s,
1668 fk_episode = %(pk_episode)s,
1669
1670 preparation = (
1671 case
1672 when %(pk_brand)s is NULL then %(preparation)s
1673 else NULL
1674 end
1675 )::text,
1676
1677 is_long_term = (
1678 case
1679 when (
1680 (%(is_long_term)s is False)
1681 and
1682 (%(duration)s is NULL)
1683 ) is True then null
1684 else %(is_long_term)s
1685 end
1686 )::boolean,
1687
1688 duration = (
1689 case
1690 when %(is_long_term)s is True then null
1691 else %(duration)s
1692 end
1693 )::interval
1694 WHERE
1695 pk = %(pk_substance_intake)s
1696 AND
1697 xmin = %(xmin_substance_intake)s
1698 RETURNING
1699 xmin as xmin_substance_intake
1700 """
1701 ]
1702 _updatable_fields = [
1703 u'started',
1704 u'discontinued',
1705 u'discontinue_reason',
1706 u'preparation',
1707 u'intake_is_approved_of',
1708 u'schedule',
1709 u'duration',
1710 u'aim',
1711 u'is_long_term',
1712 u'notes',
1713 u'pk_episode'
1714 ]
1715
1716 - def format(self, left_margin=0, date_format='%Y %B %d', one_line=True, allergy=None, show_all_brand_components=False):
1717 if one_line:
1718 return self.format_as_one_line(left_margin = left_margin, date_format = date_format)
1719
1720 return self.format_as_multiple_lines (
1721 left_margin = left_margin,
1722 date_format = date_format,
1723 allergy = allergy,
1724 show_all_brand_components = show_all_brand_components
1725 )
1726
1728
1729 if self._payload[self._idx['duration']] is None:
1730 duration = gmTools.bool2subst (
1731 self._payload[self._idx['is_long_term']],
1732 _('long-term'),
1733 _('short-term'),
1734 _('?short-term')
1735 )
1736 else:
1737 duration = gmDateTime.format_interval (
1738 self._payload[self._idx['duration']],
1739 accuracy_wanted = gmDateTime.acc_days
1740 )
1741
1742 line = u'%s%s (%s %s): %s %s%s %s (%s)' % (
1743 u' ' * left_margin,
1744 self._payload[self._idx['started']].strftime(date_format),
1745 gmTools.u_right_arrow,
1746 duration,
1747 self._payload[self._idx['substance']],
1748 self._payload[self._idx['amount']],
1749 self._payload[self._idx['unit']],
1750 self._payload[self._idx['preparation']],
1751 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing'))
1752 )
1753
1754 return line
1755
1756 - def format_as_multiple_lines(self, left_margin=0, date_format='%Y %B %d', allergy=None, show_all_brand_components=False):
1757
1758 txt = _('Substance intake entry (%s, %s) [#%s] \n') % (
1759 gmTools.bool2subst (
1760 boolean = self._payload[self._idx['is_currently_active']],
1761 true_return = gmTools.bool2subst (
1762 boolean = self._payload[self._idx['seems_inactive']],
1763 true_return = _('active, needs check'),
1764 false_return = _('active'),
1765 none_return = _('assumed active')
1766 ),
1767 false_return = _('inactive')
1768 ),
1769 gmTools.bool2subst (
1770 boolean = self._payload[self._idx['intake_is_approved_of']],
1771 true_return = _('approved'),
1772 false_return = _('unapproved')
1773 ),
1774 self._payload[self._idx['pk_substance_intake']]
1775 )
1776
1777 if allergy is not None:
1778 certainty = gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'))
1779 txt += u'\n'
1780 txt += u' !! ---- Cave ---- !!\n'
1781 txt += u' %s (%s): %s (%s)\n' % (
1782 allergy['l10n_type'],
1783 certainty,
1784 allergy['descriptor'],
1785 gmTools.coalesce(allergy['reaction'], u'')[:40]
1786 )
1787 txt += u'\n'
1788
1789 txt += u' ' + _('Substance: %s [#%s]\n') % (self._payload[self._idx['substance']], self._payload[self._idx['pk_substance']])
1790 txt += u' ' + _('Preparation: %s\n') % self._payload[self._idx['preparation']]
1791 txt += u' ' + _('Amount per dose: %s %s') % (self._payload[self._idx['amount']], self._payload[self._idx['unit']])
1792 if self.ddd is not None:
1793 txt += u' (DDD: %s %s)' % (self.ddd['ddd'], self.ddd['unit'])
1794 txt += u'\n'
1795 txt += gmTools.coalesce(self._payload[self._idx['atc_substance']], u'', _(' ATC (substance): %s\n'))
1796
1797 txt += u'\n'
1798
1799 txt += gmTools.coalesce (
1800 self._payload[self._idx['brand']],
1801 u'',
1802 _(' Brand name: %%s [#%s]\n') % self._payload[self._idx['pk_brand']]
1803 )
1804 txt += gmTools.coalesce(self._payload[self._idx['atc_brand']], u'', _(' ATC (brand): %s\n'))
1805 if show_all_brand_components and (self._payload[self._idx['pk_brand']] is not None):
1806 brand = self.containing_drug
1807 if len(brand['pk_substances']) > 1:
1808 for comp in brand['components']:
1809 if comp.startswith(self._payload[self._idx['substance']] + u'::'):
1810 continue
1811 txt += _(' Other component: %s\n') % comp
1812
1813 txt += u'\n'
1814
1815 txt += gmTools.coalesce(self._payload[self._idx['schedule']], u'', _(' Regimen: %s\n'))
1816
1817 if self._payload[self._idx['is_long_term']]:
1818 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity)
1819 else:
1820 if self._payload[self._idx['duration']] is None:
1821 duration = u''
1822 else:
1823 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(self._payload[self._idx['duration']], gmDateTime.acc_days))
1824
1825 txt += _(' Started %s%s%s\n') % (
1826 gmDateTime.pydt_strftime (
1827 self._payload[self._idx['started']],
1828 format = date_format,
1829 accuracy = gmDateTime.acc_days
1830 ),
1831 duration,
1832 gmTools.bool2subst(self._payload[self._idx['is_long_term']], _(' (long-term)'), _(' (short-term)'), u'')
1833 )
1834
1835 if self._payload[self._idx['discontinued']] is not None:
1836 txt += _(' Discontinued %s\n') % (
1837 gmDateTime.pydt_strftime (
1838 self._payload[self._idx['discontinued']],
1839 format = date_format,
1840 accuracy = gmDateTime.acc_days
1841 )
1842 )
1843 txt += _(' Reason: %s\n') % self._payload[self._idx['discontinue_reason']]
1844
1845 txt += u'\n'
1846
1847 txt += gmTools.coalesce(self._payload[self._idx['aim']], u'', _(' Aim: %s\n'))
1848 txt += gmTools.coalesce(self._payload[self._idx['episode']], u'', _(' Episode: %s\n'))
1849 txt += gmTools.coalesce(self._payload[self._idx['health_issue']], u'', _(' Health issue: %s\n'))
1850 txt += gmTools.coalesce(self._payload[self._idx['notes']], u'', _(' Advice: %s\n'))
1851
1852 txt += u'\n'
1853
1854 txt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % {
1855 'row_ver': self._payload[self._idx['row_version']],
1856 'mod_when': gmDateTime.pydt_strftime(self._payload[self._idx['modified_when']]),
1857 'mod_by': self._payload[self._idx['modified_by']]
1858 }
1859
1860 return txt
1861
1862 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
1863 allg = gmAllergy.create_allergy (
1864 allergene = self._payload[self._idx['substance']],
1865 allg_type = allergy_type,
1866 episode_id = self._payload[self._idx['pk_episode']],
1867 encounter_id = encounter_id
1868 )
1869 allg['substance'] = gmTools.coalesce (
1870 self._payload[self._idx['brand']],
1871 self._payload[self._idx['substance']]
1872 )
1873 allg['reaction'] = self._payload[self._idx['discontinue_reason']]
1874 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']])
1875 if self._payload[self._idx['external_code_brand']] is not None:
1876 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']])
1877
1878 if self._payload[self._idx['pk_brand']] is None:
1879 allg['generics'] = self._payload[self._idx['substance']]
1880 else:
1881 comps = [ c['substance'] for c in self.containing_drug.components ]
1882 if len(comps) == 0:
1883 allg['generics'] = self._payload[self._idx['substance']]
1884 else:
1885 allg['generics'] = u'; '.join(comps)
1886
1887 allg.save()
1888 return allg
1889
1890
1891
1892 - def _get_ddd(self):
1893
1894 try: self.__ddd
1895 except AttributeError: self.__ddd = None
1896
1897 if self.__ddd is not None:
1898 return self.__ddd
1899
1900 if self._payload[self._idx['atc_substance']] is not None:
1901 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']])
1902 if len(ddd) != 0:
1903 self.__ddd = ddd[0]
1904 else:
1905 if self._payload[self._idx['atc_brand']] is not None:
1906 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']])
1907 if len(ddd) != 0:
1908 self.__ddd = ddd[0]
1909
1910 return self.__ddd
1911
1912 ddd = property(_get_ddd, lambda x:x)
1913
1915 drug = self.containing_drug
1916
1917 if drug is None:
1918 return None
1919
1920 return drug.external_code
1921
1922 external_code = property(_get_external_code, lambda x:x)
1923
1925 drug = self.containing_drug
1926
1927 if drug is None:
1928 return None
1929
1930 return drug.external_code_type
1931
1932 external_code_type = property(_get_external_code_type, lambda x:x)
1933
1935 if self._payload[self._idx['pk_brand']] is None:
1936 return None
1937
1938 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1939
1940 containing_drug = property(_get_containing_drug, lambda x:x)
1941
1943 tests = [
1944
1945 ' 1-1-1-1 ',
1946
1947 '1-1-1-1',
1948 '22-1-1-1',
1949 '1/3-1-1-1',
1950 '/4-1-1-1'
1951 ]
1952 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}$"
1953 for test in tests:
1954 print test.strip(), ":", regex.match(pattern, test.strip())
1955
1957 args = {'comp': pk_component, 'subst': pk_substance, 'pat': pk_identity}
1958
1959 where_clause = u"""
1960 fk_encounter IN (
1961 SELECT pk FROM clin.encounter WHERE fk_patient = %(pat)s
1962 )
1963 AND
1964 """
1965
1966 if pk_substance is not None:
1967 where_clause += u'fk_substance = %(subst)s'
1968 if pk_component is not None:
1969 where_clause += u'fk_drug_component = %(comp)s'
1970
1971 cmd = u"""SELECT exists (
1972 SELECT 1 FROM clin.substance_intake
1973 WHERE
1974 %s
1975 LIMIT 1
1976 )""" % where_clause
1977
1978 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1979 return rows[0][0]
1980
1981 -def create_substance_intake(pk_substance=None, pk_component=None, preparation=None, encounter=None, episode=None):
1982
1983 args = {
1984 'enc': encounter,
1985 'epi': episode,
1986 'comp': pk_component,
1987 'subst': pk_substance,
1988 'prep': preparation
1989 }
1990
1991 if pk_component is None:
1992 cmd = u"""
1993 INSERT INTO clin.substance_intake (
1994 fk_encounter,
1995 fk_episode,
1996 intake_is_approved_of,
1997 fk_substance,
1998 preparation
1999 ) VALUES (
2000 %(enc)s,
2001 %(epi)s,
2002 False,
2003 %(subst)s,
2004 %(prep)s
2005 )
2006 RETURNING pk"""
2007 else:
2008 cmd = u"""
2009 INSERT INTO clin.substance_intake (
2010 fk_encounter,
2011 fk_episode,
2012 intake_is_approved_of,
2013 fk_drug_component
2014 ) VALUES (
2015 %(enc)s,
2016 %(epi)s,
2017 False,
2018 %(comp)s
2019 )
2020 RETURNING pk"""
2021
2022 try:
2023 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
2024 except gmPG2.dbapi.InternalError, e:
2025 if e.pgerror is None:
2026 raise
2027 if 'prevent_duplicate_component' in e.pgerror:
2028 _log.exception('will not create duplicate substance intake entry')
2029 _log.error(e.pgerror)
2030 return None
2031 raise
2032
2033 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
2034
2038
2077
2078
2149
2150 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s'
2151
2153
2154 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s'
2155 _cmds_store_payload = [
2156 u"""UPDATE ref.lnk_substance2brand SET
2157 fk_brand = %(pk_brand)s,
2158 fk_substance = %(pk_consumable_substance)s
2159 WHERE
2160 NOT EXISTS (
2161 SELECT 1
2162 FROM clin.substance_intake
2163 WHERE fk_drug_component = %(pk_component)s
2164 LIMIT 1
2165 )
2166 AND
2167 pk = %(pk_component)s
2168 AND
2169 xmin = %(xmin_lnk_substance2brand)s
2170 RETURNING
2171 xmin AS xmin_lnk_substance2brand
2172 """
2173 ]
2174 _updatable_fields = [
2175 u'pk_brand',
2176 u'pk_consumable_substance'
2177 ]
2178
2179
2180
2182 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
2183
2184 containing_drug = property(_get_containing_drug, lambda x:x)
2185
2187 return self._payload[self._idx['is_in_use']]
2188
2189 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
2190
2193
2194 substance = property(_get_substance, lambda x:x)
2195
2200
2202
2203 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
2204 _query_desc_only = u"""
2205 SELECT DISTINCT ON (component)
2206 pk_component,
2207 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
2208 AS component
2209 FROM ref.v_drug_components
2210 WHERE
2211 substance %(fragment_condition)s
2212 OR
2213 brand %(fragment_condition)s
2214 ORDER BY component
2215 LIMIT 50"""
2216 _query_desc_and_amount = u"""
2217
2218 SELECT DISTINCT ON (component)
2219 pk_component,
2220 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
2221 AS component
2222 FROM ref.v_drug_components
2223 WHERE
2224 %(fragment_condition)s
2225 ORDER BY component
2226 LIMIT 50"""
2227
2229 """Return matches for aFragment at start of phrases."""
2230
2231 if cDrugComponentMatchProvider._pattern.match(aFragment):
2232 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2233 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
2234 AND
2235 amount::text ILIKE %(amount)s"""
2236 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
2237 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2238 else:
2239 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2240 fragment_condition = u"ILIKE %(fragment)s"
2241 self._args['fragment'] = u"%s%%" % aFragment
2242
2243 return self._find_matches(fragment_condition)
2244
2246 """Return matches for aFragment at start of words inside phrases."""
2247
2248 if cDrugComponentMatchProvider._pattern.match(aFragment):
2249 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2250
2251 desc = regex.sub(r'\s*\d+$', u'', aFragment)
2252 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
2253
2254 fragment_condition = """(substance ~* %(desc)s OR brand ~* %(desc)s)
2255 AND
2256 amount::text ILIKE %(amount)s"""
2257
2258 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
2259 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2260 else:
2261 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2262 fragment_condition = u"~* %(fragment)s"
2263 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
2264 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
2265
2266 return self._find_matches(fragment_condition)
2267
2269 """Return matches for aFragment as a true substring."""
2270
2271 if cDrugComponentMatchProvider._pattern.match(aFragment):
2272 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2273 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
2274 AND
2275 amount::text ILIKE %(amount)s"""
2276 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
2277 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2278 else:
2279 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2280 fragment_condition = u"ILIKE %(fragment)s"
2281 self._args['fragment'] = u"%%%s%%" % aFragment
2282
2283 return self._find_matches(fragment_condition)
2284
2285
2287 """Represents a drug as marketed by a manufacturer."""
2288
2289 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s"
2290 _cmds_store_payload = [
2291 u"""UPDATE ref.branded_drug SET
2292 description = %(brand)s,
2293 preparation = %(preparation)s,
2294 atc_code = gm.nullify_empty_string(%(atc)s),
2295 external_code = gm.nullify_empty_string(%(external_code)s),
2296 external_code_type = gm.nullify_empty_string(%(external_code_type)s),
2297 is_fake = %(is_fake_brand)s,
2298 fk_data_source = %(pk_data_source)s
2299 WHERE
2300 pk = %(pk_brand)s
2301 AND
2302 xmin = %(xmin_branded_drug)s
2303 RETURNING
2304 xmin AS xmin_branded_drug
2305 """
2306 ]
2307 _updatable_fields = [
2308 u'brand',
2309 u'preparation',
2310 u'atc',
2311 u'is_fake_brand',
2312 u'external_code',
2313 u'external_code_type',
2314 u'pk_data_source'
2315 ]
2316
2318 success, data = super(self.__class__, self).save_payload(conn = conn)
2319
2320 if not success:
2321 return (success, data)
2322
2323 if self._payload[self._idx['atc']] is not None:
2324 atc = self._payload[self._idx['atc']].strip()
2325 if atc != u'':
2326 gmATC.propagate_atc (
2327 substance = self._payload[self._idx['brand']].strip(),
2328 atc = atc
2329 )
2330
2331 return (success, data)
2332
2334
2335 if self.is_in_use_by_patients:
2336 return False
2337
2338 pk_substances2keep = [ s['pk'] for s in substances ]
2339 args = {'brand': self._payload[self._idx['pk_brand']]}
2340 queries = []
2341
2342
2343 cmd = u"""
2344 INSERT INTO ref.lnk_substance2brand (
2345 fk_brand,
2346 fk_substance
2347 )
2348 SELECT
2349 %(brand)s,
2350 %(subst)s
2351 WHERE NOT EXISTS (
2352 SELECT 1
2353 FROM ref.lnk_substance2brand
2354 WHERE
2355 fk_brand = %(brand)s
2356 AND
2357 fk_substance = %(subst)s
2358 )"""
2359 for pk in pk_substances2keep:
2360 args['subst'] = pk
2361 queries.append({'cmd': cmd, 'args': args})
2362
2363
2364 args['substances2keep'] = tuple(pk_substances2keep)
2365 cmd = u"""
2366 DELETE FROM ref.lnk_substance2brand
2367 WHERE
2368 fk_brand = %(brand)s
2369 AND
2370 fk_substance NOT IN %(substances2keep)s"""
2371 queries.append({'cmd': cmd, 'args': args})
2372
2373 gmPG2.run_rw_queries(queries = queries)
2374 self.refetch_payload()
2375
2376 return True
2377
2378 - def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
2379
2380 args = {
2381 'brand': self.pk_obj,
2382 'subst': substance,
2383 'atc': atc,
2384 'pk_subst': pk_substance
2385 }
2386
2387 if pk_substance is None:
2388 consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit)
2389 args['pk_subst'] = consumable['pk']
2390
2391
2392 cmd = u"""
2393 SELECT pk_component
2394 FROM ref.v_drug_components
2395 WHERE
2396 pk_brand = %(brand)s
2397 AND
2398 ((
2399 (lower(substance) = lower(%(subst)s))
2400 OR
2401 (lower(atc_substance) = lower(%(atc)s))
2402 OR
2403 (pk_consumable_substance = %(pk_subst)s)
2404 ) IS TRUE)
2405 """
2406 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2407
2408 if len(rows) > 0:
2409 return
2410
2411
2412 cmd = u"""
2413 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance)
2414 VALUES (%(brand)s, %(pk_subst)s)
2415 """
2416 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2417 self.refetch_payload()
2418
2420 if len(self._payload[self._idx['components']]) == 1:
2421 _log.error('cannot remove the only component of a drug')
2422 return False
2423
2424 args = {'brand': self.pk_obj, 'comp': substance}
2425 cmd = u"""
2426 DELETE FROM ref.lnk_substance2brand
2427 WHERE
2428 fk_brand = %(brand)s
2429 AND
2430 fk_substance = %(comp)s
2431 AND
2432 NOT EXISTS (
2433 SELECT 1
2434 FROM clin.substance_intake
2435 WHERE fk_drug_component = %(comp)s
2436 LIMIT 1
2437 )
2438 """
2439 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2440 self.refetch_payload()
2441
2442 return True
2443
2444
2445
2447 if self._payload[self._idx['external_code']] is None:
2448 return None
2449
2450 return self._payload[self._idx['external_code']]
2451
2452 external_code = property(_get_external_code, lambda x:x)
2453
2455
2456
2457 if self._payload[self._idx['external_code_type']] is None:
2458 return None
2459
2460 return self._payload[self._idx['external_code_type']]
2461
2462 external_code_type = property(_get_external_code_type, lambda x:x)
2463
2465 cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s'
2466 args = {'brand': self._payload[self._idx['pk_brand']]}
2467 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2468 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
2469
2470 components = property(_get_components, lambda x:x)
2471
2473 if self._payload[self._idx['pk_substances']] is None:
2474 return []
2475 cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s'
2476 args = {'pks': tuple(self._payload[self._idx['pk_substances']])}
2477 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2478 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
2479
2480 components_as_substances = property(_get_components_as_substances, lambda x:x)
2481
2483 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)'
2484 args = {'fk_brand': self._payload[self._idx['pk_brand']]}
2485 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2486 return rows[0][0]
2487
2488 is_vaccine = property(_get_is_vaccine, lambda x:x)
2489
2491 cmd = u"""
2492 SELECT EXISTS (
2493 SELECT 1
2494 FROM clin.substance_intake
2495 WHERE
2496 fk_drug_component IS NOT NULL
2497 AND
2498 fk_drug_component IN (
2499 SELECT r_ls2b.pk
2500 FROM ref.lnk_substance2brand r_ls2b
2501 WHERE fk_brand = %(pk)s
2502 )
2503 LIMIT 1
2504 )"""
2505 args = {'pk': self.pk_obj}
2506
2507 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2508 return rows[0][0]
2509
2510 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
2511
2513 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description'
2514 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
2515 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
2516
2518 args = {'brand': brand_name, 'prep': preparation}
2519
2520 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)'
2521 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2522
2523 if len(rows) == 0:
2524 return None
2525
2526 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2527
2529
2530 if preparation is None:
2531 preparation = _('units')
2532
2533 if preparation.strip() == u'':
2534 preparation = _('units')
2535
2536 if return_existing:
2537 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation)
2538 if drug is not None:
2539 return drug
2540
2541 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk'
2542 args = {'brand': brand_name, 'prep': preparation}
2543 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
2544
2545 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2546
2548 queries = []
2549 args = {'pk': brand}
2550
2551
2552 cmd = u"""
2553 DELETE FROM ref.lnk_substance2brand
2554 WHERE
2555 fk_brand = %(pk)s
2556 AND
2557 NOT EXISTS (
2558 SELECT 1
2559 FROM clin.v_pat_substance_intake
2560 WHERE pk_brand = %(pk)s
2561 LIMIT 1
2562 )
2563 """
2564 queries.append({'cmd': cmd, 'args': args})
2565
2566
2567 cmd = u"""
2568 DELETE FROM ref.branded_drug
2569 WHERE
2570 pk = %(pk)s
2571 AND
2572 NOT EXISTS (
2573 SELECT 1
2574 FROM clin.v_pat_substance_intake
2575 WHERE pk_brand = %(pk)s
2576 LIMIT 1
2577 )
2578 """
2579 queries.append({'cmd': cmd, 'args': args})
2580
2581 gmPG2.run_rw_queries(queries = queries)
2582
2583
2584
2585 if __name__ == "__main__":
2586
2587 if len(sys.argv) < 2:
2588 sys.exit()
2589
2590 if sys.argv[1] != 'test':
2591 sys.exit()
2592
2593 from Gnumed.pycommon import gmLog2
2594 from Gnumed.pycommon import gmI18N
2595 from Gnumed.business import gmPerson
2596
2597 gmI18N.activate_locale()
2598
2599
2605
2607 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
2608 for drug in mmi_file:
2609 print "-------------"
2610 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2611 for stoff in drug['wirkstoffe']:
2612 print " Wirkstoff:", stoff
2613 raw_input()
2614 if mmi_file.has_unknown_fields is not None:
2615 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key
2616 for key in mmi_file.csv_fieldnames:
2617 print key, '->', drug[key]
2618 raw_input()
2619 mmi_file.close()
2620
2624
2626 mmi = cGelbeListeWineInterface()
2627 mmi_file = mmi.__let_user_select_drugs()
2628 for drug in mmi_file:
2629 print "-------------"
2630 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2631 for stoff in drug['wirkstoffe']:
2632 print " Wirkstoff:", stoff
2633 print drug
2634 mmi_file.close()
2635
2639
2641 mmi = cGelbeListeInterface()
2642 print mmi
2643 print "interface definition:", mmi.version
2644
2645 diclofenac = '7587712'
2646 phenprocoumon = '4421744'
2647 mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
2648
2649
2650
2657
2663
2664
2665
2667 drug = create_substance_intake (
2668 pk_component = 2,
2669 encounter = 1,
2670 episode = 1
2671 )
2672 print drug
2673
2678
2682
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695 test_fd_switch_to()
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706