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):
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):
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, pzn_list=None, substances=None):
543 """For this to work the BDT interaction check must be configured in the MMI.""" 544 545 if pzn_list is None: 546 if substances is None: 547 return 548 if len(substances) < 2: 549 return 550 pzn_list = [ (s.external_code_type, s.external_code) for s in substances ] 551 pzn_list = [ code_value for code_type, code_value in pzn_list if (code_value is not None) and (code_type == u'DE-PZN')] 552 553 else: 554 if len(pzn_list) < 2: 555 return 556 557 if pzn_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 pzn_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 self._payload[self._idx['strength']], 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(emr=None, output_format=u'latex', table_type=u'by-brand'):
914 915 tex = u"""\\noindent %s {\\tiny (%s)}{\\tiny \\par} 916 917 \\noindent \\begin{tabular}{|l|l|l|} 918 \\hline 919 %s & %s & {\\scriptsize %s} \\\\ 920 \\hline 921 922 \\hline 923 %%s 924 925 \\end{tabular}""" % ( 926 _('Medication list'), 927 _('ordered by brand'), 928 _('Drug'), 929 _('Regimen'), 930 _('Substances') 931 ) 932 933 current_meds = emr.get_current_substance_intake ( 934 include_inactive = False, 935 include_unapproved = False, 936 order_by = u'brand, substance' 937 ) 938 939 # aggregate data 940 line_data = {} 941 for med in current_meds: 942 identifier = gmTools.coalesce(med['brand'], med['substance']) 943 944 try: 945 line_data[identifier] 946 except KeyError: 947 line_data[identifier] = {'brand': u'', 'substances': [], 'preparation': u'', 'schedule': u'', 'aims': [], 'notes': []} 948 949 line_data[identifier]['brand'] = identifier 950 line_data[identifier]['substances'].append(u'%s%s' % (med['substance'], gmTools.coalesce(med['strength'], u'', u' %s'))) 951 line_data[identifier]['preparation'] = med['preparation'] 952 line_data[identifier]['schedule'] = gmTools.coalesce(med['schedule'], u'') 953 if med['aim'] not in line_data[identifier]['aims']: 954 line_data[identifier]['aims'].append(med['aim']) 955 if med['notes'] not in line_data[identifier]['notes']: 956 line_data[identifier]['notes'].append(med['notes']) 957 958 # create lines 959 already_seen = [] 960 lines = [] 961 line1_template = u'%s %s & %s & {\\scriptsize %s} \\\\' 962 line2_template = u' & \\multicolumn{2}{l|}{{\\scriptsize %s}} \\\\' 963 964 for med in current_meds: 965 identifier = gmTools.coalesce(med['brand'], med['substance']) 966 967 if identifier in already_seen: 968 continue 969 970 already_seen.append(identifier) 971 972 lines.append (line1_template % ( 973 line_data[identifier]['brand'], 974 line_data[identifier]['preparation'], 975 line_data[identifier]['schedule'], 976 u', '.join(line_data[identifier]['substances']) 977 )) 978 979 for aim in line_data[identifier]['aims']: 980 lines.append(line2_template % aim) 981 982 for note in line_data[identifier]['notes']: 983 lines.append(line2_template % note) 984 985 lines.append(u'\\hline') 986 987 return tex % u' \n'.join(lines)
988 #============================================================
989 -class cBrandedDrug(gmBusinessDBObject.cBusinessDBObject):
990 """Represents a drug as marketed by a manufacturer.""" 991 992 _cmd_fetch_payload = u"select *, xmin from ref.branded_drug where pk = %s" 993 _cmds_store_payload = [ 994 u"""update ref.branded_drug set 995 description = %(description)s, 996 preparation = %(preparation)s, 997 atc_code = gm.nullify_empty_string(%(atc_code)s), 998 external_code = gm.nullify_empty_string(%(external_code)s), 999 external_code_type = gm.nullify_empty_string(%(external_code_type)s), 1000 is_fake = %(is_fake)s, 1001 fk_data_source = %(fk_data_source)s 1002 where 1003 pk = %(pk)s and 1004 xmin = %(xmin)s 1005 returning 1006 xmin 1007 """ 1008 ] 1009 _updatable_fields = [ 1010 u'description', 1011 u'preparation', 1012 u'atc_code', 1013 u'is_fake', 1014 u'external_code', 1015 u'external_code_type', 1016 u'fk_data_source' 1017 ] 1018 #--------------------------------------------------------
1019 - def _get_external_code(self):
1020 if self._payload[self._idx['external_code']] is None: 1021 return None 1022 1023 return self._payload[self._idx['external_code']]
1024 1025 external_code = property(_get_external_code, lambda x:x) 1026 #--------------------------------------------------------
1027 - def _get_external_code_type(self):
1028 1029 # FIXME: maybe evaluate fk_data_source ? 1030 if self._payload[self._idx['external_code_type']] is None: 1031 return None 1032 1033 return self._payload[self._idx['external_code_type']]
1034 1035 external_code_type = property(_get_external_code_type, lambda x:x) 1036 #--------------------------------------------------------
1037 - def _get_components(self):
1038 cmd = u'select * from ref.substance_in_brand where fk_brand = %(brand)s' 1039 args = {'brand': self._payload[self._idx['pk']]} 1040 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1041 return rows
1042 1043 components = property(_get_components, lambda x:x) 1044 #--------------------------------------------------------
1045 - def add_component(self, substance=None, atc=None):
1046 1047 # normalize atc 1048 atc = gmATC.propagate_atc(substance = substance, atc = atc) 1049 1050 args = { 1051 'brand': self.pk_obj, 1052 'desc': substance, 1053 'atc': atc 1054 } 1055 1056 # already exists ? 1057 cmd = u""" 1058 SELECT pk 1059 FROM ref.substance_in_brand 1060 WHERE 1061 fk_brand = %(brand)s 1062 AND 1063 ((description = %(desc)s) OR ((atc_code = %(atc)s) IS TRUE)) 1064 """ 1065 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1066 if len(rows) > 0: 1067 return 1068 1069 # create it 1070 cmd = u""" 1071 INSERT INTO ref.substance_in_brand (fk_brand, description, atc_code) 1072 VALUES (%(brand)s, %(desc)s, %(atc)s) 1073 """ 1074 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1075 #------------------------------------------------------------
1076 - def remove_component(substance=None):
1077 delete_component_from_branded_drug(brand = self.pk_obj, component = substance)
1078 #------------------------------------------------------------
1079 -def get_substances_in_brands():
1080 cmd = u'SELECT * FROM ref.v_substance_in_brand ORDER BY brand, substance' 1081 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 1082 return rows
1083 #------------------------------------------------------------
1084 -def get_branded_drugs():
1085 1086 cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description' 1087 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 1088 1089 return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
1090 #------------------------------------------------------------
1091 -def get_drug_by_brand(brand_name=None, preparation=None):
1092 args = {'brand': brand_name, 'prep': preparation} 1093 1094 cmd = u'SELECT pk FROM ref.branded_drug WHERE description = %(brand)s AND preparation = %(prep)s' 1095 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1096 1097 if len(rows) == 0: 1098 return None 1099 1100 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1101 #------------------------------------------------------------
1102 -def create_branded_drug(brand_name=None, preparation=None, return_existing=False):
1103 1104 if preparation is None: 1105 preparation = _('units') 1106 1107 if preparation.strip() == u'': 1108 preparation = _('units') 1109 1110 drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation) 1111 1112 if drug is not None: 1113 if return_existing: 1114 return drug 1115 return None 1116 1117 cmd = u'insert into ref.branded_drug (description, preparation) values (%(brand)s, %(prep)s) returning pk' 1118 args = {'brand': brand_name, 'prep': preparation} 1119 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 1120 1121 return cBrandedDrug(aPK_obj = rows[0]['pk'])
1122 #------------------------------------------------------------
1123 -def delete_branded_drug(brand=None):
1124 cmd = u'delete from ref.branded_drug where pk = %(pk)s' 1125 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': brand}}])
1126 #------------------------------------------------------------
1127 -def delete_component_from_branded_drug(brand=None, component=None):
1128 cmd = u'delete from ref.substance_in_brand where fk_brand = %(brand)s and pk = %(comp)s' 1129 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'brand': brand, 'comp': component}}])
1130 #============================================================ 1131 # main 1132 #------------------------------------------------------------ 1133 if __name__ == "__main__": 1134 1135 if len(sys.argv) < 2: 1136 sys.exit() 1137 1138 if sys.argv[1] != 'test': 1139 sys.exit() 1140 1141 from Gnumed.pycommon import gmLog2 1142 from Gnumed.pycommon import gmI18N 1143 1144 gmI18N.activate_locale() 1145 # gmDateTime.init() 1146 #--------------------------------------------------------
1147 - def test_MMI_interface():
1148 mmi = cGelbeListeWineInterface() 1149 print mmi 1150 print "interface definition:", mmi.version 1151 print "database versions: ", mmi.get_data_source_version()
1152 #--------------------------------------------------------
1153 - def test_MMI_file():
1154 mmi_file = cGelbeListeCSVFile(filename = sys.argv[2]) 1155 for drug in mmi_file: 1156 print "-------------" 1157 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 1158 for stoff in drug['wirkstoffe']: 1159 print " Wirkstoff:", stoff 1160 print drug 1161 mmi_file.close()
1162 #--------------------------------------------------------
1163 - def test_mmi_switch_to():
1164 mmi = cGelbeListeWineInterface() 1165 mmi.switch_to_frontend(blocking = False)
1166 #--------------------------------------------------------
1167 - def test_mmi_select_drugs():
1168 mmi = cGelbeListeWineInterface() 1169 mmi_file = mmi.select_drugs() 1170 for drug in mmi_file: 1171 print "-------------" 1172 print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']) 1173 for stoff in drug['wirkstoffe']: 1174 print " Wirkstoff:", stoff 1175 print drug 1176 mmi_file.close()
1177 #--------------------------------------------------------
1178 - def test_mmi_import_drugs():
1179 mmi = cGelbeListeWineInterface() 1180 mmi.import_drugs()
1181 #--------------------------------------------------------
1182 - def test_mmi_interaction_check():
1183 mmi = cGelbeListeInterface() 1184 print mmi 1185 print "interface definition:", mmi.version 1186 # Metoprolol + Hct vs Citalopram 1187 diclofenac = '7587712' 1188 phenprocoumon = '4421744' 1189 mmi.check_drug_interactions(pzn_list = [diclofenac, phenprocoumon])
1190 #--------------------------------------------------------
1191 - def test_create_substance_intake():
1192 drug = create_substance_intake ( 1193 substance = u'Whiskey', 1194 atc = u'no ATC available', 1195 encounter = 1, 1196 episode = 1, 1197 preparation = 'a nice glass' 1198 ) 1199 print drug
1200 #--------------------------------------------------------
1201 - def test_show_components():
1202 drug = cBrandedDrug(aPK_obj = sys.argv[2]) 1203 print drug 1204 print drug.components
1205 #-------------------------------------------------------- 1206 #test_MMI_interface() 1207 #test_MMI_file() 1208 #test_mmi_switch_to() 1209 #test_mmi_select_drugs() 1210 #test_mmi_import_substances() 1211 #test_mmi_import_drugs() 1212 #test_interaction_check() 1213 #test_create_substance_intake() 1214 test_show_components() 1215 #============================================================ 1216