| 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_code'] is not None:
62 terms.append(search_term['atc_code'])
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 not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
357 _log.error('problem switching to the FreeDiams drug database')
358 return False
359
360 if blocking == True:
361 self.import_fd2gm_file_as_drugs()
362
363 return True
364 #--------------------------------------------------------
366 self.switch_to_frontend(blocking = True)
367 #--------------------------------------------------------
369 if substance_intakes is None:
370 return
371 if len(substance_intakes) < 2:
372 return
373
374 self.__create_prescription_file(substance_intakes = substance_intakes)
375 self.switch_to_frontend(mode = 'interactions', blocking = False)
376 #--------------------------------------------------------
378 if substance_intake is None:
379 return
380
381 self.__create_prescription_file(substance_intakes = [substance_intake])
382 self.switch_to_frontend(mode = 'interactions', blocking = False)
383 #--------------------------------------------------------
385 self.show_info_on_drug(substance_intake = substance_intake)
386 #--------------------------------------------------------
388 if substance_intakes is None:
389 if not self.__export_latest_prescription():
390 self.__create_prescription_file()
391 else:
392 self.__create_prescription_file(substance_intakes = substance_intakes)
393
394 self.switch_to_frontend(mode = 'prescription', blocking = True)
395 self.import_fd2gm_file_as_prescription()
396
397 return self.__imported_drugs
398 #--------------------------------------------------------
399 # internal helpers
400 #--------------------------------------------------------
402
403 if self.path_to_binary is not None:
404 return True
405
406 found, cmd = gmShellAPI.find_first_binary(binaries = [
407 r'/usr/bin/freediams',
408 r'freediams',
409 r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams',
410 r'C:\Program Files\FreeDiams\freediams.exe',
411 r'c:\programs\freediams\freediams.exe',
412 r'freediams.exe'
413 ])
414
415 if found:
416 self.path_to_binary = cmd
417 return True
418
419 try:
420 self.custom_path_to_binary
421 except AttributeError:
422 _log.error('cannot find FreeDiams binary, no custom path set')
423 return False
424
425 if self.custom_path_to_binary is None:
426 _log.error('cannot find FreeDiams binary')
427 return False
428
429 found, cmd = gmShellAPI.detect_external_binary(binary = self.custom_path_to_binary)
430 if found:
431 self.path_to_binary = cmd
432 return True
433
434 _log.error('cannot find FreeDiams binary')
435 return False
436 #--------------------------------------------------------
438
439 if self.patient is None:
440 _log.debug('cannot export latest FreeDiams prescriptions w/o patient')
441 return False
442
443 docs = self.patient.get_document_folder()
444 prescription = docs.get_latest_freediams_prescription()
445 if prescription is None:
446 _log.debug('no FreeDiams prescription available')
447 return False
448
449 for part in prescription.parts:
450 if part['filename'] == u'freediams-prescription.xml':
451 if part.export_to_file(filename = self.__fd2gm_filename) is not None:
452 return True
453
454 _log.error('cannot export latest FreeDiams prescription to XML file')
455
456 return False
457 #--------------------------------------------------------
459 """FreeDiams calls this exchange-out or prescription file.
460
461 CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel).
462 CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS.
463 AFSSAPS is the French FDA.
464
465 CIP stands for Unique Presentation Identifier (eg 30 pills plaq)
466 CIP if you want to specify the packaging of the drug (30 pills
467 thermoformed tablet...) -- actually not really usefull for french
468 doctors.
469 # .external_code_type: u'FR-CIS'
470 # .external_cod: the CIS value
471
472 OnlyForTest:
473 OnlyForTest drugs will be processed by the IA Engine but
474 not printed (regardless of FreeDiams mode). They are shown
475 in gray in the prescription view.
476
477 Select-only is a mode where FreeDiams creates a list of drugs
478 not a full prescription. In this list, users can add ForTestOnly
479 drug if they want to
480 1. print the list without some drugs
481 2. but including these drugs in the IA engine calculation
482
483 Select-Only mode does not have any relation with the ForTestOnly drugs.
484
485 IsTextual:
486 What is the use and significance of the
487 <IsTextual>true/false</IsTextual>
488 flag when both <DrugName> and <TextualDrugName> exist ?
489
490 This tag must be setted even if it sounds like a duplicated
491 data. This tag is needed inside FreeDiams code.
492
493 INN:
494 GNUmed will pass the substance in <TextualDrugName
495 and will also pass <INN>True</INN>.
496
497 Eric: Nop, this is not usefull because pure textual drugs
498 are not processed but just shown.
499 """
500 # virginize file
501 open(self.__fd2gm_filename, 'wb').close()
502
503 # make sure we've got something to do
504 if substance_intakes is None:
505 if self.patient is None:
506 _log.warning('cannot create prescription file because there is neither a patient nor a substance intake list')
507 # do fail because __export_latest_prescription() should not have been called without patient
508 return False
509 emr = self.patient.get_emr()
510 substance_intakes = emr.get_current_substance_intake (
511 include_inactive = False,
512 include_unapproved = True
513 )
514
515 drug_snippets = []
516
517 # process FD drugs
518 fd_intakes = [ i for i in substance_intakes if (
519 (i['intake_is_approved_of'] is True)
520 and
521 (i['external_code_type_brand'] is not None)
522 and
523 (i['external_code_type_brand'].startswith(u'FreeDiams::'))
524 )]
525
526 intakes_pooled_by_brand = {}
527 for intake in fd_intakes:
528 # this will leave only one entry per brand
529 # but FreeDiams knows the components ...
530 intakes_pooled_by_brand[intake['brand']] = intake
531 del fd_intakes
532
533 drug_snippet = u"""<Prescription>
534 <IsTextual>False</IsTextual>
535 <DrugName>%s</DrugName>
536 <Drug_UID>%s</Drug_UID>
537 <Drug_UID_type>%s</Drug_UID_type> <!-- not yet supported by FreeDiams -->
538 </Prescription>"""
539
540 last_db_id = u'CA_HCDPD'
541 for intake in intakes_pooled_by_brand.values():
542 last_db_id = gmTools.xml_escape_string(text = intake['external_code_type_brand'].replace(u'FreeDiams::', u'').split(u'::')[0])
543 drug_snippets.append(drug_snippet % (
544 gmTools.xml_escape_string(text = intake['brand'].strip()),
545 gmTools.xml_escape_string(text = intake['external_code_brand'].strip()),
546 last_db_id
547 ))
548
549 # process non-FD drugs
550 non_fd_intakes = [ i for i in substance_intakes if (
551 (i['intake_is_approved_of'] is True)
552 and (
553 (i['external_code_type_brand'] is None)
554 or
555 (not i['external_code_type_brand'].startswith(u'FreeDiams::'))
556 )
557 )]
558
559 non_fd_brand_intakes = [ i for i in non_fd_intakes if i['brand'] is not None ]
560 non_fd_substance_intakes = [ i for i in non_fd_intakes if i['brand'] is None ]
561 del non_fd_intakes
562
563 drug_snippet = u"""<Prescription>
564 <IsTextual>True</IsTextual>
565 <TextualDrugName>%s</TextualDrugName>
566 </Prescription>"""
567
568 for intake in non_fd_substance_intakes:
569 drug_name = u'%s %s%s (%s)%s' % (
570 intake['substance'],
571 intake['amount'],
572 intake['unit'],
573 intake['preparation'],
574 gmTools.coalesce(intake['schedule'], u'', _('\n Take: %s'))
575 )
576 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
577
578 intakes_pooled_by_brand = {}
579 for intake in non_fd_brand_intakes:
580 brand = u'%s %s' % (intake['brand'], intake['preparation'])
581 try:
582 intakes_pooled_by_brand[brand].append(intake)
583 except KeyError:
584 intakes_pooled_by_brand[brand] = [intake]
585
586 for brand, comps in intakes_pooled_by_brand.iteritems():
587 drug_name = u'%s\n' % brand
588 for comp in comps:
589 drug_name += u' %s %s%s\n' % (
590 comp['substance'],
591 comp['amount'],
592 comp['unit']
593 )
594 if comps[0]['schedule'] is not None:
595 drug_name += gmTools.coalesce(comps[0]['schedule'], u'', _('Take: %s'))
596 drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
597
598 # assemble XML file
599 xml = u"""<?xml version = "1.0" encoding = "UTF-8"?>
600
601 <FreeDiams>
602 <DrugsDatabaseName>%s</DrugsDatabaseName>
603 <FullPrescription version="0.5.0">
604
605 %s
606
607 </FullPrescription>
608 </FreeDiams>
609 """
610
611 xml_file = codecs.open(self.__fd2gm_filename, 'wb', 'utf8')
612 xml_file.write(xml % (
613 last_db_id,
614 u'\n\t\t'.join(drug_snippets)
615 ))
616 xml_file.close()
617
618 return True
619 #--------------------------------------------------------
621
622 if mode == 'interactions':
623 mode = u'select-only'
624 elif mode == 'prescription':
625 mode = u'prescriber'
626 else:
627 mode = u'select-only'
628
629 xml_file = codecs.open(self.__gm2fd_filename, 'wb', 'utf8')
630
631 xml = u"""<?xml version="1.0" encoding="UTF-8"?>
632
633 <FreeDiams_In version="0.5.0">
634 <EMR name="GNUmed" uid="unused"/>
635 <ConfigFile value="%s"/>
636 <ExchangeOut value="%s" format="xml"/>
637 <!-- <DrugsDatabase uid="can be set to a specific DB"/> -->
638 <Ui editmode="%s" blockPatientDatas="1"/>
639 %%s
640 </FreeDiams_In>
641
642 <!--
643 # FIXME: search by LOINC code and add (as soon as supported by FreeDiams ...)
644 <Creatinine value="12" unit="mg/l or mmol/l"/>
645 <Weight value="70" unit="kg or pd" />
646 <Height value="170" unit="cm or "/>
647 <ICD10 value="J11.0;A22;Z23"/>
648 -->
649 """ % (
650 self.__fd4gm_config_file,
651 self.__fd2gm_filename,
652 mode
653 )
654
655 if self.patient is None:
656 xml_file.write(xml % u'')
657 xml_file.close()
658 return
659
660 name = self.patient.get_active_name()
661 if self.patient['dob'] is None:
662 dob = u''
663 else:
664 dob = self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format)
665
666 emr = self.patient.get_emr()
667 allgs = emr.get_allergies()
668 atc_allgs = [
669 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'allergy'))
670 ]
671 atc_sens = [
672 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'sensitivity'))
673 ]
674 inn_allgs = [ a['allergene'] for a in allgs if a['type'] == u'allergy' ]
675 inn_sens = [ a['allergene'] for a in allgs if a['type'] == u'sensitivity' ]
676 # this is rather fragile: FreeDiams won't know what type of UID this is
677 # (but it will assume it is of the type of the drug database in use)
678 uid_allgs = [
679 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'allergy'))
680 ]
681 uid_sens = [
682 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'sensitivity'))
683 ]
684
685 patient_xml = u"""<Patient>
686 <Identity
687 lastnames="%s"
688 firstnames="%s"
689 uid="%s"
690 dob="%s"
691 gender="%s"
692 />
693 <ATCAllergies value="%s"/>
694 <ATCIntolerances value="%s"/>
695
696 <InnAllergies value="%s"/>
697 <InnIntolerances value="%s"/>
698
699 <DrugsUidAllergies value="%s"/>
700 <DrugsUidIntolerances value="%s"/>
701 </Patient>
702 """ % (
703 gmTools.xml_escape_string(text = name['lastnames']),
704 gmTools.xml_escape_string(text = name['firstnames']),
705 self.patient.ID,
706 dob,
707 cFreeDiamsInterface.map_gender2mf[self.patient['gender']],
708 gmTools.xml_escape_string(text = u';'.join(atc_allgs)),
709 gmTools.xml_escape_string(text = u';'.join(atc_sens)),
710 gmTools.xml_escape_string(text = u';'.join(inn_allgs)),
711 gmTools.xml_escape_string(text = u';'.join(inn_sens)),
712 gmTools.xml_escape_string(text = u';'.join(uid_allgs)),
713 gmTools.xml_escape_string(text = u';'.join(uid_sens))
714 )
715
716 xml_file.write(xml % patient_xml)
717 xml_file.close()
718 #--------------------------------------------------------
720
721 if filename is None:
722 filename = self.__fd2gm_filename
723
724 fd2gm_xml = etree.ElementTree()
725 fd2gm_xml.parse(filename)
726
727 pdfs = fd2gm_xml.findall('ExtraDatas/Printed')
728 if len(pdfs) == 0:
729 return
730
731 fd_filenames = []
732 for pdf in pdfs:
733 fd_filenames.append(pdf.attrib['file'])
734
735 docs = self.patient.get_document_folder()
736 emr = self.patient.get_emr()
737
738 prescription = docs.add_document (
739 document_type = create_document_type (
740 document_type = DOCUMENT_TYPE_PRESCRIPTION
741 )['pk_doc_type'],
742 encounter = emr.active_encounter['pk_encounter'],
743 episode = emr.add_episode (
744 episode_name = DEFAULT_MEDICATION_HISTORY_EPISODE,
745 is_open = False
746 )['pk_episode']
747 )
748 prescription['ext_ref'] = u'FreeDiams'
749 prescription.save()
750 fd_filenames.append(filename)
751 success, msg, parts = prescription.add_parts_from_files(files = fd_filenames)
752 if not success:
753 _log.error(msg)
754 return
755
756 for part in parts:
757 part['obj_comment'] = _('copy')
758 part.save()
759
760 xml_part = parts[-1]
761 xml_part['filename'] = u'freediams-prescription.xml'
762 xml_part['obj_comment'] = _('data')
763 xml_part.save()
764
765 # are we the intended reviewer ?
766 from Gnumed.business.gmPerson import gmCurrentProvider
767 me = gmCurrentProvider()
768 # if so: auto-sign the prescription
769 if xml_part['pk_intended_reviewer'] == me['pk_staff']:
770 prescription.set_reviewed(technically_abnormal = False, clinically_relevant = False)
771 #--------------------------------------------------------
773 """
774 If returning textual prescriptions (say, drugs which FreeDiams
775 did not know) then "IsTextual" will be True and UID will be -1.
776 """
777 if filename is None:
778 filename = self.__fd2gm_filename
779
780 # FIXME: do not import IsTextual drugs, or rather, make that configurable
781
782 fd2gm_xml = etree.ElementTree()
783 fd2gm_xml.parse(filename)
784
785 data_src_pk = self.create_data_source_entry()
786
787 db_def = fd2gm_xml.find('DrugsDatabaseName')
788 db_id = db_def.text.strip()
789 drug_id_name = db_def.attrib['drugUidName']
790 fd_xml_drug_entries = fd2gm_xml.findall('FullPrescription/Prescription')
791
792 self.__imported_drugs = []
793 for fd_xml_drug in fd_xml_drug_entries:
794 drug_uid = fd_xml_drug.find('Drug_UID').text.strip()
795 if drug_uid == u'-1':
796 _log.debug('skipping textual drug')
797 continue # it's a TextualDrug, skip it
798 drug_name = fd_xml_drug.find('DrugName').text.replace(', )', ')').strip()
799 drug_form = fd_xml_drug.find('DrugForm').text.strip()
800 drug_atc = fd_xml_drug.find('DrugATC')
801 if drug_atc is None:
802 drug_atc = u''
803 else:
804 if drug_atc.text is None:
805 drug_atc = u''
806 else:
807 drug_atc = drug_atc.text.strip()
808
809 # create new branded drug
810 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True)
811 self.__imported_drugs.append(new_drug)
812 new_drug['is_fake_brand'] = False
813 new_drug['atc'] = drug_atc
814 new_drug['external_code_type'] = u'FreeDiams::%s::%s' % (db_id, drug_id_name)
815 new_drug['external_code'] = drug_uid
816 new_drug['pk_data_source'] = data_src_pk
817 new_drug.save()
818
819 # parse XML for composition records
820 fd_xml_components = fd_xml_drug.getiterator('Composition')
821 comp_data = {}
822 for fd_xml_comp in fd_xml_components:
823
824 data = {}
825
826 amount = regex.match(r'\d+[.,]{0,1}\d*', fd_xml_comp.attrib['strenght'].strip()) # sic, typo
827 if amount is None:
828 amount = 99999
829 else:
830 amount = amount.group()
831 data['amount'] = amount
832
833 unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', fd_xml_comp.attrib['strenght'].strip()).strip() # sic, typo
834 if unit == u'':
835 unit = u'*?*'
836 data['unit'] = unit
837
838 molecule_name = fd_xml_comp.attrib['molecularName'].strip()
839 if molecule_name != u'':
840 create_consumable_substance(substance = molecule_name, atc = None, amount = amount, unit = unit)
841 data['molecule_name'] = molecule_name
842
843 inn_name = fd_xml_comp.attrib['inn'].strip()
844 if inn_name != u'':
845 create_consumable_substance(substance = inn_name, atc = None, amount = amount, unit = unit)
846 data['inn_name'] = molecule_name
847
848 if molecule_name == u'':
849 data['substance'] = inn_name
850 _log.info('linking INN [%s] rather than molecularName as component', inn_name)
851 else:
852 data['substance'] = molecule_name
853
854 data['nature'] = fd_xml_comp.attrib['nature'].strip()
855 data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
856
857 # merge composition records of SA/FT nature
858 try:
859 old_data = comp_data[data['nature_ID']]
860 # normalize INN
861 if old_data['inn_name'] == u'':
862 old_data['inn_name'] = data['inn_name']
863 if data['inn_name'] == u'':
864 data['inn_name'] = old_data['inn_name']
865 # normalize molecule
866 if old_data['molecule_name'] == u'':
867 old_data['molecule_name'] = data['molecule_name']
868 if data['molecule_name'] == u'':
869 data['molecule_name'] = old_data['molecule_name']
870 # FT: transformed form
871 # SA: active substance
872 # it would be preferable to use the SA record because that's what's *actually*
873 # contained in the drug, however FreeDiams does not list the amount thereof
874 # (rather that of the INN)
875 if data['nature'] == u'FT':
876 comp_data[data['nature_ID']] = data
877 else:
878 comp_data[data['nature_ID']] = old_data
879
880 # or create new record
881 except KeyError:
882 comp_data[data['nature_ID']] = data
883
884 # actually create components from (possibly merged) composition records
885 for key, data in comp_data.items():
886 new_drug.add_component (
887 substance = data['substance'],
888 atc = None,
889 amount = data['amount'],
890 unit = data['unit']
891 )
892 #============================================================
894 """Support v8.2 CSV file interface only."""
895
896 version = u'Gelbe Liste/MMI v8.2 interface'
897 default_encoding = 'cp1250'
898 bdt_line_template = u'%03d6210#%s\r\n' # Medikament verordnet auf Kassenrezept
899 bdt_line_base_length = 8
900 #--------------------------------------------------------
902
903 cDrugDataSourceInterface.__init__(self)
904
905 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version)
906
907 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
908 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
909
910 paths = gmTools.gmPaths()
911
912 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt')
913 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp')
914 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt')
915 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
916
917 self.__data_date = None
918 self.__online_update_date = None
919
920 # use adjusted config.dat
921 #--------------------------------------------------------
923
924 if self.__data_date is not None:
925 if not force_reload:
926 return {
927 'data': self.__data_date,
928 'online_update': self.__online_update_date
929 }
930 try:
931 open(self.data_date_filename, 'wb').close()
932 except StandardError:
933 _log.error('problem querying the MMI drug database for version information')
934 _log.exception('cannot create MMI drug database version file [%s]', self.data_date_filename)
935 self.__data_date = None
936 self.__online_update_date = None
937 return {
938 'data': u'?',
939 'online_update': u'?'
940 }
941
942 cmd = u'%s -DATADATE' % self.path_to_binary
943 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
944 _log.error('problem querying the MMI drug database for version information')
945 self.__data_date = None
946 self.__online_update_date = None
947 return {
948 'data': u'?',
949 'online_update': u'?'
950 }
951
952 try:
953 version_file = open(self.data_date_filename, 'rU')
954 except StandardError:
955 _log.error('problem querying the MMI drug database for version information')
956 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename)
957 self.__data_date = None
958 self.__online_update_date = None
959 return {
960 'data': u'?',
961 'online_update': u'?'
962 }
963
964 self.__data_date = version_file.readline()[:10]
965 self.__online_update_date = version_file.readline()[:10]
966 version_file.close()
967
968 return {
969 'data': self.__data_date,
970 'online_update': self.__online_update_date
971 }
972 #--------------------------------------------------------
974 versions = self.get_data_source_version()
975
976 return create_data_source (
977 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
978 short_name = u'GL/MMI',
979 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
980 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
981 language = u'de'
982 )
983 #--------------------------------------------------------
985
986 try:
987 # must make sure csv file exists
988 open(self.default_csv_filename, 'wb').close()
989 except IOError:
990 _log.exception('problem creating GL/MMI <-> GNUmed exchange file')
991 return False
992
993 if cmd is None:
994 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
995
996 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
997 _log.error('problem switching to the MMI drug database')
998 # apparently on the first call MMI does not
999 # consistently return 0 on success
1000 # return False
1001
1002 return True
1003 #--------------------------------------------------------
1005
1006 # better to clean up interactions file
1007 open(self.interactions_filename, 'wb').close()
1008
1009 if not self.switch_to_frontend(blocking = True):
1010 return None
1011
1012 return cGelbeListeCSVFile(filename = self.default_csv_filename)
1013 #--------------------------------------------------------
1015
1016 selected_drugs = self.__let_user_select_drugs()
1017 if selected_drugs is None:
1018 return None
1019
1020 new_substances = []
1021
1022 for drug in selected_drugs:
1023 atc = None # hopefully MMI eventually supports atc-per-substance in a drug...
1024 if len(drug['wirkstoffe']) == 1:
1025 atc = drug['atc']
1026 for wirkstoff in drug['wirkstoffe']:
1027 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1028
1029 selected_drugs.close()
1030
1031 return new_substances
1032 #--------------------------------------------------------
1034
1035 selected_drugs = self.__let_user_select_drugs()
1036 if selected_drugs is None:
1037 return None
1038
1039 data_src_pk = self.create_data_source_entry()
1040
1041 new_drugs = []
1042 new_substances = []
1043
1044 for entry in selected_drugs:
1045
1046 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
1047
1048 if entry[u'hilfsmittel']:
1049 _log.debug('skipping Hilfsmittel')
1050 continue
1051
1052 if entry[u'erstattbares_medizinprodukt']:
1053 _log.debug('skipping sonstiges Medizinprodukt')
1054 continue
1055
1056 # create branded drug (or get it if it already exists)
1057 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform'])
1058 if drug is None:
1059 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform'])
1060 new_drugs.append(drug)
1061
1062 # update fields
1063 drug['is_fake'] = False
1064 drug['atc_code'] = entry['atc']
1065 drug['external_code_type'] = u'DE-PZN'
1066 drug['external_code'] = entry['pzn']
1067 drug['fk_data_source'] = data_src_pk
1068 drug.save()
1069
1070 # add components to brand
1071 atc = None # hopefully MMI eventually supports atc-per-substance in a drug...
1072 if len(entry['wirkstoffe']) == 1:
1073 atc = entry['atc']
1074 for wirkstoff in entry['wirkstoffe']:
1075 drug.add_component(substance = wirkstoff, atc = atc)
1076
1077 # create as consumable substances, too
1078 atc = None # hopefully MMI eventually supports atc-per-substance in a drug...
1079 if len(entry['wirkstoffe']) == 1:
1080 atc = entry['atc']
1081 for wirkstoff in entry['wirkstoffe']:
1082 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
1083
1084 return new_drugs, new_substances
1085 #--------------------------------------------------------
1087 """For this to work the BDT interaction check must be configured in the MMI."""
1088
1089 if drug_ids_list is None:
1090 if substances is None:
1091 return
1092 if len(substances) < 2:
1093 return
1094 drug_ids_list = [ (s.external_code_type, s.external_code) for s in substances ]
1095 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')]
1096
1097 else:
1098 if len(drug_ids_list) < 2:
1099 return
1100
1101 if drug_ids_list < 2:
1102 return
1103
1104 bdt_file = codecs.open(filename = self.interactions_filename, mode = 'wb', encoding = cGelbeListeWindowsInterface.default_encoding)
1105
1106 for pzn in drug_ids_list:
1107 pzn = pzn.strip()
1108 lng = cGelbeListeWindowsInterface.bdt_line_base_length + len(pzn)
1109 bdt_file.write(cGelbeListeWindowsInterface.bdt_line_template % (lng, pzn))
1110
1111 bdt_file.close()
1112
1113 self.switch_to_frontend(blocking = False)
1114 #--------------------------------------------------------
1116 self.switch_to_frontend(blocking = True)
1117 #--------------------------------------------------------
1119
1120 cmd = None
1121
1122 if substance.external_code_type == u'DE-PZN':
1123 cmd = u'%s -PZN %s' % (self.path_to_binary, substance.external_code)
1124
1125 if cmd is None:
1126 name = gmTools.coalesce (
1127 substance['brand'],
1128 substance['substance']
1129 )
1130 cmd = u'%s -NAME %s' % (self.path_to_binary, name)
1131
1132 # better to clean up interactions file
1133 open(self.interactions_filename, 'wb').close()
1134
1135 self.switch_to_frontend(cmd = cmd)
1136 #============================================================
1138
1140 cGelbeListeWindowsInterface.__init__(self)
1141
1142 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version)
1143
1144 # FIXME: if -CLOSETOTRAY is used GNUmed cannot detect the end of MMI
1145 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
1146 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
1147
1148 paths = gmTools.gmPaths()
1149
1150 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
1151 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
1152 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
1153 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
1154 #============================================================
1156 """empirical CSV interface"""
1157
1160
1162
1163 try:
1164 csv_file = open(filename, 'rb') # FIXME: encoding ?
1165 except:
1166 _log.exception('cannot access [%s]', filename)
1167 csv_file = None
1168
1169 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
1170
1171 if csv_file is None:
1172 return False
1173
1174 csv_lines = csv.DictReader (
1175 csv_file,
1176 fieldnames = field_names,
1177 delimiter = ';'
1178 )
1179
1180 for line in csv_lines:
1181 print "--------------------------------------------------------------------"[:31]
1182 for key in field_names:
1183 tmp = ('%s ' % key)[:30]
1184 print '%s: %s' % (tmp, line[key])
1185
1186 csv_file.close()
1187
1188 # narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
1189 # line['Packungszahl'].strip(),
1190 # line['Handelsname'].strip(),
1191 # line['Form'].strip(),
1192 # line[u'Packungsgr\xf6\xdfe'].strip(),
1193 # line['Abpackungsmenge'].strip(),
1194 # line['Einheit'].strip(),
1195 # line['Hersteller'].strip(),
1196 # line['PZN'].strip()
1197 # )
1198 #============================================================
1199 drug_data_source_interfaces = {
1200 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
1201 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface,
1202 'FreeDiams (FR, US, CA, ZA)': cFreeDiamsInterface
1203 }
1204
1205 #============================================================
1206 #============================================================
1207 # substances in use across all patients
1208 #------------------------------------------------------------
1209 _SQL_get_consumable_substance = u"""
1210 SELECT *, xmin
1211 FROM ref.consumable_substance
1212 WHERE %s
1213 """
1214
1216
1217 _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s"
1218 _cmds_store_payload = [
1219 u"""UPDATE ref.consumable_substance SET
1220 description = %(description)s,
1221 atc_code = gm.nullify_empty_string(%(atc_code)s),
1222 amount = %(amount)s,
1223 unit = gm.nullify_empty_string(%(unit)s)
1224 WHERE
1225 pk = %(pk)s
1226 AND
1227 xmin = %(xmin)s
1228 AND
1229 -- must not currently be used with a patient directly
1230 NOT EXISTS (
1231 SELECT 1
1232 FROM clin.substance_intake
1233 WHERE
1234 fk_drug_component IS NULL
1235 AND
1236 fk_substance = %(pk)s
1237 LIMIT 1
1238 )
1239 AND
1240 -- must not currently be used with a patient indirectly, either
1241 NOT EXISTS (
1242 SELECT 1
1243 FROM clin.substance_intake
1244 WHERE
1245 fk_drug_component IS NOT NULL
1246 AND
1247 fk_drug_component = (
1248 SELECT r_ls2b.pk
1249 FROM ref.lnk_substance2brand r_ls2b
1250 WHERE fk_substance = %(pk)s
1251 )
1252 LIMIT 1
1253 )
1254 -- -- must not currently be used with a branded drug
1255 -- -- (but this would make it rather hard fixing branded drugs which contain only this substance)
1256 -- NOT EXISTS (
1257 -- SELECT 1
1258 -- FROM ref.lnk_substance2brand
1259 -- WHERE fk_substance = %(pk)s
1260 -- LIMIT 1
1261 -- )
1262 RETURNING
1263 xmin
1264 """
1265 ]
1266 _updatable_fields = [
1267 u'description',
1268 u'atc_code',
1269 u'amount',
1270 u'unit'
1271 ]
1272 #--------------------------------------------------------
1274 success, data = super(self.__class__, self).save_payload(conn = conn)
1275
1276 if not success:
1277 return (success, data)
1278
1279 if self._payload[self._idx['atc_code']] is not None:
1280 atc = self._payload[self._idx['atc_code']].strip()
1281 if atc != u'':
1282 gmATC.propagate_atc (
1283 substance = self._payload[self._idx['description']].strip(),
1284 atc = atc
1285 )
1286
1287 return (success, data)
1288 #--------------------------------------------------------
1289 # properties
1290 #--------------------------------------------------------
1292 cmd = u"""
1293 SELECT
1294 EXISTS (
1295 SELECT 1
1296 FROM clin.substance_intake
1297 WHERE
1298 fk_drug_component IS NULL
1299 AND
1300 fk_substance = %(pk)s
1301 LIMIT 1
1302 ) OR EXISTS (
1303 SELECT 1
1304 FROM clin.substance_intake
1305 WHERE
1306 fk_drug_component IS NOT NULL
1307 AND
1308 fk_drug_component IN (
1309 SELECT r_ls2b.pk
1310 FROM ref.lnk_substance2brand r_ls2b
1311 WHERE fk_substance = %(pk)s
1312 )
1313 LIMIT 1
1314 )"""
1315 args = {'pk': self.pk_obj}
1316
1317 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1318 return rows[0][0]
1319
1320 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
1321 #--------------------------------------------------------
1323 cmd = u"""
1324 SELECT EXISTS (
1325 SELECT 1
1326 FROM ref.lnk_substance2brand
1327 WHERE fk_substance = %(pk)s
1328 LIMIT 1
1329 )"""
1330 args = {'pk': self.pk_obj}
1331
1332 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1333 return rows[0][0]
1334
1335 is_drug_component = property(_get_is_drug_component, lambda x:x)
1336 #------------------------------------------------------------
1338 if order_by is None:
1339 order_by = u'true'
1340 else:
1341 order_by = u'true ORDER BY %s' % order_by
1342 cmd = _SQL_get_consumable_substance % order_by
1343 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
1344 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
1345 #------------------------------------------------------------
1347
1348 substance = substance
1349 if atc is not None:
1350 atc = atc.strip()
1351
1352 converted, amount = gmTools.input2decimal(amount)
1353 if not converted:
1354 raise ValueError('<amount> must be a number: %s (%s)', amount, type(amount))
1355
1356 args = {
1357 'desc': substance.strip(),
1358 'amount': amount,
1359 'unit': unit.strip(),
1360 'atc': atc
1361 }
1362 cmd = u"""
1363 SELECT pk FROM ref.consumable_substance
1364 WHERE
1365 lower(description) = lower(%(desc)s)
1366 AND
1367 amount = %(amount)s
1368 AND
1369 unit = %(unit)s
1370 """
1371 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1372
1373 if len(rows) == 0:
1374 cmd = u"""
1375 INSERT INTO ref.consumable_substance (description, atc_code, amount, unit) VALUES (
1376 %(desc)s,
1377 gm.nullify_empty_string(%(atc)s),
1378 %(amount)s,
1379 gm.nullify_empty_string(%(unit)s)
1380 ) RETURNING pk"""
1381 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
1382
1383 gmATC.propagate_atc(substance = substance, atc = atc)
1384
1385 return cConsumableSubstance(aPK_obj = rows[0]['pk'])
1386 #------------------------------------------------------------
1388 args = {'pk': substance}
1389 cmd = u"""
1390 DELETE FROM ref.consumable_substance
1391 WHERE
1392 pk = %(pk)s
1393 AND
1394
1395 -- must not currently be used with a patient
1396 NOT EXISTS (
1397 SELECT 1
1398 FROM clin.v_pat_substance_intake
1399 WHERE pk_substance = %(pk)s
1400 LIMIT 1
1401 )
1402 AND
1403
1404 -- must not currently be used with a branded drug
1405 NOT EXISTS (
1406 SELECT 1
1407 FROM ref.lnk_substance2brand
1408 WHERE fk_substance = %(pk)s
1409 LIMIT 1
1410 )"""
1411 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1412 return True
1413 #------------------------------------------------------------
1415
1416 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
1417 _query1 = u"""
1418 SELECT
1419 pk::text,
1420 (description || ' ' || amount || ' ' || unit) as subst
1421 FROM ref.consumable_substance
1422 WHERE description %(fragment_condition)s
1423 ORDER BY subst
1424 LIMIT 50"""
1425 _query2 = u"""
1426 SELECT
1427 pk::text,
1428 (description || ' ' || amount || ' ' || unit) as subst
1429 FROM ref.consumable_substance
1430 WHERE
1431 %(fragment_condition)s
1432 ORDER BY subst
1433 LIMIT 50"""
1434
1435 #--------------------------------------------------------
1437 """Return matches for aFragment at start of phrases."""
1438
1439 if cSubstanceMatchProvider._pattern.match(aFragment):
1440 self._queries = [cSubstanceMatchProvider._query2]
1441 fragment_condition = """description ILIKE %(desc)s
1442 AND
1443 amount::text ILIKE %(amount)s"""
1444 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1445 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1446 else:
1447 self._queries = [cSubstanceMatchProvider._query1]
1448 fragment_condition = u"ILIKE %(fragment)s"
1449 self._args['fragment'] = u"%s%%" % aFragment
1450
1451 return self._find_matches(fragment_condition)
1452 #--------------------------------------------------------
1454 """Return matches for aFragment at start of words inside phrases."""
1455
1456 if cSubstanceMatchProvider._pattern.match(aFragment):
1457 self._queries = [cSubstanceMatchProvider._query2]
1458
1459 desc = regex.sub(r'\s*\d+$', u'', aFragment)
1460 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
1461
1462 fragment_condition = """description ~* %(desc)s
1463 AND
1464 amount::text ILIKE %(amount)s"""
1465
1466 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
1467 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1468 else:
1469 self._queries = [cSubstanceMatchProvider._query1]
1470 fragment_condition = u"~* %(fragment)s"
1471 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
1472 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
1473
1474 return self._find_matches(fragment_condition)
1475 #--------------------------------------------------------
1477 """Return matches for aFragment as a true substring."""
1478
1479 if cSubstanceMatchProvider._pattern.match(aFragment):
1480 self._queries = [cSubstanceMatchProvider._query2]
1481 fragment_condition = """description ILIKE %(desc)s
1482 AND
1483 amount::text ILIKE %(amount)s"""
1484 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1485 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1486 else:
1487 self._queries = [cSubstanceMatchProvider._query1]
1488 fragment_condition = u"ILIKE %(fragment)s"
1489 self._args['fragment'] = u"%%%s%%" % aFragment
1490
1491 return self._find_matches(fragment_condition)
1492 #============================================================
1494 """Represents a substance currently taken by a patient."""
1495
1496 _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s"
1497 _cmds_store_payload = [
1498 u"""UPDATE clin.substance_intake SET
1499 clin_when = %(started)s,
1500 discontinued = %(discontinued)s,
1501 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s),
1502 schedule = gm.nullify_empty_string(%(schedule)s),
1503 aim = gm.nullify_empty_string(%(aim)s),
1504 narrative = gm.nullify_empty_string(%(notes)s),
1505 intake_is_approved_of = %(intake_is_approved_of)s,
1506 fk_episode = %(pk_episode)s,
1507
1508 preparation = (
1509 case
1510 when %(pk_brand)s is NULL then %(preparation)s
1511 else NULL
1512 end
1513 )::text,
1514
1515 is_long_term = (
1516 case
1517 when (
1518 (%(is_long_term)s is False)
1519 and
1520 (%(duration)s is NULL)
1521 ) is True then null
1522 else %(is_long_term)s
1523 end
1524 )::boolean,
1525
1526 duration = (
1527 case
1528 when %(is_long_term)s is True then null
1529 else %(duration)s
1530 end
1531 )::interval
1532 WHERE
1533 pk = %(pk_substance_intake)s
1534 AND
1535 xmin = %(xmin_substance_intake)s
1536 RETURNING
1537 xmin as xmin_substance_intake
1538 """
1539 ]
1540 _updatable_fields = [
1541 u'started',
1542 u'discontinued',
1543 u'discontinue_reason',
1544 u'preparation',
1545 u'intake_is_approved_of',
1546 u'schedule',
1547 u'duration',
1548 u'aim',
1549 u'is_long_term',
1550 u'notes',
1551 u'pk_episode'
1552 ]
1553 #--------------------------------------------------------
1555
1556 if self._payload[self._idx['duration']] is None:
1557 duration = gmTools.bool2subst (
1558 self._payload[self._idx['is_long_term']],
1559 _('long-term'),
1560 _('short-term'),
1561 _('?short-term')
1562 )
1563 else:
1564 duration = gmDateTime.format_interval (
1565 self._payload[self._idx['duration']],
1566 accuracy_wanted = gmDateTime.acc_days
1567 )
1568
1569 line = u'%s%s (%s %s): %s %s%s %s (%s)' % (
1570 u' ' * left_margin,
1571 self._payload[self._idx['started']].strftime(date_format),
1572 gmTools.u_right_arrow,
1573 duration,
1574 self._payload[self._idx['substance']],
1575 self._payload[self._idx['amount']],
1576 self._payload[self._idx['unit']],
1577 self._payload[self._idx['preparation']],
1578 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing'))
1579 )
1580
1581 return line
1582 #--------------------------------------------------------
1584 allg = gmAllergy.create_allergy (
1585 allergene = self._payload[self._idx['substance']],
1586 allg_type = allergy_type,
1587 episode_id = self._payload[self._idx['pk_episode']],
1588 encounter_id = encounter_id
1589 )
1590 allg['substance'] = gmTools.coalesce (
1591 self._payload[self._idx['brand']],
1592 self._payload[self._idx['substance']]
1593 )
1594 allg['reaction'] = self._payload[self._idx['discontinue_reason']]
1595 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']])
1596 if self._payload[self._idx['external_code_brand']] is not None:
1597 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']])
1598
1599 if self._payload[self._idx['pk_brand']] is None:
1600 allg['generics'] = self._payload[self._idx['substance']]
1601 else:
1602 comps = [ c['substance'] for c in self.containing_drug.components ]
1603 if len(comps) == 0:
1604 allg['generics'] = self._payload[self._idx['substance']]
1605 else:
1606 allg['generics'] = u'; '.join(comps)
1607
1608 allg.save()
1609 return allg
1610 #--------------------------------------------------------
1611 # properties
1612 #--------------------------------------------------------
1614
1615 try: self.__ddd
1616 except AttributeError: self.__ddd = None
1617
1618 if self.__ddd is not None:
1619 return self.__ddd
1620
1621 if self._payload[self._idx['atc_substance']] is not None:
1622 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']])
1623 if len(ddd) != 0:
1624 self.__ddd = ddd[0]
1625 else:
1626 if self._payload[self._idx['atc_brand']] is not None:
1627 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']])
1628 if len(ddd) != 0:
1629 self.__ddd = ddd[0]
1630
1631 return self.__ddd
1632
1633 ddd = property(_get_ddd, lambda x:x)
1634 #--------------------------------------------------------
1636 drug = self.containing_drug
1637
1638 if drug is None:
1639 return None
1640
1641 return drug.external_code
1642
1643 external_code = property(_get_external_code, lambda x:x)
1644 #--------------------------------------------------------
1646 drug = self.containing_drug
1647
1648 if drug is None:
1649 return None
1650
1651 return drug.external_code_type
1652
1653 external_code_type = property(_get_external_code_type, lambda x:x)
1654 #--------------------------------------------------------
1656 if self._payload[self._idx['pk_brand']] is None:
1657 return None
1658
1659 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1660
1661 containing_drug = property(_get_containing_drug, lambda x:x)
1662 #--------------------------------------------------------
1664 tests = [
1665 # lead, trail
1666 ' 1-1-1-1 ',
1667 # leading dose
1668 '1-1-1-1',
1669 '22-1-1-1',
1670 '1/3-1-1-1',
1671 '/4-1-1-1'
1672 ]
1673 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}$"
1674 for test in tests:
1675 print test.strip(), ":", regex.match(pattern, test.strip())
1676 #------------------------------------------------------------
1677 -def create_substance_intake(pk_substance=None, pk_component=None, preparation=None, encounter=None, episode=None):
1678
1679 args = {
1680 'enc': encounter,
1681 'epi': episode,
1682 'comp': pk_component,
1683 'subst': pk_substance,
1684 'prep': preparation
1685 }
1686
1687 if pk_component is None:
1688 cmd = u"""
1689 INSERT INTO clin.substance_intake (
1690 fk_encounter,
1691 fk_episode,
1692 intake_is_approved_of,
1693 fk_substance,
1694 preparation
1695 ) VALUES (
1696 %(enc)s,
1697 %(epi)s,
1698 False,
1699 %(subst)s,
1700 %(prep)s
1701 )
1702 RETURNING pk"""
1703 else:
1704 cmd = u"""
1705 INSERT INTO clin.substance_intake (
1706 fk_encounter,
1707 fk_episode,
1708 intake_is_approved_of,
1709 fk_drug_component
1710 ) VALUES (
1711 %(enc)s,
1712 %(epi)s,
1713 False,
1714 %(comp)s
1715 )
1716 RETURNING pk"""
1717
1718 try:
1719 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
1720 except gmPG2.dbapi.InternalError, e:
1721 if e.pgerror is None:
1722 raise
1723 if 'prevent_duplicate_component' in e.pgerror:
1724 _log.exception('will not create duplicate substance intake entry')
1725 _log.error(e.pgerror)
1726 return None
1727 raise
1728
1729 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
1730 #------------------------------------------------------------
1732 cmd = u'delete from clin.substance_intake where pk = %(pk)s'
1733 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
1734 #------------------------------------------------------------
1736
1737 tex = u'\n{\\small\n'
1738 tex += u'\\noindent %s\n' % _('Additional notes')
1739 tex += u'\n'
1740 tex += u'\\noindent \\begin{tabularx}{\\textwidth}{|X|l|X|p{7.5cm}|}\n'
1741 tex += u'\\hline\n'
1742 tex += u'%s {\\scriptsize (%s)} & %s & %s \\\\ \n' % (_('Substance'), _('Brand'), _('Strength'), _('Advice'))
1743 tex += u'\\hline\n'
1744 tex += u'%s\n'
1745 tex += u'\n'
1746 tex += u'\\end{tabularx}\n'
1747 tex += u'}\n'
1748
1749 current_meds = emr.get_current_substance_intake (
1750 include_inactive = False,
1751 include_unapproved = False,
1752 order_by = u'brand, substance'
1753 )
1754
1755 # create lines
1756 lines = []
1757 for med in current_meds:
1758 lines.append(u'%s%s %s & %s%s & %s \\\\ \n \\hline \n' % (
1759 med['substance'],
1760 gmTools.coalesce(med['brand'], u'', u' {\\scriptsize (%s)}'),
1761 med['preparation'],
1762 med['amount'],
1763 med['unit'],
1764 gmTools.coalesce(med['notes'], u'', u'{\\scriptsize %s}')
1765 ))
1766
1767 return tex % u' \n'.join(lines)
1768
1769 #------------------------------------------------------------
1771
1772 tex = u'\\noindent %s {\\tiny (%s)\\par}\n' % (_('Medication list'), _('ordered by brand'))
1773 tex += u'\n'
1774 tex += u'\\noindent \\begin{tabular}{|l|l|}\n'
1775 tex += u'\\hline\n'
1776 tex += u'%s & %s \\\\ \n' % (_('Drug'), _('Regimen'))
1777 tex += u'\\hline\n'
1778 tex += u'\n'
1779 tex += u'\\hline\n'
1780 tex += u'%s\n'
1781 tex += u'\n'
1782 tex += u'\\end{tabular}\n'
1783
1784 current_meds = emr.get_current_substance_intake (
1785 include_inactive = False,
1786 include_unapproved = False,
1787 order_by = u'brand, substance'
1788 )
1789
1790 # aggregate data
1791 line_data = {}
1792 for med in current_meds:
1793 identifier = gmTools.coalesce(med['brand'], med['substance'])
1794
1795 try:
1796 line_data[identifier]
1797 except KeyError:
1798 line_data[identifier] = {'brand': u'', 'preparation': u'', 'schedule': u'', 'aims': [], 'strengths': []}
1799
1800 line_data[identifier]['brand'] = identifier
1801 line_data[identifier]['strengths'].append(u'%s%s' % (med['amount'], med['unit'].strip()))
1802 line_data[identifier]['preparation'] = med['preparation']
1803 line_data[identifier]['schedule'] = gmTools.coalesce(med['schedule'], u'')
1804 if med['aim'] not in line_data[identifier]['aims']:
1805 line_data[identifier]['aims'].append(med['aim'])
1806
1807 # create lines
1808 already_seen = []
1809 lines = []
1810 line1_template = u'%s %s & %s \\\\'
1811 line2_template = u' & {\\scriptsize %s\\par} \\\\'
1812
1813 for med in current_meds:
1814 identifier = gmTools.coalesce(med['brand'], med['substance'])
1815
1816 if identifier in already_seen:
1817 continue
1818
1819 already_seen.append(identifier)
1820
1821 lines.append (line1_template % (
1822 line_data[identifier]['brand'],
1823 line_data[identifier]['preparation'],
1824 line_data[identifier]['schedule']
1825 ))
1826
1827 strengths = u'/'.join(line_data[identifier]['strengths'])
1828 if strengths == u'':
1829 template = u' & {\\scriptsize %s\\par} \\\\'
1830 for aim in line_data[identifier]['aims']:
1831 lines.append(template % aim)
1832 else:
1833 if len(line_data[identifier]['aims']) == 0:
1834 template = u'%s & \\\\'
1835 lines.append(template % strengths)
1836 else:
1837 template = u'%s & {\\scriptsize %s\\par} \\\\'
1838 lines.append(template % (strengths, line_data[identifier]['aims'][0]))
1839 template = u' & {\\scriptsize %s\\par} \\\\'
1840 for aim in line_data[identifier]['aims'][1:]:
1841 lines.append(template % aim)
1842
1843 lines.append(u'\\hline')
1844
1845 return tex % u' \n'.join(lines)
1846 #============================================================
1847 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s'
1848
1850
1851 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s'
1852 _cmds_store_payload = [
1853 u"""UPDATE ref.lnk_substance2brand SET
1854 fk_brand = %(pk_brand)s,
1855 fk_substance = %(pk_consumable_substance)s
1856 WHERE
1857 NOT EXISTS (
1858 SELECT 1
1859 FROM clin.substance_intake
1860 WHERE fk_drug_component = %(pk_component)s
1861 LIMIT 1
1862 )
1863 AND
1864 pk = %(pk_component)s
1865 AND
1866 xmin = %(xmin_lnk_substance2brand)s
1867 RETURNING
1868 xmin AS xmin_lnk_substance2brand
1869 """
1870 ]
1871 _updatable_fields = [
1872 u'pk_brand',
1873 u'pk_consumable_substance'
1874 ]
1875 #--------------------------------------------------------
1876 # properties
1877 #--------------------------------------------------------
1879 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1880
1881 containing_drug = property(_get_containing_drug, lambda x:x)
1882 #--------------------------------------------------------
1885
1886 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
1887 #--------------------------------------------------------
1889 return cConsumableSubstance(aPK_obj = self._payload[self._idx['pk_consumable_substance']])
1890
1891 substance = property(_get_substance, lambda x:x)
1892 #------------------------------------------------------------
1894 cmd = _SQL_get_drug_components % u'true ORDER BY brand, substance'
1895 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
1896 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
1897 #------------------------------------------------------------
1899
1900 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
1901 _query_desc_only = u"""
1902 SELECT DISTINCT ON (component)
1903 pk_component,
1904 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
1905 AS component
1906 FROM ref.v_drug_components
1907 WHERE
1908 substance %(fragment_condition)s
1909 OR
1910 brand %(fragment_condition)s
1911 ORDER BY component
1912 LIMIT 50"""
1913 _query_desc_and_amount = u"""
1914
1915 SELECT DISTINCT ON (component)
1916 pk_component,
1917 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
1918 AS component
1919 FROM ref.v_drug_components
1920 WHERE
1921 %(fragment_condition)s
1922 ORDER BY component
1923 LIMIT 50"""
1924 #--------------------------------------------------------
1926 """Return matches for aFragment at start of phrases."""
1927
1928 if cDrugComponentMatchProvider._pattern.match(aFragment):
1929 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
1930 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
1931 AND
1932 amount::text ILIKE %(amount)s"""
1933 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1934 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1935 else:
1936 self._queries = [cDrugComponentMatchProvider._query_desc_only]
1937 fragment_condition = u"ILIKE %(fragment)s"
1938 self._args['fragment'] = u"%s%%" % aFragment
1939
1940 return self._find_matches(fragment_condition)
1941 #--------------------------------------------------------
1943 """Return matches for aFragment at start of words inside phrases."""
1944
1945 if cDrugComponentMatchProvider._pattern.match(aFragment):
1946 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
1947
1948 desc = regex.sub(r'\s*\d+$', u'', aFragment)
1949 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
1950
1951 fragment_condition = """(substance ~* %(desc)s OR brand ~* %(desc)s)
1952 AND
1953 amount::text ILIKE %(amount)s"""
1954
1955 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
1956 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1957 else:
1958 self._queries = [cDrugComponentMatchProvider._query_desc_only]
1959 fragment_condition = u"~* %(fragment)s"
1960 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
1961 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
1962
1963 return self._find_matches(fragment_condition)
1964 #--------------------------------------------------------
1966 """Return matches for aFragment as a true substring."""
1967
1968 if cDrugComponentMatchProvider._pattern.match(aFragment):
1969 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
1970 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
1971 AND
1972 amount::text ILIKE %(amount)s"""
1973 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
1974 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
1975 else:
1976 self._queries = [cDrugComponentMatchProvider._query_desc_only]
1977 fragment_condition = u"ILIKE %(fragment)s"
1978 self._args['fragment'] = u"%%%s%%" % aFragment
1979
1980 return self._find_matches(fragment_condition)
1981
1982 #============================================================
1984 """Represents a drug as marketed by a manufacturer."""
1985
1986 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s"
1987 _cmds_store_payload = [
1988 u"""UPDATE ref.branded_drug SET
1989 description = %(brand)s,
1990 preparation = %(preparation)s,
1991 atc_code = gm.nullify_empty_string(%(atc)s),
1992 external_code = gm.nullify_empty_string(%(external_code)s),
1993 external_code_type = gm.nullify_empty_string(%(external_code_type)s),
1994 is_fake = %(is_fake_brand)s,
1995 fk_data_source = %(pk_data_source)s
1996 WHERE
1997 pk = %(pk_brand)s
1998 AND
1999 xmin = %(xmin_branded_drug)s
2000 RETURNING
2001 xmin AS xmin_branded_drug
2002 """
2003 ]
2004 _updatable_fields = [
2005 u'brand',
2006 u'preparation',
2007 u'atc',
2008 u'is_fake_brand',
2009 u'external_code',
2010 u'external_code_type',
2011 u'pk_data_source'
2012 ]
2013 #--------------------------------------------------------
2015 success, data = super(self.__class__, self).save_payload(conn = conn)
2016
2017 if not success:
2018 return (success, data)
2019
2020 if self._payload[self._idx['atc']] is not None:
2021 atc = self._payload[self._idx['atc']].strip()
2022 if atc != u'':
2023 gmATC.propagate_atc (
2024 substance = self._payload[self._idx['brand']].strip(),
2025 atc = atc
2026 )
2027
2028 return (success, data)
2029 #--------------------------------------------------------
2031
2032 if self.is_in_use_by_patients:
2033 return False
2034
2035 args = {'brand': self._payload[self._idx['pk_brand']]}
2036
2037 queries = [{'cmd': u"DELETE FROM ref.lnk_substance2brand WHERE fk_brand = %(brand)s", 'args': args}]
2038 cmd = u'INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) VALUES (%%(brand)s, %s)'
2039 for s in substances:
2040 queries.append({'cmd': cmd % s['pk'], 'args': args})
2041
2042 gmPG2.run_rw_queries(queries = queries)
2043 self.refetch_payload()
2044
2045 return True
2046 #--------------------------------------------------------
2047 - def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
2048
2049 args = {
2050 'brand': self.pk_obj,
2051 'subst': substance,
2052 'atc': atc,
2053 'pk_subst': pk_substance
2054 }
2055
2056 if pk_substance is None:
2057 consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit)
2058 args['pk_subst'] = consumable['pk']
2059
2060 # already a component
2061 cmd = u"""
2062 SELECT pk_component
2063 FROM ref.v_drug_components
2064 WHERE
2065 pk_brand = %(brand)s
2066 AND
2067 ((
2068 (lower(substance) = lower(%(subst)s))
2069 OR
2070 (lower(atc_substance) = lower(%(atc)s))
2071 OR
2072 (pk_consumable_substance = %(pk_subst)s)
2073 ) IS TRUE)
2074 """
2075 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2076
2077 if len(rows) > 0:
2078 return
2079
2080 # create it
2081 cmd = u"""
2082 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance)
2083 VALUES (%(brand)s, %(pk_subst)s)
2084 """
2085 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2086 self.refetch_payload()
2087 #------------------------------------------------------------
2089 if len(self._payload[self._idx['components']]) == 1:
2090 _log.error('cannot remove the only component of a drug')
2091 return False
2092
2093 args = {'brand': self.pk_obj, 'comp': substance}
2094 cmd = u"""
2095 DELETE FROM ref.lnk_substance2brand
2096 WHERE
2097 fk_brand = %(brand)s
2098 AND
2099 fk_substance = %(comp)s
2100 AND
2101 NOT EXISTS (
2102 SELECT 1
2103 FROM clin.substance_intake
2104 WHERE fk_drug_component = %(comp)s
2105 LIMIT 1
2106 )
2107 """
2108 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2109 self.refetch_payload()
2110
2111 return True
2112 #--------------------------------------------------------
2113 # properties
2114 #--------------------------------------------------------
2116 if self._payload[self._idx['external_code']] is None:
2117 return None
2118
2119 return self._payload[self._idx['external_code']]
2120
2121 external_code = property(_get_external_code, lambda x:x)
2122 #--------------------------------------------------------
2124
2125 # FIXME: maybe evaluate fk_data_source ?
2126 if self._payload[self._idx['external_code_type']] is None:
2127 return None
2128
2129 return self._payload[self._idx['external_code_type']]
2130
2131 external_code_type = property(_get_external_code_type, lambda x:x)
2132 #--------------------------------------------------------
2134 cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s'
2135 args = {'brand': self._payload[self._idx['pk_brand']]}
2136 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2137 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
2138
2139 components = property(_get_components, lambda x:x)
2140 #--------------------------------------------------------
2142 if self._payload[self._idx['pk_substances']] is None:
2143 return []
2144 cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s'
2145 args = {'pks': tuple(self._payload[self._idx['pk_substances']])}
2146 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2147 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
2148
2149 components_as_substances = property(_get_components_as_substances, lambda x:x)
2150 #--------------------------------------------------------
2152 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)'
2153 args = {'fk_brand': self._payload[self._idx['pk_brand']]}
2154 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2155 return rows[0][0]
2156
2157 is_vaccine = property(_get_is_vaccine, lambda x:x)
2158 #--------------------------------------------------------
2160 cmd = u"""
2161 SELECT EXISTS (
2162 SELECT 1
2163 FROM clin.substance_intake
2164 WHERE
2165 fk_drug_component IS NOT NULL
2166 AND
2167 fk_drug_component IN (
2168 SELECT r_ls2b.pk
2169 FROM ref.lnk_substance2brand r_ls2b
2170 WHERE fk_brand = %(pk)s
2171 )
2172 LIMIT 1
2173 )"""
2174 args = {'pk': self.pk_obj}
2175
2176 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2177 return rows[0][0]
2178
2179 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
2180 #------------------------------------------------------------
2182 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description'
2183 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
2184 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
2185 #------------------------------------------------------------
2187 args = {'brand': brand_name, 'prep': preparation}
2188
2189 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)'
2190 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
2191
2192 if len(rows) == 0:
2193 return None
2194
2195 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2196 #------------------------------------------------------------
2198
2199 if preparation is None:
2200 preparation = _('units')
2201
2202 if preparation.strip() == u'':
2203 preparation = _('units')
2204
2205 if return_existing:
2206 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation)
2207 if drug is not None:
2208 return drug
2209
2210 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk'
2211 args = {'brand': brand_name, 'prep': preparation}
2212 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
2213
2214 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2215 #------------------------------------------------------------
2217 queries = []
2218 args = {'pk': brand}
2219
2220 # delete components
2221 cmd = u"""
2222 DELETE FROM ref.lnk_substance2brand
2223 WHERE
2224 fk_brand = %(pk)s
2225 AND
2226 NOT EXISTS (
2227 SELECT 1
2228 FROM clin.v_pat_substance_intake
2229 WHERE pk_brand = %(pk)s
2230 LIMIT 1
2231 )
2232 """
2233 queries.append({'cmd': cmd, 'args': args})
2234
2235 # delete drug
2236 cmd = u"""
2237 DELETE FROM ref.branded_drug
2238 WHERE
2239 pk = %(pk)s
2240 AND
2241 NOT EXISTS (
2242 SELECT 1
2243 FROM clin.v_pat_substance_intake
2244 WHERE pk_brand = %(pk)s
2245 LIMIT 1
2246 )
2247 """
2248 queries.append({'cmd': cmd, 'args': args})
2249
2250 gmPG2.run_rw_queries(queries = queries)
2251 #============================================================
2252 # main
2253 #------------------------------------------------------------
2254 if __name__ == "__main__":
2255
2256 if len(sys.argv) < 2:
2257 sys.exit()
2258
2259 if sys.argv[1] != 'test':
2260 sys.exit()
2261
2262 from Gnumed.pycommon import gmLog2
2263 from Gnumed.pycommon import gmI18N
2264 from Gnumed.business import gmPerson
2265
2266 gmI18N.activate_locale()
2267 # gmDateTime.init()
2268 #--------------------------------------------------------
2270 mmi = cGelbeListeWineInterface()
2271 print mmi
2272 print "interface definition:", mmi.version
2273 print "database versions: ", mmi.get_data_source_version()
2274 #--------------------------------------------------------
2276 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
2277 for drug in mmi_file:
2278 print "-------------"
2279 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2280 for stoff in drug['wirkstoffe']:
2281 print " Wirkstoff:", stoff
2282 raw_input()
2283 if mmi_file.has_unknown_fields is not None:
2284 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key
2285 for key in mmi_file.csv_fieldnames:
2286 print key, '->', drug[key]
2287 raw_input()
2288 mmi_file.close()
2289 #--------------------------------------------------------
2293 #--------------------------------------------------------
2295 mmi = cGelbeListeWineInterface()
2296 mmi_file = mmi.__let_user_select_drugs()
2297 for drug in mmi_file:
2298 print "-------------"
2299 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
2300 for stoff in drug['wirkstoffe']:
2301 print " Wirkstoff:", stoff
2302 print drug
2303 mmi_file.close()
2304 #--------------------------------------------------------
2308 #--------------------------------------------------------
2310 mmi = cGelbeListeInterface()
2311 print mmi
2312 print "interface definition:", mmi.version
2313 # Metoprolol + Hct vs Citalopram
2314 diclofenac = '7587712'
2315 phenprocoumon = '4421744'
2316 mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
2317 #--------------------------------------------------------
2318 # FreeDiams
2319 #--------------------------------------------------------
2321 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12))
2322 fd = cFreeDiamsInterface()
2323 fd.patient = gmPerson.gmCurrentPatient()
2324 # fd.switch_to_frontend(blocking = True)
2325 fd.import_fd2gm_file_as_drugs(filename = sys.argv[2])
2326 #--------------------------------------------------------
2328 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12))
2329 fd = cFreeDiamsInterface()
2330 fd.patient = gmPerson.gmCurrentPatient()
2331 fd.check_interactions(substances = fd.patient.get_emr().get_current_substance_intake(include_unapproved = True))
2332 #--------------------------------------------------------
2333 # generic
2334 #--------------------------------------------------------
2336 drug = create_substance_intake (
2337 pk_component = 2,
2338 encounter = 1,
2339 episode = 1
2340 )
2341 print drug
2342 #--------------------------------------------------------
2347 #--------------------------------------------------------
2351 #--------------------------------------------------------
2353 drug2renal_insufficiency_url(search_term = 'Metoprolol')
2354 #--------------------------------------------------------
2355 # MMI/Gelbe Liste
2356 #test_MMI_interface()
2357 #test_MMI_file()
2358 #test_mmi_switch_to()
2359 #test_mmi_let_user_select_drugs()
2360 #test_mmi_import_substances()
2361 #test_mmi_import_drugs()
2362
2363 # FreeDiams
2364 #test_fd_switch_to()
2365 #test_fd_show_interactions()
2366
2367 # generic
2368 #test_interaction_check()
2369 #test_create_substance_intake()
2370 #test_show_components()
2371 #test_get_consumable_substances()
2372
2373 test_drug2renal_insufficiency_url()
2374 #============================================================
2375
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Oct 18 04:00:44 2011 | http://epydoc.sourceforge.net |