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