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 
   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.de/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 ( 752 files = fd_filenames, 753 reviewer = self.reviewer['pk_staff'] 754 ) 755 756 if not success: 757 _log.error(msg) 758 return 759 760 xml_part = parts[-1] 761 xml_part['filename'] = u'freediams-prescription.xml' 762 xml_part.save()
763 #--------------------------------------------------------
764 - def import_fd2gm_file_as_drugs(self, filename=None):
765 """ 766 If returning textual prescriptions (say, drugs which FreeDiams 767 did not know) then "IsTextual" will be True and UID will be -1. 768 """ 769 if filename is None: 770 filename = self.__fd2gm_filename 771 772 # FIXME: do not import IsTextual drugs, or rather, make that configurable 773 774 fd2gm_xml = etree.ElementTree() 775 fd2gm_xml.parse(filename) 776 777 data_src_pk = self.create_data_source_entry() 778 779 db_def = fd2gm_xml.find('DrugsDatabaseName') 780 db_id = db_def.text.strip() 781 drug_id_name = db_def.attrib['drugUidName'] 782 fd_xml_drug_entries = fd2gm_xml.findall('FullPrescription/Prescription') 783 784 self.__imported_drugs = [] 785 for fd_xml_drug in fd_xml_drug_entries: 786 drug_uid = fd_xml_drug.find('Drug_UID').text.strip() 787 if drug_uid == u'-1': 788 _log.debug('skipping textual drug') 789 continue # it's a TextualDrug, skip it 790 drug_name = fd_xml_drug.find('DrugName').text.replace(', )', ')').strip() 791 drug_form = fd_xml_drug.find('DrugForm').text.strip() 792 drug_atc = fd_xml_drug.find('DrugATC') 793 if drug_atc is None: 794 drug_atc = u'' 795 else: 796 drug_atc = drug_atc.text.strip() 797 798 # create new branded drug 799 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True) 800 self.__imported_drugs.append(new_drug) 801 new_drug['is_fake_brand'] = False 802 new_drug['atc'] = drug_atc 803 new_drug['external_code_type'] = u'FreeDiams::%s::%s' % (db_id, drug_id_name) 804 new_drug['external_code'] = drug_uid 805 new_drug['pk_data_source'] = data_src_pk 806 new_drug.save() 807 808 # parse XML for composition records 809 fd_xml_components = fd_xml_drug.getiterator('Composition') 810 comp_data = {} 811 for fd_xml_comp in fd_xml_components: 812 813 data = {} 814 815 amount = regex.match(r'\d+[.,]{0,1}\d*', fd_xml_comp.attrib['strenght'].strip()) # sic, typo 816 if amount is None: 817 amount = 99999 818 else: 819 amount = amount.group() 820 data['amount'] = amount 821 822 unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', fd_xml_comp.attrib['strenght'].strip()).strip() # sic, typo 823 if unit == u'': 824 unit = u'*?*' 825 data['unit'] = unit 826 827 molecule_name = fd_xml_comp.attrib['molecularName'].strip() 828 if molecule_name != u'': 829 create_consumable_substance(substance = molecule_name, atc = None, amount = amount, unit = unit) 830 data['molecule_name'] = molecule_name 831 832 inn_name = fd_xml_comp.attrib['inn'].strip() 833 if inn_name != u'': 834 create_consumable_substance(substance = inn_name, atc = None, amount = amount, unit = unit) 835 data['inn_name'] = molecule_name 836 837 if molecule_name == u'': 838 data['substance'] = inn_name 839 _log.info('linking INN [%s] rather than molecularName as component', inn_name) 840 else: 841 data['substance'] = molecule_name 842 843 data['nature'] = fd_xml_comp.attrib['nature'].strip() 844 data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip() 845 846 # merge composition records of SA/FT nature 847 try: 848 old_data = comp_data[data['nature_ID']] 849 # normalize INN 850 if old_data['inn_name'] == u'': 851 old_data['inn_name'] = data['inn_name'] 852 if data['inn_name'] == u'': 853 data['inn_name'] = old_data['inn_name'] 854 # normalize molecule 855 if old_data['molecule_name'] == u'': 856 old_data['molecule_name'] = data['molecule_name'] 857 if data['molecule_name'] == u'': 858 data['molecule_name'] = old_data['molecule_name'] 859 # FT: transformed form 860 # SA: active substance 861 # it would be preferable to use the SA record because that's what's *actually* 862 # contained in the drug, however FreeDiams does not list the amount thereof 863 # (rather that of the INN) 864 if data['nature'] == u'FT': 865 comp_data[data['nature_ID']] = data 866 else: 867 comp_data[data['nature_ID']] = old_data 868 869 # or create new record 870 except KeyError: 871 comp_data[data['nature_ID']] = data 872 873 # actually create components from (possibly merged) composition records 874 for key, data in comp_data.items(): 875 new_drug.add_component ( 876 substance = data['substance'], 877 atc = None, 878 amount = data['amount'], 879 unit = data['unit'] 880 )
881 #============================================================
882 -class cGelbeListeWindowsInterface(cDrugDataSourceInterface):
883 """Support v8.2 CSV file interface only.""" 884 885 version = u'Gelbe Liste/MMI v8.2 interface' 886 default_encoding = 'cp1250' 887 bdt_line_template = u'%03d6210#%s\r\n' # Medikament verordnet auf Kassenrezept 888 bdt_line_base_length = 8 889 #--------------------------------------------------------
890 - def __init__(self):
891 892 cDrugDataSourceInterface.__init__(self) 893 894 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version) 895 896 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe' 897 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY' 898 899 paths = gmTools.gmPaths() 900 901 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt') 902 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp') 903 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt') 904 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt' 905 906 self.__data_date = None 907 self.__online_update_date = None
908 909 # use adjusted config.dat 910 #--------------------------------------------------------
911 - def get_data_source_version(self, force_reload=False):
912 913 if self.__data_date is not None: 914 if not force_reload: 915 return { 916 'data': self.__data_date, 917 'online_update': self.__online_update_date 918 } 919 920 open(self.data_date_filename, 'wb').close() 921 922 cmd = u'%s -DATADATE' % self.path_to_binary 923 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True): 924 _log.error('problem querying the MMI drug database for version information') 925 self.__data_date = None 926 self.__online_update_date = None 927 return { 928 'data': u'?', 929 'online_update': u'?' 930 } 931 932 try: 933 version_file = open(self.data_date_filename, 'rU') 934 except StandardError: 935 _log.error('problem querying the MMI drug database for version information') 936 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename) 937 self.__data_date = None 938 self.__online_update_date = None 939 return { 940 'data': u'?', 941 'online_update': u'?' 942 } 943 944 self.__data_date = version_file.readline()[:10] 945 self.__online_update_date = version_file.readline()[:10] 946 version_file.close() 947 948 return { 949 'data': self.__data_date, 950 'online_update': self.__online_update_date 951 }
952 #--------------------------------------------------------
953 - def create_data_source_entry(self):
954 versions = self.get_data_source_version() 955 956 return create_data_source ( 957 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)', 958 short_name = u'GL/MMI', 959 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']), 960 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg', 961 language = u'de' 962 )
963 #--------------------------------------------------------
964 - def switch_to_frontend(self, blocking=False, cmd=None):
965 966 try: 967 # must make sure csv file exists 968 open(self.default_csv_filename, 'wb').close() 969 except IOError: 970 _log.exception('problem creating GL/MMI <-> GNUmed exchange file') 971 return False 972 973 if cmd is None: 974 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg 975 976 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking): 977 _log.error('problem switching to the MMI drug database') 978 # apparently on the first call MMI does not 979 # consistently return 0 on success 980 # return False 981 982 return True
983 #--------------------------------------------------------
984 - def __let_user_select_drugs(self):
985 986 # better to clean up interactions file 987 open(self.interactions_filename, 'wb').close() 988 989 if not self.switch_to_frontend(blocking = True): 990 return None 991 992 return cGelbeListeCSVFile(filename = self.default_csv_filename)
993 #--------------------------------------------------------
994 - def import_drugs_as_substances(self):
995 996 selected_drugs = self.__let_user_select_drugs() 997 if selected_drugs is None: 998 return None 999 1000 new_substances = [] 1001 1002 for drug in selected_drugs: 1003 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 1004 if len(drug['wirkstoffe']) == 1: 1005 atc = drug['atc'] 1006 for wirkstoff in drug['wirkstoffe']: 1007 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit)) 1008 1009 selected_drugs.close() 1010 1011 return new_substances
1012 #--------------------------------------------------------
1013 - def import_drugs(self):
1014 1015 selected_drugs = self.__let_user_select_drugs() 1016 if selected_drugs is None: 1017 return None 1018 1019 data_src_pk = self.create_data_source_entry() 1020 1021 new_drugs = [] 1022 new_substances = [] 1023 1024 for entry in selected_drugs: 1025 1026 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform']) 1027 1028 if entry[u'hilfsmittel']: 1029 _log.debug('skipping Hilfsmittel') 1030 continue 1031 1032 if entry[u'erstattbares_medizinprodukt']: 1033 _log.debug('skipping sonstiges Medizinprodukt') 1034 continue 1035 1036 # create branded drug (or get it if it already exists) 1037 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform']) 1038 if drug is None: 1039 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform']) 1040 new_drugs.append(drug) 1041 1042 # update fields 1043 drug['is_fake'] = False 1044 drug['atc_code'] = entry['atc'] 1045 drug['external_code_type'] = u'DE-PZN' 1046 drug['external_code'] = entry['pzn'] 1047 drug['fk_data_source'] = data_src_pk 1048 drug.save() 1049 1050 # add components to brand 1051 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 1052 if len(entry['wirkstoffe']) == 1: 1053 atc = entry['atc'] 1054 for wirkstoff in entry['wirkstoffe']: 1055 drug.add_component(substance = wirkstoff, atc = atc) 1056 1057 # create as consumable substances, too 1058 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 1059 if len(entry['wirkstoffe']) == 1: 1060 atc = entry['atc'] 1061 for wirkstoff in entry['wirkstoffe']: 1062 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit)) 1063 1064 return new_drugs, new_substances
1065 #--------------------------------------------------------
1066 - def check_interactions(self, drug_ids_list=None, substances=None):
1067 """For this to work the BDT interaction check must be configured in the MMI.""" 1068 1069 if drug_ids_list is None: 1070 if substances is None: 1071 return 1072 if len(substances) < 2: 1073 return 1074 drug_ids_list = [ (s.external_code_type, s.external_code) for s in substances ] 1075 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')] 1076 1077 else: 1078 if len(drug_ids_list) < 2: 1079 return 1080 1081 if drug_ids_list < 2: 1082 return 1083 1084 bdt_file = codecs.open(filename = self.interactions_filename, mode = 'wb', encoding = cGelbeListeWindowsInterface.default_encoding) 1085 1086 for pzn in drug_ids_list: 1087 pzn = pzn.strip() 1088 lng = cGelbeListeWindowsInterface.bdt_line_base_length + len(pzn) 1089 bdt_file.write(cGelbeListeWindowsInterface.bdt_line_template % (lng, pzn)) 1090 1091 bdt_file.close() 1092 1093 self.switch_to_frontend(blocking = False)
1094 #--------------------------------------------------------
1095 - def show_info_on_drug(self, drug=None):
1096 self.switch_to_frontend(blocking = True)
1097 #--------------------------------------------------------
1098 - def show_info_on_substance(self, substance=None):
1099 1100 cmd = None 1101 1102 if substance.external_code_type == u'DE-PZN': 1103 cmd = u'%s -PZN %s' % (self.path_to_binary, substance.external_code) 1104 1105 if cmd is None: 1106 name = gmTools.coalesce ( 1107 substance['brand'], 1108 substance['substance'] 1109 ) 1110 cmd = u'%s -NAME %s' % (self.path_to_binary, name) 1111 1112 # better to clean up interactions file 1113 open(self.interactions_filename, 'wb').close() 1114 1115 self.switch_to_frontend(cmd = cmd)
1116 #============================================================
1117 -class cGelbeListeWineInterface(cGelbeListeWindowsInterface):
1118
1119 - def __init__(self):
1120 cGelbeListeWindowsInterface.__init__(self) 1121 1122 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version) 1123 1124 # FIXME: if -CLOSETOTRAY is used GNUmed cannot detect the end of MMI 1125 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"' 1126 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"' 1127 1128 paths = gmTools.gmPaths() 1129 1130 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv') 1131 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv' 1132 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt') 1133 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
1134 #============================================================
1135 -class cIfapInterface(cDrugDataSourceInterface):
1136 """empirical CSV interface""" 1137
1138 - def __init__(self):
1139 pass
1140
1141 - def print_transfer_file(self, filename=None):
1142 1143 try: 1144 csv_file = open(filename, 'rb') # FIXME: encoding ? 1145 except: 1146 _log.exception('cannot access [%s]', filename) 1147 csv_file = None 1148 1149 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split() 1150 1151 if csv_file is None: 1152 return False 1153 1154 csv_lines = csv.DictReader ( 1155 csv_file, 1156 fieldnames = field_names, 1157 delimiter = ';' 1158 ) 1159 1160 for line in csv_lines: 1161 print "--------------------------------------------------------------------"[:31] 1162 for key in field_names: 1163 tmp = ('%s ' % key)[:30] 1164 print '%s: %s' % (tmp, line[key]) 1165 1166 csv_file.close()
1167 1168 # narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % ( 1169 # line['Packungszahl'].strip(), 1170 # line['Handelsname'].strip(), 1171 # line['Form'].strip(), 1172 # line[u'Packungsgr\xf6\xdfe'].strip(), 1173 # line['Abpackungsmenge'].strip(), 1174 # line['Einheit'].strip(), 1175 # line['Hersteller'].strip(), 1176 # line['PZN'].strip() 1177 # ) 1178 #============================================================ 1179 drug_data_source_interfaces = { 1180 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface, 1181 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface, 1182 'FreeDiams (FR, US, CA, ZA)': cFreeDiamsInterface 1183 } 1184 1185 #============================================================ 1186 #============================================================ 1187 # substances in use across all patients 1188 #------------------------------------------------------------ 1189 _SQL_get_consumable_substance = u""" 1190 SELECT *, xmin 1191 FROM ref.consumable_substance 1192 WHERE %s 1193 """ 1194
1195 -class cConsumableSubstance(gmBusinessDBObject.cBusinessDBObject):
1196 1197 _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s" 1198 _cmds_store_payload = [ 1199 u"""UPDATE ref.consumable_substance SET 1200 description = %(description)s, 1201 atc_code = gm.nullify_empty_string(%(atc_code)s), 1202 amount = %(amount)s, 1203 unit = gm.nullify_empty_string(%(unit)s) 1204 WHERE 1205 pk = %(pk)s 1206 AND 1207 xmin = %(xmin)s 1208 AND 1209 -- must not currently be used with a patient directly 1210 NOT EXISTS ( 1211 SELECT 1 1212 FROM clin.substance_intake 1213 WHERE 1214 fk_drug_component IS NULL 1215 AND 1216 fk_substance = %(pk)s 1217 LIMIT 1 1218 ) 1219 AND 1220 -- must not currently be used with a patient indirectly, either 1221 NOT EXISTS ( 1222 SELECT 1 1223 FROM clin.substance_intake 1224 WHERE 1225 fk_drug_component IS NOT NULL 1226 AND 1227 fk_drug_component = ( 1228 SELECT r_ls2b.pk 1229 FROM ref.lnk_substance2brand r_ls2b 1230 WHERE fk_substance = %(pk)s 1231 ) 1232 LIMIT 1 1233 ) 1234 -- -- must not currently be used with a branded drug 1235 -- -- (but this would make it rather hard fixing branded drugs which contain only this substance) 1236 -- NOT EXISTS ( 1237 -- SELECT 1 1238 -- FROM ref.lnk_substance2brand 1239 -- WHERE fk_substance = %(pk)s 1240 -- LIMIT 1 1241 -- ) 1242 RETURNING 1243 xmin 1244 """ 1245 ] 1246 _updatable_fields = [ 1247 u'description', 1248 u'atc_code', 1249 u'amount', 1250 u'unit' 1251 ] 1252 #--------------------------------------------------------
1253 - def save_payload(self, conn=None):
1254 success, data = super(self.__class__, self).save_payload(conn = conn) 1255 1256 if not success: 1257 return (success, data) 1258 1259 if self._payload[self._idx['atc_code']] is not None: 1260 atc = self._payload[self._idx['atc_code']].strip() 1261 if atc != u'': 1262 gmATC.propagate_atc ( 1263 substance = self._payload[self._idx['description']].strip(), 1264 atc = atc 1265 ) 1266 1267 return (success, data)
1268 #-------------------------------------------------------- 1269 # properties 1270 #--------------------------------------------------------
1271 - def _get_is_in_use_by_patients(self):
1272 cmd = u""" 1273 SELECT 1274 EXISTS ( 1275 SELECT 1 1276 FROM clin.substance_intake 1277 WHERE 1278 fk_drug_component IS NULL 1279 AND 1280 fk_substance = %(pk)s 1281 LIMIT 1 1282 ) OR EXISTS ( 1283 SELECT 1 1284 FROM clin.substance_intake 1285 WHERE 1286 fk_drug_component IS NOT NULL 1287 AND 1288 fk_drug_component = ( 1289 SELECT r_ls2b.pk 1290 FROM ref.lnk_substance2brand r_ls2b 1291 WHERE fk_substance = %(pk)s 1292 ) 1293 LIMIT 1 1294 )""" 1295 args = {'pk': self.pk_obj} 1296 1297 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1298 return rows[0][0]
1299 1300 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x) 1301 #--------------------------------------------------------
1302 - def _get_is_drug_component(self):
1303 cmd = u""" 1304 SELECT EXISTS ( 1305 SELECT 1 1306 FROM ref.lnk_substance2brand 1307 WHERE fk_substance = %(pk)s 1308 LIMIT 1 1309 )""" 1310 args = {'pk': self.pk_obj} 1311 1312 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1313 return rows[0][0]
1314 1315 is_drug_component = property(_get_is_drug_component, lambda x:x)
1316 #------------------------------------------------------------
1317 -def get_consumable_substances(order_by=None):
1318 if order_by is None: 1319 order_by = u'true' 1320 else: 1321 order_by = u'true ORDER BY %s' % order_by 1322 cmd = _SQL_get_consumable_substance % order_by 1323 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 1324 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
1325 #------------------------------------------------------------
1326 -def create_consumable_substance(substance=None, atc=None, amount=None, unit=None):
1327 1328 substance = substance 1329 if atc is not None: 1330 atc = atc.strip() 1331 1332 converted, amount = gmTools.input2decimal(amount) 1333 if not converted: 1334 raise ValueError('<amount> must be a number: %s (%s)', amount, type(amount)) 1335 1336 args = { 1337 'desc': substance.strip(), 1338 'amount': amount, 1339 'unit': unit.strip(), 1340 'atc': atc 1341 } 1342 cmd = u""" 1343 SELECT pk FROM ref.consumable_substance 1344 WHERE 1345 lower(description) = lower(%(desc)s) 1346 AND 1347 amount = %(amount)s 1348 AND 1349 unit = %(unit)s 1350 """ 1351 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 1352 1353 if len(rows) == 0: 1354 cmd = u""" 1355 INSERT INTO ref.consumable_substance (description, atc_code, amount, unit) VALUES ( 1356 %(desc)s, 1357 gm.nullify_empty_string(%(atc)s), 1358 %(amount)s, 1359 gm.nullify_empty_string(%(unit)s) 1360 ) RETURNING pk""" 1361 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 1362 1363 gmATC.propagate_atc(substance = substance, atc = atc) 1364 1365 return cConsumableSubstance(aPK_obj = rows[0]['pk'])
1366 #------------------------------------------------------------
1367 -def delete_consumable_substance(substance=None):
1368 args = {'pk': substance} 1369 cmd = u""" 1370 DELETE FROM ref.consumable_substance 1371 WHERE 1372 pk = %(pk)s 1373 AND 1374 1375 -- must not currently be used with a patient 1376 NOT EXISTS ( 1377 SELECT 1 1378 FROM clin.v_pat_substance_intake 1379 WHERE pk_substance = %(pk)s 1380 LIMIT 1 1381 ) 1382 AND 1383 1384 -- must not currently be used with a branded drug 1385 NOT EXISTS ( 1386 SELECT 1 1387 FROM ref.lnk_substance2brand 1388 WHERE fk_substance = %(pk)s 1389 LIMIT 1 1390 )""" 1391 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1392 return True
1393 #------------------------------------------------------------
1394 -class cSubstanceMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
1395 1396 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE) 1397 _query1 = u""" 1398 SELECT 1399 pk::text, 1400 (description || ' ' || amount || unit) as subst 1401 FROM ref.consumable_substance 1402 WHERE description %(fragment_condition)s 1403 ORDER BY subst 1404 LIMIT 50""" 1405 _query2 = u""" 1406 SELECT 1407 pk::text, 1408 (description || ' ' || amount || unit) as subst 1409 FROM ref.consumable_substance 1410 WHERE 1411 %(fragment_condition)s 1412 ORDER BY subst 1413 LIMIT 50""" 1414 1415 #--------------------------------------------------------
1416 - def getMatchesByPhrase(self, aFragment):
1417 """Return matches for aFragment at start of phrases.""" 1418 1419 if cSubstanceMatchProvider._pattern.match(aFragment): 1420 self._queries = [cSubstanceMatchProvider._query2] 1421 fragment_condition = """description ILIKE %(desc)s 1422 AND 1423 amount::text ILIKE %(amount)s""" 1424 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment) 1425 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment) 1426 else: 1427 self._queries = [cSubstanceMatchProvider._query1] 1428 fragment_condition = u"ILIKE %(fragment)s" 1429 self._args['fragment'] = u"%s%%" % aFragment 1430 1431 return self._find_matches(fragment_condition)
1432 #--------------------------------------------------------
1433 - def getMatchesByWord(self, aFragment):
1434 """Return matches for aFragment at start of words inside phrases.""" 1435 1436 if cSubstanceMatchProvider._pattern.match(aFragment): 1437 self._queries = [cSubstanceMatchProvider._query2] 1438 1439 desc = regex.sub(r'\s*\d+$', u'', aFragment) 1440 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False) 1441 1442 fragment_condition = """description ~* %(desc)s 1443 AND 1444 amount::text ILIKE %(amount)s""" 1445 1446 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc) 1447 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment) 1448 else: 1449 self._queries = [cSubstanceMatchProvider._query1] 1450 fragment_condition = u"~* %(fragment)s" 1451 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False) 1452 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment) 1453 1454 return self._find_matches(fragment_condition)
1455 #--------------------------------------------------------
1456 - def getMatchesBySubstr(self, aFragment):
1457 """Return matches for aFragment as a true substring.""" 1458 1459 if cSubstanceMatchProvider._pattern.match(aFragment): 1460 self._queries = [cSubstanceMatchProvider._query2] 1461 fragment_condition = """description ILIKE %(desc)s 1462 AND 1463 amount::text ILIKE %(amount)s""" 1464 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment) 1465 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment) 1466 else: 1467 self._queries = [cSubstanceMatchProvider._query1] 1468 fragment_condition = u"ILIKE %(fragment)s" 1469 self._args['fragment'] = u"%%%s%%" % aFragment 1470 1471 return self._find_matches(fragment_condition)
1472 #============================================================
1473 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
1474 """Represents a substance currently taken by a patient.""" 1475 1476 _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s" 1477 _cmds_store_payload = [ 1478 u"""UPDATE clin.substance_intake SET 1479 clin_when = %(started)s, 1480 discontinued = %(discontinued)s, 1481 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s), 1482 schedule = gm.nullify_empty_string(%(schedule)s), 1483 aim = gm.nullify_empty_string(%(aim)s), 1484 narrative = gm.nullify_empty_string(%(notes)s), 1485 intake_is_approved_of = %(intake_is_approved_of)s, 1486 fk_episode = %(pk_episode)s, 1487 1488 preparation = ( 1489 case 1490 when %(pk_brand)s is NULL then %(preparation)s 1491 else NULL 1492 end 1493 )::text, 1494 1495 is_long_term = ( 1496 case 1497 when ( 1498 (%(is_long_term)s is False) 1499 and 1500 (%(duration)s is NULL) 1501 ) is True then null 1502 else %(is_long_term)s 1503 end 1504 )::boolean, 1505 1506 duration = ( 1507 case 1508 when %(is_long_term)s is True then null 1509 else %(duration)s 1510 end 1511 )::interval 1512 WHERE 1513 pk = %(pk_substance_intake)s 1514 AND 1515 xmin = %(xmin_substance_intake)s 1516 RETURNING 1517 xmin as xmin_substance_intake 1518 """ 1519 ] 1520 _updatable_fields = [ 1521 u'started', 1522 u'discontinued', 1523 u'discontinue_reason', 1524 u'preparation', 1525 u'intake_is_approved_of', 1526 u'schedule', 1527 u'duration', 1528 u'aim', 1529 u'is_long_term', 1530 u'notes', 1531 u'pk_episode' 1532 ] 1533 #--------------------------------------------------------
1534 - def format(self, left_margin=0, date_format='%Y-%m-%d'):
1535 1536 if self._payload[self._idx['duration']] is None: 1537 duration = gmTools.bool2subst ( 1538 self._payload[self._idx['is_long_term']], 1539 _('long-term'), 1540 _('short-term'), 1541 _('?short-term') 1542 ) 1543 else: 1544 duration = gmDateTime.format_interval ( 1545 self._payload[self._idx['duration']], 1546 accuracy_wanted = gmDateTime.acc_days 1547 ) 1548 1549 line = u'%s%s (%s %s): %s %s%s %s (%s)' % ( 1550 u' ' * left_margin, 1551 self._payload[self._idx['started']].strftime(date_format), 1552 gmTools.u_right_arrow, 1553 duration, 1554 self._payload[self._idx['substance']], 1555 self._payload[self._idx['amount']], 1556 self._payload[self._idx['unit']], 1557 self._payload[self._idx['preparation']], 1558 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing')) 1559 ) 1560 1561 return line
1562 #--------------------------------------------------------
1563 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
1564 allg = gmAllergy.create_allergy ( 1565 allergene = self._payload[self._idx['substance']], 1566 allg_type = allergy_type, 1567 episode_id = self._payload[self._idx['pk_episode']], 1568 encounter_id = encounter_id 1569 ) 1570 allg['substance'] = gmTools.coalesce ( 1571 self._payload[self._idx['brand']], 1572 self._payload[self._idx['substance']] 1573 ) 1574 allg['reaction'] = self._payload[self._idx['discontinue_reason']] 1575 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']]) 1576 if self._payload[self._idx['external_code_brand']] is not None: 1577 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']]) 1578 comps = [ c['substance'] for c in self.containing_drug.components ] 1579 if len(comps) == 0: 1580 allg['generics'] = self._payload[self._idx['substance']] 1581 else: 1582 allg['generics'] = u'; '.join(comps) 1583 1584 allg.save() 1585 return allg
1586 #-------------------------------------------------------- 1587 # properties 1588 #--------------------------------------------------------
1589 - def _get_ddd(self):
1590 1591 try: self.__ddd 1592 except AttributeError: self.__ddd = None 1593 1594 if self.__ddd is not None: 1595 return self.__ddd 1596 1597 if self._payload[self._idx['atc_substance']] is not None: 1598 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']]) 1599 if len(ddd) != 0: 1600 self.__ddd = ddd[0] 1601 else: 1602 if self._payload[self._idx['atc_brand']] is not None: 1603 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']]) 1604 if len(ddd) != 0: 1605 self.__ddd = ddd[0] 1606 1607 return self.__ddd
1608 1609 ddd = property(_get_ddd, lambda x:x) 1610 #--------------------------------------------------------
1611 - def _get_external_code(self):
1612 drug = self.containing_drug 1613 1614 if drug is None: 1615 return None 1616 1617 return drug.external_code
1618 1619 external_code = property(_get_external_code, lambda x:x) 1620 #--------------------------------------------------------
1621 - def _get_external_code_type(self):
1622 drug = self.containing_drug 1623 1624 if drug is None: 1625 return None 1626 1627 return drug.external_code_type
1628 1629 external_code_type = property(_get_external_code_type, lambda x:x) 1630 #--------------------------------------------------------
1631 - def _get_containing_drug(self):
1632 if self._payload[self._idx['pk_brand']] is None: 1633 return None 1634 1635 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1636 1637 containing_drug = property(_get_containing_drug, lambda x:x) 1638 #--------------------------------------------------------
1639 - def _get_parsed_schedule(self):
1640 tests = [ 1641 # lead, trail 1642 ' 1-1-1-1 ', 1643 # leading dose 1644 '1-1-1-1', 1645 '22-1-1-1', 1646 '1/3-1-1-1', 1647 '/4-1-1-1' 1648 ] 1649 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}$" 1650 for test in tests: 1651 print test.strip(), ":", regex.match(pattern, test.strip())
1652 #------------------------------------------------------------
1653 -def create_substance_intake(pk_substance=None, pk_component=None, preparation=None, encounter=None, episode=None):
1654 1655 args = { 1656 'enc': encounter, 1657 'epi': episode, 1658 'comp': pk_component, 1659 'subst': pk_substance, 1660 'prep': preparation 1661 } 1662 1663 if pk_component is None: 1664 cmd = u""" 1665 INSERT INTO clin.substance_intake ( 1666 fk_encounter, 1667 fk_episode, 1668 intake_is_approved_of, 1669 fk_substance, 1670 preparation 1671 ) VALUES ( 1672 %(enc)s, 1673 %(epi)s, 1674 False, 1675 %(subst)s, 1676 %(prep)s 1677 ) 1678 RETURNING pk""" 1679 else: 1680 cmd = u""" 1681 INSERT INTO clin.substance_intake ( 1682 fk_encounter, 1683 fk_episode, 1684 intake_is_approved_of, 1685 fk_drug_component 1686 ) VALUES ( 1687 %(enc)s, 1688 %(epi)s, 1689 False, 1690 %(comp)s 1691 ) 1692 RETURNING pk""" 1693 1694 try: 1695 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 1696 except gmPG2.dbapi.InternalError, e: 1697 if e.pgerror is None: 1698 raise 1699 if 'prevent_duplicate_component' in e.pgerror: 1700 _log.exception('will not create duplicate substance intake entry') 1701 _log.error(e.pgerror) 1702 return None 1703 raise 1704 1705 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
1706 #------------------------------------------------------------
1707 -def delete_substance_intake(substance=None):
1708 cmd = u'delete from clin.substance_intake where pk = %(pk)s' 1709 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
1710 #------------------------------------------------------------
1711 -def format_substance_intake_notes(emr=None, output_format=u'latex', table_type=u'by-brand'):
1712 1713 tex = u'\n{\\small\n' 1714 tex += u'\\noindent %s\n' % _('Additional notes') 1715 tex += u'\n' 1716 tex += u'\\noindent \\begin{tabular}{|l|l|l|l|}\n' 1717 tex += u'\\hline\n' 1718 tex += u'%s & %s & %s & \\\\ \n' % (_('Substance'), _('Strength'), _('Brand')) 1719 tex += u'\\hline\n' 1720 tex += u'%s\n' 1721 tex += u'\n' 1722 tex += u'\\end{tabular}\n' 1723 tex += u'}\n' 1724 1725 current_meds = emr.get_current_substance_intake ( 1726 include_inactive = False, 1727 include_unapproved = False, 1728 order_by = u'brand, substance' 1729 ) 1730 1731 # create lines 1732 lines = [] 1733 for med in current_meds: 1734 1735 lines.append(u'%s & %s%s & %s %s & {\\scriptsize %s} \\\\ \n \\hline \n' % ( 1736 med['substance'], 1737 med['amount'], 1738 med['unit'], 1739 gmTools.coalesce(med['brand'], u''), 1740 med['preparation'], 1741 gmTools.coalesce(med['notes'], u'') 1742 )) 1743 1744 return tex % u' \n'.join(lines)
1745 1746 #------------------------------------------------------------
1747 -def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-brand'):
1748 1749 tex = u'\\noindent %s {\\tiny (%s)\\par}\n' % (_('Medication list'), _('ordered by brand')) 1750 tex += u'\n' 1751 tex += u'\\noindent \\begin{tabular}{|l|l|}\n' 1752 tex += u'\\hline\n' 1753 tex += u'%s & %s \\\\ \n' % (_('Drug'), _('Regimen')) 1754 tex += u'\\hline\n' 1755 tex += u'\n' 1756 tex += u'\\hline\n' 1757 tex += u'%s\n' 1758 tex += u'\n' 1759 tex += u'\\end{tabular}\n' 1760 1761 current_meds = emr.get_current_substance_intake ( 1762 include_inactive = False, 1763 include_unapproved = False, 1764 order_by = u'brand, substance' 1765 ) 1766 1767 # aggregate data 1768 line_data = {} 1769 for med in current_meds: 1770 identifier = gmTools.coalesce(med['brand'], med['substance']) 1771 1772 try: 1773 line_data[identifier] 1774 except KeyError: 1775 line_data[identifier] = {'brand': u'', 'preparation': u'', 'schedule': u'', 'aims': [], 'strengths': []} 1776 1777 line_data[identifier]['brand'] = identifier 1778 line_data[identifier]['strengths'].append(u'%s%s' % (med['amount'], med['unit'].strip())) 1779 line_data[identifier]['preparation'] = med['preparation'] 1780 line_data[identifier]['schedule'] = gmTools.coalesce(med['schedule'], u'') 1781 if med['aim'] not in line_data[identifier]['aims']: 1782 line_data[identifier]['aims'].append(med['aim']) 1783 1784 # create lines 1785 already_seen = [] 1786 lines = [] 1787 line1_template = u'%s %s & %s \\\\' 1788 line2_template = u' & {\\scriptsize %s\\par} \\\\' 1789 1790 for med in current_meds: 1791 identifier = gmTools.coalesce(med['brand'], med['substance']) 1792 1793 if identifier in already_seen: 1794 continue 1795 1796 already_seen.append(identifier) 1797 1798 lines.append (line1_template % ( 1799 line_data[identifier]['brand'], 1800 line_data[identifier]['preparation'], 1801 line_data[identifier]['schedule'] 1802 )) 1803 1804 strengths = u'/'.join(line_data[identifier]['strengths']) 1805 if strengths == u'': 1806 template = u' & {\\scriptsize %s\\par} \\\\' 1807 for aim in line_data[identifier]['aims']: 1808 lines.append(template % aim) 1809 else: 1810 if len(line_data[identifier]['aims']) == 0: 1811 template = u'%s & \\\\' 1812 lines.append(template % strengths) 1813 else: 1814 template = u'%s & {\\scriptsize %s\\par} \\\\' 1815 lines.append(template % (strengths, line_data[identifier]['aims'][0])) 1816 template = u' & {\\scriptsize %s\\par} \\\\' 1817 for aim in line_data[identifier]['aims'][1:]: 1818 lines.append(template % aim) 1819 1820 lines.append(u'\\hline') 1821 1822 return tex % u' \n'.join(lines)
1823 #============================================================ 1824 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s' 1825
1826 -class cDrugComponent(gmBusinessDBObject.cBusinessDBObject):
1827 1828 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s' 1829 _cmds_store_payload = [ 1830 u"""UPDATE ref.lnk_substance2brand SET 1831 fk_brand = %(pk_brand)s, 1832 fk_substance = %(pk_consumable_substance)s 1833 WHERE 1834 NOT EXISTS ( 1835 SELECT 1 1836 FROM clin.substance_intake 1837 WHERE fk_drug_component = %(pk_component)s 1838 LIMIT 1 1839 ) 1840 AND 1841 pk = %(pk_component)s 1842 AND 1843 xmin = %(xmin_lnk_substance2brand)s 1844 RETURNING 1845 xmin AS xmin_lnk_substance2brand 1846 """ 1847 ] 1848 _updatable_fields = [ 1849 u'pk_brand', 1850 u'pk_consumable_substance' 1851 ] 1852 #-------------------------------------------------------- 1853 # properties 1854 #--------------------------------------------------------
1855 - def _get_containing_drug(self):
1856 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1857 1858 containing_drug = property(_get_containing_drug, lambda x:x) 1859 #--------------------------------------------------------
1860 - def _get_is_in_use_by_patients(self):
1861 return self._payload[self._idx['is_in_use']]
1862 1863 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x) 1864 #--------------------------------------------------------
1865 - def _get_substance(self):
1866 return cConsumableSubstance(aPK_obj = self._payload[self._idx['pk_consumable_substance']])
1867 1868 substance = property(_get_substance, lambda x:x)
1869 #------------------------------------------------------------
1870 -def get_drug_components():
1871 cmd = _SQL_get_drug_components % u'true ORDER BY brand, substance' 1872 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 1873 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
1874 #------------------------------------------------------------
1875 -class cDrugComponentMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
1876 1877 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE) 1878 _query_desc_only = u""" 1879 SELECT DISTINCT ON (component) 1880 pk_component, 1881 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')') 1882 AS component 1883 FROM ref.v_drug_components 1884 WHERE 1885 substance %(fragment_condition)s 1886 OR 1887 brand %(fragment_condition)s 1888 ORDER BY component 1889 LIMIT 50""" 1890 _query_desc_and_amount = u""" 1891 1892 SELECT DISTINCT ON (component) 1893 pk_component, 1894 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')') 1895 AS component 1896 FROM ref.v_drug_components 1897 WHERE 1898 %(fragment_condition)s 1899 ORDER BY component 1900 LIMIT 50""" 1901 #--------------------------------------------------------
1902 - def getMatchesByPhrase(self, aFragment):
1903 """Return matches for aFragment at start of phrases.""" 1904 1905 if cDrugComponentMatchProvider._pattern.match(aFragment): 1906 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount] 1907 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s) 1908 AND 1909 amount::text ILIKE %(amount)s""" 1910 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment) 1911 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment) 1912 else: 1913 self._queries = [cDrugComponentMatchProvider._query_desc_only] 1914 fragment_condition = u"ILIKE %(fragment)s" 1915 self._args['fragment'] = u"%s%%" % aFragment 1916 1917 return self._find_matches(fragment_condition)
1918 #--------------------------------------------------------
1919 - def getMatchesByWord(self, aFragment):
1920 """Return matches for aFragment at start of words inside phrases.""" 1921 1922 if cDrugComponentMatchProvider._pattern.match(aFragment): 1923 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount] 1924 1925 desc = regex.sub(r'\s*\d+$', u'', aFragment) 1926 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False) 1927 1928 fragment_condition = """(substance ~* %(desc)s OR brand ~* %(desc)s) 1929 AND 1930 amount::text ILIKE %(amount)s""" 1931 1932 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc) 1933 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment) 1934 else: 1935 self._queries = [cDrugComponentMatchProvider._query_desc_only] 1936 fragment_condition = u"~* %(fragment)s" 1937 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False) 1938 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment) 1939 1940 return self._find_matches(fragment_condition)
1941 #--------------------------------------------------------
1942 - def getMatchesBySubstr(self, aFragment):
1943 """Return matches for aFragment as a true substring.""" 1944 1945 if cDrugComponentMatchProvider._pattern.match(aFragment): 1946 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount] 1947 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s) 1948 AND 1949 amount::text ILIKE %(amount)s""" 1950 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment) 1951 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment) 1952 else: 1953 self._queries = [cDrugComponentMatchProvider._query_desc_only] 1954 fragment_condition = u"ILIKE %(fragment)s" 1955 self._args['fragment'] = u"%%%s%%" % aFragment 1956 1957 return self._find_matches(fragment_condition)
1958 1959 #============================================================
1960 -class cBrandedDrug(gmBusinessDBObject.cBusinessDBObject):
1961 """Represents a drug as marketed by a manufacturer.""" 1962 1963 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s" 1964 _cmds_store_payload = [ 1965 u"""UPDATE ref.branded_drug SET 1966 description = %(brand)s, 1967 preparation = %(preparation)s, 1968 atc_code = gm.nullify_empty_string(%(atc)s), 1969 external_code = gm.nullify_empty_string(%(external_code)s), 1970 external_code_type = gm.nullify_empty_string(%(external_code_type)s), 1971 is_fake = %(is_fake_brand)s, 1972 fk_data_source = %(pk_data_source)s 1973 WHERE 1974 pk = %(pk_brand)s 1975 AND 1976 xmin = %(xmin_branded_drug)s 1977 RETURNING 1978 xmin AS xmin_branded_drug 1979 """ 1980 ] 1981 _updatable_fields = [ 1982 u'brand', 1983 u'preparation', 1984 u'atc', 1985 u'is_fake_brand', 1986 u'external_code', 1987 u'external_code_type', 1988 u'pk_data_source' 1989 ] 1990 #--------------------------------------------------------
1991 - def save_payload(self, conn=None):
1992 success, data = super(self.__class__, self).save_payload(conn = conn) 1993 1994 if not success: 1995 return (success, data) 1996 1997 if self._payload[self._idx['atc']] is not None: 1998 atc = self._payload[self._idx['atc']].strip() 1999 if atc != u'': 2000 gmATC.propagate_atc ( 2001 substance = self._payload[self._idx['brand']].strip(), 2002 atc = atc 2003 ) 2004 2005 return (success, data)
2006 #--------------------------------------------------------
2007 - def set_substances_as_components(self, substances=None):
2008 2009 if self._payload[self._idx['is_in_use']]: 2010 return False 2011 2012 args = {'brand': self._payload[self._idx['pk_brand']]} 2013 2014 queries = [{'cmd': u"DELETE FROM ref.lnk_substance2brand WHERE fk_brand = %(brand)s", 'args': args}] 2015 cmd = u'INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) VALUES (%%(brand)s, %s)' 2016 for s in substances: 2017 queries.append({'cmd': cmd % s['pk'], 'args': args}) 2018 2019 gmPG2.run_rw_queries(queries = queries) 2020 self.refetch_payload() 2021 2022 return True
2023 #--------------------------------------------------------
2024 - def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
2025 2026 args = { 2027 'brand': self.pk_obj, 2028 'subst': substance, 2029 'atc': atc, 2030 'pk_subst': pk_substance 2031 } 2032 2033 if pk_substance is None: 2034 consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit) 2035 args['pk_subst'] = consumable['pk'] 2036 2037 # already a component 2038 cmd = u""" 2039 SELECT pk_component 2040 FROM ref.v_drug_components 2041 WHERE 2042 pk_brand = %(brand)s 2043 AND 2044 (( 2045 (lower(substance) = lower(%(subst)s)) 2046 OR 2047 (lower(atc_substance) = lower(%(atc)s)) 2048 OR 2049 (pk_consumable_substance = %(pk_subst)s) 2050 ) IS TRUE) 2051 """ 2052 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 2053 2054 if len(rows) > 0: 2055 return 2056 2057 # create it 2058 cmd = u""" 2059 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) 2060 VALUES (%(brand)s, %(pk_subst)s) 2061 """ 2062 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2063 self.refetch_payload()
2064 #------------------------------------------------------------
2065 - def remove_component(self, substance=None):
2066 if len(self._payload[self._idx['components']]) == 1: 2067 _log.error('cannot remove the only component of a drug') 2068 return False 2069 2070 args = {'brand': self.pk_obj, 'comp': substance} 2071 cmd = u""" 2072 DELETE FROM ref.lnk_substance2brand 2073 WHERE 2074 fk_brand = %(brand)s 2075 AND 2076 fk_substance = %(comp)s 2077 AND 2078 NOT EXISTS ( 2079 SELECT 1 2080 FROM clin.substance_intake 2081 WHERE fk_drug_component = %(comp)s 2082 LIMIT 1 2083 ) 2084 """ 2085 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2086 self.refetch_payload() 2087 2088 return True
2089 #-------------------------------------------------------- 2090 # properties 2091 #--------------------------------------------------------
2092 - def _get_external_code(self):
2093 if self._payload[self._idx['external_code']] is None: 2094 return None 2095 2096 return self._payload[self._idx['external_code']]
2097 2098 external_code = property(_get_external_code, lambda x:x) 2099 #--------------------------------------------------------
2100 - def _get_external_code_type(self):
2101 2102 # FIXME: maybe evaluate fk_data_source ? 2103 if self._payload[self._idx['external_code_type']] is None: 2104 return None 2105 2106 return self._payload[self._idx['external_code_type']]
2107 2108 external_code_type = property(_get_external_code_type, lambda x:x) 2109 #--------------------------------------------------------
2110 - def _get_components(self):
2111 cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s' 2112 args = {'brand': self._payload[self._idx['pk_brand']]} 2113 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2114 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
2115 2116 components = property(_get_components, lambda x:x) 2117 #--------------------------------------------------------
2119 if self._payload[self._idx['pk_substances']] is None: 2120 return [] 2121 cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s' 2122 args = {'pks': tuple(self._payload[self._idx['pk_substances']])} 2123 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2124 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
2125 2126 components_as_substances = property(_get_components_as_substances, lambda x:x) 2127 #--------------------------------------------------------
2128 - def _get_is_vaccine(self):
2129 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)' 2130 args = {'fk_brand': self._payload[self._idx['pk_brand']]} 2131 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 2132 return rows[0][0]
2133 2134 is_vaccine = property(_get_is_vaccine, lambda x:x)
2135 #------------------------------------------------------------
2136 -def get_branded_drugs():
2137 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description' 2138 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 2139 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
2140 #------------------------------------------------------------
2141 -def get_drug_by_brand(brand_name=None, preparation=None):
2142 args = {'brand': brand_name, 'prep': preparation} 2143 2144 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)' 2145 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 2146 2147 if len(rows) == 0: 2148 return None 2149 2150 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2151 #------------------------------------------------------------
2152 -def create_branded_drug(brand_name=None, preparation=None, return_existing=False):
2153 2154 if preparation is None: 2155 preparation = _('units') 2156 2157 if preparation.strip() == u'': 2158 preparation = _('units') 2159 2160 if return_existing: 2161 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation) 2162 if drug is not None: 2163 return drug 2164 2165 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk' 2166 args = {'brand': brand_name, 'prep': preparation} 2167 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 2168 2169 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2170 #------------------------------------------------------------
2171 -def delete_branded_drug(brand=None):
2172 queries = [] 2173 args = {'pk': brand} 2174 2175 # delete components 2176 cmd = u""" 2177 DELETE FROM ref.lnk_substance2brand 2178 WHERE 2179 fk_brand = %(pk)s 2180 AND 2181 NOT EXISTS ( 2182 SELECT 1 2183 FROM clin.v_pat_substance_intake 2184 WHERE pk_brand = %(pk)s 2185 LIMIT 1 2186 ) 2187 """ 2188 queries.append({'cmd': cmd, 'args': args}) 2189 2190 # delete drug 2191 cmd = u""" 2192 DELETE FROM ref.branded_drug 2193 WHERE 2194 pk = %(pk)s 2195 AND 2196 NOT EXISTS ( 2197 SELECT 1 2198 FROM clin.v_pat_substance_intake 2199 WHERE pk_brand = %(pk)s 2200 LIMIT 1 2201 ) 2202 """ 2203 queries.append({'cmd': cmd, 'args': args}) 2204 2205 gmPG2.run_rw_queries(queries = queries)
2206 #============================================================ 2207 # main 2208 #------------------------------------------------------------ 2209 if __name__ == "__main__": 2210 2211 if len(sys.argv) < 2: 2212 sys.exit() 2213 2214 if sys.argv[1] != 'test': 2215 sys.exit() 2216 2217 from Gnumed.pycommon import gmLog2 2218 from Gnumed.pycommon import gmI18N 2219 from Gnumed.business import gmPerson 2220 2221 gmI18N.activate_locale() 2222 # gmDateTime.init() 2223 #--------------------------------------------------------
2224 - def test_MMI_interface():
2225 mmi = cGelbeListeWineInterface() 2226 print mmi 2227 print "interface definition:", mmi.version 2228 print "database versions: ", mmi.get_data_source_version()
2229 #--------------------------------------------------------
2230 - def test_MMI_file():
2231 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2]) 2232 for drug in mmi_file: 2233 print "-------------" 2234 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 2235 for stoff in drug['wirkstoffe']: 2236 print " Wirkstoff:", stoff 2237 raw_input() 2238 if mmi_file.has_unknown_fields is not None: 2239 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key 2240 for key in mmi_file.csv_fieldnames: 2241 print key, '->', drug[key] 2242 raw_input() 2243 mmi_file.close()
2244 #--------------------------------------------------------
2245 - def test_mmi_switch_to():
2246 mmi = cGelbeListeWineInterface() 2247 mmi.switch_to_frontend(blocking = False)
2248 #--------------------------------------------------------
2249 - def test_mmi_let_user_select_drugs():
2250 mmi = cGelbeListeWineInterface() 2251 mmi_file = mmi.__let_user_select_drugs() 2252 for drug in mmi_file: 2253 print "-------------" 2254 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 2255 for stoff in drug['wirkstoffe']: 2256 print " Wirkstoff:", stoff 2257 print drug 2258 mmi_file.close()
2259 #--------------------------------------------------------
2260 - def test_mmi_import_drugs():
2261 mmi = cGelbeListeWineInterface() 2262 mmi.import_drugs()
2263 #--------------------------------------------------------
2264 - def test_mmi_interaction_check():
2265 mmi = cGelbeListeInterface() 2266 print mmi 2267 print "interface definition:", mmi.version 2268 # Metoprolol + Hct vs Citalopram 2269 diclofenac = '7587712' 2270 phenprocoumon = '4421744' 2271 mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
2272 #-------------------------------------------------------- 2273 # FreeDiams 2274 #--------------------------------------------------------
2275 - def test_fd_switch_to():
2276 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12)) 2277 fd = cFreeDiamsInterface() 2278 fd.patient = gmPerson.gmCurrentPatient() 2279 # fd.switch_to_frontend(blocking = True) 2280 fd.import_fd2gm_file_as_drugs(filename = sys.argv[2])
2281 #--------------------------------------------------------
2282 - def test_fd_show_interactions():
2283 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12)) 2284 fd = cFreeDiamsInterface() 2285 fd.patient = gmPerson.gmCurrentPatient() 2286 fd.check_interactions(substances = fd.patient.get_emr().get_current_substance_intake(include_unapproved = True))
2287 #-------------------------------------------------------- 2288 # generic 2289 #--------------------------------------------------------
2290 - def test_create_substance_intake():
2291 drug = create_substance_intake ( 2292 pk_component = 2, 2293 encounter = 1, 2294 episode = 1 2295 ) 2296 print drug
2297 #--------------------------------------------------------
2298 - def test_show_components():
2299 drug = cBrandedDrug(aPK_obj = sys.argv[2]) 2300 print drug 2301 print drug.components
2302 #--------------------------------------------------------
2303 - def test_get_consumable_substances():
2304 for s in get_consumable_substances(): 2305 print s
2306 #-------------------------------------------------------- 2307 #-------------------------------------------------------- 2308 # MMI/Gelbe Liste 2309 #test_MMI_interface() 2310 #test_MMI_file() 2311 #test_mmi_switch_to() 2312 #test_mmi_let_user_select_drugs() 2313 #test_mmi_import_substances() 2314 #test_mmi_import_drugs() 2315 2316 # FreeDiams 2317 test_fd_switch_to() 2318 #test_fd_show_interactions() 2319 2320 # generic 2321 #test_interaction_check() 2322 #test_create_substance_intake() 2323 #test_show_components() 2324 #test_get_consumable_substances() 2325 #============================================================ 2326