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

Source Code for Module Gnumed.business.gmMedication

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