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

Source Code for Module Gnumed.business.gmMedication

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