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 version = u'FreeDiams v0.4.2 interface' 255 default_encoding = 'utf8' 256 default_dob_format = '%Y/%m/%d' 257 258 map_gender2mf = { 259 'm': u'M', 260 'f': u'F', 261 'tf': u'H', 262 'tm': u'H', 263 'h': u'H' 264 } 265 #--------------------------------------------------------
266 - def __init__(self):
267 cDrugDataSourceInterface.__init__(self) 268 _log.info(cFreeDiamsInterface.version) 269 270 paths = gmTools.gmPaths() 271 self.__exchange_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2freediams.xml')
272 #--------------------------------------------------------
273 - def get_data_source_version(self):
274 #> Coded. Available next release 275 #> Use --version or -version or -v 276 return u'0.4.2'
277 # ~/.freediams/config.ini: [License] -> AcceptedVersion=.... 278 #--------------------------------------------------------
279 - def create_data_source_entry(self):
280 return create_data_source ( 281 long_name = u'"FreeDiams" Drug Database Frontend', 282 short_name = u'FreeDiams', 283 version = self.get_data_source_version(), 284 source = u'http://ericmaeker.fr/FreeMedForms/di-manual/index.html', 285 language = u'fr' # actually to be multi-locale 286 )
287 #--------------------------------------------------------
288 - def switch_to_frontend(self, blocking=False):
289 """http://ericmaeker.fr/FreeMedForms/di-manual/ligne_commandes.html""" 290 291 found, cmd = gmShellAPI.find_first_binary(binaries = [ 292 self.custom_path_to_binary, 293 r'/usr/bin/freediams', 294 r'freediams', 295 r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams', 296 r'c:\programs\freediams\freediams.exe', 297 r'freediams.exe' 298 ]) 299 300 if not found: 301 _log.error('cannot find FreeDiams binary') 302 return False 303 304 # make sure csv file exists 305 open(self.__exchange_filename, 'wb').close() 306 #args = u'--exchange="%s" --blockpatientdatas="1"' % self.__exchange_filename 307 args = u'--blockpatientdatas="1"' 308 309 if self.patient is not None: 310 names = self.patient.get_active_name() 311 args += u' --patientname="%(lastnames)s, %(firstnames)s"' % names 312 args += u' --patientsurname="%(lastnames)s"' % names 313 args += u' --gender=%s' % cFreeDiamsInterface.map_gender2mf[self.patient['gender']] 314 if self.patient['dob'] is not None: 315 args += u' --dateofbirth=%s' % self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format) 316 317 cmd = r'%s %s' % (cmd, args) 318 319 if not gmShellAPI.run_command_in_shell(command = cmd): 320 _log.error('problem switching to the FreeDiams drug database') 321 return False 322 323 return True
324 #--------------------------------------------------------
325 - def select_drugs(self):
326 self.switch_to_frontend()
327 #--------------------------------------------------------
328 - def import_drugs(self):
329 """FreeDiams ONLY use CIS. 330 331 CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel). 332 CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS. 333 AFSSAPS is the French FDA. 334 335 CIP stands for Unique Presentation Identifier (eg 30 pills plaq) 336 CIP if you want to specify the packaging of the drug (30 pills 337 thermoformed tablet...) -- actually not really usefull for french 338 doctors. 339 """ 340 self.switch_to_frontend()
341 # .external_code_type: u'FR-CIS' 342 # .external_cod: the CIS value 343 #--------------------------------------------------------
344 - def check_drug_interactions(self, drug_ids_list=None, substances=None):
345 self.switch_to_frontend()
346 #--------------------------------------------------------
347 - def show_info_on_drug(self, drug=None):
348 # pass in CIS 349 self.switch_to_frontend()
350 #============================================================
351 -class cGelbeListeWindowsInterface(cDrugDataSourceInterface):
352 """Support v8.2 CSV file interface only.""" 353 354 version = u'Gelbe Liste/MMI v8.2 interface' 355 default_encoding = 'cp1250' 356 bdt_line_template = u'%03d6210#%s\r\n' # Medikament verordnet auf Kassenrezept 357 bdt_line_base_length = 8 358 #--------------------------------------------------------
359 - def __init__(self):
360 361 cDrugDataSourceInterface.__init__(self) 362 363 _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version) 364 365 self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe' 366 self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY' 367 368 paths = gmTools.gmPaths() 369 370 self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt') 371 self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp') 372 self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt') 373 self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt' 374 375 self.__data_date = None 376 self.__online_update_date = None
377 378 # use adjusted config.dat 379 #--------------------------------------------------------
380 - def get_data_source_version(self, force_reload=False):
381 382 if self.__data_date is not None: 383 if not force_reload: 384 return { 385 'data': self.__data_date, 386 'online_update': self.__online_update_date 387 } 388 389 open(self.data_date_filename, 'wb').close() 390 391 cmd = u'%s -DATADATE' % self.path_to_binary 392 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True): 393 _log.error('problem querying the MMI drug database for version information') 394 self.__data_date = None 395 self.__online_update_date = None 396 return { 397 'data': u'?', 398 'online_update': u'?' 399 } 400 401 try: 402 version_file = open(self.data_date_filename, 'rU') 403 except StandardError: 404 _log.error('problem querying the MMI drug database for version information') 405 _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename) 406 self.__data_date = None 407 self.__online_update_date = None 408 return { 409 'data': u'?', 410 'online_update': u'?' 411 } 412 413 self.__data_date = version_file.readline()[:10] 414 self.__online_update_date = version_file.readline()[:10] 415 version_file.close() 416 417 return { 418 'data': self.__data_date, 419 'online_update': self.__online_update_date 420 }
421 #--------------------------------------------------------
422 - def create_data_source_entry(self):
423 versions = self.get_data_source_version() 424 425 return create_data_source ( 426 long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)', 427 short_name = u'GL/MMI', 428 version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']), 429 source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg', 430 language = u'de' 431 )
432 #--------------------------------------------------------
433 - def switch_to_frontend(self, blocking=False, cmd=None):
434 435 # must make sure csv file exists 436 open(self.default_csv_filename, 'wb').close() 437 438 if cmd is None: 439 cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg 440 441 if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking): 442 _log.error('problem switching to the MMI drug database') 443 # apparently on the first call MMI does not 444 # consistently return 0 on success 445 # return False 446 447 return True
448 #--------------------------------------------------------
449 - def select_drugs(self, filename=None):
450 451 # better to clean up interactions file 452 open(self.interactions_filename, 'wb').close() 453 454 if not self.switch_to_frontend(blocking = True): 455 return None 456 457 return cGelbeListeCSVFile(filename = self.default_csv_filename)
458 #--------------------------------------------------------
459 - def import_drugs_as_substances(self):
460 461 selected_drugs = self.select_drugs() 462 if selected_drugs is None: 463 return None 464 465 new_substances = [] 466 467 for drug in selected_drugs: 468 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 469 if len(drug['wirkstoffe']) == 1: 470 atc = drug['atc'] 471 for wirkstoff in drug['wirkstoffe']: 472 new_substances.append(create_used_substance(substance = wirkstoff, atc = atc)) 473 474 selected_drugs.close() 475 476 return new_substances
477 #--------------------------------------------------------
478 - def import_drugs(self):
479 480 selected_drugs = self.select_drugs() 481 if selected_drugs is None: 482 return None 483 484 data_src_pk = self.create_data_source_entry() 485 486 new_drugs = [] 487 new_substances = [] 488 489 for entry in selected_drugs: 490 491 _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform']) 492 493 if entry[u'hilfsmittel']: 494 _log.debug('skipping Hilfsmittel') 495 continue 496 497 if entry[u'erstattbares_medizinprodukt']: 498 _log.debug('skipping sonstiges Medizinprodukt') 499 continue 500 501 # create branded drug (or get it if it already exists) 502 drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform']) 503 if drug is None: 504 drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform']) 505 new_drugs.append(drug) 506 507 # update fields 508 drug['is_fake'] = False 509 drug['atc_code'] = entry['atc'] 510 drug['external_code_type'] = u'DE-PZN' 511 drug['external_code'] = entry['pzn'] 512 drug['fk_data_source'] = data_src_pk 513 drug.save() 514 515 # add components to brand 516 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 517 if len(entry['wirkstoffe']) == 1: 518 atc = entry['atc'] 519 for wirkstoff in entry['wirkstoffe']: 520 drug.add_component(substance = wirkstoff, atc = atc) 521 522 # create as consumable substances, too 523 atc = None # hopefully MMI eventually supports atc-per-substance in a drug... 524 if len(entry['wirkstoffe']) == 1: 525 atc = entry['atc'] 526 for wirkstoff in entry['wirkstoffe']: 527 new_substances.append(create_used_substance(substance = wirkstoff, atc = atc)) 528 529 return new_drugs, new_substances
530 #--------------------------------------------------------
531 - def check_drug_interactions(self, drug_ids_list=None, substances=None):
532 """For this to work the BDT interaction check must be configured in the MMI.""" 533 534 if drug_ids_list is None: 535 if substances is None: 536 return 537 if len(substances) < 2: 538 return 539 drug_ids_list = [ (s.external_code_type, s.external_code) for s in substances ] 540 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')] 541 542 else: 543 if len(drug_ids_list) < 2: 544 return 545 546 if drug_ids_list < 2: 547 return 548 549 bdt_file = codecs.open(filename = self.interactions_filename, mode = 'wb', encoding = cGelbeListeWindowsInterface.default_encoding) 550 551 for pzn in drug_ids_list: 552 pzn = pzn.strip() 553 lng = cGelbeListeWindowsInterface.bdt_line_base_length + len(pzn) 554 bdt_file.write(cGelbeListeWindowsInterface.bdt_line_template % (lng, pzn)) 555 556 bdt_file.close() 557 558 self.switch_to_frontend(blocking = False)
559 #--------------------------------------------------------
560 - def show_info_on_substance(self, substance=None):
561 562 cmd = None 563 564 if substance.external_code_type == u'DE-PZN': 565 cmd = u'%s -PZN %s' % (self.path_to_binary, substance.external_code) 566 567 if cmd is None: 568 name = gmTools.coalesce ( 569 substance['brand'], 570 substance['substance'] 571 ) 572 cmd = u'%s -NAME %s' % (self.path_to_binary, name) 573 574 # better to clean up interactions file 575 open(self.interactions_filename, 'wb').close() 576 577 self.switch_to_frontend(cmd = cmd)
578 #============================================================
579 -class cGelbeListeWineInterface(cGelbeListeWindowsInterface):
580
581 - def __init__(self):
582 cGelbeListeWindowsInterface.__init__(self) 583 584 _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version) 585 586 # FIXME: if -CLOSETOTRAY is used GNUmed cannot detect the end of MMI 587 self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"' 588 self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"' 589 590 paths = gmTools.gmPaths() 591 592 self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv') 593 self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv' 594 self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt') 595 self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
596 #============================================================
597 -class cIfapInterface(cDrugDataSourceInterface):
598 """empirical CSV interface""" 599
600 - def __init__(self):
601 pass
602
603 - def print_transfer_file(self, filename=None):
604 605 try: 606 csv_file = open(filename, 'rb') # FIXME: encoding ? 607 except: 608 _log.exception('cannot access [%s]', filename) 609 csv_file = None 610 611 field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split() 612 613 if csv_file is None: 614 return False 615 616 csv_lines = csv.DictReader ( 617 csv_file, 618 fieldnames = field_names, 619 delimiter = ';' 620 ) 621 622 for line in csv_lines: 623 print "--------------------------------------------------------------------"[:31] 624 for key in field_names: 625 tmp = ('%s ' % key)[:30] 626 print '%s: %s' % (tmp, line[key]) 627 628 csv_file.close()
629 630 # narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % ( 631 # line['Packungszahl'].strip(), 632 # line['Handelsname'].strip(), 633 # line['Form'].strip(), 634 # line[u'Packungsgr\xf6\xdfe'].strip(), 635 # line['Abpackungsmenge'].strip(), 636 # line['Einheit'].strip(), 637 # line['Hersteller'].strip(), 638 # line['PZN'].strip() 639 # ) 640 #============================================================ 641 drug_data_source_interfaces = { 642 'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface, 643 'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface, 644 'FreeDiams (France, US, Canada)': cFreeDiamsInterface 645 } 646 #============================================================ 647 # substances in use across all patients 648 #------------------------------------------------------------
649 -def get_substances_in_use():
650 cmd = u'select * from clin.consumed_substance order by description' 651 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}]) 652 return rows
653 #------------------------------------------------------------
654 -def get_substance_by_pk(pk=None):
655 cmd = u'select * from clin.consumed_substance WHERE pk = %(pk)s' 656 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': pk}}]) 657 if len(rows) == 0: 658 return None 659 return rows[0]
660 #------------------------------------------------------------
661 -def create_used_substance(substance=None, atc=None):
662 663 substance = substance.strip() 664 665 if atc is not None: 666 atc = atc.strip() 667 668 args = {'desc': substance, 'atc': atc} 669 670 cmd = u'select pk, atc_code, description from clin.consumed_substance where description = %(desc)s' 671 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 672 673 if len(rows) == 0: 674 cmd = u'insert into clin.consumed_substance (description, atc_code) values (%(desc)s, gm.nullify_empty_string(%(atc)s)) returning pk, atc_code, description' 675 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 676 677 gmATC.propagate_atc(substance = substance, atc = atc) 678 679 row = rows[0] 680 # unfortunately not a real dict so no setting stuff by keyword 681 #row['atc_code'] = args['atc'] 682 row[1] = args['atc'] 683 return row
684 #------------------------------------------------------------
685 -def delete_used_substance(substance=None):
686 args = {'pk': substance} 687 cmd = u""" 688 delete from clin.consumed_substance 689 where 690 pk = %(pk)s and not exists ( 691 select 1 from clin.substance_intake 692 where fk_substance = %(pk)s 693 )""" 694 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
695 #============================================================
696 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
697 """Represents a substance currently taken by a patient.""" 698 699 _cmd_fetch_payload = u"select * from clin.v_pat_substance_intake where pk_substance_intake = %s" 700 _cmds_store_payload = [ 701 u"""update clin.substance_intake set 702 clin_when = %(started)s, 703 discontinued = %(discontinued)s, 704 discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s), 705 strength = gm.nullify_empty_string(%(strength)s), 706 preparation = %(preparation)s, 707 schedule = gm.nullify_empty_string(%(schedule)s), 708 aim = gm.nullify_empty_string(%(aim)s), 709 narrative = gm.nullify_empty_string(%(notes)s), 710 intake_is_approved_of = %(intake_is_approved_of)s, 711 712 -- is_long_term = %(is_long_term)s, 713 is_long_term = ( 714 case 715 when ( 716 (%(is_long_term)s is False) 717 and 718 (%(duration)s is NULL) 719 ) is True then null 720 else %(is_long_term)s 721 end 722 )::boolean, 723 duration = ( 724 case 725 when %(is_long_term)s is True then null 726 else %(duration)s 727 end 728 )::interval, 729 730 fk_brand = %(pk_brand)s, 731 fk_substance = %(pk_substance)s, 732 fk_episode = %(pk_episode)s 733 where 734 pk = %(pk_substance_intake)s and 735 xmin = %(xmin_substance_intake)s 736 returning 737 xmin as xmin_substance_intake 738 """ 739 ] 740 _updatable_fields = [ 741 u'started', 742 u'discontinued', 743 u'discontinue_reason', 744 u'preparation', 745 u'strength', 746 u'intake_is_approved_of', 747 u'schedule', 748 u'duration', 749 u'aim', 750 u'is_long_term', 751 u'notes', 752 u'pk_brand', 753 u'pk_substance', 754 u'pk_episode' 755 ] 756 #--------------------------------------------------------
757 - def format(self, left_margin=0, date_format='%Y-%m-%d'):
758 759 if self._payload[self._idx['duration']] is None: 760 duration = gmTools.bool2subst ( 761 self._payload[self._idx['is_long_term']], 762 _('long-term'), 763 _('short-term'), 764 _('?short-term') 765 ) 766 else: 767 duration = gmDateTime.format_interval ( 768 self._payload[self._idx['duration']], 769 accuracy_wanted = gmDateTime.acc_days 770 ) 771 772 line = u'%s%s (%s %s): %s %s %s (%s)' % ( 773 u' ' * left_margin, 774 self._payload[self._idx['started']].strftime(date_format), 775 gmTools.u_right_arrow, 776 duration, 777 self._payload[self._idx['substance']], 778 gmTools.coalesce(self._payload[self._idx['strength']], u''), 779 self._payload[self._idx['preparation']], 780 gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing')) 781 ) 782 783 return line
784 #--------------------------------------------------------
785 - def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
786 allg = gmAllergy.create_allergy ( 787 substance = gmTools.coalesce ( 788 self._payload[self._idx['brand']], 789 self._payload[self._idx['substance']] 790 ), 791 allg_type = allergy_type, 792 episode_id = self._payload[self._idx['pk_episode']], 793 encounter_id = encounter_id 794 ) 795 allg['reaction'] = self._payload[self._idx['discontinue_reason']] 796 allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']]) 797 if self._payload[self._idx['external_code_brand']] is not None: 798 allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']]) 799 allg['allergene'] = self._payload[self._idx['substance']] 800 allg['generics'] = self._payload[self._idx['substance']] 801 802 allg.save() 803 return allg
804 #-------------------------------------------------------- 805 # properties 806 #--------------------------------------------------------
807 - def _get_ddd(self):
808 809 try: self.__ddd 810 except AttributeError: self.__ddd = None 811 812 if self.__ddd is not None: 813 return self.__ddd 814 815 if self._payload[self._idx['atc_substance']] is not None: 816 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']]) 817 if len(ddd) != 0: 818 self.__ddd = ddd[0] 819 else: 820 if self._payload[self._idx['atc_brand']] is not None: 821 ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']]) 822 if len(ddd) != 0: 823 self.__ddd = ddd[0] 824 825 return self.__ddd
826 827 ddd = property(_get_ddd, lambda x:x) 828 #--------------------------------------------------------
829 - def _get_external_code(self):
830 drug = self.containing_drug 831 832 if drug is None: 833 return None 834 835 return drug.external_code
836 837 external_code = property(_get_external_code, lambda x:x) 838 #--------------------------------------------------------
839 - def _get_external_code_type(self):
840 drug = self.containing_drug 841 842 if drug is None: 843 return None 844 845 return drug.external_code_type
846 847 external_code_type = property(_get_external_code_type, lambda x:x) 848 #--------------------------------------------------------
849 - def _get_containing_drug(self):
850 if self._payload[self._idx['pk_brand']] is None: 851 return None 852 853 return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
854 855 containing_drug = property(_get_containing_drug, lambda x:x) 856 #--------------------------------------------------------
857 - def _get_parsed_schedule(self):
858 tests = [ 859 # lead, trail 860 ' 1-1-1-1 ', 861 # leading dose 862 '1-1-1-1', 863 '22-1-1-1', 864 '1/3-1-1-1', 865 '/4-1-1-1' 866 ] 867 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}$" 868 for test in tests: 869 print test.strip(), ":", regex.match(pattern, test.strip())
870 #------------------------------------------------------------
871 -def create_substance_intake(substance=None, atc=None, encounter=None, episode=None, preparation=None):
872 873 args = { 874 'enc': encounter, 875 'epi': episode, 876 'prep': preparation, 877 'subst': create_used_substance(substance = substance, atc = atc)['pk'] 878 } 879 880 cmd = u""" 881 insert into clin.substance_intake ( 882 fk_encounter, 883 fk_episode, 884 fk_substance, 885 preparation, 886 intake_is_approved_of 887 ) values ( 888 %(enc)s, 889 %(epi)s, 890 %(subst)s, 891 gm.nullify_empty_string(%(prep)s), 892 False 893 ) 894 returning pk 895 """ 896 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 897 return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
898 #------------------------------------------------------------
899 -def delete_substance_intake(substance=None):
900 cmd = u'delete from clin.substance_intake where pk = %(pk)s' 901 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
902 #------------------------------------------------------------
903 -def format_substance_intake_notes(emr=None, output_format=u'latex', table_type=u'by-brand'):
904 905 tex = u'\n{\\small\n' 906 tex += u'\\noindent %s\n' % _('Additional notes') 907 tex += u'\n' 908 tex += u'\\noindent \\begin{tabular}{|l|l|l|l|}\n' 909 tex += u'\\hline\n' 910 tex += u'%s & %s & %s & \\\\ \n' % (_('Substance'), _('Strength'), _('Brand')) 911 tex += u'\\hline\n' 912 tex += u'%s\n' 913 tex += u'\n' 914 tex += u'\\end{tabular}\n' 915 tex += u'}\n' 916 917 current_meds = emr.get_current_substance_intake ( 918 include_inactive = False, 919 include_unapproved = False, 920 order_by = u'brand, substance' 921 ) 922 923 # create lines 924 lines = [] 925 for med in current_meds: 926 927 lines.append(u'%s & %s & %s %s & {\\scriptsize %s} \\\\ \n \\hline \n' % ( 928 med['substance'], 929 gmTools.coalesce(med['strength'], u''), 930 gmTools.coalesce(med['brand'], u''), 931 med['preparation'], 932 gmTools.coalesce(med['notes'], u'') 933 )) 934 935 return tex % u' \n'.join(lines)
936 937 #------------------------------------------------------------
938 -def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-brand'):
939 940 tex = u'\\noindent %s {\\tiny (%s)\\par}\n' % (_('Medication list'), _('ordered by brand')) 941 tex += u'\n' 942 tex += u'\\noindent \\begin{tabular}{|l|l|}\n' 943 tex += u'\\hline\n' 944 tex += u'%s & %s \\\\ \n' % (_('Drug'), _('Regimen')) 945 tex += u'\\hline\n' 946 tex += u'\n' 947 tex += u'\\hline\n' 948 tex += u'%s\n' 949 tex += u'\n' 950 tex += u'\\end{tabular}\n' 951 952 current_meds = emr.get_current_substance_intake ( 953 include_inactive = False, 954 include_unapproved = False, 955 order_by = u'brand, substance' 956 ) 957 958 # aggregate data 959 line_data = {} 960 for med in current_meds: 961 identifier = gmTools.coalesce(med['brand'], med['substance']) 962 963 try: 964 line_data[identifier] 965 except KeyError: 966 line_data[identifier] = {'brand': u'', 'preparation': u'', 'schedule': u'', 'aims': [], 'strengths': []} 967 968 line_data[identifier]['brand'] = identifier 969 if med['strength'] is not None: 970 line_data[identifier]['strengths'].append(med['strength'].strip()) 971 line_data[identifier]['preparation'] = med['preparation'] 972 line_data[identifier]['schedule'] = gmTools.coalesce(med['schedule'], u'') 973 if med['aim'] not in line_data[identifier]['aims']: 974 line_data[identifier]['aims'].append(med['aim']) 975 976 # create lines 977 already_seen = [] 978 lines = [] 979 line1_template = u'%s %s & %s \\\\' 980 line2_template = u' & {\\scriptsize %s\\par} \\\\' 981 982 for med in current_meds: 983 identifier = gmTools.coalesce(med['brand'], med['substance']) 984 985 if identifier in already_seen: 986 continue 987 988 already_seen.append(identifier) 989 990 lines.append (line1_template % ( 991 line_data[identifier]['brand'], 992 line_data[identifier]['preparation'], 993 line_data[identifier]['schedule'] 994 )) 995 996 strengths = u'/'.join(line_data[identifier]['strengths']) 997 if strengths == u'': 998 template = u' & {\\scriptsize %s\\par} \\\\' 999 for aim in line_data[identifier]['aims']: 1000 lines.append(template % aim) 1001 else: 1002 if len(line_data[identifier]['aims']) == 0: 1003 template = u'%s & \\\\' 1004 lines.append(template % strengths) 1005 else: 1006 template = u'%s & {\\scriptsize %s\\par} \\\\' 1007 lines.append(template % (strengths, line_data[identifier]['aims'][0])) 1008 template = u' & {\\scriptsize %s\\par} \\\\' 1009 for aim in line_data[identifier]['aims'][1:]: 1010 lines.append(template % aim) 1011 1012 lines.append(u'\\hline') 1013 1014 return tex % u' \n'.join(lines)
1015 #============================================================
1016 -class cBrandedDrug(gmBusinessDBObject.cBusinessDBObject):
1017 """Represents a drug as marketed by a manufacturer.""" 1018 1019 _cmd_fetch_payload = u"select *, xmin from ref.branded_drug where pk = %s" 1020 _cmds_store_payload = [ 1021 u"""update ref.branded_drug set 1022 description = %(description)s, 1023 preparation = %(preparation)s, 1024 atc_code = gm.nullify_empty_string(%(atc_code)s), 1025 external_code = gm.nullify_empty_string(%(external_code)s), 1026 external_code_type = gm.nullify_empty_string(%(external_code_type)s), 1027 is_fake = %(is_fake)s, 1028 fk_data_source = %(fk_data_source)s 1029 where 1030 pk = %(pk)s and 1031 xmin = %(xmin)s 1032 returning 1033 xmin 1034 """ 1035 ] 1036 _updatable_fields = [ 1037 u'description', 1038 u'preparation', 1039 u'atc_code', 1040 u'is_fake', 1041 u'external_code', 1042 u'external_code_type', 1043 u'fk_data_source' 1044 ] 1045 #--------------------------------------------------------
1046 - def _get_external_code(self):
1047 if self._payload[self._idx['external_code']] is None: 1048 return None 1049 1050 return self._payload[self._idx['external_code']]
1051 1052 external_code = property(_get_external_code, lambda x:x) 1053 #--------------------------------------------------------
1054 - def _get_external_code_type(self):
1055 1056 # FIXME: maybe evaluate fk_data_source ? 1057 if self._payload[self._idx['external_code_type']] is None: 1058 return None 1059 1060 return self._payload[self._idx['external_code_type']]
1061 1062 external_code_type = property(_get_external_code_type, lambda x:x) 1063 #--------------------------------------------------------
1064 - def _get_components(self):
1065 cmd = u'select * from ref.substance_in_brand where fk_brand = %(brand)s' 1066 args = {'brand': self._payload[self._idx['pk']]} 1067 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1068 return rows
1069 1070 components = property(_get_components, lambda x:x) 1071 #--------------------------------------------------------
1072 - def _get_is_vaccine(self):
1073 cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)' 1074 args = {'fk_brand': self._payload[self._idx['pk']]} 1075 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1076 return rows[0][0]
1077 1078 is_vaccine = property(_get_is_vaccine, lambda x:x) 1079 #--------------------------------------------------------
1080 - def add_component(self, substance=None, atc=None):
1081 1082 # normalize atc 1083 atc = gmATC.propagate_atc(substance = substance, atc = atc) 1084 1085 args = { 1086 'brand': self.pk_obj, 1087 'desc': substance, 1088 'atc': atc 1089 } 1090 1091 # already exists ? 1092 cmd = u""" 1093 SELECT pk 1094 FROM ref.substance_in_brand 1095 WHERE 1096 fk_brand = %(brand)s 1097 AND 1098 ((description = %(desc)s) OR ((atc_code = %(atc)s) IS TRUE)) 1099 """ 1100 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1101 if len(rows) > 0: 1102 return 1103 1104 # create it 1105 cmd = u""" 1106 INSERT INTO ref.substance_in_brand (fk_brand, description, atc_code) 1107 VALUES (%(brand)s, %(desc)s, %(atc)s) 1108 """ 1109 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1110 #------------------------------------------------------------
1111 - def remove_component(substance=None):
1112 delete_component_from_branded_drug(brand = self.pk_obj, component = substance)
1113 #------------------------------------------------------------
1114 -def get_substances_in_brands():
1115 cmd = u'SELECT * FROM ref.v_substance_in_brand ORDER BY brand, substance' 1116 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 1117 return rows
1118 #------------------------------------------------------------
1119 -def get_branded_drugs():
1120 1121 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description' 1122 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 1123 1124 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
1125 #------------------------------------------------------------
1126 -def get_drug_by_brand(brand_name=None, preparation=None):
1127 args = {'brand': brand_name, 'prep': preparation} 1128 1129 cmd = u'SELECT pk FROM ref.branded_drug WHERE description = %(brand)s AND preparation = %(prep)s' 1130 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1131 1132 if len(rows) == 0: 1133 return None 1134 1135 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1136 #------------------------------------------------------------
1137 -def create_branded_drug(brand_name=None, preparation=None, return_existing=False):
1138 1139 if preparation is None: 1140 preparation = _('units') 1141 1142 if preparation.strip() == u'': 1143 preparation = _('units') 1144 1145 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation) 1146 1147 if drug is not None: 1148 if return_existing: 1149 return drug 1150 return None 1151 1152 cmd = u'insert into ref.branded_drug (description, preparation) values (%(brand)s, %(prep)s) returning pk' 1153 args = {'brand': brand_name, 'prep': preparation} 1154 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 1155 1156 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1157 #------------------------------------------------------------
1158 -def delete_branded_drug(brand=None):
1159 cmd = u'delete from ref.branded_drug where pk = %(pk)s' 1160 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': brand}}])
1161 #------------------------------------------------------------
1162 -def delete_component_from_branded_drug(brand=None, component=None):
1163 cmd = u'delete from ref.substance_in_brand where fk_brand = %(brand)s and pk = %(comp)s' 1164 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'brand': brand, 'comp': component}}])
1165 #============================================================ 1166 # main 1167 #------------------------------------------------------------ 1168 if __name__ == "__main__": 1169 1170 if len(sys.argv) < 2: 1171 sys.exit() 1172 1173 if sys.argv[1] != 'test': 1174 sys.exit() 1175 1176 from Gnumed.pycommon import gmLog2 1177 from Gnumed.pycommon import gmI18N 1178 1179 gmI18N.activate_locale() 1180 # gmDateTime.init() 1181 #--------------------------------------------------------
1182 - def test_MMI_interface():
1183 mmi = cGelbeListeWineInterface() 1184 print mmi 1185 print "interface definition:", mmi.version 1186 print "database versions: ", mmi.get_data_source_version()
1187 #--------------------------------------------------------
1188 - def test_MMI_file():
1189 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2]) 1190 for drug in mmi_file: 1191 print "-------------" 1192 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 1193 for stoff in drug['wirkstoffe']: 1194 print " Wirkstoff:", stoff 1195 print drug 1196 mmi_file.close()
1197 #--------------------------------------------------------
1198 - def test_mmi_switch_to():
1199 mmi = cGelbeListeWineInterface() 1200 mmi.switch_to_frontend(blocking = False)
1201 #--------------------------------------------------------
1202 - def test_mmi_select_drugs():
1203 mmi = cGelbeListeWineInterface() 1204 mmi_file = mmi.select_drugs() 1205 for drug in mmi_file: 1206 print "-------------" 1207 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 1208 for stoff in drug['wirkstoffe']: 1209 print " Wirkstoff:", stoff 1210 print drug 1211 mmi_file.close()
1212 #--------------------------------------------------------
1213 - def test_mmi_import_drugs():
1214 mmi = cGelbeListeWineInterface() 1215 mmi.import_drugs()
1216 #--------------------------------------------------------
1217 - def test_mmi_interaction_check():
1218 mmi = cGelbeListeInterface() 1219 print mmi 1220 print "interface definition:", mmi.version 1221 # Metoprolol + Hct vs Citalopram 1222 diclofenac = '7587712' 1223 phenprocoumon = '4421744' 1224 mmi.check_drug_interactions(drug_ids_list = [diclofenac, phenprocoumon])
1225 #--------------------------------------------------------
1226 - def test_create_substance_intake():
1227 drug = create_substance_intake ( 1228 substance = u'Whiskey', 1229 atc = u'no ATC available', 1230 encounter = 1, 1231 episode = 1, 1232 preparation = 'a nice glass' 1233 ) 1234 print drug
1235 #--------------------------------------------------------
1236 - def test_show_components():
1237 drug = cBrandedDrug(aPK_obj = sys.argv[2]) 1238 print drug 1239 print drug.components
1240 #-------------------------------------------------------- 1241 #test_MMI_interface() 1242 #test_MMI_file() 1243 #test_mmi_switch_to() 1244 #test_mmi_select_drugs() 1245 #test_mmi_import_substances() 1246 #test_mmi_import_drugs() 1247 #test_interaction_check() 1248 #test_create_substance_intake() 1249 test_show_components() 1250 #============================================================ 1251