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