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