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