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.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(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 = ( 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{tabular}{|l|l|l|l|}\n' 1741 tex += u'\\hline\n' 1742 tex += u'%s & %s & %s & \\\\ \n' % (_('Substance'), _('Strength'), _('Brand')) 1743 tex += u'\\hline\n' 1744 tex += u'%s\n' 1745 tex += u'\n' 1746 tex += u'\\end{tabular}\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 1759 lines.append(u'%s & %s%s & %s %s & {\\scriptsize %s} \\\\ \n \\hline \n' % ( 1760 med['substance'], 1761 med['amount'], 1762 med['unit'], 1763 gmTools.coalesce(med['brand'], u''), 1764 med['preparation'], 1765 gmTools.coalesce(med['notes'], u'') 1766 )) 1767 1768 return tex % u' \n'.join(lines)
1769 1770 #------------------------------------------------------------
1771 -def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-brand'):
1772 1773 tex = u'\\noindent %s {\\tiny (%s)\\par}\n' % (_('Medication list'), _('ordered by brand')) 1774 tex += u'\n' 1775 tex += u'\\noindent \\begin{tabular}{|l|l|}\n' 1776 tex += u'\\hline\n' 1777 tex += u'%s & %s \\\\ \n' % (_('Drug'), _('Regimen')) 1778 tex += u'\\hline\n' 1779 tex += u'\n' 1780 tex += u'\\hline\n' 1781 tex += u'%s\n' 1782 tex += u'\n' 1783 tex += u'\\end{tabular}\n' 1784 1785 current_meds = emr.get_current_substance_intake ( 1786 include_inactive = False, 1787 include_unapproved = False, 1788 order_by = u'brand, substance' 1789 ) 1790 1791 # aggregate data 1792 line_data = {} 1793 for med in current_meds: 1794 identifier = gmTools.coalesce(med['brand'], med['substance']) 1795 1796 try: 1797 line_data[identifier] 1798 except KeyError: 1799 line_data[identifier] = {'brand': u'', 'preparation': u'', 'schedule': u'', 'aims': [], 'strengths': []} 1800 1801 line_data[identifier]['brand'] = identifier 1802 line_data[identifier]['strengths'].append(u'%s%s' % (med['amount'], med['unit'].strip())) 1803 line_data[identifier]['preparation'] = med['preparation'] 1804 line_data[identifier]['schedule'] = gmTools.coalesce(med['schedule'], u'') 1805 if med['aim'] not in line_data[identifier]['aims']: 1806 line_data[identifier]['aims'].append(med['aim']) 1807 1808 # create lines 1809 already_seen = [] 1810 lines = [] 1811 line1_template = u'%s %s & %s \\\\' 1812 line2_template = u' & {\\scriptsize %s\\par} \\\\' 1813 1814 for med in current_meds: 1815 identifier = gmTools.coalesce(med['brand'], med['substance']) 1816 1817 if identifier in already_seen: 1818 continue 1819 1820 already_seen.append(identifier) 1821 1822 lines.append (line1_template % ( 1823 line_data[identifier]['brand'], 1824 line_data[identifier]['preparation'], 1825 line_data[identifier]['schedule'] 1826 )) 1827 1828 strengths = u'/'.join(line_data[identifier]['strengths']) 1829 if strengths == u'': 1830 template = u' & {\\scriptsize %s\\par} \\\\' 1831 for aim in line_data[identifier]['aims']: 1832 lines.append(template % aim) 1833 else: 1834 if len(line_data[identifier]['aims']) == 0: 1835 template = u'%s & \\\\' 1836 lines.append(template % strengths) 1837 else: 1838 template = u'%s & {\\scriptsize %s\\par} \\\\' 1839 lines.append(template % (strengths, line_data[identifier]['aims'][0])) 1840 template = u' & {\\scriptsize %s\\par} \\\\' 1841 for aim in line_data[identifier]['aims'][1:]: 1842 lines.append(template % aim) 1843 1844 lines.append(u'\\hline') 1845 1846 return tex % u' \n'.join(lines)
1847 #============================================================ 1848 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s' 1849
1850 -class cDrugComponent(gmBusinessDBObject.cBusinessDBObject):
1851 1852 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s' 1853 _cmds_store_payload = [ 1854 u"""UPDATE ref.lnk_substance2brand SET 1855 fk_brand = %(pk_brand)s, 1856 fk_substance = %(pk_consumable_substance)s 1857 WHERE 1858 NOT EXISTS ( 1859 SELECT 1 1860 FROM clin.substance_intake 1861 WHERE fk_drug_component = %(pk_component)s 1862 LIMIT 1 1863 ) 1864 AND 1865 pk = %(pk_component)s 1866 AND 1867 xmin = %(xmin_lnk_substance2brand)s 1868 RETURNING 1869 xmin AS xmin_lnk_substance2brand 1870 """ 1871 ] 1872 _updatable_fields = [ 1873 u'pk_brand', 1874 u'pk_consumable_substance' 1875 ] 1876 #-------------------------------------------------------- 1877 # properties 1878 #--------------------------------------------------------
1879 - def _get_containing_drug(self):
1880 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1881 1882 containing_drug = property(_get_containing_drug, lambda x:x) 1883 #--------------------------------------------------------
1884 - def _get_is_in_use_by_patients(self):
1885 return self._payload[self._idx['is_in_use']]
1886 1887 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x) 1888 #--------------------------------------------------------
1889 - def _get_substance(self):
1890 return cConsumableSubstance(aPK_obj = self._payload[self._idx['pk_consumable_substance']])
1891 1892 substance = property(_get_substance, lambda x:x)
1893 #------------------------------------------------------------
1894 -def get_drug_components():
1895 cmd = _SQL_get_drug_components % u'true ORDER BY brand, substance' 1896 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 1897 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
1898 #------------------------------------------------------------
1899 -class cDrugComponentMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
1900 1901 _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE) 1902 _query_desc_only = u""" 1903 SELECT DISTINCT ON (component) 1904 pk_component, 1905 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')') 1906 AS component 1907 FROM ref.v_drug_components 1908 WHERE 1909 substance %(fragment_condition)s 1910 OR 1911 brand %(fragment_condition)s 1912 ORDER BY component 1913 LIMIT 50""" 1914 _query_desc_and_amount = u""" 1915 1916 SELECT DISTINCT ON (component) 1917 pk_component, 1918 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')') 1919 AS component 1920 FROM ref.v_drug_components 1921 WHERE 1922 %(fragment_condition)s 1923 ORDER BY component 1924 LIMIT 50""" 1925 #--------------------------------------------------------
1926 - def getMatchesByPhrase(self, aFragment):
1927 """Return matches for aFragment at start of phrases.""" 1928 1929 if cDrugComponentMatchProvider._pattern.match(aFragment): 1930 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount] 1931 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s) 1932 AND 1933 amount::text ILIKE %(amount)s""" 1934 self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment) 1935 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment) 1936 else: 1937 self._queries = [cDrugComponentMatchProvider._query_desc_only] 1938 fragment_condition = u"ILIKE %(fragment)s" 1939 self._args['fragment'] = u"%s%%" % aFragment 1940 1941 return self._find_matches(fragment_condition)
1942 #--------------------------------------------------------
1943 - def getMatchesByWord(self, aFragment):
1944 """Return matches for aFragment at start of words inside phrases.""" 1945 1946 if cDrugComponentMatchProvider._pattern.match(aFragment): 1947 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount] 1948 1949 desc = regex.sub(r'\s*\d+$', u'', aFragment) 1950 desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False) 1951 1952 fragment_condition = """(substance ~* %(desc)s OR brand ~* %(desc)s) 1953 AND 1954 amount::text ILIKE %(amount)s""" 1955 1956 self._args['desc'] = u"( %s)|(^%s)" % (desc, desc) 1957 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment) 1958 else: 1959 self._queries = [cDrugComponentMatchProvider._query_desc_only] 1960 fragment_condition = u"~* %(fragment)s" 1961 aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False) 1962 self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment) 1963 1964 return self._find_matches(fragment_condition)
1965 #--------------------------------------------------------
1966 - def getMatchesBySubstr(self, aFragment):
1967 """Return matches for aFragment as a true substring.""" 1968 1969 if cDrugComponentMatchProvider._pattern.match(aFragment): 1970 self._queries = [cDrugComponentMatchProvider._query_desc_and_amount] 1971 fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s) 1972 AND 1973 amount::text ILIKE %(amount)s""" 1974 self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment) 1975 self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment) 1976 else: 1977 self._queries = [cDrugComponentMatchProvider._query_desc_only] 1978 fragment_condition = u"ILIKE %(fragment)s" 1979 self._args['fragment'] = u"%%%s%%" % aFragment 1980 1981 return self._find_matches(fragment_condition)
1982 1983 #============================================================
1984 -class cBrandedDrug(gmBusinessDBObject.cBusinessDBObject):
1985 """Represents a drug as marketed by a manufacturer.""" 1986 1987 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s" 1988 _cmds_store_payload = [ 1989 u"""UPDATE ref.branded_drug SET 1990 description = %(brand)s, 1991 preparation = %(preparation)s, 1992 atc_code = gm.nullify_empty_string(%(atc)s), 1993 external_code = gm.nullify_empty_string(%(external_code)s), 1994 external_code_type = gm.nullify_empty_string(%(external_code_type)s), 1995 is_fake = %(is_fake_brand)s, 1996 fk_data_source = %(pk_data_source)s 1997 WHERE 1998 pk = %(pk_brand)s 1999 AND 2000 xmin = %(xmin_branded_drug)s 2001 RETURNING 2002 xmin AS xmin_branded_drug 2003 """ 2004 ] 2005 _updatable_fields = [ 2006 u'brand', 2007 u'preparation', 2008 u'atc', 2009 u'is_fake_brand', 2010 u'external_code', 2011 u'external_code_type', 2012 u'pk_data_source' 2013 ] 2014 #--------------------------------------------------------
2015 - def save_payload(self, conn=None):
2016 success, data = super(self.__class__, self).save_payload(conn = conn) 2017 2018 if not success: 2019 return (success, data) 2020 2021 if self._payload[self._idx['atc']] is not None: 2022 atc = self._payload[self._idx['atc']].strip() 2023 if atc != u'': 2024 gmATC.propagate_atc ( 2025 substance = self._payload[self._idx['brand']].strip(), 2026 atc = atc 2027 ) 2028 2029 return (success, data)
2030 #--------------------------------------------------------
2031 - def set_substances_as_components(self, substances=None):
2032 2033 if self._payload[self._idx['is_in_use']]: 2034 return False 2035 2036 args = {'brand': self._payload[self._idx['pk_brand']]} 2037 2038 queries = [{'cmd': u"DELETE FROM ref.lnk_substance2brand WHERE fk_brand = %(brand)s", 'args': args}] 2039 cmd = u'INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) VALUES (%%(brand)s, %s)' 2040 for s in substances: 2041 queries.append({'cmd': cmd % s['pk'], 'args': args}) 2042 2043 gmPG2.run_rw_queries(queries = queries) 2044 self.refetch_payload() 2045 2046 return True
2047 #--------------------------------------------------------
2048 - def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
2049 2050 args = { 2051 'brand': self.pk_obj, 2052 'subst': substance, 2053 'atc': atc, 2054 'pk_subst': pk_substance 2055 } 2056 2057 if pk_substance is None: 2058 consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit) 2059 args['pk_subst'] = consumable['pk'] 2060 2061 # already a component 2062 cmd = u""" 2063 SELECT pk_component 2064 FROM ref.v_drug_components 2065 WHERE 2066 pk_brand = %(brand)s 2067 AND 2068 (( 2069 (lower(substance) = lower(%(subst)s)) 2070 OR 2071 (lower(atc_substance) = lower(%(atc)s)) 2072 OR 2073 (pk_consumable_substance = %(pk_subst)s) 2074 ) IS TRUE) 2075 """ 2076 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 2077 2078 if len(rows) > 0: 2079 return 2080 2081 # create it 2082 cmd = u""" 2083 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) 2084 VALUES (%(brand)s, %(pk_subst)s) 2085 """ 2086 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2087 self.refetch_payload()
2088 #------------------------------------------------------------
2089 - def remove_component(self, substance=None):
2090 if len(self._payload[self._idx['components']]) == 1: 2091 _log.error('cannot remove the only component of a drug') 2092 return False 2093 2094 args = {'brand': self.pk_obj, 'comp': substance} 2095 cmd = u""" 2096 DELETE FROM ref.lnk_substance2brand 2097 WHERE 2098 fk_brand = %(brand)s 2099 AND 2100 fk_substance = %(comp)s 2101 AND 2102 NOT EXISTS ( 2103 SELECT 1 2104 FROM clin.substance_intake 2105 WHERE fk_drug_component = %(comp)s 2106 LIMIT 1 2107 ) 2108 """ 2109 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2110 self.refetch_payload() 2111 2112 return True
2113 #-------------------------------------------------------- 2114 # properties 2115 #--------------------------------------------------------
2116 - def _get_external_code(self):
2117 if self._payload[self._idx['external_code']] is None: 2118 return None 2119 2120 return self._payload[self._idx['external_code']]
2121 2122 external_code = property(_get_external_code, lambda x:x) 2123 #--------------------------------------------------------
2124 - def _get_external_code_type(self):
2125 2126 # FIXME: maybe evaluate fk_data_source ? 2127 if self._payload[self._idx['external_code_type']] is None: 2128 return None 2129 2130 return self._payload[self._idx['external_code_type']]
2131 2132 external_code_type = property(_get_external_code_type, lambda x:x) 2133 #--------------------------------------------------------
2134 - def _get_components(self):
2135 cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s' 2136 args = {'brand': self._payload[self._idx['pk_brand']]} 2137 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2138 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
2139 2140 components = property(_get_components, lambda x:x) 2141 #--------------------------------------------------------
2143 if self._payload[self._idx['pk_substances']] is None: 2144 return [] 2145 cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s' 2146 args = {'pks': tuple(self._payload[self._idx['pk_substances']])} 2147 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2148 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
2149 2150 components_as_substances = property(_get_components_as_substances, lambda x:x) 2151 #--------------------------------------------------------
2152 - def _get_is_vaccine(self):
2153 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)' 2154 args = {'fk_brand': self._payload[self._idx['pk_brand']]} 2155 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 2156 return rows[0][0]
2157 2158 is_vaccine = property(_get_is_vaccine, lambda x:x)
2159 #------------------------------------------------------------
2160 -def get_branded_drugs():
2161 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description' 2162 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 2163 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
2164 #------------------------------------------------------------
2165 -def get_drug_by_brand(brand_name=None, preparation=None):
2166 args = {'brand': brand_name, 'prep': preparation} 2167 2168 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)' 2169 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 2170 2171 if len(rows) == 0: 2172 return None 2173 2174 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2175 #------------------------------------------------------------
2176 -def create_branded_drug(brand_name=None, preparation=None, return_existing=False):
2177 2178 if preparation is None: 2179 preparation = _('units') 2180 2181 if preparation.strip() == u'': 2182 preparation = _('units') 2183 2184 if return_existing: 2185 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation) 2186 if drug is not None: 2187 return drug 2188 2189 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk' 2190 args = {'brand': brand_name, 'prep': preparation} 2191 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 2192 2193 return cBrandedDrug(aPK_obj = rows[0]['pk'])
2194 #------------------------------------------------------------
2195 -def delete_branded_drug(brand=None):
2196 queries = [] 2197 args = {'pk': brand} 2198 2199 # delete components 2200 cmd = u""" 2201 DELETE FROM ref.lnk_substance2brand 2202 WHERE 2203 fk_brand = %(pk)s 2204 AND 2205 NOT EXISTS ( 2206 SELECT 1 2207 FROM clin.v_pat_substance_intake 2208 WHERE pk_brand = %(pk)s 2209 LIMIT 1 2210 ) 2211 """ 2212 queries.append({'cmd': cmd, 'args': args}) 2213 2214 # delete drug 2215 cmd = u""" 2216 DELETE FROM ref.branded_drug 2217 WHERE 2218 pk = %(pk)s 2219 AND 2220 NOT EXISTS ( 2221 SELECT 1 2222 FROM clin.v_pat_substance_intake 2223 WHERE pk_brand = %(pk)s 2224 LIMIT 1 2225 ) 2226 """ 2227 queries.append({'cmd': cmd, 'args': args}) 2228 2229 gmPG2.run_rw_queries(queries = queries)
2230 #============================================================ 2231 # main 2232 #------------------------------------------------------------ 2233 if __name__ == "__main__": 2234 2235 if len(sys.argv) < 2: 2236 sys.exit() 2237 2238 if sys.argv[1] != 'test': 2239 sys.exit() 2240 2241 from Gnumed.pycommon import gmLog2 2242 from Gnumed.pycommon import gmI18N 2243 from Gnumed.business import gmPerson 2244 2245 gmI18N.activate_locale() 2246 # gmDateTime.init() 2247 #--------------------------------------------------------
2248 - def test_MMI_interface():
2249 mmi = cGelbeListeWineInterface() 2250 print mmi 2251 print "interface definition:", mmi.version 2252 print "database versions: ", mmi.get_data_source_version()
2253 #--------------------------------------------------------
2254 - def test_MMI_file():
2255 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2]) 2256 for drug in mmi_file: 2257 print "-------------" 2258 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 2259 for stoff in drug['wirkstoffe']: 2260 print " Wirkstoff:", stoff 2261 raw_input() 2262 if mmi_file.has_unknown_fields is not None: 2263 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key 2264 for key in mmi_file.csv_fieldnames: 2265 print key, '->', drug[key] 2266 raw_input() 2267 mmi_file.close()
2268 #--------------------------------------------------------
2269 - def test_mmi_switch_to():
2270 mmi = cGelbeListeWineInterface() 2271 mmi.switch_to_frontend(blocking = False)
2272 #--------------------------------------------------------
2273 - def test_mmi_let_user_select_drugs():
2274 mmi = cGelbeListeWineInterface() 2275 mmi_file = mmi.__let_user_select_drugs() 2276 for drug in mmi_file: 2277 print "-------------" 2278 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 2279 for stoff in drug['wirkstoffe']: 2280 print " Wirkstoff:", stoff 2281 print drug 2282 mmi_file.close()
2283 #--------------------------------------------------------
2284 - def test_mmi_import_drugs():
2285 mmi = cGelbeListeWineInterface() 2286 mmi.import_drugs()
2287 #--------------------------------------------------------
2288 - def test_mmi_interaction_check():
2289 mmi = cGelbeListeInterface() 2290 print mmi 2291 print "interface definition:", mmi.version 2292 # Metoprolol + Hct vs Citalopram 2293 diclofenac = '7587712' 2294 phenprocoumon = '4421744' 2295 mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
2296 #-------------------------------------------------------- 2297 # FreeDiams 2298 #--------------------------------------------------------
2299 - def test_fd_switch_to():
2300 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12)) 2301 fd = cFreeDiamsInterface() 2302 fd.patient = gmPerson.gmCurrentPatient() 2303 # fd.switch_to_frontend(blocking = True) 2304 fd.import_fd2gm_file_as_drugs(filename = sys.argv[2])
2305 #--------------------------------------------------------
2306 - def test_fd_show_interactions():
2307 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12)) 2308 fd = cFreeDiamsInterface() 2309 fd.patient = gmPerson.gmCurrentPatient() 2310 fd.check_interactions(substances = fd.patient.get_emr().get_current_substance_intake(include_unapproved = True))
2311 #-------------------------------------------------------- 2312 # generic 2313 #--------------------------------------------------------
2314 - def test_create_substance_intake():
2315 drug = create_substance_intake ( 2316 pk_component = 2, 2317 encounter = 1, 2318 episode = 1 2319 ) 2320 print drug
2321 #--------------------------------------------------------
2322 - def test_show_components():
2323 drug = cBrandedDrug(aPK_obj = sys.argv[2]) 2324 print drug 2325 print drug.components
2326 #--------------------------------------------------------
2327 - def test_get_consumable_substances():
2328 for s in get_consumable_substances(): 2329 print s
2330 #-------------------------------------------------------- 2331 #-------------------------------------------------------- 2332 # MMI/Gelbe Liste 2333 #test_MMI_interface() 2334 #test_MMI_file() 2335 #test_mmi_switch_to() 2336 #test_mmi_let_user_select_drugs() 2337 #test_mmi_import_substances() 2338 #test_mmi_import_drugs() 2339 2340 # FreeDiams 2341 test_fd_switch_to() 2342 #test_fd_show_interactions() 2343 2344 # generic 2345 #test_interaction_check() 2346 #test_create_substance_intake() 2347 #test_show_components() 2348 #test_get_consumable_substances() 2349 #============================================================ 2350