Package Gnumed :: Package business :: Module gmMedication
[frames] | no frames]

Source Code for Module Gnumed.business.gmMedication

   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  #============================================================ 
45 -def _on_substance_intake_modified():
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 #============================================================
52 -def drug2renal_insufficiency_url(search_term=None):
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
128 -class cGelbeListeCSVFile(object):
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 #--------------------------------------------------------
200 - def __init__(self, filename=None):
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 #--------------------------------------------------------
220 - def __iter__(self):
221 return self
222 #--------------------------------------------------------
223 - def next(self):
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 #--------------------------------------------------------
237 - def close(self, truncate=True):
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 #--------------------------------------------------------
245 - def _get_has_unknown_fields(self):
247 248 has_unknown_fields = property(_get_has_unknown_fields, lambda x:x)
249 #============================================================
250 -class cDrugDataSourceInterface(object):
251 252 #--------------------------------------------------------
253 - def __init__(self):
254 self.patient = None 255 self.reviewer = None 256 self.custom_path_to_binary = None
257 #--------------------------------------------------------
258 - def get_data_source_version(self):
259 raise NotImplementedError
260 #--------------------------------------------------------
261 - def create_data_source_entry(self):
262 raise NotImplementedError
263 #--------------------------------------------------------
264 - def switch_to_frontend(self, blocking=False):
265 raise NotImplementedError
266 #--------------------------------------------------------
267 - def import_drugs(self):
268 self.switch_to_frontend()
269 #--------------------------------------------------------
270 - def check_interactions(self, substance_intakes=None):
271 self.switch_to_frontend()
272 #--------------------------------------------------------
273 - def show_info_on_drug(self, substance_intake=None):
274 self.switch_to_frontend()
275 #--------------------------------------------------------
276 - def show_info_on_substance(self, substance_intake=None):
277 self.switch_to_frontend()
278 #--------------------------------------------------------
279 - def prescribe(self, substance_intakes=None):
280 self.switch_to_frontend() 281 return []
282 #============================================================
283 -class cFreeDiamsInterface(cDrugDataSourceInterface):
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 #--------------------------------------------------------
297 - def __init__(self):
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 #--------------------------------------------------------
313 - def get_data_source_version(self):
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 #--------------------------------------------------------
333 - def create_data_source_entry(self):
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 #--------------------------------------------------------
342 - def switch_to_frontend(self, blocking=False, mode='interactions'):
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 #--------------------------------------------------------
365 - def import_drugs(self):
366 self.switch_to_frontend(blocking = True)
367 #--------------------------------------------------------
368 - def check_interactions(self, substance_intakes=None):
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 #--------------------------------------------------------
377 - def show_info_on_drug(self, substance_intake=None):
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 #--------------------------------------------------------
384 - def show_info_on_substance(self, substance_intake=None):
385 self.show_info_on_drug(substance_intake = substance_intake)
386 #--------------------------------------------------------
387 - def prescribe(self, substance_intakes=None):
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 #--------------------------------------------------------
401 - def __detect_binary(self):
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 #--------------------------------------------------------
458 - def __create_prescription_file(self, substance_intakes=None):
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 #--------------------------------------------------------
620 - def __create_gm2fd_file(self, mode='interactions'):
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 #--------------------------------------------------------
719 - def import_fd2gm_file_as_prescription(self, filename=None):
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 #--------------------------------------------------------
772 - def import_fd2gm_file_as_drugs(self, filename=None):
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 #============================================================
893 -class cGelbeListeWindowsInterface(cDrugDataSourceInterface):
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 #--------------------------------------------------------
901 - def __init__(self):
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 #--------------------------------------------------------
922 - def get_data_source_version(self, force_reload=False):
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 #--------------------------------------------------------
973 - def create_data_source_entry(self):
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 #--------------------------------------------------------
984 - def switch_to_frontend(self, blocking=False, cmd=None):
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 #--------------------------------------------------------
1004 - def __let_user_select_drugs(self):
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 #--------------------------------------------------------
1014 - def import_drugs_as_substances(self):
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 #--------------------------------------------------------
1033 - def import_drugs(self):
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 #--------------------------------------------------------
1086 - def check_interactions(self, drug_ids_list=None, substances=None):
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 #--------------------------------------------------------
1115 - def show_info_on_drug(self, drug=None):
1116 self.switch_to_frontend(blocking = True)
1117 #--------------------------------------------------------
1118 - def show_info_on_substance(self, substance=None):
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 #============================================================
1137 -class cGelbeListeWineInterface(cGelbeListeWindowsInterface):
1138
1139 - def __init__(self):
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 #============================================================
1155 -class cIfapInterface(cDrugDataSourceInterface):
1156 """empirical CSV interface""" 1157
1158 - def __init__(self):
1159 pass
1160
1161 - def print_transfer_file(self, filename=None):
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
1215 -class cConsumableSubstance(gmBusinessDBObject.cBusinessDBObject):
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 #--------------------------------------------------------
1273 - def save_payload(self, conn=None):
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 #--------------------------------------------------------
1291 - def _get_is_in_use_by_patients(self):
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 #--------------------------------------------------------
1322 - def _get_is_drug_component(self):
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 #------------------------------------------------------------
1337 -def get_consumable_substances(order_by=None):
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 #------------------------------------------------------------
1346 -def create_consumable_substance(substance=None, atc=None, amount=None, unit=None):
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 #------------------------------------------------------------
1387 -def delete_consumable_substance(substance=None):
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 #------------------------------------------------------------
1414 -class cSubstanceMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
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 #--------------------------------------------------------
1436 - def getMatchesByPhrase(self, aFragment):
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 #--------------------------------------------------------
1453 - def getMatchesByWord(self, aFragment):
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 #--------------------------------------------------------
1476 - def getMatchesBySubstr(self, aFragment):
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 #============================================================
1493 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
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 #--------------------------------------------------------
1554 - def format(self, left_margin=0, date_format='%Y-%m-%d'):
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 #--------------------------------------------------------
1583 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
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 #--------------------------------------------------------
1613 - def _get_ddd(self):
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 #--------------------------------------------------------
1635 - def _get_external_code(self):
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 #--------------------------------------------------------
1645 - def _get_external_code_type(self):
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 #--------------------------------------------------------
1655 - def _get_containing_drug(self):
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 #--------------------------------------------------------
1663 - def _get_parsed_schedule(self):
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 #------------------------------------------------------------
1731 -def delete_substance_intake(substance=None):
1732 cmd = u'delete from clin.substance_intake where pk = %(pk)s' 1733 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
1734 #------------------------------------------------------------
1735 -def format_substance_intake_notes(emr=None, output_format=u'latex', table_type=u'by-brand'):
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 #------------------------------------------------------------
1770 -def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-brand'):
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
1849 -class cDrugComponent(gmBusinessDBObject.cBusinessDBObject):
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 #--------------------------------------------------------
1878 - def _get_containing_drug(self):
1879 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1880 1881 containing_drug = property(_get_containing_drug, lambda x:x) 1882 #--------------------------------------------------------
1883 - def _get_is_in_use_by_patients(self):
1884 return self._payload[self._idx['is_in_use']]
1885 1886 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x) 1887 #--------------------------------------------------------
1888 - def _get_substance(self):
1889 return cConsumableSubstance(aPK_obj = self._payload[self._idx['pk_consumable_substance']])
1890 1891 substance = property(_get_substance, lambda x:x)
1892 #------------------------------------------------------------
1893 -def get_drug_components():
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 #------------------------------------------------------------
1898 -class cDrugComponentMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
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 #--------------------------------------------------------
1925 - def getMatchesByPhrase(self, aFragment):
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 #--------------------------------------------------------
1942 - def getMatchesByWord(self, aFragment):
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 #--------------------------------------------------------
1965 - def getMatchesBySubstr(self, aFragment):
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 #============================================================
1983 -class cBrandedDrug(gmBusinessDBObject.cBusinessDBObject):
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 #--------------------------------------------------------
2014 - def save_payload(self, conn=None):
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 #--------------------------------------------------------
2030 - def set_substances_as_components(self, substances=None):
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 #------------------------------------------------------------
2088 - def remove_component(self, substance=None):
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 #--------------------------------------------------------
2115 - def _get_external_code(self):
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 #--------------------------------------------------------
2123 - def _get_external_code_type(self):
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 #--------------------------------------------------------
2133 - def _get_components(self):
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 #--------------------------------------------------------
2151 - def _get_is_vaccine(self):
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 #--------------------------------------------------------
2159 - def _get_is_in_use_by_patients(self):
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 #------------------------------------------------------------
2181 -def get_branded_drugs():
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 #------------------------------------------------------------
2186 -def get_drug_by_brand(brand_name=None, preparation=None):
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 #------------------------------------------------------------
2197 -def create_branded_drug(brand_name=None, preparation=None, return_existing=False):
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 #------------------------------------------------------------
2216 -def delete_branded_drug(brand=None):
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 #--------------------------------------------------------
2269 - def test_MMI_interface():
2270 mmi = cGelbeListeWineInterface() 2271 print mmi 2272 print "interface definition:", mmi.version 2273 print "database versions: ", mmi.get_data_source_version()
2274 #--------------------------------------------------------
2275 - def test_MMI_file():
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 #--------------------------------------------------------
2290 - def test_mmi_switch_to():
2291 mmi = cGelbeListeWineInterface() 2292 mmi.switch_to_frontend(blocking = False)
2293 #--------------------------------------------------------
2294 - def test_mmi_let_user_select_drugs():
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 #--------------------------------------------------------
2305 - def test_mmi_import_drugs():
2306 mmi = cGelbeListeWineInterface() 2307 mmi.import_drugs()
2308 #--------------------------------------------------------
2309 - def test_mmi_interaction_check():
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 #--------------------------------------------------------
2320 - def test_fd_switch_to():
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 #--------------------------------------------------------
2327 - def test_fd_show_interactions():
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 #--------------------------------------------------------
2335 - def test_create_substance_intake():
2336 drug = create_substance_intake ( 2337 pk_component = 2, 2338 encounter = 1, 2339 episode = 1 2340 ) 2341 print drug
2342 #--------------------------------------------------------
2343 - def test_show_components():
2344 drug = cBrandedDrug(aPK_obj = sys.argv[2]) 2345 print drug 2346 print drug.components
2347 #--------------------------------------------------------
2348 - def test_get_consumable_substances():
2349 for s in get_consumable_substances(): 2350 print s
2351 #--------------------------------------------------------
2352 - def test_drug2renal_insufficiency_url():
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