| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf8 -*-
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 #============================================================
46 """Always relates to the active patient."""
47 gmHooks.run_hook_script(hook = u'after_substance_intake_modified')
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 terms = []
58 names = []
59
60 if isinstance(search_term, cBrandedDrug):
61 if search_term['atc'] is not None:
62 terms.append(search_term['atc'])
63
64 elif isinstance(search_term, cSubstanceIntakeEntry):
65 names.append(search_term['substance'])
66 if search_term['atc_brand'] is not None:
67 terms.append(search_term['atc_brand'])
68 if search_term['atc_substance'] is not None:
69 terms.append(search_term['atc_substance'])
70
71 elif search_term is not None:
72 names.append(u'%s' % search_term)
73 terms.extend(gmATC.text2atc(text = u'%s' % search_term, fuzzy = True))
74
75 for name in names:
76 if name.endswith('e'):
77 terms.append(name[:-1])
78 else:
79 terms.append(name)
80
81 #url_template = u'http://www.google.de/#q=site%%3Adosing.de+%s'
82 #url = url_template % u'+OR+'.join(terms)
83
84 url_template = u'http://www.google.com/search?hl=de&source=hp&q=site%%3Adosing.de+%s&btnG=Google-Suche'
85 url = url_template % u'+OR+'.join(terms)
86
87 _log.debug(u'renal insufficiency URL: %s', url)
88
89 return url
90 #============================================================
91 # this should be in gmCoding.py
92 -def create_data_source(long_name=None, short_name=None, version=None, source=None, language=None):
93
94 args = {
95 'lname': long_name,
96 'sname': short_name,
97 'ver': version,
98 'src': source,
99 'lang': language
100 }
101
102 cmd = u"""select pk from ref.data_source where name_long = %(lname)s and name_short = %(sname)s and version = %(ver)s"""
103 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
104 if len(rows) > 0:
105 return rows[0]['pk']
106
107 cmd = u"""
108 INSERT INTO ref.data_source (name_long, name_short, version, source, lang)
109 VALUES (
110 %(lname)s,
111 %(sname)s,
112 %(ver)s,
113 %(src)s,
114 %(lang)s
115 )
116 returning pk
117 """
118 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
119
120 return rows[0]['pk']
121 #============================================================
122 # wishlist:
123 # - --conf-file= for glwin.exe
124 # - wirkstoff: Konzentration auch in Multiprodukten
125 # - wirkstoff: ATC auch in Multiprodukten
126 # - Suche nach ATC per CLI
127
129 """Iterator over a Gelbe Liste/MMI v8.2 CSV file."""
130
131 version = u'Gelbe Liste/MMI v8.2 CSV file interface'
132 default_transfer_file_windows = r"c:\rezept.txt"
133 #default_encoding = 'cp1252'
134 default_encoding = 'cp1250'
135 csv_fieldnames = [
136 u'name',
137 u'packungsgroesse', # obsolete, use "packungsmenge"
138 u'darreichungsform',
139 u'packungstyp',
140 u'festbetrag',
141 u'avp',
142 u'hersteller',
143 u'rezepttext',
144 u'pzn',
145 u'status_vertrieb',
146 u'status_rezeptpflicht',
147 u'status_fachinfo',
148 u'btm',
149 u'atc',
150 u'anzahl_packungen',
151 u'zuzahlung_pro_packung',
152 u'einheit',
153 u'schedule_morgens',
154 u'schedule_mittags',
155 u'schedule_abends',
156 u'schedule_nachts',
157 u'status_dauermedikament',
158 u'status_hausliste',
159 u'status_negativliste',
160 u'ik_nummer',
161 u'status_rabattvertrag',
162 u'wirkstoffe',
163 u'wirkstoffmenge',
164 u'wirkstoffeinheit',
165 u'wirkstoffmenge_bezug',
166 u'wirkstoffmenge_bezugseinheit',
167 u'status_import',
168 u'status_lifestyle',
169 u'status_ausnahmeliste',
170 u'packungsmenge',
171 u'apothekenpflicht',
172 u'status_billigere_packung',
173 u'rezepttyp',
174 u'besonderes_arzneimittel', # Abstimmungsverfahren SGB-V
175 u't_rezept_pflicht', # Thalidomid-Rezept
176 u'erstattbares_medizinprodukt',
177 u'hilfsmittel',
178 u'hzv_rabattkennung',
179 u'hzv_preis'
180 ]
181 boolean_fields = [
182 u'status_rezeptpflicht',
183 u'status_fachinfo',
184 u'btm',
185 u'status_dauermedikament',
186 u'status_hausliste',
187 u'status_negativliste',
188 u'status_rabattvertrag',
189 u'status_import',
190 u'status_lifestyle',
191 u'status_ausnahmeliste',
192 u'apothekenpflicht',
193 u'status_billigere_packung',
194 u'besonderes_arzneimittel', # Abstimmungsverfahren SGB-V
195 u't_rezept_pflicht',
196 u'erstattbares_medizinprodukt',
197 u'hilfsmittel'
198 ]
199 #--------------------------------------------------------
201
202 _log.info(cGelbeListeCSVFile.version)
203
204 self.filename = filename
205 if filename is None:
206 self.filename = cGelbeListeCSVFile.default_transfer_file_windows
207
208 _log.debug('reading Gelbe Liste/MMI drug data from [%s]', self.filename)
209
210 self.csv_file = codecs.open(filename = filename, mode = 'rUb', encoding = cGelbeListeCSVFile.default_encoding)
211
212 self.csv_lines = gmTools.unicode_csv_reader (
213 self.csv_file,
214 fieldnames = cGelbeListeCSVFile.csv_fieldnames,
215 delimiter = ';',
216 quotechar = '"',
217 dict = True
218 )
219 #--------------------------------------------------------
222 #--------------------------------------------------------
224 line = self.csv_lines.next()
225
226 for field in cGelbeListeCSVFile.boolean_fields:
227 line[field] = (line[field].strip() == u'T')
228
229 # split field "Wirkstoff" by ";"
230 if line['wirkstoffe'].strip() == u'':
231 line['wirkstoffe'] = []
232 else:
233 line['wirkstoffe'] = [ wirkstoff.strip() for wirkstoff in line['wirkstoffe'].split(u';') ]
234
235 return line
236 #--------------------------------------------------------
238 try: self.csv_file.close()
239 except: pass
240
241 if truncate:
242 try: os.open(self.filename, 'wb').close
243 except: pass
244 #--------------------------------------------------------
247
248 has_unknown_fields = property(_get_has_unknown_fields, lambda x:x)
249 #============================================================
251
252 #--------------------------------------------------------
257 #--------------------------------------------------------
260 #--------------------------------------------------------
263 #--------------------------------------------------------
266 #--------------------------------------------------------
268 self.switch_to_frontend()
269 #--------------------------------------------------------
271 self.switch_to_frontend()
272 #--------------------------------------------------------
274 self.switch_to_frontend()
275 #--------------------------------------------------------
277 self.switch_to_frontend()
278 #--------------------------------------------------------
282 #============================================================
284
285 version = u'FreeDiams v0.5.4 interface'
286 default_encoding = 'utf8'
287 default_dob_format = '%Y/%m/%d'
288
289 map_gender2mf = {
290 'm': u'M',
291 'f': u'F',
292 'tf': u'H',
293 'tm': u'H',
294 'h': u'H'
295 }
296 #--------------------------------------------------------
298 cDrugDataSourceInterface.__init__(self)
299 _log.info(cFreeDiamsInterface.version)
300
301 self.__imported_drugs = []
302
303 self.__gm2fd_filename = gmTools.get_unique_filename(prefix = r'gm2freediams-', suffix = r'.xml')
304 _log.debug('GNUmed -> FreeDiams "exchange-in" file: %s', self.__gm2fd_filename)
305 self.__fd2gm_filename = gmTools.get_unique_filename(prefix = r'freediams2gm-', suffix = r'.xml')
306 _log.debug('GNUmed <-> FreeDiams "exchange-out"/"prescription" file: %s', self.__fd2gm_filename)
307 paths = gmTools.gmPaths()
308 self.__fd4gm_config_file = os.path.join(paths.home_dir, '.gnumed', 'freediams4gm.conf')
309
310 self.path_to_binary = None
311 self.__detect_binary()
312 #--------------------------------------------------------
314 # ~/.freediams/config.ini: [License] -> AcceptedVersion=....
315
316 if not self.__detect_binary():
317 return False
318
319 freediams = subprocess.Popen (
320 args = u'--version', # --version or -version or -v
321 executable = self.path_to_binary,
322 stdout = subprocess.PIPE,
323 stderr = subprocess.PIPE,
324 # close_fds = True, # Windows can't do that in conjunction with stdout/stderr = ... :-(
325 universal_newlines = True
326 )
327 data, errors = freediams.communicate()
328 version = regex.search('FreeDiams\s\d.\d.\d', data).group().split()[1]
329 _log.debug('FreeDiams %s', version)
330
331 return version
332 #--------------------------------------------------------
334 return create_data_source (
335 long_name = u'"FreeDiams" Drug Database Frontend',
336 short_name = u'FreeDiams',
337 version = self.get_data_source_version(),
338 source = u'http://ericmaeker.fr/FreeMedForms/di-manual/index.html',
339 language = u'fr' # actually to be multi-locale
340 )
341 #--------------------------------------------------------
343 """http://ericmaeker.fr/FreeMedForms/di-manual/en/html/ligne_commandes.html"""
344
345 _log.debug('calling FreeDiams in [%s] mode', mode)
346
347 self.__imported_drugs = []
348
349 if not self.__detect_binary():
350 return False
351
352 self.__create_gm2fd_file(mode = mode)
353
354 args = u'--exchange-in="%s"' % (self.__gm2fd_filename)
355 cmd = r'%s %s' % (self.path_to_binary, args)
356 if os.name == 'nt':
357 blocking = True
358 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
359 _log.error('problem switching to the FreeDiams drug database')
360 return False
361
362 if blocking == True:
363 self.import_fd2gm_file_as_drugs()
364
365 return True
366 #--------------------------------------------------------
368 self.switch_to_frontend(blocking = True)
369 #--------------------------------------------------------
371 if substance_intakes is None:
372 return
373 if len(substance_intakes) < 2:
374 return
375
376 self.__create_prescription_file(substance_intakes = substance_intakes)
377 self.switch_to_frontend(mode = 'interactions', blocking = False)
378 #--------------------------------------------------------
380 if substance_intake is None:
381 return
382
383 self.__create_prescription_file(substance_intakes = [substance_intake])
384 self.switch_to_frontend(mode = 'interactions', blocking = False)
385 #--------------------------------------------------------
387 self.show_info_on_drug(substance_intake = substance_intake)
388 #--------------------------------------------------------
390 if substance_intakes is None:
391 if not self.__export_latest_prescription():
392 self.__create_prescription_file()
393 else:
394 self.__create_prescription_file(substance_intakes = substance_intakes)
395
396 self.switch_to_frontend(mode = 'prescription', blocking = True)
397 self.import_fd2gm_file_as_prescription()
398
399 return self.__imported_drugs
400 #--------------------------------------------------------
401 # internal helpers
402 #--------------------------------------------------------
404
405 if self.path_to_binary is not None:
406 return True
407
408 found, cmd = gmShellAPI.find_first_binary(binaries = [
409 r'/usr/bin/freediams',
410 r'freediams',
411 r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams',
412 r'C:\Program Files (x86)\FreeDiams\freediams.exe',
413 r'C:\Program Files\FreeDiams\freediams.exe',
414 r'c:\programs\freediams\freediams.exe',
415 r'freediams.exe'
416 ])
417
418 if found:
419 self.path_to_binary = cmd
420 return True
421
422 try:
423 self.custom_path_to_binary
424 except AttributeError:
425 _log.error('cannot find FreeDiams binary, no custom path set')
426 return False
427
428 if self.custom_path_to_binary is None:
429 _log.error('cannot find FreeDiams binary')
430 return False
431
432 found, cmd = gmShellAPI.detect_external_binary(binary = self.custom_path_to_binary)
433 if found:
434 self.path_to_binary = cmd
435 return True
436
437 _log.error('cannot find FreeDiams binary')
438 return False
439 #--------------------------------------------------------
441
442 if self.patient is None:
443 _log.debug('cannot export latest FreeDiams prescriptions w/o patient')
444 return False
445
446 docs = self.patient.get_document_folder()
447 prescription = docs.get_latest_freediams_prescription()
448 if prescription is None:
449 _log.debug('no FreeDiams prescription available')
450 return False
451
452 for part in prescription.parts:
453 if part['filename'] == u'freediams-prescription.xml':
454 if part.export_to_file(filename = self.__fd2gm_filename) is not None:
455 return True
456
457 _log.error('cannot export latest FreeDiams prescription to XML file')
458
459 return False
460 #--------------------------------------------------------
462 """FreeDiams calls this exchange-out or prescription file.
463
464 CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel).
465 CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS.
466 AFSSAPS is the French FDA.
467
468 CIP stands for Unique Presentation Identifier (eg 30 pills plaq)
469 CIP if you want to specify the packaging of the drug (30 pills
470 thermoformed tablet...) -- actually not really usefull for french
471 doctors.
472 # .external_code_type: u'FR-CIS'
473 # .external_cod: the CIS value
474
475 OnlyForTest:
476 OnlyForTest drugs will be processed by the IA Engine but
477 not printed (regardless of FreeDiams mode). They are shown
478 in gray in the prescription view.
479
480 Select-only is a mode where FreeDiams creates a list of drugs
481 not a full prescription. In this list, users can add ForTestOnly
482 drug if they want to
483 1. print the list without some drugs
484 2. but including these drugs in the IA engine calculation
485
486 Select-Only mode does not have any relation with the ForTestOnly drugs.
487
488 IsTextual:
489 What is the use and significance of the
490 <IsTextual>true/false</IsTextual>
491 flag when both <DrugName> and <TextualDrugName> exist ?
492
493 This tag must be setted even if it sounds like a duplicated
494 data. This tag is needed inside FreeDiams code.
495
496 INN:
497 GNUmed will pass the substance in <TextualDrugName
498 and will also pass <INN>True</INN>.
499
500 Eric: Nop, this is not usefull because pure textual drugs
501 are not processed but just shown.
502 """
503 # virginize file
504 open(self.__fd2gm_filename, 'wb').close()
505
506 # make sure we've got something to do
507 if substance_intakes is None:
508 if self.patient is None:
509 _log.warning('cannot create prescription file because there is neither a patient nor a substance intake list')
510 # do fail because __export_latest_prescription() should not have been called without patient
511 return False
512 emr = self.patient.get_emr()
513 substance_intakes = emr.get_current_substance_intake (
514 include_inactive = False,
515 include_unapproved = True
516 )
517
518 drug_snippets = []
519
520 # process FD drugs
521 fd_intakes = [ i for i in substance_intakes if (
522 (i['intake_is_approved_of'] is True)
523 and
524 (i['external_code_type_brand'] is not None)
525 and
526 (i['external_code_type_brand'].startswith(u'FreeDiams::'))
527 )]
528
529 intakes_pooled_by_brand = {}
530 for intake in fd_intakes:
531 # this will leave only one entry per brand
532 # but FreeDiams knows the components ...
533 intakes_pooled_by_brand[intake['brand']] = intake
534 del fd_intakes
535
536 drug_snippet = u"""<Prescription>
537 <IsTextual>False</IsTextual>
538 <DrugName>%s</DrugName>
539 <Drug_UID>%s</Drug_UID>
540 <Drug_UID_type>%s</Drug_UID_type> <!-- not yet supported by FreeDiams -->
541 </Prescription>"""
542
543 last_db_id = u'CA_HCDPD'
544 for intake in intakes_pooled_by_brand.values():
545 last_db_id = gmTools.xml_escape_string(text = intake['external_code_type_brand'].replace(u'FreeDiams::', u'').split(u'::')[0])
546 drug_snippets.append(drug_snippet % (
547 gmTools.xml_escape_string(text = intake['brand'].strip()),
548 gmTools.xml_escape_string(text = intake['external_code_brand'].strip()),
549 last_db_id
550 ))
551
552 # process non-FD drugs
553 non_fd_intakes = [ i for i in substance_intakes if (
554 (i['intake_is_approved_of'] is True)
555 and (
556 (i['external_code_type_brand'] is None)
557 or
558 (not i['external_code_type_brand'].startswith(u'FreeDiams::'))
559 )
560 )]
561
562 non_fd_brand_intakes = [ i for i in non_fd_intakes if i['brand'] is not None ]
563 non_fd_substance_intakes = [ i for i in non_fd_intakes if i['brand'] is None ]
564 del non_fd_intakes
565
566 drug_snippet = u"""<Prescription>
567 <IsTextual>True</IsTextual>
568 <TextualDrugName>%s</TextualDrugName>
569 </Prescription>"""
570
571 for intake in non_fd_substance_intakes:
572 drug_name = u'%s %s%s (%s)%s' % (
573 intake['substance'],
574 intake['amount'],
575 intake['unit'],
576 intake['preparation'],
577 gmTools.coalesce(intake['schedule'], u'', _('\n Take: %s'))
578 )
579 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
580
581 intakes_pooled_by_brand = {}
582 for intake in non_fd_brand_intakes:
583 brand = u'%s %s' % (intake['brand'], intake['preparation'])
584 try:
585 intakes_pooled_by_brand[brand].append(intake)
586 except KeyError:
587 intakes_pooled_by_brand[brand] = [intake]
588
589 for brand, comps in intakes_pooled_by_brand.iteritems():
590 drug_name = u'%s\n' % brand
591 for comp in comps:
592 drug_name += u' %s %s%s\n' % (
593 comp['substance'],
594 comp['amount'],
595 comp['unit']
596 )
597 if comps[0]['schedule'] is not None:
598 drug_name += gmTools.coalesce(comps[0]['schedule'], u'', _('Take: %s'))
599 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
600
601 # assemble XML file
602 xml = u"""<?xml version = "1.0" encoding = "UTF-8"?>
603
604 <FreeDiams>
605 <DrugsDatabaseName>%s</DrugsDatabaseName>
606 <FullPrescription version="0.5.0">
607
608 %s
609
610 </FullPrescription>
611 </FreeDiams>
612 """
613
614 xml_file = codecs.open(self.__fd2gm_filename, 'wb', 'utf8')
615 xml_file.write(xml % (
616 last_db_id,
617 u'\n\t\t'.join(drug_snippets)
618 ))
619 xml_file.close()
620
621 return True
622 #--------------------------------------------------------
624
625 if mode == 'interactions':
626 mode = u'select-only'
627 elif mode == 'prescription':
628 mode = u'prescriber'
629 else:
630 mode = u'select-only'
631
632 xml_file = codecs.open(self.__gm2fd_filename, 'wb', 'utf8')
633
634 xml = u"""<?xml version="1.0" encoding="UTF-8"?>
635
636 <FreeDiams_In version="0.5.0">
637 <EMR name="GNUmed" uid="unused"/>
638 <ConfigFile value="%s"/>
639 <ExchangeOut value="%s" format="xml"/>
640 <!-- <DrugsDatabase uid="can be set to a specific DB"/> -->
641 <Ui editmode="%s" blockPatientDatas="1"/>
642 %%s
643 </FreeDiams_In>
644
645 <!--
646 # FIXME: search by LOINC code and add (as soon as supported by FreeDiams ...)
647 <Creatinine value="12" unit="mg/l or mmol/l"/>
648 <Weight value="70" unit="kg or pd" />
649 <Height value="170" unit="cm or "/>
650 <ICD10 value="J11.0;A22;Z23"/>
651 -->
652 """ % (
653 self.__fd4gm_config_file,
654 self.__fd2gm_filename,
655 mode
656 )
657
658 if self.patient is None:
659 xml_file.write(xml % u'')
660 xml_file.close()
661 return
662
663 name = self.patient.get_active_name()
664 if self.patient['dob'] is None:
665 dob = u''
666 else:
667 dob = self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format)
668
669 emr = self.patient.get_emr()
670 allgs = emr.get_allergies()
671 atc_allgs = [
672 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'allergy'))
673 ]
674 atc_sens = [
675 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'sensitivity'))
676 ]
677 inn_allgs = [
678 a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'allergy'))
679 ]
680 inn_sens = [
681 a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'sensitivity'))
682 ]
683 # this is rather fragile: FreeDiams won't know what type of UID this is
684 # (but it will assume it is of the type of the drug database in use)
685 # but eventually FreeDiams puts all drugs into one database :-)
686 uid_allgs = [
687 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'allergy'))
688 ]
689 uid_sens = [
690 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'sensitivity'))
691 ]
692
693 patient_xml = u"""<Patient>
694 <Identity
695 lastnames="%s"
696 firstnames="%s"
697 uid="%s"
698 dob="%s"
699 gender="%s"
700 />
701 <ATCAllergies value="%s"/>
702 <ATCIntolerances value="%s"/>
703
704 <InnAllergies value="%s"/>
705 <InnIntolerances value="%s"/>
706
707 <DrugsUidAllergies value="%s"/>
708 <DrugsUidIntolerances value="%s"/>
709 </Patient>
710 """ % (
711 gmTools.xml_escape_string(text = name['lastnames']),
712 gmTools.xml_escape_string(text = name['firstnames']),
713 self.patient.ID,
714 dob,
715 cFreeDiamsInterface.map_gender2mf[self.patient['gender']],
716 gmTools.xml_escape_string(text = u';'.join(atc_allgs)),
717 gmTools.xml_escape_string(text = u';'.join(atc_sens)),
718 gmTools.xml_escape_string(text = u';'.join(inn_allgs)),
719 gmTools.xml_escape_string(text = u';'.join(inn_sens)),
720 gmTools.xml_escape_string(text = u';'.join(uid_allgs)),
721 gmTools.xml_escape_string(text = u';'.join(uid_sens))
722 )
723
724 xml_file.write(xml % patient_xml)
725 xml_file.close()
726 #--------------------------------------------------------
728
729 if filename is None:
730 filename = self.__fd2gm_filename
731
732 fd2gm_xml = etree.ElementTree()
733 fd2gm_xml.parse(filename)
734
735 pdfs = fd2gm_xml.findall('ExtraDatas/Printed')
736 if len(pdfs) == 0:
737 return
738
739 fd_filenames = []
740 for pdf in pdfs:
741 fd_filenames.append(pdf.attrib['file'])
742
743 docs = self.patient.get_document_folder()
744 emr = self.patient.get_emr()
745
746 prescription = docs.add_document (
747 document_type = create_document_type (
748 document_type = DOCUMENT_TYPE_PRESCRIPTION
749 )['pk_doc_type'],
750 encounter = emr.active_encounter['pk_encounter'],
751 episode = emr.add_episode (
752 episode_name = DEFAULT_MEDICATION_HISTORY_EPISODE,
753 is_open = False
754 )['pk_episode']
755 )
756 prescription['ext_ref'] = u'FreeDiams'
757 prescription.save()
758 fd_filenames.append(filename)
759 success, msg, parts = prescription.add_parts_from_files(files = fd_filenames)
760 if not success:
761 _log.error(msg)
762 return
763
764 for part in parts:
765 part['obj_comment'] = _('copy')
766 part.save()
767
768 xml_part = parts[-1]
769 xml_part['filename'] = u'freediams-prescription.xml'
770 xml_part['obj_comment'] = _('data')
771 xml_part.save()
772
773 # are we the intended reviewer ?
774 from Gnumed.business.gmPerson import gmCurrentProvider
775 me = gmCurrentProvider()
776 # if so: auto-sign the prescription
777 if xml_part['pk_intended_reviewer'] == me['pk_staff']:
778 prescription.set_reviewed(technically_abnormal = False, clinically_relevant = False)
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 # FIXME: do not import IsTextual drugs, or rather, make that configurable
789
790 fd2gm_xml = etree.ElementTree()
791 fd2gm_xml.parse(filename)
792
793 data_src_pk = self.create_data_source_entry()
794
795 db_def = fd2gm_xml.find('DrugsDatabaseName')
796 db_id = db_def.text.strip()
797 drug_id_name = db_def.attrib['drugUidName']
798 fd_xml_drug_entries = fd2gm_xml.findall('FullPrescription/Prescription')
799
800 self.__imported_drugs = []
801 for fd_xml_drug in fd_xml_drug_entries:
802 drug_uid = fd_xml_drug.find('Drug_UID').text.strip()
803 if drug_uid == u'-1':
804 _log.debug('skipping textual drug')
805 continue # it's a TextualDrug, skip it
806 drug_name = fd_xml_drug.find('DrugName').text.replace(', )', ')').strip()
807 drug_form = fd_xml_drug.find('DrugForm').text.strip()
808 drug_atc = fd_xml_drug.find('DrugATC')
809 if drug_atc is None:
810 drug_atc = u''
811 else:
812 if drug_atc.text is None:
813 drug_atc = u''
814 else:
815 drug_atc = drug_atc.text.strip()
816
817 # create new branded drug
818 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True)
819 self.__imported_drugs.append(new_drug)
820 new_drug['is_fake_brand'] = False
821 new_drug['atc'] = drug_atc
822 new_drug['external_code_type'] = u'FreeDiams::%s::%s' % (db_id, drug_id_name)
823 new_drug['external_code'] = drug_uid
824 new_drug['pk_data_source'] = data_src_pk
825 new_drug.save()
826
827 # parse XML for composition records
828 fd_xml_components = fd_xml_drug.getiterator('Composition')
829 comp_data = {}
830 for fd_xml_comp in fd_xml_components:
831
832 data = {}
833
834 amount = regex.match(r'\d+[.,]{0,1}\d*', fd_xml_comp.attrib['strenght'].strip()) # sic, typo
835 if amount is None:
836 amount = 99999
837 else:
838 amount = amount.group()
839 data['amount'] = amount
840
841 unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', fd_xml_comp.attrib['strenght'].strip()).strip() # sic, typo
842 if unit == u'':
843 unit = u'*?*'
844 data['unit'] = unit
845
846 molecule_name = fd_xml_comp.attrib['molecularName'].strip()
847 if molecule_name != u'':
848 create_consumable_substance(substance = molecule_name, atc = None, amount = amount, unit = unit)
849 data['molecule_name'] = molecule_name
850
851 inn_name = fd_xml_comp.attrib['inn'].strip()
852 if inn_name != u'':
853 create_consumable_substance(substance = inn_name, atc = None, amount = amount, unit = unit)
854 data['inn_name'] = molecule_name
855
856 if molecule_name == u'':
857 data['substance'] = inn_name
858 _log.info('linking INN [%s] rather than molecularName as component', inn_name)
859 else:
860 data['substance'] = molecule_name
861
862 data['nature'] = fd_xml_comp.attrib['nature'].strip()
863 data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
864
865 # merge composition records of SA/FT nature
866 try:
867 old_data = comp_data[data['nature_ID']]
868 # normalize INN
869 if old_data['inn_name'] == u'':
870 old_data['inn_name'] = data['inn_name']
871 if data['inn_name'] == u'':
872 data['inn_name'] = old_data['inn_name']
873 # normalize molecule
874 if old_data['molecule_name'] == u'':
875 old_data['molecule_name'] = data['molecule_name']
876 if data['molecule_name'] == u'':
877 data['molecule_name'] = old_data['molecule_name']
878 # FT: transformed form
879 # SA: active substance
880 # it would be preferable to use the SA record because that's what's *actually*
881 # contained in the drug, however FreeDiams does not list the amount thereof
882 # (rather that of the INN)
883 if data['nature'] == u'FT':
884 comp_data[data['nature_ID']] = data
885 else:
886 comp_data[data['nature_ID']] = old_data
887
888 # or create new record
889 except KeyError:
890 comp_data[data['nature_ID']] = data
891
892 # actually create components from (possibly merged) composition records
893 for key, data in comp_data.items():
894 new_drug.add_component (
895 substance = data['substance'],
896 atc = None,
897 amount = data['amount'],
898 unit = data['unit']
899 )
900 #============================================================
902 """Support v8.2 CSV file interface only."""
903
904 version = u'Gelbe Liste/MMI v8.2 interface'
905 default_encoding = 'cp1250'
906 bdt_line_template = u'%03d6210#%s\r\n' # Medikament verordnet auf Kassenrezept
907 bdt_line_base_length = 8
908 #--------------------------------------------------------
910
911 cDrugDataSourceInterface.__init__(self)
912
913 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version)
914
915 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
916 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
917
918 paths = gmTools.gmPaths()
919
920 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt')
921 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp')
922 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt')
923 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
924
925 self.__data_date = None
926 self.__online_update_date = None
927
928 # use adjusted config.dat
929 #--------------------------------------------------------
931
932 if self.__data_date is not None:
933 if not force_reload:
934 return {
935 'data': self.__data_date,
936 'online_update': self.__online_update_date
937 }
938 try:
939 open(self.data_date_filename, 'wb').close()
940 except StandardError:
941 _log.error('problem querying the MMI drug database for version information')
942 _log.exception('cannot create MMI drug database version file [%s]', self.data_date_filename)
943 self.__data_date = None
944 self.__online_update_date = None
945 return {
946 'data': u'?',
947 'online_update': u'?'
948 }
949
950 cmd = u'%s -DATADATE' % self.path_to_binary
951 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
952 _log.error('problem querying the MMI drug database for version information')
953 self.__data_date = None
954 self.__online_update_date = None
955 return {
956 'data': u'?',
957 'online_update': u'?'
958 }
959
960 try:
961 version_file = open(self.data_date_filename, 'rU')
962 except StandardError:
963 _log.error('problem querying the MMI drug database for version information')
964 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename)
965 self.__data_date = None
966 self.__online_update_date = None
967 return {
968 'data': u'?',
969 'online_update': u'?'
970 }
971
972 self.__data_date = version_file.readline()[:10]
973 self.__online_update_date = version_file.readline()[:10]
974 version_file.close()
975
976 return {
977 'data': self.__data_date,
978 'online_update': self.__online_update_date
979 }
980 #--------------------------------------------------------
982 versions = self.get_data_source_version()
983
984 return create_data_source (
985 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
986 short_name = u'GL/MMI',
987 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
988 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
989 language = u'de'
990 )
991 #--------------------------------------------------------
993
994 try:
995 # must make sure csv file exists
996 open(self.default_csv_filename, 'wb').close()
997 except IOError:
998 _log.exception('problem creating GL/MMI <-> GNUmed exchange file')
999 return False
1000
1001 if cmd is None:
1002 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
1003
1004 if os.name == 'nt':
1005 blocking = True
1006 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
1007 _log.error('problem switching to the MMI drug database')
1008 # apparently on the first call MMI does not
1009 # consistently return 0 on success
1010 # return False
1011
1012 return True
1013 #--------------------------------------------------------
1015
1016 # better to clean up interactions file
1017 open(self.interactions_filename, 'wb').close()
1018
1019 if not self.switch_to_frontend(blocking = True):
1020 return None
1021
1022 return cGelbeListeCSVFile(filename = self.default_csv_filename)
1023 #--------------------------------------------------------
1025
1026 selected_drugs = self.__let_user_select_drugs()
1027 if selected_drugs is None:
1028 return None
1029
1030 new_substances = []
1031
1032 for drug in selected_drugs:
1033 atc = None # hopefully MMI eventually supports atc-per-substance in a drug...
1034 if len(drug['wirkstoffe']) == 1:
1035 atc = drug['atc']
1036 for wirkstoff in drug['wirkstoffe']:
1037 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1038
1039 selected_drugs.close()
1040
1041 return new_substances
1042 #--------------------------------------------------------
1044
1045 selected_drugs = self.__let_user_select_drugs()
1046 if selected_drugs is None:
1047 return None
1048
1049 data_src_pk = self.create_data_source_entry()
1050
1051 new_drugs = []
1052 new_substances = []
1053
1054 for entry in selected_drugs:
1055
1056 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
1057
1058 if entry[u'hilfsmittel']:
1059 _log.debug('skipping Hilfsmittel')
1060 continue
1061
1062 if entry[u'erstattbares_medizinprodukt']:
1063 _log.debug('skipping sonstiges Medizinprodukt')
1064 continue
1065
1066 # create branded drug (or get it if it already exists)
1067 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform'])
1068 if drug is None:
1069 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform'])
1070 new_drugs.append(drug)
1071
1072 # update fields
1073 drug['is_fake_brand'] = False
1074 drug['atc'] = entry['atc']
1075 drug['external_code_type'] = u'DE-PZN'
1076 drug['external_code'] = entry['pzn']
1077 drug['fk_data_source'] = data_src_pk
1078 drug.save()
1079
1080 # add components to brand
1081 atc = None # hopefully MMI eventually supports atc-per-substance in a drug...
1082 if len(entry['wirkstoffe']) == 1:
1083 atc = entry['atc']
1084 for wirkstoff in entry['wirkstoffe']:
1085 drug.add_component(substance = wirkstoff, atc = atc)
1086
1087 # create as consumable substances, too
1088 atc = None # hopefully MMI eventually supports atc-per-substance in a drug...
1089 if len(entry['wirkstoffe']) == 1:
1090 atc = entry['atc']
1091 for wirkstoff in entry['wirkstoffe']:
1092 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1093
1094 return new_drugs, new_substances
1095 #--------------------------------------------------------
1097 """For this to work the BDT interaction check must be configured in the MMI."""
1098
1099 if drug_ids_list is None:
1100 if substances is None:
1101 return
1102 if len(substances) < 2:
1103 return
1104 drug_ids_list = [ (s.external_code_type, s.external_code) for s in substances ]
1105 drug_ids_list = [ code_value for code_type, code_value in drug_ids_list if (code_value is not None) and (code_type == u'DE-PZN')]
1106
1107 else:
1108 if len(drug_ids_list) < 2:
1109 return
1110
1111 if drug_ids_list < 2:
1112 return
1113
1114 bdt_file = codecs.open(filename = self.interactions_filename, mode = 'wb', encoding = cGelbeListeWindowsInterface.default_encoding)
1115
1116 for pzn in drug_ids_list:
1117 pzn = pzn.strip()
1118 lng = cGelbeListeWindowsInterface.bdt_line_base_length + len(pzn)
1119 bdt_file.write(cGelbeListeWindowsInterface.bdt_line_template % (lng, pzn))
1120
1121 bdt_file.close()
1122
1123 self.switch_to_frontend(blocking = True)
1124 #--------------------------------------------------------
1126 self.switch_to_frontend(blocking = True)
1127 #--------------------------------------------------------
1129
1130 cmd = None
1131
1132 if substance.external_code_type == u'DE-PZN':
1133 cmd = u'%s -PZN %s' % (self.path_to_binary, substance.external_code)
1134
1135 if cmd is None:
1136 name = gmTools.coalesce (
1137 substance['brand'],
1138 substance['substance']
1139 )
1140 cmd = u'%s -NAME %s' % (self.path_to_binary, name)
1141
1142 # better to clean up interactions file
1143 open(self.interactions_filename, 'wb').close()
1144
1145 self.switch_to_frontend(cmd = cmd)
1146 #============================================================
1148
1150 cGelbeListeWindowsInterface.__init__(self)
1151
1152 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version)
1153
1154 # FIXME: if -CLOSETOTRAY is used GNUmed cannot detect the end of MMI
1155 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
1156 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
1157
1158 paths = gmTools.gmPaths()
1159
1160 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
1161 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
1162 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
1163 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
1164 #============================================================
1166 """empirical CSV interface"""
1167
1170
1172
1173 try:
1174 csv_file = open(filename, 'rb') # FIXME: encoding ?
1175 except:
1176 _log.exception('cannot access [%s]', filename)
1177 csv_file = None
1178
1179 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
1180
1181 if csv_file is None:
1182 return False
1183
1184 csv_lines = csv.DictReader (
1185 csv_file,
1186 fieldnames = field_names,
1187 delimiter = ';'
1188 )
1189
1190 for line in csv_lines:
1191 print "--------------------------------------------------------------------"[:31]
1192 for key in field_names:
1193 tmp = ('%s ' % key)[:30]
1194 print '%s: %s' % (tmp, line[key])
1195
1196 csv_file.close()
1197
1198 # narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
1199 # line['Packungszahl'].strip(),
1200 # line['Handelsname'].strip(),
1201 # line['Form'].strip(),
1202 # line[u'Packungsgr\xf6\xdfe'].strip(),
1203 # line['Abpackungsmenge'].strip(),
1204 # line['Einheit'].strip(),
1205 # line['Hersteller'].strip(),
1206 # line['PZN'].strip()
1207 # )
1208 #============================================================
1209 drug_data_source_interfaces = {
1210 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
1211 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface,
1212 'FreeDiams (FR, US, CA, ZA)': cFreeDiamsInterface
1213 }
1214
1215 #============================================================
1216 #============================================================
1217 # substances in use across all patients
1218 #------------------------------------------------------------
1219 _SQL_get_consumable_substance = u"""
1220 SELECT *, xmin
1221 FROM ref.consumable_substance
1222 WHERE %s
1223 """
1224
1226
1227 _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s"
1228 _cmds_store_payload = [
1229 u"""UPDATE ref.consumable_substance SET
1230 description = %(description)s,
1231 atc_code = gm.nullify_empty_string(%(atc_code)s),
1232 amount = %(amount)s,
1233 unit = gm.nullify_empty_string(%(unit)s)
1234 WHERE
1235 pk = %(pk)s
1236 AND
1237 xmin = %(xmin)s
1238 AND
1239 -- must not currently be used with a patient directly
1240 NOT EXISTS (
1241 SELECT 1
1242 FROM clin.substance_intake
1243 WHERE
1244 fk_drug_component IS NULL
1245 AND
1246 fk_substance = %(pk)s
1247 LIMIT 1
1248 )
1249 AND
1250 -- must not currently be used with a patient indirectly, either
1251 NOT EXISTS (
1252 SELECT 1
1253 FROM clin.substance_intake
1254 WHERE
1255 fk_drug_component IS NOT NULL
1256 AND
1257 fk_drug_component = (
1258 SELECT r_ls2b.pk
1259 FROM ref.lnk_substance2brand r_ls2b
1260 WHERE fk_substance = %(pk)s
1261 )
1262 LIMIT 1
1263 )
1264 -- -- must not currently be used with a branded drug
1265 -- -- (but this would make it rather hard fixing branded drugs which contain only this substance)
1266 -- NOT EXISTS (
1267 -- SELECT 1
1268 -- FROM ref.lnk_substance2brand
1269 -- WHERE fk_substance = %(pk)s
1270 -- LIMIT 1
1271 -- )
1272 RETURNING
1273 xmin
1274 """
1275 ]
1276 _updatable_fields = [
1277 u'description',
1278 u'atc_code',
1279 u'amount',
1280 u'unit'
1281 ]
1282 #--------------------------------------------------------
1284 success, data = super(self.__class__, self).save_payload(conn = conn)
1285
1286 if not success:
1287 return (success, data)
1288
1289 if self._payload[self._idx['atc_code']] is not None:
1290 atc = self._payload[self._idx['atc_code']].strip()
1291 if atc != u'':
1292 gmATC.propagate_atc (
1293 substance = self._payload[self._idx['description']].strip(),
1294 atc = atc
1295 )
1296
1297 return (success, data)
1298 #--------------------------------------------------------
1299 # properties
1300 #--------------------------------------------------------
1302 cmd = u"""
1303 SELECT
1304 EXISTS (
1305 SELECT 1
1306 FROM clin.substance_intake
1307 WHERE
1308 fk_drug_component IS NULL
1309 AND
1310 fk_substance = %(pk)s
1311 LIMIT 1
1312 ) OR EXISTS (
1313 SELECT 1
1314 FROM clin.substance_intake
1315 WHERE
1316 fk_drug_component IS NOT NULL
1317 AND
1318 fk_drug_component IN (
1319 SELECT r_ls2b.pk
1320 FROM ref.lnk_substance2brand r_ls2b
1321 WHERE fk_substance = %(pk)s
1322 )
1323 LIMIT 1
1324 )"""
1325 args = {'pk': self.pk_obj}
1326
1327 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1328 return rows[0][0]
1329
1330 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
1331 #--------------------------------------------------------
1333 cmd = u"""
1334 SELECT EXISTS (
1335 SELECT 1
1336 FROM ref.lnk_substance2brand
1337 WHERE fk_substance = %(pk)s
1338 LIMIT 1
1339 )"""
1340 args = {'pk': self.pk_obj}
1341
1342 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1343 return rows[0][0]
1344
1345 is_drug_component = property(_get_is_drug_component, lambda x:x)
1346 #------------------------------------------------------------
1348 if order_by is None:
1349 order_by = u'true'
1350 else:
1351 order_by = u'true ORDER BY %s' % order_by
1352 cmd = _SQL_get_consumable_substance % order_by
1353 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
1354 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
1355 #------------------------------------------------------------
1357
1358 substance = substance
1359 if atc is not None:
1360 atc = atc.strip()
1361
1362 converted, amount = gmTools.input2decimal(amount)
1363 if not converted:
1364 raise ValueError('<amount> must be a number: %s (%s)', amount, type(amount))
1365
1366 args = {
1367 'desc': substance.strip(),
1368 'amount': amount,
1369 'unit': unit.strip(),
1370 'atc': atc
1371 }
1372 cmd = u"""
1373 SELECT pk FROM ref.consumable_substance
1374 WHERE
1375 lower(description) = lower(%(desc)s)
1376 AND
1377 amount = %(amount)s
1378 AND
1379 unit = %(unit)s
1380 """
1381 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1382
1383 if len(rows) == 0:
1384 cmd = u"""
1385 INSERT INTO ref.consumable_substance (description, atc_code, amount, unit) VALUES (
1386 %(desc)s,
1387 gm.nullify_empty_string(%(atc)s),
1388 %(amount)s,
1389 gm.nullify_empty_string(%(unit)s)
1390 ) RETURNING pk"""
1391 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
1392
1393 gmATC.propagate_atc(substance = substance, atc = atc)
1394
1395 return cConsumableSubstance(aPK_obj = rows[0]['pk'])
1396 #------------------------------------------------------------
1398 args = {'pk': substance}
1399 cmd = u"""
1400 DELETE FROM ref.consumable_substance
1401 WHERE
1402 pk = %(pk)s
1403 AND
1404
1405 -- must not currently be used with a patient
1406 NOT EXISTS (
1407 SELECT 1
1408 FROM clin.v_pat_substance_intake
1409 WHERE pk_substance = %(pk)s
1410 LIMIT 1
1411 )
1412 AND
1413
1414 -- must not currently be used with a branded drug
1415 NOT EXISTS (
1416 SELECT 1
1417 FROM ref.lnk_substance2brand
1418 WHERE fk_substance = %(pk)s
1419 LIMIT 1
1420 )"""
1421 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1422 return True
1423 #------------------------------------------------------------
1425
1426 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
1427 _query1 = u"""
1428 SELECT
1429 pk::text,
1430 (description || ' ' || amount || ' ' || unit) as subst
1431 FROM ref.consumable_substance
1432 WHERE description %(fragment_condition)s
1433 ORDER BY subst
1434 LIMIT 50"""
1435 _query2 = u"""
1436 SELECT
1437 pk::text,
1438 (description || ' ' || amount || ' ' || unit) as subst
1439 FROM ref.consumable_substance
1440 WHERE
1441 %(fragment_condition)s
1442 ORDER BY subst
1443 LIMIT 50"""
1444
1445 #--------------------------------------------------------
1447 """Return matches for aFragment at start of phrases."""
1448
1449 if cSubstanceMatchProvider._pattern.match(aFragment):
1450 self._queries = [cSubstanceMatchProvider._query2]
1451 fragment_condition = """description ILIKE %(desc)s
1452 AND
1453 amount::text ILIKE %(amount)s"""
1454 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1455 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1456 else:
1457 self._queries = [cSubstanceMatchProvider._query1]
1458 fragment_condition = u"ILIKE %(fragment)s"
1459 self._args['fragment'] = u"%s%%" % aFragment
1460
1461 return self._find_matches(fragment_condition)
1462 #--------------------------------------------------------
1464 """Return matches for aFragment at start of words inside phrases."""
1465
1466 if cSubstanceMatchProvider._pattern.match(aFragment):
1467 self._queries = [cSubstanceMatchProvider._query2]
1468
1469 desc = regex.sub(r'\s*\d+$', u'', aFragment)
1470 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
1471
1472 fragment_condition = """description ~* %(desc)s
1473 AND
1474 amount::text ILIKE %(amount)s"""
1475
1476 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
1477 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1478 else:
1479 self._queries = [cSubstanceMatchProvider._query1]
1480 fragment_condition = u"~* %(fragment)s"
1481 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
1482 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
1483
1484 return self._find_matches(fragment_condition)
1485 #--------------------------------------------------------
1487 """Return matches for aFragment as a true substring."""
1488
1489 if cSubstanceMatchProvider._pattern.match(aFragment):
1490 self._queries = [cSubstanceMatchProvider._query2]
1491 fragment_condition = """description ILIKE %(desc)s
1492 AND
1493 amount::text ILIKE %(amount)s"""
1494 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1495 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1496 else:
1497 self._queries = [cSubstanceMatchProvider._query1]
1498 fragment_condition = u"ILIKE %(fragment)s"
1499 self._args['fragment'] = u"%%%s%%" % aFragment
1500
1501 return self._find_matches(fragment_condition)
1502 #============================================================
1504 """Represents a substance currently taken by a patient."""
1505
1506 _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s"
1507 _cmds_store_payload = [
1508 u"""UPDATE clin.substance_intake SET
1509 clin_when = %(started)s,
1510 discontinued = %(discontinued)s,
1511 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s),
1512 schedule = gm.nullify_empty_string(%(schedule)s),
1513 aim = gm.nullify_empty_string(%(aim)s),
1514 narrative = gm.nullify_empty_string(%(notes)s),
1515 intake_is_approved_of = %(intake_is_approved_of)s,
1516 fk_episode = %(pk_episode)s,
1517
1518 preparation = (
1519 case
1520 when %(pk_brand)s is NULL then %(preparation)s
1521 else NULL
1522 end
1523 )::text,
1524
1525 is_long_term = (
1526 case
1527 when (
1528 (%(is_long_term)s is False)
1529 and
1530 (%(duration)s is NULL)
1531 ) is True then null
1532 else %(is_long_term)s
1533 end
1534 )::boolean,
1535
1536 duration = (
1537 case
1538 when %(is_long_term)s is True then null
1539 else %(duration)s
1540 end
1541 )::interval
1542 WHERE
1543 pk = %(pk_substance_intake)s
1544 AND
1545 xmin = %(xmin_substance_intake)s
1546 RETURNING
1547 xmin as xmin_substance_intake
1548 """
1549 ]
1550 _updatable_fields = [
1551 u'started',
1552 u'discontinued',
1553 u'discontinue_reason',
1554 u'preparation',
1555 u'intake_is_approved_of',
1556 u'schedule',
1557 u'duration',
1558 u'aim',
1559 u'is_long_term',
1560 u'notes',
1561 u'pk_episode'
1562 ]
1563 #--------------------------------------------------------
1565 if one_line:
1566 return self.format_as_one_line(left_margin = left_margin, date_format = date_format)
1567
1568 return self.format_as_multiple_lines(left_margin = left_margin, date_format = date_format, allergy = allergy)
1569 #--------------------------------------------------------
1571
1572 if self._payload[self._idx['duration']] is None:
1573 duration = gmTools.bool2subst (
1574 self._payload[self._idx['is_long_term']],
1575 _('long-term'),
1576 _('short-term'),
1577 _('?short-term')
1578 )
1579 else:
1580 duration = gmDateTime.format_interval (
1581 self._payload[self._idx['duration']],
1582 accuracy_wanted = gmDateTime.acc_days
1583 )
1584
1585 line = u'%s%s (%s %s): %s %s%s %s (%s)' % (
1586 u' ' * left_margin,
1587 self._payload[self._idx['started']].strftime(date_format),
1588 gmTools.u_right_arrow,
1589 duration,
1590 self._payload[self._idx['substance']],
1591 self._payload[self._idx['amount']],
1592 self._payload[self._idx['unit']],
1593 self._payload[self._idx['preparation']],
1594 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing'))
1595 )
1596
1597 return line
1598 #--------------------------------------------------------
1600
1601 txt = _('Substance intake entry (%s, %s) [#%s] \n') % (
1602 gmTools.bool2subst (
1603 boolean = self._payload[self._idx['is_currently_active']],
1604 true_return = gmTools.bool2subst (
1605 boolean = self._payload[self._idx['seems_inactive']],
1606 true_return = _('active, needs check'),
1607 false_return = _('active'),
1608 none_return = _('assumed active')
1609 ),
1610 false_return = _('inactive')
1611 ),
1612 gmTools.bool2subst (
1613 boolean = self._payload[self._idx['intake_is_approved_of']],
1614 true_return = _('approved'),
1615 false_return = _('unapproved')
1616 ),
1617 self._payload[self._idx['pk_substance_intake']]
1618 )
1619
1620 if allergy is not None:
1621 certainty = gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'))
1622 txt += u'\n'
1623 txt += u' !! ---- Cave ---- !!\n'
1624 txt += u' %s (%s): %s (%s)\n' % (
1625 allergy['l10n_type'],
1626 certainty,
1627 allergy['descriptor'],
1628 gmTools.coalesce(allergy['reaction'], u'')[:40]
1629 )
1630 txt += u'\n'
1631
1632 txt += u' ' + _('Substance: %s [#%s]\n') % (self._payload[self._idx['substance']], self._payload[self._idx['pk_substance']])
1633 txt += u' ' + _('Preparation: %s\n') % self._payload[self._idx['preparation']]
1634 txt += u' ' + _('Amount per dose: %s %s') % (self._payload[self._idx['amount']], self._payload[self._idx['unit']])
1635 if self.ddd is not None:
1636 txt += u' (DDD: %s %s)' % (self.ddd['ddd'], self.ddd['unit'])
1637 txt += u'\n'
1638 txt += gmTools.coalesce(self._payload[self._idx['atc_substance']], u'', _(' ATC (substance): %s\n'))
1639
1640 txt += u'\n'
1641
1642 txt += gmTools.coalesce (
1643 self._payload[self._idx['brand']],
1644 u'',
1645 _(' Brand name: %%s [#%s]\n') % self._payload[self._idx['pk_brand']]
1646 )
1647 txt += gmTools.coalesce(self._payload[self._idx['atc_brand']], u'', _(' ATC (brand): %s\n'))
1648
1649 txt += u'\n'
1650
1651 txt += gmTools.coalesce(self._payload[self._idx['schedule']], u'', _(' Regimen: %s\n'))
1652
1653 if self._payload[self._idx['is_long_term']]:
1654 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity)
1655 else:
1656 if self._payload[self._idx['duration']] is None:
1657 duration = u''
1658 else:
1659 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(self._payload[self._idx['duration']], gmDateTime.acc_days))
1660
1661 txt += _(' Started %s%s%s\n') % (
1662 gmDateTime.pydt_strftime (
1663 self._payload[self._idx['started']],
1664 format = date_format,
1665 accuracy = gmDateTime.acc_days
1666 ),
1667 duration,
1668 gmTools.bool2subst(self._payload[self._idx['is_long_term']], _(' (long-term)'), _(' (short-term)'), u'')
1669 )
1670
1671 if self._payload[self._idx['discontinued']] is not None:
1672 txt += _(' Discontinued %s\n') % (
1673 gmDateTime.pydt_strftime (
1674 self._payload[self._idx['discontinued']],
1675 format = date_format,
1676 accuracy = gmDateTime.acc_days
1677 )
1678 )
1679 txt += _(' Reason: %s\n') % self._payload[self._idx['discontinue_reason']]
1680
1681 txt += u'\n'
1682
1683 txt += gmTools.coalesce(self._payload[self._idx['aim']], u'', _(' Aim: %s\n'))
1684 txt += gmTools.coalesce(self._payload[self._idx['episode']], u'', _(' Episode: %s\n'))
1685 txt += gmTools.coalesce(self._payload[self._idx['health_issue']], u'', _(' Health issue: %s\n'))
1686 txt += gmTools.coalesce(self._payload[self._idx['notes']], u'', _(' Advice: %s\n'))
1687
1688 txt += u'\n'
1689
1690 txt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % {
1691 'row_ver': self._payload[self._idx['row_version']],
1692 'mod_when': gmDateTime.pydt_strftime(self._payload[self._idx['modified_when']]),
1693 'mod_by': self._payload[self._idx['modified_by']]
1694 }
1695
1696 return txt
1697 #--------------------------------------------------------
1699 allg = gmAllergy.create_allergy (
1700 allergene = self._payload[self._idx['substance']],
1701 allg_type = allergy_type,
1702 episode_id = self._payload[self._idx['pk_episode']],
1703 encounter_id = encounter_id
1704 )
1705 allg['substance'] = gmTools.coalesce (
1706 self._payload[self._idx['brand']],
1707 self._payload[self._idx['substance']]
1708 )
1709 allg['reaction'] = self._payload[self._idx['discontinue_reason']]
1710 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']])
1711 if self._payload[self._idx['external_code_brand']] is not None:
1712 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']])
1713
1714 if self._payload[self._idx['pk_brand']] is None:
1715 allg['generics'] = self._payload[self._idx['substance']]
1716 else:
1717 comps = [ c['substance'] for c in self.containing_drug.components ]
1718 if len(comps) == 0:
1719 allg['generics'] = self._payload[self._idx['substance']]
1720 else:
1721 allg['generics'] = u'; '.join(comps)
1722
1723 allg.save()
1724 return allg
1725 #--------------------------------------------------------
1726 # properties
1727 #--------------------------------------------------------
1729
1730 try: self.__ddd
1731 except AttributeError: self.__ddd = None
1732
1733 if self.__ddd is not None:
1734 return self.__ddd
1735
1736 if self._payload[self._idx['atc_substance']] is not None:
1737 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']])
1738 if len(ddd) != 0:
1739 self.__ddd = ddd[0]
1740 else:
1741 if self._payload[self._idx['atc_brand']] is not None:
1742 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']])
1743 if len(ddd) != 0:
1744 self.__ddd = ddd[0]
1745
1746 return self.__ddd
1747
1748 ddd = property(_get_ddd, lambda x:x)
1749 #--------------------------------------------------------
1751 drug = self.containing_drug
1752
1753 if drug is None:
1754 return None
1755
1756 return drug.external_code
1757
1758 external_code = property(_get_external_code, lambda x:x)
1759 #--------------------------------------------------------
1761 drug = self.containing_drug
1762
1763 if drug is None:
1764 return None
1765
1766 return drug.external_code_type
1767
1768 external_code_type = property(_get_external_code_type, lambda x:x)
1769 #--------------------------------------------------------
1771 if self._payload[self._idx['pk_brand']] is None:
1772 return None
1773
1774 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1775
1776 containing_drug = property(_get_containing_drug, lambda x:x)
1777 #--------------------------------------------------------
1779 tests = [
1780 # lead, trail
1781 ' 1-1-1-1 ',
1782 # leading dose
1783 '1-1-1-1',
1784 '22-1-1-1',
1785 '1/3-1-1-1',
1786 '/4-1-1-1'
1787 ]
1788 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}$"
1789 for test in tests:
1790 print test.strip(), ":", regex.match(pattern, test.strip())
1791 #------------------------------------------------------------
1793 args = {'comp': pk_component, 'subst': pk_substance, 'pat': pk_identity}
1794
1795 where_clause = u"""
1796 fk_encounter IN (
1797 SELECT pk FROM clin.encounter WHERE fk_patient = %(pat)s
1798 )
1799 AND
1800 """
1801
1802 if pk_substance is not None:
1803 where_clause += u'fk_substance = %(subst)s'
1804 if pk_component is not None:
1805 where_clause += u'fk_drug_component = %(comp)s'
1806
1807 cmd = u"""SELECT exists (
1808 SELECT 1 FROM clin.substance_intake
1809 WHERE
1810 %s
1811 LIMIT 1
1812 )""" % where_clause
1813
1814 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1815 return rows[0][0]
1816 #------------------------------------------------------------
1817 -def create_substance_intake(pk_substance=None, pk_component=None, preparation=None, encounter=None, episode=None):
1818
1819 args = {
1820 'enc': encounter,
1821 'epi': episode,
1822 'comp': pk_component,
1823 'subst': pk_substance,
1824 'prep': preparation
1825 }
1826
1827 if pk_component is None:
1828 cmd = u"""
1829 INSERT INTO clin.substance_intake (
1830 fk_encounter,
1831 fk_episode,
1832 intake_is_approved_of,
1833 fk_substance,
1834 preparation
1835 ) VALUES (
1836 %(enc)s,
1837 %(epi)s,
1838 False,
1839 %(subst)s,
1840 %(prep)s
1841 )
1842 RETURNING pk"""
1843 else:
1844 cmd = u"""
1845 INSERT INTO clin.substance_intake (
1846 fk_encounter,
1847 fk_episode,
1848 intake_is_approved_of,
1849 fk_drug_component
1850 ) VALUES (
1851 %(enc)s,
1852 %(epi)s,
1853 False,
1854 %(comp)s
1855 )
1856 RETURNING pk"""
1857
1858 try:
1859 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
1860 except gmPG2.dbapi.InternalError, e:
1861 if e.pgerror is None:
1862 raise
1863 if 'prevent_duplicate_component' in e.pgerror:
1864 _log.exception('will not create duplicate substance intake entry')
1865 _log.error(e.pgerror)
1866 return None
1867 raise
1868
1869 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
1870 #------------------------------------------------------------
1872 cmd = u'delete from clin.substance_intake where pk = %(pk)s'
1873 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
1874 #------------------------------------------------------------
1876
1877 tex = u'\n{\\small\n'
1878 tex += u'\\noindent %s\n' % _('Additional notes')
1879 tex += u'\n'
1880 tex += u'\\noindent \\begin{tabularx}{\\textwidth}{|X|l|X|p{7.5cm}|}\n'
1881 tex += u'\\hline\n'
1882 tex += u'%s {\\scriptsize (%s)} & %s & %s \\\\ \n' % (_('Substance'), _('Brand'), _('Strength'), _('Advice'))
1883 tex += u'\\hline\n'
1884 tex += u'%s\n'
1885 tex += u'\n'
1886 tex += u'\\end{tabularx}\n'
1887 tex += u'}\n'
1888
1889 current_meds = emr.get_current_substance_intake (
1890 include_inactive = False,
1891 include_unapproved = False,
1892 order_by = u'brand, substance'
1893 )
1894
1895 # create lines
1896 lines = []
1897 for med in current_meds:
1898 lines.append(u'%s%s %s & %s%s & %s \\\\ \n \\hline \n' % (
1899 med['substance'],
1900 gmTools.coalesce(med['brand'], u'', u' {\\scriptsize (%s)}'),
1901 med['preparation'],
1902 med['amount'],
1903 med['unit'],
1904 gmTools.coalesce(med['notes'], u'', u'{\\scriptsize %s}')
1905 ))
1906
1907 return tex % u' \n'.join(lines)
1908
1909 #------------------------------------------------------------
1911
1912 tex = u'\\noindent %s {\\tiny (%s)\\par}\n' % (_('Medication list'), _('ordered by brand'))
1913 tex += u'\n'
1914 tex += u'\\noindent \\begin{tabular}{|l|l|}\n'
1915 tex += u'\\hline\n'
1916 tex += u'%s & %s \\\\ \n' % (_('Drug'), _('Regimen'))
1917 tex += u'\\hline\n'
1918 tex += u'\n'
1919 tex += u'\\hline\n'
1920 tex += u'%s\n'
1921 tex += u'\n'
1922 tex += u'\\end{tabular}\n'
1923
1924 current_meds = emr.get_current_substance_intake (
1925 include_inactive = False,
1926 include_unapproved = False,
1927 order_by = u'brand, substance'
1928 )
1929
1930 # aggregate data
1931 line_data = {}
1932 for med in current_meds:
1933 identifier = gmTools.coalesce(med['brand'], med['substance'])
1934
1935 try:
1936 line_data[identifier]
1937 except KeyError:
1938 line_data[identifier] = {'brand': u'', 'preparation': u'', 'schedule': u'', 'aims': [], 'strengths': []}
1939
1940 line_data[identifier]['brand'] = identifier
1941 line_data[identifier]['strengths'].append(u'%s%s' % (med['amount'], med['unit'].strip()))
1942 line_data[identifier]['preparation'] = med['preparation']
1943 line_data[identifier]['schedule'] = gmTools.coalesce(med['schedule'], u'')
1944 if med['aim'] not in line_data[identifier]['aims']:
1945 line_data[identifier]['aims'].append(med['aim'])
1946
1947 # create lines
1948 already_seen = []
1949 lines = []
1950 line1_template = u'%s %s & %s \\\\'
1951 line2_template = u' & {\\scriptsize %s\\par} \\\\'
1952
1953 for med in current_meds:
1954 identifier = gmTools.coalesce(med['brand'], med['substance'])
1955
1956 if identifier in already_seen:
1957 continue
1958
1959 already_seen.append(identifier)
1960
1961 lines.append (line1_template % (
1962 line_data[identifier]['brand'],
1963 line_data[identifier]['preparation'],
1964 line_data[identifier]['schedule']
1965 ))
1966
1967 strengths = u'/'.join(line_data[identifier]['strengths'])
1968 if strengths == u'':
1969 template = u' & {\\scriptsize %s\\par} \\\\'
1970 for aim in line_data[identifier]['aims']:
1971 lines.append(template % aim)
1972 else:
1973 if len(line_data[identifier]['aims']) == 0:
1974 template = u'%s & \\\\'
1975 lines.append(template % strengths)
1976 else:
1977 template = u'%s & {\\scriptsize %s\\par} \\\\'
1978 lines.append(template % (strengths, line_data[identifier]['aims'][0]))
1979 template = u' & {\\scriptsize %s\\par} \\\\'
1980 for aim in line_data[identifier]['aims'][1:]:
1981 lines.append(template % aim)
1982
1983 lines.append(u'\\hline')
1984
1985 return tex % u' \n'.join(lines)
1986 #============================================================
1987 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s'
1988
1990
1991 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s'
1992 _cmds_store_payload = [
1993 u"""UPDATE ref.lnk_substance2brand SET
1994 fk_brand = %(pk_brand)s,
1995 fk_substance = %(pk_consumable_substance)s
1996 WHERE
1997 NOT EXISTS (
1998 SELECT 1
1999 FROM clin.substance_intake
2000 WHERE fk_drug_component = %(pk_component)s
2001 LIMIT 1
2002 )
2003 AND
2004 pk = %(pk_component)s
2005 AND
2006 xmin = %(xmin_lnk_substance2brand)s
2007 RETURNING
2008 xmin AS xmin_lnk_substance2brand
2009 """
2010 ]
2011 _updatable_fields = [
2012 u'pk_brand',
2013 u'pk_consumable_substance'
2014 ]
2015 #--------------------------------------------------------
2016 # properties
2017 #--------------------------------------------------------
2019 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
2020
2021 containing_drug = property(_get_containing_drug, lambda x:x)
2022 #--------------------------------------------------------
2025
2026 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
2027 #--------------------------------------------------------
2029 return cConsumableSubstance(aPK_obj = self._payload[self._idx['pk_consumable_substance']])
2030
2031 substance = property(_get_substance, lambda x:x)
2032 #------------------------------------------------------------
2034 cmd = _SQL_get_drug_components % u'true ORDER BY brand, substance'
2035 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
2036 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
2037 #------------------------------------------------------------
2039
2040 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
2041 _query_desc_only = u"""
2042 SELECT DISTINCT ON (component)
2043 pk_component,
2044 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
2045 AS component
2046 FROM ref.v_drug_components
2047 WHERE
2048 substance %(fragment_condition)s
2049 OR
2050 brand %(fragment_condition)s
2051 ORDER BY component
2052 LIMIT 50"""
2053 _query_desc_and_amount = u"""
2054
2055 SELECT DISTINCT ON (component)
2056 pk_component,
2057 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
2058 AS component
2059 FROM ref.v_drug_components
2060 WHERE
2061 %(fragment_condition)s
2062 ORDER BY component
2063 LIMIT 50"""
2064 #--------------------------------------------------------
2066 """Return matches for aFragment at start of phrases."""
2067
2068 if cDrugComponentMatchProvider._pattern.match(aFragment):
2069 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2070 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
2071 AND
2072 amount::text ILIKE %(amount)s"""
2073 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
2074 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2075 else:
2076 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2077 fragment_condition = u"ILIKE %(fragment)s"
2078 self._args['fragment'] = u"%s%%" % aFragment
2079
2080 return self._find_matches(fragment_condition)
2081 #--------------------------------------------------------
2083 """Return matches for aFragment at start of words inside phrases."""
2084
2085 if cDrugComponentMatchProvider._pattern.match(aFragment):
2086 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2087
2088 desc = regex.sub(r'\s*\d+$', u'', aFragment)
2089 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
2090
2091 fragment_condition = """(substance ~* %(desc)s OR brand ~* %(desc)s)
2092 AND
2093 amount::text ILIKE %(amount)s"""
2094
2095 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
2096 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2097 else:
2098 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2099 fragment_condition = u"~* %(fragment)s"
2100 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
2101 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
2102
2103 return self._find_matches(fragment_condition)
2104 #--------------------------------------------------------
2106 """Return matches for aFragment as a true substring."""
2107
2108 if cDrugComponentMatchProvider._pattern.match(aFragment):
2109 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
2110 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
2111 AND
2112 amount::text ILIKE %(amount)s"""
2113 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
2114 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
2115 else:
2116 self._queries = [cDrugComponentMatchProvider._query_desc_only]
2117 fragment_condition = u"ILIKE %(fragment)s"
2118 self._args['fragment'] = u"%%%s%%" % aFragment
2119
2120 return self._find_matches(fragment_condition)
2121
2122 #============================================================
2124 """Represents a drug as marketed by a manufacturer."""
2125
2126 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s"
2127 _cmds_store_payload = [
2128 u"""UPDATE ref.branded_drug SET
2129 description = %(brand)s,
2130 preparation = %(preparation)s,
2131 atc_code = gm.nullify_empty_string(%(atc)s),
2132 external_code = gm.nullify_empty_string(%(external_code)s),
2133 external_code_type = gm.nullify_empty_string(%(external_code_type)s),
2134 is_fake = %(is_fake_brand)s,
2135 fk_data_source = %(pk_data_source)s
2136 WHERE
2137 pk = %(pk_brand)s
2138 AND
2139 xmin = %(xmin_branded_drug)s
2140 RETURNING
2141 xmin AS xmin_branded_drug
2142 """
2143 ]
2144 _updatable_fields = [
2145 u'brand',
2146 u'preparation',
2147 u'atc',
2148 u'is_fake_brand',
2149 u'external_code',
2150 u'external_code_type',
2151 u'pk_data_source'
2152 ]
2153 #--------------------------------------------------------
2155 success, data = super(self.__class__, self).save_payload(conn = conn)
2156
2157 if not success:
2158 return (success, data)
2159
2160 if self._payload[self._idx['atc']] is not None:
2161 atc = self._payload[self._idx['atc']].strip()
2162 if atc != u'':
2163 gmATC.propagate_atc (
2164 substance = self._payload[self._idx['brand']].strip(),
2165 atc = atc
2166 )
2167
2168 return (success, data)
2169 #--------------------------------------------------------
2171
2172 if self.is_in_use_by_patients:
2173 return False
2174
2175 args = {'brand': self._payload[self._idx['pk_brand']]}
2176
2177 queries = [{'cmd': u"DELETE FROM ref.lnk_substance2brand WHERE fk_brand = %(brand)s", 'args': args}]
2178 cmd = u'INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) VALUES (%%(brand)s, %s)'
2179 for s in substances:
2180 queries.append({'cmd': cmd % s['pk'], 'args': args})
2181
2182 gmPG2.run_rw_queries(queries = queries)
2183 self.refetch_payload()
2184
2185 return True
2186 #--------------------------------------------------------
2187 - def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
2188
2189 args = {
2190 'brand': self.pk_obj,
2191 'subst': substance,
2192 'atc': atc,
2193 'pk_subst': pk_substance
2194 }
2195
2196 if pk_substance is None:
2197 consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit)
2198 args['pk_subst'] = consumable['pk']
2199
2200 # already a component
2201 cmd = u"""
2202 SELECT pk_component
2203 FROM ref.v_drug_components
2204 WHERE
2205 pk_brand = %(brand)s
2206 AND
2207 ((
2208 (lower(substance) = lower(%(subst)s))
2209 OR
2210 (lower(atc_substance) = lower(%(atc)s))
2211 OR
2212 (pk_consumable_substance = %(pk_subst)s)
2213 ) IS TRUE)
2214 """
2215 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2216
2217 if len(rows) > 0:
2218 return
2219
2220 # create it
2221 cmd = u"""
2222 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance)
2223 VALUES (%(brand)s, %(pk_subst)s)
2224 """
2225 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2226 self.refetch_payload()
2227 #------------------------------------------------------------
2229 if len(self._payload[self._idx['components']]) == 1:
2230 _log.error('cannot remove the only component of a drug')
2231 return False
2232
2233 args = {'brand': self.pk_obj, 'comp': substance}
2234 cmd = u"""
2235 DELETE FROM ref.lnk_substance2brand
2236 WHERE
2237 fk_brand = %(brand)s
2238 AND
2239 fk_substance = %(comp)s
2240 AND
2241 NOT EXISTS (
2242 SELECT 1
2243 FROM clin.substance_intake
2244 WHERE fk_drug_component = %(comp)s
2245 LIMIT 1
2246 )
2247 """
2248 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2249 self.refetch_payload()
2250
2251 return True
2252 #--------------------------------------------------------
2253 # properties
2254 #--------------------------------------------------------
2256 if self._payload[self._idx['external_code']] is None:
2257 return None
2258
2259 return self._payload[self._idx['external_code']]
2260
2261 external_code = property(_get_external_code, lambda x:x)
2262 #--------------------------------------------------------
2264
2265 # FIXME: maybe evaluate fk_data_source ?
2266 if self._payload[self._idx['external_code_type']] is None:
2267 return None
2268
2269 return self._payload[self._idx['external_code_type']]
2270
2271 external_code_type = property(_get_external_code_type, lambda x:x)
2272 #--------------------------------------------------------
2274 cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s'
2275 args = {'brand': self._payload[self._idx['pk_brand']]}
2276 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2277 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
2278
2279 components = property(_get_components, lambda x:x)
2280 #--------------------------------------------------------
2282 if self._payload[self._idx['pk_substances']] is None:
2283 return []
2284 cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s'
2285 args = {'pks': tuple(self._payload[self._idx['pk_substances']])}
2286 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2287 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
2288
2289 components_as_substances = property(_get_components_as_substances, lambda x:x)
2290 #--------------------------------------------------------
2292 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)'
2293 args = {'fk_brand': self._payload[self._idx['pk_brand']]}
2294 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2295 return rows[0][0]
2296
2297 is_vaccine = property(_get_is_vaccine, lambda x:x)
2298 #--------------------------------------------------------
2300 cmd = u"""
2301 SELECT EXISTS (
2302 SELECT 1
2303 FROM clin.substance_intake
2304 WHERE
2305 fk_drug_component IS NOT NULL
2306 AND
2307 fk_drug_component IN (
2308 SELECT r_ls2b.pk
2309 FROM ref.lnk_substance2brand r_ls2b
2310 WHERE fk_brand = %(pk)s
2311 )
2312 LIMIT 1
2313 )"""
2314 args = {'pk': self.pk_obj}
2315
2316 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2317 return rows[0][0]
2318
2319 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
2320 #------------------------------------------------------------
2322 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description'
2323 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
2324 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
2325 #------------------------------------------------------------
2327 args = {'brand': brand_name, 'prep': preparation}
2328
2329 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)'
2330 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2331
2332 if len(rows) == 0:
2333 return None
2334
2335 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2336 #------------------------------------------------------------
2338
2339 if preparation is None:
2340 preparation = _('units')
2341
2342 if preparation.strip() == u'':
2343 preparation = _('units')
2344
2345 if return_existing:
2346 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation)
2347 if drug is not None:
2348 return drug
2349
2350 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk'
2351 args = {'brand': brand_name, 'prep': preparation}
2352 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
2353
2354 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2355 #------------------------------------------------------------
2357 queries = []
2358 args = {'pk': brand}
2359
2360 # delete components
2361 cmd = u"""
2362 DELETE FROM ref.lnk_substance2brand
2363 WHERE
2364 fk_brand = %(pk)s
2365 AND
2366 NOT EXISTS (
2367 SELECT 1
2368 FROM clin.v_pat_substance_intake
2369 WHERE pk_brand = %(pk)s
2370 LIMIT 1
2371 )
2372 """
2373 queries.append({'cmd': cmd, 'args': args})
2374
2375 # delete drug
2376 cmd = u"""
2377 DELETE FROM ref.branded_drug
2378 WHERE
2379 pk = %(pk)s
2380 AND
2381 NOT EXISTS (
2382 SELECT 1
2383 FROM clin.v_pat_substance_intake
2384 WHERE pk_brand = %(pk)s
2385 LIMIT 1
2386 )
2387 """
2388 queries.append({'cmd': cmd, 'args': args})
2389
2390 gmPG2.run_rw_queries(queries = queries)
2391 #============================================================
2392 # main
2393 #------------------------------------------------------------
2394 if __name__ == "__main__":
2395
2396 if len(sys.argv) < 2:
2397 sys.exit()
2398
2399 if sys.argv[1] != 'test':
2400 sys.exit()
2401
2402 from Gnumed.pycommon import gmLog2
2403 from Gnumed.pycommon import gmI18N
2404 from Gnumed.business import gmPerson
2405
2406 gmI18N.activate_locale()
2407 # gmDateTime.init()
2408 #--------------------------------------------------------
2410 mmi = cGelbeListeWineInterface()
2411 print mmi
2412 print "interface definition:", mmi.version
2413 print "database versions: ", mmi.get_data_source_version()
2414 #--------------------------------------------------------
2416 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
2417 for drug in mmi_file:
2418 print "-------------"
2419 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2420 for stoff in drug['wirkstoffe']:
2421 print " Wirkstoff:", stoff
2422 raw_input()
2423 if mmi_file.has_unknown_fields is not None:
2424 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key
2425 for key in mmi_file.csv_fieldnames:
2426 print key, '->', drug[key]
2427 raw_input()
2428 mmi_file.close()
2429 #--------------------------------------------------------
2433 #--------------------------------------------------------
2435 mmi = cGelbeListeWineInterface()
2436 mmi_file = mmi.__let_user_select_drugs()
2437 for drug in mmi_file:
2438 print "-------------"
2439 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2440 for stoff in drug['wirkstoffe']:
2441 print " Wirkstoff:", stoff
2442 print drug
2443 mmi_file.close()
2444 #--------------------------------------------------------
2448 #--------------------------------------------------------
2450 mmi = cGelbeListeInterface()
2451 print mmi
2452 print "interface definition:", mmi.version
2453 # Metoprolol + Hct vs Citalopram
2454 diclofenac = '7587712'
2455 phenprocoumon = '4421744'
2456 mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
2457 #--------------------------------------------------------
2458 # FreeDiams
2459 #--------------------------------------------------------
2461 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12))
2462 fd = cFreeDiamsInterface()
2463 fd.patient = gmPerson.gmCurrentPatient()
2464 # fd.switch_to_frontend(blocking = True)
2465 fd.import_fd2gm_file_as_drugs(filename = sys.argv[2])
2466 #--------------------------------------------------------
2468 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12))
2469 fd = cFreeDiamsInterface()
2470 fd.patient = gmPerson.gmCurrentPatient()
2471 fd.check_interactions(substances = fd.patient.get_emr().get_current_substance_intake(include_unapproved = True))
2472 #--------------------------------------------------------
2473 # generic
2474 #--------------------------------------------------------
2476 drug = create_substance_intake (
2477 pk_component = 2,
2478 encounter = 1,
2479 episode = 1
2480 )
2481 print drug
2482 #--------------------------------------------------------
2487 #--------------------------------------------------------
2491 #--------------------------------------------------------
2493 drug2renal_insufficiency_url(search_term = 'Metoprolol')
2494 #--------------------------------------------------------
2495 # MMI/Gelbe Liste
2496 #test_MMI_interface()
2497 #test_MMI_file()
2498 #test_mmi_switch_to()
2499 #test_mmi_let_user_select_drugs()
2500 #test_mmi_import_substances()
2501 #test_mmi_import_drugs()
2502
2503 # FreeDiams
2504 #test_fd_switch_to()
2505 #test_fd_show_interactions()
2506
2507 # generic
2508 #test_interaction_check()
2509 #test_create_substance_intake()
2510 #test_show_components()
2511 #test_get_consumable_substances()
2512
2513 test_drug2renal_insufficiency_url()
2514 #============================================================
2515
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri May 11 03:58:29 2012 | http://epydoc.sourceforge.net |