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