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 
   5  """ 
   6  #============================================================ 
   7  __version__ = "$Revision: 1.21 $" 
   8  __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>" 
   9   
  10  import sys, logging, csv, codecs, os, re as regex, subprocess, decimal 
  11  from xml.etree import ElementTree as etree 
  12   
  13   
  14  if __name__ == '__main__': 
  15          sys.path.insert(0, '../../') 
  16  from Gnumed.pycommon import gmBusinessDBObject, gmPG2, gmShellAPI, gmTools 
  17  from Gnumed.pycommon import gmDispatcher, gmDateTime, gmHooks 
  18  from Gnumed.business import gmATC, gmAllergy 
  19   
  20   
  21  _log = logging.getLogger('gm.meds') 
  22  _log.info(__version__) 
  23   
  24  #============================================================ 
25 -def _on_substance_intake_modified():
26 """Always relates to the active patient.""" 27 gmHooks.run_hook_script(hook = u'after_substance_intake_modified')
28 29 gmDispatcher.connect(_on_substance_intake_modified, u'substance_intake_mod_db') 30 31 #============================================================
32 -def drug2renal_insufficiency_url(search_term=None):
33 34 if search_term is None: 35 return u'http://www.dosing.de' 36 37 terms = [] 38 names = [] 39 40 if isinstance(search_term, cBrandedDrug): 41 if search_term['atc_code'] is not None: 42 terms.append(search_term['atc_code']) 43 44 elif isinstance(search_term, cSubstanceIntakeEntry): 45 names.append(search_term['substance']) 46 if search_term['atc_brand'] is not None: 47 terms.append(search_term['atc_brand']) 48 if search_term['atc_substance'] is not None: 49 terms.append(search_term['atc_substance']) 50 51 elif search_term is not None: 52 names.append(u'%s' % search_term) 53 terms.extend(gmATC.text2atc(text = u'%s' % search_term, fuzzy = True)) 54 55 for name in names: 56 if name.endswith('e'): 57 terms.append(name[:-1]) 58 else: 59 terms.append(name) 60 61 #url_template = u'http://www.google.de/#q=site%%3Adosing.de+%s' 62 #url = url_template % u'+OR+'.join(terms) 63 64 url_template = u'http://www.google.de/search?hl=de&source=hp&q=site%%3Adosing.de+%s&btnG=Google-Suche' 65 url = url_template % u'+OR+'.join(terms) 66 67 _log.debug(u'renal insufficiency URL: %s', url) 68 69 return url
70 #============================================================ 71 # this should be in gmCoding.py
72 -def create_data_source(long_name=None, short_name=None, version=None, source=None, language=None):
73 74 args = { 75 'lname': long_name, 76 'sname': short_name, 77 'ver': version, 78 'src': source, 79 'lang': language 80 } 81 82 cmd = u"""select pk from ref.data_source where name_long = %(lname)s and name_short = %(sname)s and version = %(ver)s""" 83 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 84 if len(rows) > 0: 85 return rows[0]['pk'] 86 87 cmd = u""" 88 INSERT INTO ref.data_source (name_long, name_short, version, source, lang) 89 VALUES ( 90 %(lname)s, 91 %(sname)s, 92 %(ver)s, 93 %(src)s, 94 %(lang)s 95 ) 96 returning pk 97 """ 98 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 99 100 return rows[0]['pk']
101 #============================================================ 102 # wishlist: 103 # - --conf-file= for glwin.exe 104 # - wirkstoff: Konzentration auch in Multiprodukten 105 # - wirkstoff: ATC auch in Multiprodukten 106 # - Suche nach ATC per CLI 107
108 -class cGelbeListeCSVFile(object):
109 """Iterator over a Gelbe Liste/MMI v8.2 CSV file.""" 110 111 version = u'Gelbe Liste/MMI v8.2 CSV file interface' 112 default_transfer_file_windows = r"c:\rezept.txt" 113 #default_encoding = 'cp1252' 114 default_encoding = 'cp1250' 115 csv_fieldnames = [ 116 u'name', 117 u'packungsgroesse', # obsolete, use "packungsmenge" 118 u'darreichungsform', 119 u'packungstyp', 120 u'festbetrag', 121 u'avp', 122 u'hersteller', 123 u'rezepttext', 124 u'pzn', 125 u'status_vertrieb', 126 u'status_rezeptpflicht', 127 u'status_fachinfo', 128 u'btm', 129 u'atc', 130 u'anzahl_packungen', 131 u'zuzahlung_pro_packung', 132 u'einheit', 133 u'schedule_morgens', 134 u'schedule_mittags', 135 u'schedule_abends', 136 u'schedule_nachts', 137 u'status_dauermedikament', 138 u'status_hausliste', 139 u'status_negativliste', 140 u'ik_nummer', 141 u'status_rabattvertrag', 142 u'wirkstoffe', 143 u'wirkstoffmenge', 144 u'wirkstoffeinheit', 145 u'wirkstoffmenge_bezug', 146 u'wirkstoffmenge_bezugseinheit', 147 u'status_import', 148 u'status_lifestyle', 149 u'status_ausnahmeliste', 150 u'packungsmenge', 151 u'apothekenpflicht', 152 u'status_billigere_packung', 153 u'rezepttyp', 154 u'besonderes_arzneimittel', # Abstimmungsverfahren SGB-V 155 u't_rezept_pflicht', # Thalidomid-Rezept 156 u'erstattbares_medizinprodukt', 157 u'hilfsmittel', 158 u'hzv_rabattkennung', 159 u'hzv_preis' 160 ] 161 boolean_fields = [ 162 u'status_rezeptpflicht', 163 u'status_fachinfo', 164 u'btm', 165 u'status_dauermedikament', 166 u'status_hausliste', 167 u'status_negativliste', 168 u'status_rabattvertrag', 169 u'status_import', 170 u'status_lifestyle', 171 u'status_ausnahmeliste', 172 u'apothekenpflicht', 173 u'status_billigere_packung', 174 u'besonderes_arzneimittel', # Abstimmungsverfahren SGB-V 175 u't_rezept_pflicht', 176 u'erstattbares_medizinprodukt', 177 u'hilfsmittel' 178 ] 179 #--------------------------------------------------------
180 - def __init__(self, filename=None):
181 182 _log.info(cGelbeListeCSVFile.version) 183 184 self.filename = filename 185 if filename is None: 186 self.filename = cGelbeListeCSVFile.default_transfer_file_windows 187 188 _log.debug('reading Gelbe Liste/MMI drug data from [%s]', self.filename) 189 190 self.csv_file = codecs.open(filename = filename, mode = 'rUb', encoding = cGelbeListeCSVFile.default_encoding) 191 192 self.csv_lines = gmTools.unicode_csv_reader ( 193 self.csv_file, 194 fieldnames = cGelbeListeCSVFile.csv_fieldnames, 195 delimiter = ';', 196 quotechar = '"', 197 dict = True 198 )
199 #--------------------------------------------------------
200 - def __iter__(self):
201 return self
202 #--------------------------------------------------------
203 - def next(self):
204 line = self.csv_lines.next() 205 206 for field in cGelbeListeCSVFile.boolean_fields: 207 line[field] = (line[field].strip() == u'T') 208 209 # split field "Wirkstoff" by ";" 210 if line['wirkstoffe'].strip() == u'': 211 line['wirkstoffe'] = [] 212 else: 213 line['wirkstoffe'] = [ wirkstoff.strip() for wirkstoff in line['wirkstoffe'].split(u';') ] 214 215 return line
216 #--------------------------------------------------------
217 - def close(self, truncate=True):
218 try: self.csv_file.close() 219 except: pass 220 221 if truncate: 222 try: os.open(self.filename, 'wb').close 223 except: pass
224 #--------------------------------------------------------
225 - def _get_has_unknown_fields(self):
227 228 has_unknown_fields = property(_get_has_unknown_fields, lambda x:x)
229 #============================================================
230 -class cDrugDataSourceInterface(object):
231 232 #--------------------------------------------------------
233 - def __init__(self):
234 self.patient = None 235 self.custom_path_to_binary = None
236 #--------------------------------------------------------
237 - def get_data_source_version(self):
238 raise NotImplementedError
239 #--------------------------------------------------------
240 - def create_data_source_entry(self):
241 raise NotImplementedError
242 #--------------------------------------------------------
243 - def switch_to_frontend(self, blocking=False):
244 raise NotImplementedError
245 #--------------------------------------------------------
246 - def select_drugs(self):
247 raise NotImplementedError
248 #--------------------------------------------------------
249 - def import_drugs(self):
250 raise NotImplementedError
251 #--------------------------------------------------------
252 - def check_drug_interactions(self, drug_ids_list=None, substances=None):
253 raise NotImplementedError
254 #--------------------------------------------------------
255 - def show_info_on_drug(self, drug=None):
256 raise NotImplementedError
257 #--------------------------------------------------------
258 - def show_info_on_substance(self, substance=None):
259 raise NotImplementedError
260 #============================================================
261 -class cFreeDiamsInterface(cDrugDataSourceInterface):
262 263 version = u'FreeDiams v0.5.0 interface' 264 default_encoding = 'utf8' 265 default_dob_format = '%Y/%m/%d' 266 267 map_gender2mf = { 268 'm': u'M', 269 'f': u'F', 270 'tf': u'H', 271 'tm': u'H', 272 'h': u'H' 273 } 274 #--------------------------------------------------------
275 - def __init__(self):
276 cDrugDataSourceInterface.__init__(self) 277 _log.info(cFreeDiamsInterface.version) 278 279 paths = gmTools.gmPaths() 280 self.__gm2fd_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2freediams.xml') 281 self.__fd2gm_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'freediams2gm.xml') 282 self.__fd4gm_config_file = os.path.join(paths.home_dir, '.gnumed', 'freediams4gm.conf') 283 284 self.path_to_binary = None 285 self.__detect_binary()
286 #--------------------------------------------------------
287 - def get_data_source_version(self):
288 # ~/.freediams/config.ini: [License] -> AcceptedVersion=.... 289 290 if not self.__detect_binary(): 291 return False 292 293 freediams = subprocess.Popen ( 294 args = u'--version', # --version or -version or -v 295 executable = self.path_to_binary, 296 stdout = subprocess.PIPE, 297 stderr = subprocess.PIPE, 298 # close_fds = True, # Windows can't do that in conjunction with stdout/stderr = ... :-( 299 universal_newlines = True 300 ) 301 data, errors = freediams.communicate() 302 version = regex.search('FreeDiams\s\d.\d.\d', data).group().split()[1] 303 _log.debug('FreeDiams %s', version) 304 305 return version
306 #--------------------------------------------------------
307 - def create_data_source_entry(self):
308 return create_data_source ( 309 long_name = u'"FreeDiams" Drug Database Frontend', 310 short_name = u'FreeDiams', 311 version = self.get_data_source_version(), 312 source = u'http://ericmaeker.fr/FreeMedForms/di-manual/index.html', 313 language = u'fr' # actually to be multi-locale 314 )
315 #--------------------------------------------------------
316 - def switch_to_frontend(self, blocking=False):
317 """http://ericmaeker.fr/FreeMedForms/di-manual/en/html/ligne_commandes.html""" 318 319 if not self.__detect_binary(): 320 return False 321 322 self.__create_gm2fd_file() 323 open(self.__fd2gm_filename, 'wb').close() 324 325 args = u'--exchange-in="%s"' % (self.__gm2fd_filename) 326 cmd = r'%s %s' % (self.path_to_binary, args) 327 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking): 328 _log.error('problem switching to the FreeDiams drug database') 329 return False 330 331 return True
332 #--------------------------------------------------------
333 - def select_drugs(self):
334 self.switch_to_frontend()
335 #--------------------------------------------------------
336 - def import_drugs(self):
337 """FreeDiams ONLY use CIS. 338 339 CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel). 340 CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS. 341 AFSSAPS is the French FDA. 342 343 CIP stands for Unique Presentation Identifier (eg 30 pills plaq) 344 CIP if you want to specify the packaging of the drug (30 pills 345 thermoformed tablet...) -- actually not really usefull for french 346 doctors. 347 # .external_code_type: u'FR-CIS' 348 # .external_cod: the CIS value 349 """ 350 self.switch_to_frontend(blocking = True) 351 self.import_fd2gm_file()
352 #--------------------------------------------------------
353 - def check_drug_interactions(self, drug_ids_list=None, substances=None):
354 355 if substances is None: 356 return 357 if len(substances) < 2: 358 return 359 drug_ids_list = [ (s.external_code_type, s.external_code) for s in substances ] 360 drug_ids_list = [ (code_value, code_type) for code_type, code_value in drug_ids_list if (code_value is not None)] 361 362 if drug_ids_list < 2: 363 return 364 365 self.__create_prescription_file(drug_ids_list = drug_ids_list) 366 self.switch_to_frontend(blocking = False)
367 #--------------------------------------------------------
368 - def show_info_on_drug(self, drug=None):
369 if drug is None: 370 return 371 self.switch_to_frontend()
372 #--------------------------------------------------------
373 - def show_info_on_substance(self, substance=None):
374 if substance is None: 375 return 376 self.__create_prescription_file ( 377 drug_ids_list = [(substance.external_code, substance.external_code_type)] 378 ) 379 self.switch_to_frontend()
380 #-------------------------------------------------------- 381 # internal helpers 382 #--------------------------------------------------------
383 - def __detect_binary(self):
384 385 if self.path_to_binary is not None: 386 return True 387 388 found, cmd = gmShellAPI.find_first_binary(binaries = [ 389 r'/usr/bin/freediams', 390 r'freediams', 391 r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams', 392 r'c:\programs\freediams\freediams.exe', 393 r'freediams.exe' 394 ]) 395 396 if found: 397 self.path_to_binary = cmd 398 return True 399 400 try: 401 self.custom_path_to_binary 402 except AttributeError: 403 _log.error('cannot find FreeDiams binary, no custom path set') 404 return False 405 406 found, cmd = gmShellAPI.detect_external_binary(binary = self.custom_path_to_binary) 407 if found: 408 self.path_to_binary = cmd 409 return True 410 411 _log.error('cannot find FreeDiams binary') 412 return False
413 #--------------------------------------------------------
414 - def __create_prescription_file(self, drug_ids_list=None):
415 416 # FIXME: pass in more data 417 418 xml = u"""<?xml version = "1.0" encoding = "UTF-8"?> 419 420 <FreeDiams> 421 <DrugsDatabaseName>%s</DrugsDatabaseName> 422 <FullPrescription version="0.4.0"> 423 424 %s 425 426 </FullPrescription> 427 </FreeDiams> 428 """ 429 drug_snippet = u"""<Prescription> 430 <OnlyForTest>True</OnlyForTest> 431 <Drug_UID>%s</Drug_UID> 432 <!-- <Drug_UID_type>%s</Drug_UID_type> --> 433 </Prescription>""" 434 435 xml_file = codecs.open(self.__fd2gm_filename, 'wb', 'utf8') 436 437 last_db_id = u'CA_HCDPD' 438 drug_snippets = [] 439 for drug_id, db_id in drug_ids_list: 440 if db_id is not None: 441 last_db_id = db_id.replace(u'FreeDiams::', u'') 442 drug_snippets.append(drug_snippet % (drug_id, last_db_id)) 443 444 xml_file.write(xml % ( 445 last_db_id, 446 u'\n\t\t'.join(drug_snippets) 447 )) 448 449 xml_file.close()
450 #--------------------------------------------------------
451 - def __create_gm2fd_file(self):
452 453 xml_file = codecs.open(self.__gm2fd_filename, 'wb', 'utf8') 454 455 xml = u"""<?xml version="1.0" encoding="UTF-8"?> 456 457 <!-- Eric says the order of same-level nodes does not matter. --> 458 459 <FreeDiams_In version="0.5.0"> 460 <EMR name="GNUmed" uid="unused"/> 461 <ConfigFile value="%s"/> 462 <ExchangeOut value="%s" format="xml"/> <!-- should perhaps better be html_xml ? --> 463 <!-- <DrugsDatabase uid="can be set to a specific DB"/> --> 464 <Ui editmode="select-only" blockPatientDatas="1"/> 465 %%s 466 </FreeDiams_In> 467 468 <!-- 469 # FIXME: search by LOINC code and add (as soon as supported by FreeDiams ...) 470 <Creatinine value="12" unit="mg/l or mmol/l"/> 471 <Weight value="70" unit="kg or pd" /> 472 <Height value="170" unit="cm or "/> 473 <ICD10 value="J11.0;A22;Z23"/> 474 --> 475 """ % ( 476 self.__fd4gm_config_file, 477 self.__fd2gm_filename 478 ) 479 480 if self.patient is None: 481 xml_file.write(xml % u'') 482 xml_file.close() 483 return 484 485 name = self.patient.get_active_name() 486 if self.patient['dob'] is None: 487 dob = u'' 488 else: 489 dob = self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format) 490 491 emr = self.patient.get_emr() 492 allgs = emr.get_allergies() 493 atc_allgs = [ 494 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'allergy')) 495 ] 496 atc_sens = [ 497 a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'sensitivity')) 498 ] 499 inn_allgs = [ a['allergene'] for a in allgs if a['type'] == u'allergy' ] 500 inn_sens = [ a['allergene'] for a in allgs if a['type'] == u'sensitivity' ] 501 # this is rather fragile: FreeDiams won't know what type of UID this is 502 # (but it will assume it is of the type of the drug database in use) 503 uid_allgs = [ 504 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'allergy')) 505 ] 506 uid_sens = [ 507 a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'sensitivity')) 508 ] 509 510 patient_xml = u"""<Patient> 511 <Identity 512 lastnames="%s" 513 firstnames="%s" 514 uid="%s" 515 dob="%s" 516 gender="%s" 517 /> 518 <ATCAllergies value="%s"/> 519 <ATCIntolerances value="%s"/> 520 521 <InnAllergies value="%s"/> 522 <InnIntolerances value="%s"/> 523 524 <DrugsUidAllergies value="%s"/> 525 <DrugsUidIntolerances value="%s"/> 526 </Patient> 527 """ % ( 528 name['lastnames'], 529 name['firstnames'], 530 self.patient.ID, 531 dob, 532 cFreeDiamsInterface.map_gender2mf[self.patient['gender']], 533 u';'.join(atc_allgs), 534 u';'.join(atc_sens), 535 u';'.join(inn_allgs), 536 u';'.join(inn_sens), 537 u';'.join(uid_allgs), 538 u';'.join(uid_sens) 539 ) 540 541 xml_file.write(xml % patient_xml) 542 xml_file.close()
543 #--------------------------------------------------------
544 - def import_fd2gm_file(self):
545 """ 546 If returning textual prescriptions (say, drugs which FreeDiams 547 did not know) then "IsTextual" will be True and UID will be -1. 548 """ 549 fd2gm_xml = etree.ElementTree() 550 fd2gm_xml.parse(self.__fd2gm_filename) 551 552 data_src_pk = self.create_data_source_entry() 553 554 db_id = fd2gm_xml.find('DrugsDatabaseName').text.strip() 555 drugs = fd2gm_xml.findall('FullPrescription/Prescription') 556 for drug in drugs: 557 drug_name = drug.find('DrugName').text.replace(', )', ')').strip() 558 drug_uid = drug.find('Drug_UID').text.strip() 559 drug_form = drug.find('DrugForm').text.strip() 560 #drug_atc = drug.find('DrugATC').text.strip() # asked Eric to include 561 562 new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True) 563 # update fields 564 new_drug['is_fake_brand'] = False 565 #new_drug['atc'] = drug_atc 566 new_drug['external_code_type'] = u'FreeDiams::%s' % db_id 567 new_drug['external_code'] = drug_uid 568 new_drug['pk_data_source'] = data_src_pk 569 new_drug.save() 570 571 components = drug.getiterator('Composition') 572 for comp in components: 573 574 subst = comp.attrib['molecularName'].strip() 575 576 amount = regex.match(r'\d+[.,]{0,1}\d*', comp.attrib['strenght'].strip()) # sic, typo 577 if amount is None: 578 amount = 99999 579 else: 580 amount = amount.group() 581 582 unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', comp.attrib['strenght'].strip()).strip() # sic, typo 583 if unit == u'': 584 unit = u'*?*' 585 586 inn = comp.attrib['inn'].strip() 587 if inn != u'': 588 create_consumable_substance(substance = inn, atc = None, amount = amount, unit = unit) 589 if subst == u'': 590 subst = inn 591 592 new_drug.add_component(substance = subst, atc = None, amount = amount, unit = unit)
593 #============================================================
594 -class cGelbeListeWindowsInterface(cDrugDataSourceInterface):
595 """Support v8.2 CSV file interface only.""" 596 597 version = u'Gelbe Liste/MMI v8.2 interface' 598 default_encoding = 'cp1250' 599 bdt_line_template = u'%03d6210#%s\r\n' # Medikament verordnet auf Kassenrezept 600 bdt_line_base_length = 8 601 #--------------------------------------------------------
602 - def __init__(self):
603 604 cDrugDataSourceInterface.__init__(self) 605 606 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version) 607 608 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe' 609 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY' 610 611 paths = gmTools.gmPaths() 612 613 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt') 614 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp') 615 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt') 616 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt' 617 618 self.__data_date = None 619 self.__online_update_date = None
620 621 # use adjusted config.dat 622 #--------------------------------------------------------
623 - def get_data_source_version(self, force_reload=False):
624 625 if self.__data_date is not None: 626 if not force_reload: 627 return { 628 'data': self.__data_date, 629 'online_update': self.__online_update_date 630 } 631 632 open(self.data_date_filename, 'wb').close() 633 634 cmd = u'%s -DATADATE' % self.path_to_binary 635 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True): 636 _log.error('problem querying the MMI drug database for version information') 637 self.__data_date = None 638 self.__online_update_date = None 639 return { 640 'data': u'?', 641 'online_update': u'?' 642 } 643 644 try: 645 version_file = open(self.data_date_filename, 'rU') 646 except StandardError: 647 _log.error('problem querying the MMI drug database for version information') 648 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename) 649 self.__data_date = None 650 self.__online_update_date = None 651 return { 652 'data': u'?', 653 'online_update': u'?' 654 } 655 656 self.__data_date = version_file.readline()[:10] 657 self.__online_update_date = version_file.readline()[:10] 658 version_file.close() 659 660 return { 661 'data': self.__data_date, 662 'online_update': self.__online_update_date 663 }
664 #--------------------------------------------------------
665 - def create_data_source_entry(self):
666 versions = self.get_data_source_version() 667 668 return create_data_source ( 669 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)', 670 short_name = u'GL/MMI', 671 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']), 672 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg', 673 language = u'de' 674 )
675 #--------------------------------------------------------
676 - def switch_to_frontend(self, blocking=False, cmd=None):
677 678 # must make sure csv file exists 679 open(self.default_csv_filename, 'wb').close() 680 681 if cmd is None: 682 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg 683 684 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking): 685 _log.error('problem switching to the MMI drug database') 686 # apparently on the first call MMI does not 687 # consistently return 0 on success 688 # return False 689 690 return True
691 #--------------------------------------------------------
692 - def select_drugs(self, filename=None):
693 694 # better to clean up interactions file 695 open(self.interactions_filename, 'wb').close() 696 697 if not self.switch_to_frontend(blocking = True): 698 return None 699 700 return cGelbeListeCSVFile(filename = self.default_csv_filename)
701 #--------------------------------------------------------
702 - def import_drugs_as_substances(self):
703 704 selected_drugs = self.select_drugs() 705 if selected_drugs is None: 706 return None 707 708 new_substances = [] 709 710 for drug in selected_drugs: 711 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 712 if len(drug['wirkstoffe']) == 1: 713 atc = drug['atc'] 714 for wirkstoff in drug['wirkstoffe']: 715 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit)) 716 717 selected_drugs.close() 718 719 return new_substances
720 #--------------------------------------------------------
721 - def import_drugs(self):
722 723 selected_drugs = self.select_drugs() 724 if selected_drugs is None: 725 return None 726 727 data_src_pk = self.create_data_source_entry() 728 729 new_drugs = [] 730 new_substances = [] 731 732 for entry in selected_drugs: 733 734 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform']) 735 736 if entry[u'hilfsmittel']: 737 _log.debug('skipping Hilfsmittel') 738 continue 739 740 if entry[u'erstattbares_medizinprodukt']: 741 _log.debug('skipping sonstiges Medizinprodukt') 742 continue 743 744 # create branded drug (or get it if it already exists) 745 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform']) 746 if drug is None: 747 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform']) 748 new_drugs.append(drug) 749 750 # update fields 751 drug['is_fake'] = False 752 drug['atc_code'] = entry['atc'] 753 drug['external_code_type'] = u'DE-PZN' 754 drug['external_code'] = entry['pzn'] 755 drug['fk_data_source'] = data_src_pk 756 drug.save() 757 758 # add components to brand 759 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 760 if len(entry['wirkstoffe']) == 1: 761 atc = entry['atc'] 762 for wirkstoff in entry['wirkstoffe']: 763 drug.add_component(substance = wirkstoff, atc = atc) 764 765 # create as consumable substances, too 766 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 767 if len(entry['wirkstoffe']) == 1: 768 atc = entry['atc'] 769 for wirkstoff in entry['wirkstoffe']: 770 new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit)) 771 772 return new_drugs, new_substances
773 #--------------------------------------------------------
774 - def check_drug_interactions(self, drug_ids_list=None, substances=None):
775 """For this to work the BDT interaction check must be configured in the MMI.""" 776 777 if drug_ids_list is None: 778 if substances is None: 779 return 780 if len(substances) < 2: 781 return 782 drug_ids_list = [ (s.external_code_type, s.external_code) for s in substances ] 783 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')] 784 785 else: 786 if len(drug_ids_list) < 2: 787 return 788 789 if drug_ids_list < 2: 790 return 791 792 bdt_file = codecs.open(filename = self.interactions_filename, mode = 'wb', encoding = cGelbeListeWindowsInterface.default_encoding) 793 794 for pzn in drug_ids_list: 795 pzn = pzn.strip() 796 lng = cGelbeListeWindowsInterface.bdt_line_base_length + len(pzn) 797 bdt_file.write(cGelbeListeWindowsInterface.bdt_line_template % (lng, pzn)) 798 799 bdt_file.close() 800 801 self.switch_to_frontend(blocking = False)
802 #--------------------------------------------------------
803 - def show_info_on_drug(self, drug=None):
804 self.switch_to_frontend(blocking = True)
805 #--------------------------------------------------------
806 - def show_info_on_substance(self, substance=None):
807 808 cmd = None 809 810 if substance.external_code_type == u'DE-PZN': 811 cmd = u'%s -PZN %s' % (self.path_to_binary, substance.external_code) 812 813 if cmd is None: 814 name = gmTools.coalesce ( 815 substance['brand'], 816 substance['substance'] 817 ) 818 cmd = u'%s -NAME %s' % (self.path_to_binary, name) 819 820 # better to clean up interactions file 821 open(self.interactions_filename, 'wb').close() 822 823 self.switch_to_frontend(cmd = cmd)
824 #============================================================
825 -class cGelbeListeWineInterface(cGelbeListeWindowsInterface):
826
827 - def __init__(self):
828 cGelbeListeWindowsInterface.__init__(self) 829 830 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version) 831 832 # FIXME: if -CLOSETOTRAY is used GNUmed cannot detect the end of MMI 833 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"' 834 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"' 835 836 paths = gmTools.gmPaths() 837 838 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv') 839 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv' 840 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt') 841 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
842 #============================================================
843 -class cIfapInterface(cDrugDataSourceInterface):
844 """empirical CSV interface""" 845
846 - def __init__(self):
847 pass
848
849 - def print_transfer_file(self, filename=None):
850 851 try: 852 csv_file = open(filename, 'rb') # FIXME: encoding ? 853 except: 854 _log.exception('cannot access [%s]', filename) 855 csv_file = None 856 857 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split() 858 859 if csv_file is None: 860 return False 861 862 csv_lines = csv.DictReader ( 863 csv_file, 864 fieldnames = field_names, 865 delimiter = ';' 866 ) 867 868 for line in csv_lines: 869 print "--------------------------------------------------------------------"[:31] 870 for key in field_names: 871 tmp = ('%s ' % key)[:30] 872 print '%s: %s' % (tmp, line[key]) 873 874 csv_file.close()
875 876 # narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % ( 877 # line['Packungszahl'].strip(), 878 # line['Handelsname'].strip(), 879 # line['Form'].strip(), 880 # line[u'Packungsgr\xf6\xdfe'].strip(), 881 # line['Abpackungsmenge'].strip(), 882 # line['Einheit'].strip(), 883 # line['Hersteller'].strip(), 884 # line['PZN'].strip() 885 # ) 886 #============================================================ 887 drug_data_source_interfaces = { 888 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface, 889 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface, 890 'FreeDiams (France, US, Canada)': cFreeDiamsInterface 891 } 892 #============================================================ 893 #============================================================ 894 # substances in use across all patients 895 #------------------------------------------------------------ 896 _SQL_get_consumable_substance = u""" 897 SELECT *, xmin 898 FROM ref.consumable_substance 899 WHERE %s 900 """ 901
902 -class cConsumableSubstance(gmBusinessDBObject.cBusinessDBObject):
903 904 _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s" 905 _cmds_store_payload = [ 906 u"""UPDATE ref.consumable_substance SET 907 description = %(description)s, 908 atc_code = gm.nullify_empty_string(%(atc_code)s), 909 amount = %(amount)s, 910 unit = gm.nullify_empty_string(%(unit)s) 911 WHERE 912 pk = %(pk)s 913 AND 914 xmin = %(xmin)s 915 AND 916 -- must not currently be used with a patient directly 917 NOT EXISTS ( 918 SELECT 1 919 FROM clin.substance_intake 920 WHERE 921 fk_drug_component IS NULL 922 AND 923 fk_substance = %(pk)s 924 LIMIT 1 925 ) 926 AND 927 -- must not currently be used with a patient indirectly, either 928 NOT EXISTS ( 929 SELECT 1 930 FROM clin.substance_intake 931 WHERE 932 fk_drug_component IS NOT NULL 933 AND 934 fk_drug_component = ( 935 SELECT r_ls2b.pk 936 FROM ref.lnk_substance2brand r_ls2b 937 WHERE fk_substance = %(pk)s 938 ) 939 LIMIT 1 940 ) 941 -- -- must not currently be used with a branded drug 942 -- -- (but this would make it rather hard fixing branded drugs which contain only this substance) 943 -- NOT EXISTS ( 944 -- SELECT 1 945 -- FROM ref.lnk_substance2brand 946 -- WHERE fk_substance = %(pk)s 947 -- LIMIT 1 948 -- ) 949 RETURNING 950 xmin 951 """ 952 ] 953 _updatable_fields = [ 954 u'description', 955 u'atc_code', 956 u'amount', 957 u'unit' 958 ] 959 #--------------------------------------------------------
960 - def save_payload(self, conn=None):
961 success, data = super(self.__class__, self).save_payload(conn = conn) 962 963 if not success: 964 return (success, data) 965 966 if self._payload[self._idx['atc_code']] is not None: 967 atc = self._payload[self._idx['atc_code']].strip() 968 if atc != u'': 969 gmATC.propagate_atc ( 970 substance = self._payload[self._idx['description']].strip(), 971 atc = atc 972 ) 973 974 return (success, data)
975 #-------------------------------------------------------- 976 # properties 977 #--------------------------------------------------------
978 - def _get_is_in_use_by_patients(self):
979 cmd = u""" 980 SELECT 981 EXISTS ( 982 SELECT 1 983 FROM clin.substance_intake 984 WHERE 985 fk_drug_component IS NULL 986 AND 987 fk_substance = %(pk)s 988 LIMIT 1 989 ) OR EXISTS ( 990 SELECT 1 991 FROM clin.substance_intake 992 WHERE 993 fk_drug_component IS NOT NULL 994 AND 995 fk_drug_component = ( 996 SELECT r_ls2b.pk 997 FROM ref.lnk_substance2brand r_ls2b 998 WHERE fk_substance = %(pk)s 999 ) 1000 LIMIT 1 1001 )""" 1002 args = {'pk': self.pk_obj} 1003 1004 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1005 return rows[0][0]
1006 1007 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x) 1008 #--------------------------------------------------------
1009 - def _get_is_drug_component(self):
1010 cmd = u""" 1011 SELECT EXISTS ( 1012 SELECT 1 1013 FROM ref.lnk_substance2brand 1014 WHERE fk_substance = %(pk)s 1015 LIMIT 1 1016 )""" 1017 args = {'pk': self.pk_obj} 1018 1019 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1020 return rows[0][0]
1021 1022 is_drug_component = property(_get_is_drug_component, lambda x:x)
1023 #------------------------------------------------------------
1024 -def get_consumable_substances(order_by=None):
1025 if order_by is None: 1026 order_by = u'true' 1027 else: 1028 order_by = u'true ORDER BY %s' % order_by 1029 cmd = _SQL_get_consumable_substance % order_by 1030 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 1031 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
1032 #------------------------------------------------------------
1033 -def create_consumable_substance(substance=None, atc=None, amount=None, unit=None):
1034 1035 substance = substance 1036 if atc is not None: 1037 atc = atc.strip() 1038 1039 args = { 1040 'desc': substance.strip(), 1041 'amount': decimal.Decimal(amount), 1042 'unit': unit.strip(), 1043 'atc': atc 1044 } 1045 cmd = u""" 1046 SELECT pk FROM ref.consumable_substance 1047 WHERE 1048 lower(description) = lower(%(desc)s) 1049 AND 1050 amount = %(amount)s 1051 AND 1052 unit = %(unit)s 1053 """ 1054 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 1055 1056 if len(rows) == 0: 1057 cmd = u""" 1058 INSERT INTO ref.consumable_substance (description, atc_code, amount, unit) VALUES ( 1059 %(desc)s, 1060 gm.nullify_empty_string(%(atc)s), 1061 %(amount)s, 1062 gm.nullify_empty_string(%(unit)s) 1063 ) RETURNING pk""" 1064 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 1065 1066 gmATC.propagate_atc(substance = substance, atc = atc) 1067 1068 return cConsumableSubstance(aPK_obj = rows[0]['pk'])
1069 #------------------------------------------------------------
1070 -def delete_consumable_substance(substance=None):
1071 args = {'pk': substance} 1072 cmd = u""" 1073 DELETE FROM ref.consumable_substance 1074 WHERE 1075 pk = %(pk)s 1076 AND 1077 1078 -- must not currently be used with a patient 1079 NOT EXISTS ( 1080 SELECT 1 1081 FROM clin.v_pat_substance_intake 1082 WHERE pk_substance = %(pk)s 1083 LIMIT 1 1084 ) 1085 AND 1086 1087 -- must not currently be used with a branded drug 1088 NOT EXISTS ( 1089 SELECT 1 1090 FROM ref.lnk_substance2brand 1091 WHERE fk_substance = %(pk)s 1092 LIMIT 1 1093 )""" 1094 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1095 return True
1096 #============================================================
1097 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
1098 """Represents a substance currently taken by a patient.""" 1099 1100 _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s" 1101 _cmds_store_payload = [ 1102 u"""UPDATE clin.substance_intake SET 1103 clin_when = %(started)s, 1104 discontinued = %(discontinued)s, 1105 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s), 1106 preparation = %(preparation)s, 1107 schedule = gm.nullify_empty_string(%(schedule)s), 1108 aim = gm.nullify_empty_string(%(aim)s), 1109 narrative = gm.nullify_empty_string(%(notes)s), 1110 intake_is_approved_of = %(intake_is_approved_of)s, 1111 1112 -- is_long_term = %(is_long_term)s, 1113 is_long_term = ( 1114 case 1115 when ( 1116 (%(is_long_term)s is False) 1117 and 1118 (%(duration)s is NULL) 1119 ) is True then null 1120 else %(is_long_term)s 1121 end 1122 )::boolean, 1123 duration = ( 1124 case 1125 when %(is_long_term)s is True then null 1126 else %(duration)s 1127 end 1128 )::interval, 1129 1130 fk_drug_component = %(pk_drug_component)s, 1131 fk_substance = %(pk_substance)s, 1132 fk_episode = %(pk_episode)s 1133 WHERE 1134 pk = %(pk_substance_intake)s 1135 AND 1136 xmin = %(xmin_substance_intake)s 1137 RETURNING 1138 xmin as xmin_substance_intake 1139 """ 1140 ] 1141 _updatable_fields = [ 1142 u'started', 1143 u'discontinued', 1144 u'discontinue_reason', 1145 u'preparation', 1146 u'intake_is_approved_of', 1147 u'schedule', 1148 u'duration', 1149 u'aim', 1150 u'is_long_term', 1151 u'notes', 1152 u'pk_drug_component', 1153 u'pk_substance', 1154 u'pk_episode' 1155 ] 1156 #--------------------------------------------------------
1157 - def format(self, left_margin=0, date_format='%Y-%m-%d'):
1158 1159 if self._payload[self._idx['duration']] is None: 1160 duration = gmTools.bool2subst ( 1161 self._payload[self._idx['is_long_term']], 1162 _('long-term'), 1163 _('short-term'), 1164 _('?short-term') 1165 ) 1166 else: 1167 duration = gmDateTime.format_interval ( 1168 self._payload[self._idx['duration']], 1169 accuracy_wanted = gmDateTime.acc_days 1170 ) 1171 1172 line = u'%s%s (%s %s): %s %s%s %s (%s)' % ( 1173 u' ' * left_margin, 1174 self._payload[self._idx['started']].strftime(date_format), 1175 gmTools.u_right_arrow, 1176 duration, 1177 self._payload[self._idx['substance']], 1178 self._payload[self._idx['amount']], 1179 self._payload[self._idx['unit']], 1180 self._payload[self._idx['preparation']], 1181 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing')) 1182 ) 1183 1184 return line
1185 #--------------------------------------------------------
1186 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
1187 allg = gmAllergy.create_allergy ( 1188 allergene = self._payload[self._idx['substance']], 1189 allg_type = allergy_type, 1190 episode_id = self._payload[self._idx['pk_episode']], 1191 encounter_id = encounter_id 1192 ) 1193 allg['substance'] = gmTools.coalesce ( 1194 self._payload[self._idx['brand']], 1195 self._payload[self._idx['substance']] 1196 ) 1197 allg['reaction'] = self._payload[self._idx['discontinue_reason']] 1198 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']]) 1199 if self._payload[self._idx['external_code_brand']] is not None: 1200 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']]) 1201 allg['allergene'] = self._payload[self._idx['substance']] 1202 comps = [ c['substance'] for c in self.containing_drug.components ] 1203 if len(comps) == 0: 1204 allg['generics'] = self._payload[self._idx['substance']] 1205 else: 1206 allg['generics'] = u'; '.join(comps) 1207 1208 allg.save() 1209 return allg
1210 #-------------------------------------------------------- 1211 # properties 1212 #--------------------------------------------------------
1213 - def _get_ddd(self):
1214 1215 try: self.__ddd 1216 except AttributeError: self.__ddd = None 1217 1218 if self.__ddd is not None: 1219 return self.__ddd 1220 1221 if self._payload[self._idx['atc_substance']] is not None: 1222 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']]) 1223 if len(ddd) != 0: 1224 self.__ddd = ddd[0] 1225 else: 1226 if self._payload[self._idx['atc_brand']] is not None: 1227 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']]) 1228 if len(ddd) != 0: 1229 self.__ddd = ddd[0] 1230 1231 return self.__ddd
1232 1233 ddd = property(_get_ddd, lambda x:x) 1234 #--------------------------------------------------------
1235 - def _get_external_code(self):
1236 drug = self.containing_drug 1237 1238 if drug is None: 1239 return None 1240 1241 return drug.external_code
1242 1243 external_code = property(_get_external_code, lambda x:x) 1244 #--------------------------------------------------------
1245 - def _get_external_code_type(self):
1246 drug = self.containing_drug 1247 1248 if drug is None: 1249 return None 1250 1251 return drug.external_code_type
1252 1253 external_code_type = property(_get_external_code_type, lambda x:x) 1254 #--------------------------------------------------------
1255 - def _get_containing_drug(self):
1256 if self._payload[self._idx['pk_brand']] is None: 1257 return None 1258 1259 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1260 1261 containing_drug = property(_get_containing_drug, lambda x:x) 1262 #--------------------------------------------------------
1263 - def _get_parsed_schedule(self):
1264 tests = [ 1265 # lead, trail 1266 ' 1-1-1-1 ', 1267 # leading dose 1268 '1-1-1-1', 1269 '22-1-1-1', 1270 '1/3-1-1-1', 1271 '/4-1-1-1' 1272 ] 1273 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}$" 1274 for test in tests: 1275 print test.strip(), ":", regex.match(pattern, test.strip())
1276 #------------------------------------------------------------
1277 -def create_substance_intake(pk_substance=None, pk_component=None, preparation=None, encounter=None, episode=None):
1278 1279 args = { 1280 'enc': encounter, 1281 'epi': episode, 1282 'comp': pk_component, 1283 'subst': pk_substance, 1284 'prep': preparation 1285 } 1286 1287 if pk_component is None: 1288 cmd = u""" 1289 INSERT INTO clin.substance_intake ( 1290 fk_encounter, 1291 fk_episode, 1292 intake_is_approved_of, 1293 fk_substance, 1294 preparation 1295 ) VALUES ( 1296 %(enc)s, 1297 %(epi)s, 1298 False, 1299 %(subst)s, 1300 %(prep)s 1301 ) 1302 RETURNING pk""" 1303 else: 1304 cmd = u""" 1305 INSERT INTO clin.substance_intake ( 1306 fk_encounter, 1307 fk_episode, 1308 intake_is_approved_of, 1309 fk_drug_component 1310 ) VALUES ( 1311 %(enc)s, 1312 %(epi)s, 1313 False, 1314 %(comp)s 1315 ) 1316 RETURNING pk""" 1317 1318 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 1319 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
1320 #------------------------------------------------------------
1321 -def create_substance_intake_old(substance=None, drug_component=None, atc=None, encounter=None, episode=None, preparation=None, amount=None, unit=None):
1322 1323 args = { 1324 'enc': encounter, 1325 'epi': episode, 1326 'prep': preparation, 1327 'comp': drug_component, 1328 'subst': create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit)['pk'] 1329 } 1330 1331 if drug_component is None: 1332 cmd = u""" 1333 INSERT INTO clin.substance_intake ( 1334 fk_encounter, 1335 fk_episode, 1336 intake_is_approved_of, 1337 fk_substance, 1338 preparation 1339 ) VALUES ( 1340 %(enc)s, 1341 %(epi)s, 1342 False, 1343 %(subst)s, 1344 gm.nullify_empty_string(%(prep)s) 1345 ) 1346 RETURNING pk""" 1347 else: 1348 cmd = u""" 1349 INSERT INTO clin.substance_intake ( 1350 fk_encounter, 1351 fk_episode, 1352 intake_is_approved_of. 1353 fk_drug_component 1354 ) VALUES ( 1355 %(enc)s, 1356 %(epi)s, 1357 False, 1358 %(comp)s 1359 ) 1360 RETURNING pk""" 1361 1362 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 1363 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
1364 #------------------------------------------------------------
1365 -def delete_substance_intake(substance=None):
1366 cmd = u'delete from clin.substance_intake where pk = %(pk)s' 1367 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
1368 #------------------------------------------------------------
1369 -def format_substance_intake_notes(emr=None, output_format=u'latex', table_type=u'by-brand'):
1370 1371 tex = u'\n{\\small\n' 1372 tex += u'\\noindent %s\n' % _('Additional notes') 1373 tex += u'\n' 1374 tex += u'\\noindent \\begin{tabular}{|l|l|l|l|}\n' 1375 tex += u'\\hline\n' 1376 tex += u'%s & %s & %s & \\\\ \n' % (_('Substance'), _('Strength'), _('Brand')) 1377 tex += u'\\hline\n' 1378 tex += u'%s\n' 1379 tex += u'\n' 1380 tex += u'\\end{tabular}\n' 1381 tex += u'}\n' 1382 1383 current_meds = emr.get_current_substance_intake ( 1384 include_inactive = False, 1385 include_unapproved = False, 1386 order_by = u'brand, substance' 1387 ) 1388 1389 # create lines 1390 lines = [] 1391 for med in current_meds: 1392 1393 lines.append(u'%s & %s%s & %s %s & {\\scriptsize %s} \\\\ \n \\hline \n' % ( 1394 med['substance'], 1395 med['amount'], 1396 med['unit'], 1397 gmTools.coalesce(med['brand'], u''), 1398 med['preparation'], 1399 gmTools.coalesce(med['notes'], u'') 1400 )) 1401 1402 return tex % u' \n'.join(lines)
1403 1404 #------------------------------------------------------------
1405 -def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-brand'):
1406 1407 tex = u'\\noindent %s {\\tiny (%s)\\par}\n' % (_('Medication list'), _('ordered by brand')) 1408 tex += u'\n' 1409 tex += u'\\noindent \\begin{tabular}{|l|l|}\n' 1410 tex += u'\\hline\n' 1411 tex += u'%s & %s \\\\ \n' % (_('Drug'), _('Regimen')) 1412 tex += u'\\hline\n' 1413 tex += u'\n' 1414 tex += u'\\hline\n' 1415 tex += u'%s\n' 1416 tex += u'\n' 1417 tex += u'\\end{tabular}\n' 1418 1419 current_meds = emr.get_current_substance_intake ( 1420 include_inactive = False, 1421 include_unapproved = False, 1422 order_by = u'brand, substance' 1423 ) 1424 1425 # aggregate data 1426 line_data = {} 1427 for med in current_meds: 1428 identifier = gmTools.coalesce(med['brand'], med['substance']) 1429 1430 try: 1431 line_data[identifier] 1432 except KeyError: 1433 line_data[identifier] = {'brand': u'', 'preparation': u'', 'schedule': u'', 'aims': [], 'strengths': []} 1434 1435 line_data[identifier]['brand'] = identifier 1436 line_data[identifier]['strengths'].append(u'%s%s' % (med['amount'].strip(), med['unit'].strip())) 1437 line_data[identifier]['preparation'] = med['preparation'] 1438 line_data[identifier]['schedule'] = gmTools.coalesce(med['schedule'], u'') 1439 if med['aim'] not in line_data[identifier]['aims']: 1440 line_data[identifier]['aims'].append(med['aim']) 1441 1442 # create lines 1443 already_seen = [] 1444 lines = [] 1445 line1_template = u'%s %s & %s \\\\' 1446 line2_template = u' & {\\scriptsize %s\\par} \\\\' 1447 1448 for med in current_meds: 1449 identifier = gmTools.coalesce(med['brand'], med['substance']) 1450 1451 if identifier in already_seen: 1452 continue 1453 1454 already_seen.append(identifier) 1455 1456 lines.append (line1_template % ( 1457 line_data[identifier]['brand'], 1458 line_data[identifier]['preparation'], 1459 line_data[identifier]['schedule'] 1460 )) 1461 1462 strengths = u'/'.join(line_data[identifier]['strengths']) 1463 if strengths == u'': 1464 template = u' & {\\scriptsize %s\\par} \\\\' 1465 for aim in line_data[identifier]['aims']: 1466 lines.append(template % aim) 1467 else: 1468 if len(line_data[identifier]['aims']) == 0: 1469 template = u'%s & \\\\' 1470 lines.append(template % strengths) 1471 else: 1472 template = u'%s & {\\scriptsize %s\\par} \\\\' 1473 lines.append(template % (strengths, line_data[identifier]['aims'][0])) 1474 template = u' & {\\scriptsize %s\\par} \\\\' 1475 for aim in line_data[identifier]['aims'][1:]: 1476 lines.append(template % aim) 1477 1478 lines.append(u'\\hline') 1479 1480 return tex % u' \n'.join(lines)
1481 #============================================================ 1482 _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s' 1483
1484 -class cDrugComponent(gmBusinessDBObject.cBusinessDBObject):
1485 1486 _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s' 1487 _cmds_store_payload = [ 1488 u"""UPDATE ref.lnk_substance2brand SET 1489 fk_brand = %(pk_brand)s, 1490 fk_substance = %(pk_consumable_substance)s 1491 WHERE 1492 NOT EXISTS ( 1493 SELECT 1 1494 FROM clin.substance_intake 1495 WHERE fk_drug_component = %(pk_component)s 1496 LIMIT 1 1497 ) 1498 AND 1499 pk = %(pk_component)s 1500 AND 1501 xmin = %(xmin_lnk_substance2brand)s 1502 RETURNING 1503 xmin AS xmin_lnk_substance2brand 1504 """ 1505 ] 1506 _updatable_fields = [ 1507 u'pk_brand', 1508 u'pk_consumable_substance' 1509 ] 1510 #-------------------------------------------------------- 1511 # properties 1512 #--------------------------------------------------------
1513 - def _get_containing_drug(self):
1514 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
1515 1516 containing_drug = property(_get_containing_drug, lambda x:x) 1517 #--------------------------------------------------------
1518 - def _get_is_in_use_by_patients(self):
1519 return self._payload[self._idx['is_in_use']]
1520 1521 is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x) 1522 #--------------------------------------------------------
1523 - def _get_substance(self):
1524 return cConsumableSubstance(aPK_obj = self._payload[self._idx['pk_consumable_substance']])
1525 1526 substance = property(_get_substance, lambda x:x)
1527 #------------------------------------------------------------
1528 -def get_drug_components():
1529 cmd = _SQL_get_drug_components % u'true ORDER BY brand, substance' 1530 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 1531 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
1532 #============================================================
1533 -class cBrandedDrug(gmBusinessDBObject.cBusinessDBObject):
1534 """Represents a drug as marketed by a manufacturer.""" 1535 1536 _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s" 1537 _cmds_store_payload = [ 1538 u"""UPDATE ref.branded_drug SET 1539 description = %(brand)s, 1540 preparation = %(preparation)s, 1541 atc_code = gm.nullify_empty_string(%(atc)s), 1542 external_code = gm.nullify_empty_string(%(external_code)s), 1543 external_code_type = gm.nullify_empty_string(%(external_code_type)s), 1544 is_fake = %(is_fake_brand)s, 1545 fk_data_source = %(pk_data_source)s 1546 WHERE 1547 pk = %(pk_brand)s 1548 AND 1549 xmin = %(xmin_branded_drug)s 1550 RETURNING 1551 xmin AS xmin_branded_drug 1552 """ 1553 ] 1554 _updatable_fields = [ 1555 u'brand', 1556 u'preparation', 1557 u'atc', 1558 u'is_fake_brand', 1559 u'external_code', 1560 u'external_code_type', 1561 u'pk_data_source' 1562 ] 1563 #--------------------------------------------------------
1564 - def save_payload(self, conn=None):
1565 success, data = super(self.__class__, self).save_payload(conn = conn) 1566 1567 if not success: 1568 return (success, data) 1569 1570 if self._payload[self._idx['atc']] is not None: 1571 atc = self._payload[self._idx['atc']].strip() 1572 if atc != u'': 1573 gmATC.propagate_atc ( 1574 substance = self._payload[self._idx['brand']].strip(), 1575 atc = atc 1576 ) 1577 1578 return (success, data)
1579 #--------------------------------------------------------
1580 - def set_substances_as_components(self, substances=None):
1581 1582 if self._payload[self._idx['is_in_use']]: 1583 return False 1584 1585 args = {'brand': self._payload[self._idx['pk_brand']]} 1586 1587 queries = [{'cmd': u"DELETE FROM ref.lnk_substance2brand WHERE fk_brand = %(brand)s", 'args': args}] 1588 cmd = u'INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) VALUES (%%(brand)s, %s)' 1589 for s in substances: 1590 queries.append({'cmd': cmd % s['pk'], 'args': args}) 1591 1592 gmPG2.run_rw_queries(queries = queries) 1593 self.refetch_payload() 1594 1595 return True
1596 #--------------------------------------------------------
1597 - def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
1598 1599 args = { 1600 'brand': self.pk_obj, 1601 'subst': consumable['description'], 1602 'atc': consumable['atc_code'], 1603 'pk_subst': pk_substance 1604 } 1605 1606 if pk_substance is None: 1607 consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit) 1608 args['pk_subst'] = consumable['pk'] 1609 1610 # already a component 1611 cmd = u""" 1612 SELECT pk_component 1613 FROM ref.v_drug_components 1614 WHERE 1615 pk_brand = %(brand)s 1616 AND 1617 (( 1618 (lower(substance) = lower(%(subst)s)) 1619 OR 1620 (lower(atc_substance) = lower(%(atc)s)) 1621 OR 1622 (pk_consumable_substance = %(pk_subst)s) 1623 ) IS TRUE) 1624 """ 1625 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1626 1627 if len(rows) > 0: 1628 return 1629 1630 # create it 1631 cmd = u""" 1632 INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) 1633 VALUES (%(brand)s, %(pk_subst)s) 1634 """ 1635 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1636 #------------------------------------------------------------
1637 - def remove_component(self, substance=None):
1638 if len(self._payload[self._idx['components']]) == 1: 1639 _log.error('cannot remove the only component of a drug') 1640 return False 1641 1642 args = {'brand': self.pk_obj, 'comp': substance} 1643 cmd = u""" 1644 DELETE FROM ref.lnk_substance2brand 1645 WHERE 1646 fk_brand = %(brand)s 1647 AND 1648 fk_substance = %(comp)s 1649 AND 1650 NOT EXISTS ( 1651 SELECT 1 1652 FROM clin.substance_intake 1653 WHERE fk_drug_component = %(comp)s 1654 LIMIT 1 1655 ) 1656 """ 1657 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1658 return True
1659 #-------------------------------------------------------- 1660 # properties 1661 #--------------------------------------------------------
1662 - def _get_external_code(self):
1663 if self._payload[self._idx['external_code']] is None: 1664 return None 1665 1666 return self._payload[self._idx['external_code']]
1667 1668 external_code = property(_get_external_code, lambda x:x) 1669 #--------------------------------------------------------
1670 - def _get_external_code_type(self):
1671 1672 # FIXME: maybe evaluate fk_data_source ? 1673 if self._payload[self._idx['external_code_type']] is None: 1674 return None 1675 1676 return self._payload[self._idx['external_code_type']]
1677 1678 external_code_type = property(_get_external_code_type, lambda x:x) 1679 #--------------------------------------------------------
1680 - def _get_components(self):
1681 cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s' 1682 args = {'brand': self._payload[self._idx['pk_brand']]} 1683 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1684 return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
1685 1686 components = property(_get_components, lambda x:x) 1687 #--------------------------------------------------------
1689 if self._payload[self._idx['pk_substances']] is None: 1690 return [] 1691 cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s' 1692 args = {'pks': tuple(self._payload[self._idx['pk_substances']])} 1693 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1694 return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
1695 1696 components_as_substances = property(_get_components_as_substances, lambda x:x) 1697 #--------------------------------------------------------
1698 - def _get_is_vaccine(self):
1699 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)' 1700 args = {'fk_brand': self._payload[self._idx['pk_brand']]} 1701 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1702 return rows[0][0]
1703 1704 is_vaccine = property(_get_is_vaccine, lambda x:x)
1705 #------------------------------------------------------------
1706 -def get_branded_drugs():
1707 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description' 1708 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 1709 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
1710 #------------------------------------------------------------
1711 -def get_drug_by_brand(brand_name=None, preparation=None):
1712 args = {'brand': brand_name, 'prep': preparation} 1713 1714 cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)' 1715 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1716 1717 if len(rows) == 0: 1718 return None 1719 1720 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1721 #------------------------------------------------------------
1722 -def create_branded_drug(brand_name=None, preparation=None, return_existing=False):
1723 1724 if preparation is None: 1725 preparation = _('units') 1726 1727 if preparation.strip() == u'': 1728 preparation = _('units') 1729 1730 if return_existing: 1731 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation) 1732 if drug is not None: 1733 return drug 1734 1735 cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk' 1736 args = {'brand': brand_name, 'prep': preparation} 1737 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 1738 1739 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1740 #------------------------------------------------------------
1741 -def delete_branded_drug(brand=None):
1742 queries = [] 1743 args = {'pk': brand} 1744 1745 # delete components 1746 cmd = u""" 1747 DELETE FROM ref.lnk_substance2brand 1748 WHERE 1749 fk_brand = %(pk)s 1750 AND 1751 NOT EXISTS ( 1752 SELECT 1 1753 FROM clin.v_pat_substance_intake 1754 WHERE pk_brand = %(pk)s 1755 LIMIT 1 1756 ) 1757 """ 1758 queries.append({'cmd': cmd, 'args': args}) 1759 1760 # delete drug 1761 cmd = u""" 1762 DELETE FROM ref.branded_drug 1763 WHERE 1764 pk = %(pk)s 1765 AND 1766 NOT EXISTS ( 1767 SELECT 1 1768 FROM clin.v_pat_substance_intake 1769 WHERE pk_brand = %(pk)s 1770 LIMIT 1 1771 ) 1772 """ 1773 queries.append({'cmd': cmd, 'args': args}) 1774 1775 gmPG2.run_rw_queries(queries = queries)
1776 #============================================================ 1777 # main 1778 #------------------------------------------------------------ 1779 if __name__ == "__main__": 1780 1781 if len(sys.argv) < 2: 1782 sys.exit() 1783 1784 if sys.argv[1] != 'test': 1785 sys.exit() 1786 1787 from Gnumed.pycommon import gmLog2 1788 from Gnumed.pycommon import gmI18N 1789 from Gnumed.business import gmPerson 1790 1791 gmI18N.activate_locale() 1792 # gmDateTime.init() 1793 #--------------------------------------------------------
1794 - def test_MMI_interface():
1795 mmi = cGelbeListeWineInterface() 1796 print mmi 1797 print "interface definition:", mmi.version 1798 print "database versions: ", mmi.get_data_source_version()
1799 #--------------------------------------------------------
1800 - def test_MMI_file():
1801 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2]) 1802 for drug in mmi_file: 1803 print "-------------" 1804 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 1805 for stoff in drug['wirkstoffe']: 1806 print " Wirkstoff:", stoff 1807 raw_input() 1808 if mmi_file.has_unknown_fields is not None: 1809 print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key 1810 for key in mmi_file.csv_fieldnames: 1811 print key, '->', drug[key] 1812 raw_input() 1813 mmi_file.close()
1814 #--------------------------------------------------------
1815 - def test_mmi_switch_to():
1816 mmi = cGelbeListeWineInterface() 1817 mmi.switch_to_frontend(blocking = False)
1818 #--------------------------------------------------------
1819 - def test_mmi_select_drugs():
1820 mmi = cGelbeListeWineInterface() 1821 mmi_file = mmi.select_drugs() 1822 for drug in mmi_file: 1823 print "-------------" 1824 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 1825 for stoff in drug['wirkstoffe']: 1826 print " Wirkstoff:", stoff 1827 print drug 1828 mmi_file.close()
1829 #--------------------------------------------------------
1830 - def test_mmi_import_drugs():
1831 mmi = cGelbeListeWineInterface() 1832 mmi.import_drugs()
1833 #--------------------------------------------------------
1834 - def test_mmi_interaction_check():
1835 mmi = cGelbeListeInterface() 1836 print mmi 1837 print "interface definition:", mmi.version 1838 # Metoprolol + Hct vs Citalopram 1839 diclofenac = '7587712' 1840 phenprocoumon = '4421744' 1841 mmi.check_drug_interactions(drug_ids_list = [diclofenac, phenprocoumon])
1842 #-------------------------------------------------------- 1843 # FreeDiams 1844 #--------------------------------------------------------
1845 - def test_fd_switch_to():
1846 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12)) 1847 fd = cFreeDiamsInterface() 1848 fd.patient = gmPerson.gmCurrentPatient() 1849 fd.switch_to_frontend(blocking = True) 1850 fd.import_fd2gm_file()
1851 #--------------------------------------------------------
1852 - def test_fd_show_interactions():
1853 gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12)) 1854 fd = cFreeDiamsInterface() 1855 fd.patient = gmPerson.gmCurrentPatient() 1856 fd.check_drug_interactions(substances = fd.patient.get_emr().get_current_substance_intake(include_unapproved = True))
1857 #-------------------------------------------------------- 1858 # generic 1859 #--------------------------------------------------------
1860 - def test_create_substance_intake():
1861 drug = create_substance_intake ( 1862 substance = u'Whiskey', 1863 encounter = 1, 1864 episode = 1 1865 ) 1866 print drug
1867 #--------------------------------------------------------
1868 - def test_show_components():
1869 drug = cBrandedDrug(aPK_obj = sys.argv[2]) 1870 print drug 1871 print drug.components
1872 #--------------------------------------------------------
1873 - def test_get_consumable_substances():
1874 for s in get_consumable_substances(): 1875 print s
1876 #-------------------------------------------------------- 1877 # MMI/Gelbe Liste 1878 #test_MMI_interface() 1879 #test_MMI_file() 1880 #test_mmi_switch_to() 1881 #test_mmi_select_drugs() 1882 #test_mmi_import_substances() 1883 #test_mmi_import_drugs() 1884 1885 # FreeDiams 1886 #test_fd_switch_to() 1887 test_fd_show_interactions() 1888 1889 # generic 1890 #test_interaction_check() 1891 #test_create_substance_intake() 1892 #test_show_components() 1893 #test_get_consumable_substances() 1894 #============================================================ 1895