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