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