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