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