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