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 
  11   
  12   
  13  if __name__ == '__main__': 
  14          sys.path.insert(0, '../../') 
  15  from Gnumed.pycommon import gmBusinessDBObject, gmPG2, gmShellAPI, gmTools 
  16  from Gnumed.pycommon import gmDispatcher, gmDateTime, gmHooks 
  17  from Gnumed.business import gmATC, gmAllergy 
  18   
  19   
  20  _log = logging.getLogger('gm.meds') 
  21  _log.info(__version__) 
  22   
  23  #============================================================ 
24 -def _on_substance_intake_modified():
25 """Always relates to the active patient.""" 26 gmHooks.run_hook_script(hook = u'after_substance_intake_modified')
27 28 gmDispatcher.connect(_on_substance_intake_modified, u'substance_intake_mod_db') 29 30 #============================================================
31 -def drug2renal_insufficiency_url(search_term=None):
32 33 if search_term is None: 34 return u'http://www.dosing.de' 35 36 terms = [] 37 names = [] 38 39 if isinstance(search_term, cBrandedDrug): 40 if search_term['atc_code'] is not None: 41 terms.append(search_term['atc_code']) 42 43 elif isinstance(search_term, cSubstanceIntakeEntry): 44 names.append(search_term['substance']) 45 if search_term['atc_brand'] is not None: 46 terms.append(search_term['atc_brand']) 47 if search_term['atc_substance'] is not None: 48 terms.append(search_term['atc_substance']) 49 50 elif search_term is not None: 51 names.append(u'%s' % search_term) 52 terms.extend(gmATC.text2atc(text = u'%s' % search_term, fuzzy = True)) 53 54 for name in names: 55 if name.endswith('e'): 56 terms.append(name[:-1]) 57 else: 58 terms.append(name) 59 60 #url_template = u'http://www.google.de/#q=site%%3Adosing.de+%s' 61 #url = url_template % u'+OR+'.join(terms) 62 63 url_template = u'http://www.google.de/search?hl=de&source=hp&q=site%%3Adosing.de+%s&btnG=Google-Suche' 64 url = url_template % u'+OR+'.join(terms) 65 66 _log.debug(u'renal insufficiency URL: %s', url) 67 68 return url
69 #============================================================ 70 # this should be in gmCoding.py
71 -def create_data_source(long_name=None, short_name=None, version=None, source=None, language=None):
72 73 args = { 74 'lname': long_name, 75 'sname': short_name, 76 'ver': version, 77 'src': source, 78 'lang': language 79 } 80 81 cmd = u"""select pk from ref.data_source where name_long = %(lname)s and name_short = %(sname)s and version = %(ver)s""" 82 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 83 if len(rows) > 0: 84 return rows[0]['pk'] 85 86 cmd = u""" 87 INSERT INTO ref.data_source (name_long, name_short, version, source, lang) 88 VALUES ( 89 %(lname)s, 90 %(sname)s, 91 %(ver)s, 92 %(src)s, 93 %(lang)s 94 ) 95 returning pk 96 """ 97 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 98 99 return rows[0]['pk']
100 #============================================================ 101 # wishlist: 102 # - --conf-file= for glwin.exe 103 # - wirkstoff: Konzentration auch in Multiprodukten 104 # - wirkstoff: ATC auch in Multiprodukten 105 # - Suche nach ATC per CLI 106
107 -class cGelbeListeCSVFile(object):
108 """Iterator over a Gelbe Liste/MMI v8.2 CSV file.""" 109 110 version = u'Gelbe Liste/MMI v8.2 CSV file interface' 111 default_transfer_file_windows = r"c:\rezept.txt" 112 #default_encoding = 'cp1252' 113 default_encoding = 'cp1250' 114 csv_fieldnames = [ 115 u'name', 116 u'packungsgroesse', # obsolete, use "packungsmenge" 117 u'darreichungsform', 118 u'packungstyp', 119 u'festbetrag', 120 u'avp', 121 u'hersteller', 122 u'rezepttext', 123 u'pzn', 124 u'status_vertrieb', 125 u'status_rezeptpflicht', 126 u'status_fachinfo', 127 u'btm', 128 u'atc', 129 u'anzahl_packungen', 130 u'zuzahlung_pro_packung', 131 u'einheit', 132 u'schedule_morgens', 133 u'schedule_mittags', 134 u'schedule_abends', 135 u'schedule_nachts', 136 u'status_dauermedikament', 137 u'status_hausliste', 138 u'status_negativliste', 139 u'ik_nummer', 140 u'status_rabattvertrag', 141 u'wirkstoffe', 142 u'wirkstoffmenge', 143 u'wirkstoffeinheit', 144 u'wirkstoffmenge_bezug', 145 u'wirkstoffmenge_bezugseinheit', 146 u'status_import', 147 u'status_lifestyle', 148 u'status_ausnahmeliste', 149 u'packungsmenge', 150 u'apothekenpflicht', 151 u'status_billigere_packung', 152 u'rezepttyp', 153 u'besonderes_arzneimittel', # Abstimmungsverfahren SGB-V 154 u't_rezept_pflicht', # Thalidomid-Rezept 155 u'erstattbares_medizinprodukt', 156 u'hilfsmittel', 157 u'hzv_rabattkennung', 158 u'hzv_preis' 159 ] 160 boolean_fields = [ 161 u'status_rezeptpflicht', 162 u'status_fachinfo', 163 u'btm', 164 u'status_dauermedikament', 165 u'status_hausliste', 166 u'status_negativliste', 167 u'status_rabattvertrag', 168 u'status_import', 169 u'status_lifestyle', 170 u'status_ausnahmeliste', 171 u'apothekenpflicht', 172 u'status_billigere_packung', 173 u'besonderes_arzneimittel', # Abstimmungsverfahren SGB-V 174 u't_rezept_pflicht', 175 u'erstattbares_medizinprodukt', 176 u'hilfsmittel' 177 ] 178 #--------------------------------------------------------
179 - def __init__(self, filename=None):
180 181 _log.info(cGelbeListeCSVFile.version) 182 183 self.filename = filename 184 if filename is None: 185 self.filename = cGelbeListeCSVFile.default_transfer_file_windows 186 187 _log.debug('reading Gelbe Liste/MMI drug data from [%s]', self.filename) 188 189 self.csv_file = codecs.open(filename = filename, mode = 'rUb', encoding = cGelbeListeCSVFile.default_encoding) 190 191 self.csv_lines = gmTools.unicode_csv_reader ( 192 self.csv_file, 193 fieldnames = cGelbeListeCSVFile.csv_fieldnames, 194 delimiter = ';', 195 quotechar = '"', 196 dict = True 197 )
198 #--------------------------------------------------------
199 - def __iter__(self):
200 return self
201 #--------------------------------------------------------
202 - def next(self):
203 line = self.csv_lines.next() 204 205 for field in cGelbeListeCSVFile.boolean_fields: 206 line[field] = (line[field].strip() == u'T') 207 208 # split field "Wirkstoff" by ";" 209 if line['wirkstoffe'].strip() == u'': 210 line['wirkstoffe'] = [] 211 else: 212 line['wirkstoffe'] = [ wirkstoff.strip() for wirkstoff in line['wirkstoffe'].split(u';') ] 213 214 return line
215 #--------------------------------------------------------
216 - def close(self, truncate=True):
217 try: self.csv_file.close() 218 except: pass 219 220 if truncate: 221 try: os.open(self.filename, 'wb').close 222 except: pass
223 #============================================================
224 -class cDrugDataSourceInterface(object):
225 226 #--------------------------------------------------------
227 - def __init__(self):
228 self.patient = None 229 self.custom_path_to_binary = None
230 #--------------------------------------------------------
231 - def get_data_source_version(self):
232 raise NotImplementedError
233 #--------------------------------------------------------
234 - def create_data_source_entry(self):
235 raise NotImplementedError
236 #--------------------------------------------------------
237 - def switch_to_frontend(self, blocking=False):
238 raise NotImplementedError
239 #--------------------------------------------------------
240 - def select_drugs(self):
241 raise NotImplementedError
242 #--------------------------------------------------------
243 - def import_drugs(self):
244 raise NotImplementedError
245 #--------------------------------------------------------
246 - def check_drug_interactions(self, drug_ids_list=None, substances=None):
247 raise NotImplementedError
248 #--------------------------------------------------------
249 - def show_info_on_drug(self, drug=None):
250 raise NotImplementedError
251 #============================================================
252 -class cFreeDiamsInterface(cDrugDataSourceInterface):
253 254 """http://ericmaeker.fr/FreeMedForms/di-manual/ligne_commandes.html""" 255 256 version = u'FreeDiams v0.3.0 interface' 257 default_encoding = 'utf8' 258 default_dob_format = '%d/%m/%Y' 259 260 map_gender2mf = { 261 'm': u'M', 262 'f': u'F', 263 'tf': u'F', 264 'tm': u'M', 265 'h': u'H' 266 } 267 #--------------------------------------------------------
268 - def __init__(self):
269 cDrugDataSourceInterface.__init__(self) 270 _log.info(cFreeDiamsInterface.version) 271 272 paths = gmTools.gmPaths() 273 self.__exchange_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2freediams.xml')
274 #--------------------------------------------------------
275 - def get_data_source_version(self):
276 #> Coded. Available next release 277 #> Use --version or -version or -v 278 return u'0.3.0'
279 # ~/.freediams/config.ini: [License] -> AcceptedVersion=.... 280 #--------------------------------------------------------
281 - def create_data_source_entry(self):
282 return create_data_source ( 283 long_name = u'"FreeDiams" Drug Database Frontend', 284 short_name = u'FreeDiams', 285 version = self.get_data_source_version(), 286 source = u'http://ericmaeker.fr/FreeMedForms/di-manual/index.html', 287 language = u'fr' # actually to be multi-locale 288 )
289 #--------------------------------------------------------
290 - def switch_to_frontend(self, blocking=False):
291 """ --medintux : définit une utilisation spécifique à MedinTux. 292 --exchange="xxx" : définit le fichier d'échange entre les deux applications. 293 --chrono : Chronomètres diverses fonctions du testeur d'interactions (proposé à des fins de déboggage) 294 --transmit-dosage = non documenté. 295 """ 296 found, cmd = gmShellAPI.find_first_binary(binaries = [ 297 self.custom_path_to_binary, 298 r'/usr/bin/freediams', 299 r'freediams', 300 r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams', 301 r'c:\programs\freediams\freediams.exe', 302 r'freediams.exe' 303 ]) 304 305 if not found: 306 _log.error('cannot find FreeDiams binary') 307 return False 308 309 # make sure csv file exists 310 open(self.__exchange_filename, 'wb').close() 311 args = u'--exchange="%s"' % self.__exchange_filename 312 313 if self.patient is not None: 314 315 args += u' --patientname="%(firstnames)s %(lastnames)s"' % self.patient.get_active_name() 316 317 args += u' --gender=%s' % cFreeDiamsInterface.map_gender2mf[self.patient['gender']] 318 319 if self.patient['dob'] is not None: 320 args += u' --dateofbirth="%s"' % self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format) 321 322 # FIXME: search by LOINC code and add 323 # --weight="dd" : définit le poids du patient (en kg) 324 # --size="ddd" : définit la taille du patient (en cm) 325 # --clcr="dd.d" : définit la clairance de la créatinine du patient (en ml/min) 326 # --creatinin="dd" : définit la créatininémie du patient (en mg/l) 327 328 cmd = r'%s %s' % (cmd, args) 329 330 if not gmShellAPI.run_command_in_shell(command = cmd): 331 _log.error('problem switching to the FreeDiams drug database') 332 return False 333 334 return True
335 #--------------------------------------------------------
336 - def select_drugs(self):
337 self.switch_to_frontend()
338 #--------------------------------------------------------
339 - def import_drugs(self):
340 """FreeDiams ONLY use CIS. 341 342 CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel). 343 CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS. 344 AFSSAPS is the French FDA. 345 346 CIP stands for Unique Presentation Identifier (eg 30 pills plaq) 347 CIP if you want to specify the packaging of the drug (30 pills 348 thermoformed tablet...) -- actually not really usefull for french 349 doctors. 350 """ 351 self.switch_to_frontend()
352 # .external_code_type: u'FR-CIS' 353 # .external_cod: the CIS value 354 #--------------------------------------------------------
355 - def check_drug_interactions(self, drug_ids_list=None, substances=None):
356 self.switch_to_frontend()
357 #--------------------------------------------------------
358 - def show_info_on_drug(self, drug=None):
359 # pass in CIS 360 self.switch_to_frontend()
361 #============================================================
362 -class cGelbeListeWindowsInterface(cDrugDataSourceInterface):
363 """Support v8.2 CSV file interface only.""" 364 365 version = u'Gelbe Liste/MMI v8.2 interface' 366 default_encoding = 'cp1250' 367 bdt_line_template = u'%03d6210#%s\r\n' # Medikament verordnet auf Kassenrezept 368 bdt_line_base_length = 8 369 #--------------------------------------------------------
370 - def __init__(self):
371 372 cDrugDataSourceInterface.__init__(self) 373 374 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version) 375 376 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe' 377 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY' 378 379 paths = gmTools.gmPaths() 380 381 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt') 382 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp') 383 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt') 384 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt' 385 386 self.__data_date = None 387 self.__online_update_date = None
388 389 # use adjusted config.dat 390 #--------------------------------------------------------
391 - def get_data_source_version(self, force_reload=False):
392 393 if self.__data_date is not None: 394 if not force_reload: 395 return { 396 'data': self.__data_date, 397 'online_update': self.__online_update_date 398 } 399 400 open(self.data_date_filename, 'wb').close() 401 402 cmd = u'%s -DATADATE' % self.path_to_binary 403 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True): 404 _log.error('problem querying the MMI drug database for version information') 405 self.__data_date = None 406 self.__online_update_date = None 407 return { 408 'data': u'?', 409 'online_update': u'?' 410 } 411 412 try: 413 version_file = open(self.data_date_filename, 'rU') 414 except StandardError: 415 _log.error('problem querying the MMI drug database for version information') 416 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename) 417 self.__data_date = None 418 self.__online_update_date = None 419 return { 420 'data': u'?', 421 'online_update': u'?' 422 } 423 424 self.__data_date = version_file.readline()[:10] 425 self.__online_update_date = version_file.readline()[:10] 426 version_file.close() 427 428 return { 429 'data': self.__data_date, 430 'online_update': self.__online_update_date 431 }
432 #--------------------------------------------------------
433 - def create_data_source_entry(self):
434 versions = self.get_data_source_version() 435 436 return create_data_source ( 437 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)', 438 short_name = u'GL/MMI', 439 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']), 440 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg', 441 language = u'de' 442 )
443 #--------------------------------------------------------
444 - def switch_to_frontend(self, blocking=False, cmd=None):
445 446 # must make sure csv file exists 447 open(self.default_csv_filename, 'wb').close() 448 449 if cmd is None: 450 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg 451 452 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking): 453 _log.error('problem switching to the MMI drug database') 454 # apparently on the first call MMI does not 455 # consistently return 0 on success 456 # return False 457 458 return True
459 #--------------------------------------------------------
460 - def select_drugs(self, filename=None):
461 462 # better to clean up interactions file 463 open(self.interactions_filename, 'wb').close() 464 465 if not self.switch_to_frontend(blocking = True): 466 return None 467 468 return cGelbeListeCSVFile(filename = self.default_csv_filename)
469 #--------------------------------------------------------
470 - def import_drugs_as_substances(self):
471 472 selected_drugs = self.select_drugs() 473 if selected_drugs is None: 474 return None 475 476 new_substances = [] 477 478 for drug in selected_drugs: 479 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 480 if len(drug['wirkstoffe']) == 1: 481 atc = drug['atc'] 482 for wirkstoff in drug['wirkstoffe']: 483 new_substances.append(create_used_substance(substance = wirkstoff, atc = atc)) 484 485 selected_drugs.close() 486 487 return new_substances
488 #--------------------------------------------------------
489 - def import_drugs(self):
490 491 selected_drugs = self.select_drugs() 492 if selected_drugs is None: 493 return None 494 495 data_src_pk = self.create_data_source_entry() 496 497 new_drugs = [] 498 new_substances = [] 499 500 for entry in selected_drugs: 501 502 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform']) 503 504 if entry[u'hilfsmittel']: 505 _log.debug('skipping Hilfsmittel') 506 continue 507 508 if entry[u'erstattbares_medizinprodukt']: 509 _log.debug('skipping sonstiges Medizinprodukt') 510 continue 511 512 # create branded drug (or get it if it already exists) 513 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform']) 514 if drug is None: 515 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform']) 516 new_drugs.append(drug) 517 518 # update fields 519 drug['is_fake'] = False 520 drug['atc_code'] = entry['atc'] 521 drug['external_code_type'] = u'DE-PZN' 522 drug['external_code'] = entry['pzn'] 523 drug['fk_data_source'] = data_src_pk 524 drug.save() 525 526 # add components to brand 527 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 528 if len(entry['wirkstoffe']) == 1: 529 atc = entry['atc'] 530 for wirkstoff in entry['wirkstoffe']: 531 drug.add_component(substance = wirkstoff, atc = atc) 532 533 # create as consumable substances, too 534 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 535 if len(entry['wirkstoffe']) == 1: 536 atc = entry['atc'] 537 for wirkstoff in entry['wirkstoffe']: 538 new_substances.append(create_used_substance(substance = wirkstoff, atc = atc)) 539 540 return new_drugs, new_substances
541 #--------------------------------------------------------
542 - def check_drug_interactions(self, drug_ids_list=None, substances=None):
543 """For this to work the BDT interaction check must be configured in the MMI.""" 544 545 if drug_ids_list is None: 546 if substances is None: 547 return 548 if len(substances) < 2: 549 return 550 drug_ids_list = [ (s.external_code_type, s.external_code) for s in substances ] 551 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')] 552 553 else: 554 if len(drug_ids_list) < 2: 555 return 556 557 if drug_ids_list < 2: 558 return 559 560 bdt_file = codecs.open(filename = self.interactions_filename, mode = 'wb', encoding = cGelbeListeWindowsInterface.default_encoding) 561 562 for pzn in drug_ids_list: 563 pzn = pzn.strip() 564 lng = cGelbeListeWindowsInterface.bdt_line_base_length + len(pzn) 565 bdt_file.write(cGelbeListeWindowsInterface.bdt_line_template % (lng, pzn)) 566 567 bdt_file.close() 568 569 self.switch_to_frontend(blocking = False)
570 #--------------------------------------------------------
571 - def show_info_on_substance(self, substance=None):
572 573 cmd = None 574 575 if substance.external_code_type == u'DE-PZN': 576 cmd = u'%s -PZN %s' % (self.path_to_binary, substance.external_code) 577 578 if cmd is None: 579 name = gmTools.coalesce ( 580 substance['brand'], 581 substance['substance'] 582 ) 583 cmd = u'%s -NAME %s' % (self.path_to_binary, name) 584 585 # better to clean up interactions file 586 open(self.interactions_filename, 'wb').close() 587 588 self.switch_to_frontend(cmd = cmd)
589 #============================================================
590 -class cGelbeListeWineInterface(cGelbeListeWindowsInterface):
591
592 - def __init__(self):
593 cGelbeListeWindowsInterface.__init__(self) 594 595 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version) 596 597 # FIXME: if -CLOSETOTRAY is used GNUmed cannot detect the end of MMI 598 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"' 599 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"' 600 601 paths = gmTools.gmPaths() 602 603 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv') 604 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv' 605 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt') 606 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
607 #============================================================
608 -class cIfapInterface(cDrugDataSourceInterface):
609 """empirical CSV interface""" 610
611 - def __init__(self):
612 pass
613
614 - def print_transfer_file(self, filename=None):
615 616 try: 617 csv_file = open(filename, 'rb') # FIXME: encoding ? 618 except: 619 _log.exception('cannot access [%s]', filename) 620 csv_file = None 621 622 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split() 623 624 if csv_file is None: 625 return False 626 627 csv_lines = csv.DictReader ( 628 csv_file, 629 fieldnames = field_names, 630 delimiter = ';' 631 ) 632 633 for line in csv_lines: 634 print "--------------------------------------------------------------------"[:31] 635 for key in field_names: 636 tmp = ('%s ' % key)[:30] 637 print '%s: %s' % (tmp, line[key]) 638 639 csv_file.close()
640 641 # narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % ( 642 # line['Packungszahl'].strip(), 643 # line['Handelsname'].strip(), 644 # line['Form'].strip(), 645 # line[u'Packungsgr\xf6\xdfe'].strip(), 646 # line['Abpackungsmenge'].strip(), 647 # line['Einheit'].strip(), 648 # line['Hersteller'].strip(), 649 # line['PZN'].strip() 650 # ) 651 #============================================================ 652 drug_data_source_interfaces = { 653 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface, 654 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface, 655 'France: FreeDiams': cFreeDiamsInterface 656 } 657 #============================================================ 658 # substances in use across all patients 659 #------------------------------------------------------------
660 -def get_substances_in_use():
661 cmd = u'select * from clin.consumed_substance order by description' 662 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}]) 663 return rows
664 #------------------------------------------------------------
665 -def get_substance_by_pk(pk=None):
666 cmd = u'select * from clin.consumed_substance WHERE pk = %(pk)s' 667 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': pk}}]) 668 if len(rows) == 0: 669 return None 670 return rows[0]
671 #------------------------------------------------------------
672 -def create_used_substance(substance=None, atc=None):
673 674 substance = substance.strip() 675 676 if atc is not None: 677 atc = atc.strip() 678 679 args = {'desc': substance, 'atc': atc} 680 681 cmd = u'select pk, atc_code, description from clin.consumed_substance where description = %(desc)s' 682 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 683 684 if len(rows) == 0: 685 cmd = u'insert into clin.consumed_substance (description, atc_code) values (%(desc)s, gm.nullify_empty_string(%(atc)s)) returning pk, atc_code, description' 686 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 687 688 gmATC.propagate_atc(substance = substance, atc = atc) 689 690 row = rows[0] 691 # unfortunately not a real dict so no setting stuff by keyword 692 #row['atc_code'] = args['atc'] 693 row[1] = args['atc'] 694 return row
695 #------------------------------------------------------------
696 -def delete_used_substance(substance=None):
697 args = {'pk': substance} 698 cmd = u""" 699 delete from clin.consumed_substance 700 where 701 pk = %(pk)s and not exists ( 702 select 1 from clin.substance_intake 703 where fk_substance = %(pk)s 704 )""" 705 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
706 #============================================================
707 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
708 """Represents a substance currently taken by a patient.""" 709 710 _cmd_fetch_payload = u"select * from clin.v_pat_substance_intake where pk_substance_intake = %s" 711 _cmds_store_payload = [ 712 u"""update clin.substance_intake set 713 clin_when = %(started)s, 714 discontinued = %(discontinued)s, 715 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s), 716 strength = gm.nullify_empty_string(%(strength)s), 717 preparation = %(preparation)s, 718 schedule = gm.nullify_empty_string(%(schedule)s), 719 aim = gm.nullify_empty_string(%(aim)s), 720 narrative = gm.nullify_empty_string(%(notes)s), 721 intake_is_approved_of = %(intake_is_approved_of)s, 722 723 -- is_long_term = %(is_long_term)s, 724 is_long_term = ( 725 case 726 when ( 727 (%(is_long_term)s is False) 728 and 729 (gm.is_null_or_blank_string(%(duration)s) is True) 730 ) is True then null 731 else %(is_long_term)s 732 end 733 )::boolean, 734 duration = ( 735 case 736 when %(is_long_term)s is True then null 737 else gm.nullify_empty_string(%(duration)s) 738 end 739 )::interval, 740 741 fk_brand = %(pk_brand)s, 742 fk_substance = %(pk_substance)s, 743 fk_episode = %(pk_episode)s 744 where 745 pk = %(pk_substance_intake)s and 746 xmin = %(xmin_substance_intake)s 747 returning 748 xmin as xmin_substance_intake 749 """ 750 ] 751 _updatable_fields = [ 752 u'started', 753 u'discontinued', 754 u'discontinue_reason', 755 u'preparation', 756 u'strength', 757 u'intake_is_approved_of', 758 u'schedule', 759 u'duration', 760 u'aim', 761 u'is_long_term', 762 u'notes', 763 u'pk_brand', 764 u'pk_substance', 765 u'pk_episode' 766 ] 767 #--------------------------------------------------------
768 - def format(self, left_margin=0, date_format='%Y-%m-%d'):
769 770 if self._payload[self._idx['duration']] is None: 771 duration = gmTools.bool2subst ( 772 self._payload[self._idx['is_long_term']], 773 _('long-term'), 774 _('short-term'), 775 _('?short-term') 776 ) 777 else: 778 duration = gmDateTime.format_interval ( 779 self._payload[self._idx['duration']], 780 accuracy_wanted = gmDateTime.acc_days 781 ) 782 783 line = u'%s%s (%s %s): %s %s %s (%s)' % ( 784 u' ' * left_margin, 785 self._payload[self._idx['started']].strftime(date_format), 786 gmTools.u_right_arrow, 787 duration, 788 self._payload[self._idx['substance']], 789 gmTools.coalesce(self._payload[self._idx['strength']], u''), 790 self._payload[self._idx['preparation']], 791 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing')) 792 ) 793 794 return line
795 #--------------------------------------------------------
796 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
797 allg = gmAllergy.create_allergy ( 798 substance = gmTools.coalesce ( 799 self._payload[self._idx['brand']], 800 self._payload[self._idx['substance']] 801 ), 802 allg_type = allergy_type, 803 episode_id = self._payload[self._idx['pk_episode']], 804 encounter_id = encounter_id 805 ) 806 allg['reaction'] = self._payload[self._idx['discontinue_reason']] 807 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']]) 808 if self._payload[self._idx['external_code_brand']] is not None: 809 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']]) 810 allg['generics'] = self._payload[self._idx['substance']] 811 812 allg.save() 813 return allg
814 #-------------------------------------------------------- 815 # properties 816 #--------------------------------------------------------
817 - def _get_ddd(self):
818 819 try: self.__ddd 820 except AttributeError: self.__ddd = None 821 822 if self.__ddd is not None: 823 return self.__ddd 824 825 if self._payload[self._idx['atc_substance']] is not None: 826 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']]) 827 if len(ddd) != 0: 828 self.__ddd = ddd[0] 829 else: 830 if self._payload[self._idx['atc_brand']] is not None: 831 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']]) 832 if len(ddd) != 0: 833 self.__ddd = ddd[0] 834 835 return self.__ddd
836 837 ddd = property(_get_ddd, lambda x:x) 838 #--------------------------------------------------------
839 - def _get_external_code(self):
840 drug = self.containing_drug 841 842 if drug is None: 843 return None 844 845 return drug.external_code
846 847 external_code = property(_get_external_code, lambda x:x) 848 #--------------------------------------------------------
849 - def _get_external_code_type(self):
850 drug = self.containing_drug 851 852 if drug is None: 853 return None 854 855 return drug.external_code_type
856 857 external_code_type = property(_get_external_code_type, lambda x:x) 858 #--------------------------------------------------------
859 - def _get_containing_drug(self):
860 if self._payload[self._idx['pk_brand']] is None: 861 return None 862 863 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
864 865 containing_drug = property(_get_containing_drug, lambda x:x) 866 #--------------------------------------------------------
867 - def _get_parsed_schedule(self):
868 tests = [ 869 # lead, trail 870 ' 1-1-1-1 ', 871 # leading dose 872 '1-1-1-1', 873 '22-1-1-1', 874 '1/3-1-1-1', 875 '/4-1-1-1' 876 ] 877 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}$" 878 for test in tests: 879 print test.strip(), ":", regex.match(pattern, test.strip())
880 #------------------------------------------------------------
881 -def create_substance_intake(substance=None, atc=None, encounter=None, episode=None, preparation=None):
882 883 args = { 884 'enc': encounter, 885 'epi': episode, 886 'prep': preparation, 887 'subst': create_used_substance(substance = substance, atc = atc)['pk'] 888 } 889 890 cmd = u""" 891 insert into clin.substance_intake ( 892 fk_encounter, 893 fk_episode, 894 fk_substance, 895 preparation, 896 intake_is_approved_of 897 ) values ( 898 %(enc)s, 899 %(epi)s, 900 %(subst)s, 901 gm.nullify_empty_string(%(prep)s), 902 False 903 ) 904 returning pk 905 """ 906 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 907 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
908 #------------------------------------------------------------
909 -def delete_substance_intake(substance=None):
910 cmd = u'delete from clin.substance_intake where pk = %(pk)s' 911 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
912 #------------------------------------------------------------
913 -def format_substance_intake_notes(emr=None, output_format=u'latex', table_type=u'by-brand'):
914 915 tex = u'\n{\\small\n' 916 tex += u'\\noindent %s\n' % _('Additional notes') 917 tex += u'\n' 918 tex += u'\\noindent \\begin{tabular}{|l|l|l|l|}\n' 919 tex += u'\\hline\n' 920 tex += u'%s & %s & %s & \\\\ \n' % (_('Substance'), _('Strength'), _('Brand')) 921 tex += u'\\hline\n' 922 tex += u'%s\n' 923 tex += u'\n' 924 tex += u'\\end{tabular}\n' 925 tex += u'}\n' 926 927 current_meds = emr.get_current_substance_intake ( 928 include_inactive = False, 929 include_unapproved = False, 930 order_by = u'brand, substance' 931 ) 932 933 # create lines 934 lines = [] 935 for med in current_meds: 936 937 lines.append(u'%s & %s & %s %s & {\\scriptsize %s} \\\\ \n \\hline \n' % ( 938 med['substance'], 939 gmTools.coalesce(med['strength'], u''), 940 gmTools.coalesce(med['brand'], u''), 941 med['preparation'], 942 gmTools.coalesce(med['notes'], u'') 943 )) 944 945 return tex % u' \n'.join(lines)
946 947 #------------------------------------------------------------
948 -def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-brand'):
949 950 tex = u'\\noindent %s {\\tiny (%s)\\par}\n' % (_('Medication list'), _('ordered by brand')) 951 tex += u'\n' 952 tex += u'\\noindent \\begin{tabular}{|l|l|}\n' 953 tex += u'\\hline\n' 954 tex += u'%s & %s \\\\ \n' % (_('Drug'), _('Regimen')) 955 tex += u'\\hline\n' 956 tex += u'\n' 957 tex += u'\\hline\n' 958 tex += u'%s\n' 959 tex += u'\n' 960 tex += u'\\end{tabular}\n' 961 962 current_meds = emr.get_current_substance_intake ( 963 include_inactive = False, 964 include_unapproved = False, 965 order_by = u'brand, substance' 966 ) 967 968 # aggregate data 969 line_data = {} 970 for med in current_meds: 971 identifier = gmTools.coalesce(med['brand'], med['substance']) 972 973 try: 974 line_data[identifier] 975 except KeyError: 976 line_data[identifier] = {'brand': u'', 'preparation': u'', 'schedule': u'', 'aims': [], 'strengths': []} 977 978 line_data[identifier]['brand'] = identifier 979 if med['strength'] is not None: 980 line_data[identifier]['strengths'].append(med['strength'].strip()) 981 line_data[identifier]['preparation'] = med['preparation'] 982 line_data[identifier]['schedule'] = gmTools.coalesce(med['schedule'], u'') 983 if med['aim'] not in line_data[identifier]['aims']: 984 line_data[identifier]['aims'].append(med['aim']) 985 986 # create lines 987 already_seen = [] 988 lines = [] 989 line1_template = u'%s %s & %s \\\\' 990 line2_template = u' & {\\scriptsize %s\\par} \\\\' 991 992 for med in current_meds: 993 identifier = gmTools.coalesce(med['brand'], med['substance']) 994 995 if identifier in already_seen: 996 continue 997 998 already_seen.append(identifier) 999 1000 lines.append (line1_template % ( 1001 line_data[identifier]['brand'], 1002 line_data[identifier]['preparation'], 1003 line_data[identifier]['schedule'] 1004 )) 1005 1006 strengths = u'/'.join(line_data[identifier]['strengths']) 1007 if strengths == u'': 1008 template = u' & {\\scriptsize %s\\par} \\\\' 1009 for aim in line_data[identifier]['aims']: 1010 lines.append(template % aim) 1011 else: 1012 if len(line_data[identifier]['aims']) == 0: 1013 template = u'%s & \\\\' 1014 lines.append(template % strengths) 1015 else: 1016 template = u'%s & {\\scriptsize %s\\par} \\\\' 1017 lines.append(template % (strengths, line_data[identifier]['aims'][0])) 1018 template = u' & {\\scriptsize %s\\par} \\\\' 1019 for aim in line_data[identifier]['aims'][1:]: 1020 lines.append(template % aim) 1021 1022 lines.append(u'\\hline') 1023 1024 return tex % u' \n'.join(lines)
1025 #============================================================
1026 -class cBrandedDrug(gmBusinessDBObject.cBusinessDBObject):
1027 """Represents a drug as marketed by a manufacturer.""" 1028 1029 _cmd_fetch_payload = u"select *, xmin from ref.branded_drug where pk = %s" 1030 _cmds_store_payload = [ 1031 u"""update ref.branded_drug set 1032 description = %(description)s, 1033 preparation = %(preparation)s, 1034 atc_code = gm.nullify_empty_string(%(atc_code)s), 1035 external_code = gm.nullify_empty_string(%(external_code)s), 1036 external_code_type = gm.nullify_empty_string(%(external_code_type)s), 1037 is_fake = %(is_fake)s, 1038 fk_data_source = %(fk_data_source)s 1039 where 1040 pk = %(pk)s and 1041 xmin = %(xmin)s 1042 returning 1043 xmin 1044 """ 1045 ] 1046 _updatable_fields = [ 1047 u'description', 1048 u'preparation', 1049 u'atc_code', 1050 u'is_fake', 1051 u'external_code', 1052 u'external_code_type', 1053 u'fk_data_source' 1054 ] 1055 #--------------------------------------------------------
1056 - def _get_external_code(self):
1057 if self._payload[self._idx['external_code']] is None: 1058 return None 1059 1060 return self._payload[self._idx['external_code']]
1061 1062 external_code = property(_get_external_code, lambda x:x) 1063 #--------------------------------------------------------
1064 - def _get_external_code_type(self):
1065 1066 # FIXME: maybe evaluate fk_data_source ? 1067 if self._payload[self._idx['external_code_type']] is None: 1068 return None 1069 1070 return self._payload[self._idx['external_code_type']]
1071 1072 external_code_type = property(_get_external_code_type, lambda x:x) 1073 #--------------------------------------------------------
1074 - def _get_components(self):
1075 cmd = u'select * from ref.substance_in_brand where fk_brand = %(brand)s' 1076 args = {'brand': self._payload[self._idx['pk']]} 1077 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1078 return rows
1079 1080 components = property(_get_components, lambda x:x) 1081 #--------------------------------------------------------
1082 - def add_component(self, substance=None, atc=None):
1083 1084 # normalize atc 1085 atc = gmATC.propagate_atc(substance = substance, atc = atc) 1086 1087 args = { 1088 'brand': self.pk_obj, 1089 'desc': substance, 1090 'atc': atc 1091 } 1092 1093 # already exists ? 1094 cmd = u""" 1095 SELECT pk 1096 FROM ref.substance_in_brand 1097 WHERE 1098 fk_brand = %(brand)s 1099 AND 1100 ((description = %(desc)s) OR ((atc_code = %(atc)s) IS TRUE)) 1101 """ 1102 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1103 if len(rows) > 0: 1104 return 1105 1106 # create it 1107 cmd = u""" 1108 INSERT INTO ref.substance_in_brand (fk_brand, description, atc_code) 1109 VALUES (%(brand)s, %(desc)s, %(atc)s) 1110 """ 1111 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1112 #------------------------------------------------------------
1113 - def remove_component(substance=None):
1114 delete_component_from_branded_drug(brand = self.pk_obj, component = substance)
1115 #------------------------------------------------------------
1116 -def get_substances_in_brands():
1117 cmd = u'SELECT * FROM ref.v_substance_in_brand ORDER BY brand, substance' 1118 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 1119 return rows
1120 #------------------------------------------------------------
1121 -def get_branded_drugs():
1122 1123 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description' 1124 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 1125 1126 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
1127 #------------------------------------------------------------
1128 -def get_drug_by_brand(brand_name=None, preparation=None):
1129 args = {'brand': brand_name, 'prep': preparation} 1130 1131 cmd = u'SELECT pk FROM ref.branded_drug WHERE description = %(brand)s AND preparation = %(prep)s' 1132 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1133 1134 if len(rows) == 0: 1135 return None 1136 1137 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1138 #------------------------------------------------------------
1139 -def create_branded_drug(brand_name=None, preparation=None, return_existing=False):
1140 1141 if preparation is None: 1142 preparation = _('units') 1143 1144 if preparation.strip() == u'': 1145 preparation = _('units') 1146 1147 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation) 1148 1149 if drug is not None: 1150 if return_existing: 1151 return drug 1152 return None 1153 1154 cmd = u'insert into ref.branded_drug (description, preparation) values (%(brand)s, %(prep)s) returning pk' 1155 args = {'brand': brand_name, 'prep': preparation} 1156 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 1157 1158 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1159 #------------------------------------------------------------
1160 -def delete_branded_drug(brand=None):
1161 cmd = u'delete from ref.branded_drug where pk = %(pk)s' 1162 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': brand}}])
1163 #------------------------------------------------------------
1164 -def delete_component_from_branded_drug(brand=None, component=None):
1165 cmd = u'delete from ref.substance_in_brand where fk_brand = %(brand)s and pk = %(comp)s' 1166 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'brand': brand, 'comp': component}}])
1167 #============================================================ 1168 # main 1169 #------------------------------------------------------------ 1170 if __name__ == "__main__": 1171 1172 if len(sys.argv) < 2: 1173 sys.exit() 1174 1175 if sys.argv[1] != 'test': 1176 sys.exit() 1177 1178 from Gnumed.pycommon import gmLog2 1179 from Gnumed.pycommon import gmI18N 1180 1181 gmI18N.activate_locale() 1182 # gmDateTime.init() 1183 #--------------------------------------------------------
1184 - def test_MMI_interface():
1185 mmi = cGelbeListeWineInterface() 1186 print mmi 1187 print "interface definition:", mmi.version 1188 print "database versions: ", mmi.get_data_source_version()
1189 #--------------------------------------------------------
1190 - def test_MMI_file():
1191 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2]) 1192 for drug in mmi_file: 1193 print "-------------" 1194 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 1195 for stoff in drug['wirkstoffe']: 1196 print " Wirkstoff:", stoff 1197 print drug 1198 mmi_file.close()
1199 #--------------------------------------------------------
1200 - def test_mmi_switch_to():
1201 mmi = cGelbeListeWineInterface() 1202 mmi.switch_to_frontend(blocking = False)
1203 #--------------------------------------------------------
1204 - def test_mmi_select_drugs():
1205 mmi = cGelbeListeWineInterface() 1206 mmi_file = mmi.select_drugs() 1207 for drug in mmi_file: 1208 print "-------------" 1209 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 1210 for stoff in drug['wirkstoffe']: 1211 print " Wirkstoff:", stoff 1212 print drug 1213 mmi_file.close()
1214 #--------------------------------------------------------
1215 - def test_mmi_import_drugs():
1216 mmi = cGelbeListeWineInterface() 1217 mmi.import_drugs()
1218 #--------------------------------------------------------
1219 - def test_mmi_interaction_check():
1220 mmi = cGelbeListeInterface() 1221 print mmi 1222 print "interface definition:", mmi.version 1223 # Metoprolol + Hct vs Citalopram 1224 diclofenac = '7587712' 1225 phenprocoumon = '4421744' 1226 mmi.check_drug_interactions(drug_ids_list = [diclofenac, phenprocoumon])
1227 #--------------------------------------------------------
1228 - def test_create_substance_intake():
1229 drug = create_substance_intake ( 1230 substance = u'Whiskey', 1231 atc = u'no ATC available', 1232 encounter = 1, 1233 episode = 1, 1234 preparation = 'a nice glass' 1235 ) 1236 print drug
1237 #--------------------------------------------------------
1238 - def test_show_components():
1239 drug = cBrandedDrug(aPK_obj = sys.argv[2]) 1240 print drug 1241 print drug.components
1242 #-------------------------------------------------------- 1243 #test_MMI_interface() 1244 #test_MMI_file() 1245 #test_mmi_switch_to() 1246 #test_mmi_select_drugs() 1247 #test_mmi_import_substances() 1248 #test_mmi_import_drugs() 1249 #test_interaction_check() 1250 #test_create_substance_intake() 1251 test_show_components() 1252 #============================================================ 1253