Package Gnumed :: Package business :: Module gmMedication
[frames] | no frames]

Source Code for Module Gnumed.business.gmMedication

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