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