Package Gnumed :: Package wxpython :: Module gmMacro
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmMacro

   1  #  coding: utf8 
   2  """GNUmed macro primitives. 
   3   
   4  This module implements functions a macro can legally use. 
   5  """ 
   6  #===================================================================== 
   7  __version__ = "$Revision: 1.51 $" 
   8  __author__ = "K.Hilbert <karsten.hilbert@gmx.net>" 
   9   
  10  import sys, time, random, types, logging 
  11   
  12   
  13  import wx 
  14   
  15   
  16  if __name__ == '__main__': 
  17          sys.path.insert(0, '../../') 
  18  from Gnumed.pycommon import gmI18N 
  19  if __name__ == '__main__': 
  20          gmI18N.activate_locale() 
  21          gmI18N.install_domain() 
  22  from Gnumed.pycommon import gmGuiBroker 
  23  from Gnumed.pycommon import gmTools 
  24  from Gnumed.pycommon import gmBorg 
  25  from Gnumed.pycommon import gmExceptions 
  26  from Gnumed.pycommon import gmCfg2 
  27  from Gnumed.pycommon import gmDateTime 
  28   
  29  from Gnumed.business import gmPerson 
  30  from Gnumed.business import gmStaff 
  31  from Gnumed.business import gmDemographicRecord 
  32  from Gnumed.business import gmMedication 
  33  from Gnumed.business import gmPathLab 
  34  from Gnumed.business import gmPersonSearch 
  35  from Gnumed.business import gmVaccination 
  36  from Gnumed.business import gmPersonSearch 
  37   
  38  from Gnumed.wxpython import gmGuiHelpers 
  39  from Gnumed.wxpython import gmNarrativeWidgets 
  40  from Gnumed.wxpython import gmPatSearchWidgets 
  41  from Gnumed.wxpython import gmPersonContactWidgets 
  42  from Gnumed.wxpython import gmPlugin 
  43  from Gnumed.wxpython import gmEMRStructWidgets 
  44  from Gnumed.wxpython import gmListWidgets 
  45  from Gnumed.wxpython import gmDemographicsWidgets 
  46   
  47   
  48  _log = logging.getLogger('gm.scripting') 
  49  _cfg = gmCfg2.gmCfgData() 
  50   
  51  #===================================================================== 
  52  known_placeholders = [ 
  53          'lastname', 
  54          'firstname', 
  55          'title', 
  56          'date_of_birth', 
  57          'progress_notes', 
  58          'soap', 
  59          'soap_s', 
  60          'soap_o', 
  61          'soap_a', 
  62          'soap_p', 
  63          'soap_u', 
  64          u'client_version', 
  65          u'current_provider', 
  66          u'primary_praxis_provider',                     # primary provider for current patient in this praxis 
  67          u'allergy_state' 
  68  ] 
  69   
  70   
  71  # values for the following placeholders must be injected from the outside before 
  72  # using them, in use they must conform to the "placeholder::::max length" syntax, 
  73  # as long as they resolve to None they return themselves 
  74  _injectable_placeholders = { 
  75          u'form_name_long': None, 
  76          u'form_name_short': None, 
  77          u'form_version': None 
  78  } 
  79   
  80   
  81  # the following must satisfy the pattern "$<name::args::(optional) max string length>$" when used 
  82  known_variant_placeholders = [ 
  83          u'soap', 
  84          u'progress_notes',                      # "args" holds: categories//template 
  85                                                                  #       categories: string with 'soapu '; ' ' == None == admin 
  86                                                                  #       template:       u'something %s something'               (do not include // in template !) 
  87          u'emr_journal',                         # "args" format:   <categories>//<template>//<line length>//<time range>//<target format> 
  88                                                                  #       categories:        string with any of "s", "o", "a", "p", "u", " "; 
  89                                                                  #                                  (" " == None == admin category) 
  90                                                                  #       template:          something %s something else 
  91                                                                  #                                  (Do not include // in the template !) 
  92                                                                  #       line length:   the length of individual lines, not the total placeholder length 
  93                                                                  #       time range:        the number of weeks going back in time 
  94                                                                  #       target format: "tex" or anything else, if "tex", data will be tex-escaped 
  95          u'date_of_birth', 
  96   
  97          u'patient_address',                     # "args": <type of address>//<optional formatting template> 
  98          u'adr_street',                          # "args" holds: type of address 
  99          u'adr_number', 
 100          u'adr_subunit', 
 101          u'adr_location', 
 102          u'adr_suburb', 
 103          u'adr_postcode', 
 104          u'adr_region', 
 105          u'adr_country', 
 106   
 107          u'patient_comm',                                                # args: comm channel type as per database 
 108          u'patient_tags',                                                # "args" holds: <%(key)s-template>//<separator> 
 109  #       u'patient_tags_table',                                  # "args" holds: no args 
 110          u'external_id',                                                 # args: <type of ID>//<issuer of ID> 
 111          u'gender_mapper',                                               # "args" holds: <value when person is male> // <is female> // <is other> 
 112                                                                                          #                               eg. "male//female//other" 
 113                                                                                          #                               or: "Lieber Patient//Liebe Patientin" 
 114          u'current_meds',                                                # "args" holds: line template 
 115          u'current_meds_table',                                  # "args" holds: format, options 
 116                                                                                          #                               currently only "latex" 
 117          u'current_meds_notes',                                  # "args" holds: format, options 
 118          u'lab_table',                                                   # "args" holds: format (currently "latex" only) 
 119          u'latest_vaccs_table',                                  # "args" holds: format, options 
 120          u'vaccination_history',                                 # "args": <%(key)s-template//date format> to format one vaccination per line 
 121          u'today',                                                               # "args" holds: strftime format 
 122          u'tex_escape',                                                  # "args" holds: string to escape 
 123          u'allergies',                                                   # "args" holds: line template, one allergy per line 
 124          u'allergy_list',                                                # "args" holds: template per allergy, allergies on one line 
 125          u'problems',                                                    # "args" holds: line template, one problem per line 
 126          u'PHX',                                                                 # Past medical HiXtory, "args" holds: line template//separator//strftime date format//escape style (latex, currently) 
 127          u'name',                                                                # "args" holds: template for name parts arrangement 
 128          u'free_text',                                                   # show a dialog for entering some free text 
 129          u'soap_for_encounters',                                 # "args" holds: soap cats // strftime date format 
 130          u'encounter_list',                                              # "args" holds: per-encounter template, each ends up on one line 
 131          u'current_provider_external_id',                # args: <type of ID>//<issuer of ID> 
 132          u'primary_praxis_provider_external_id', # args: <type of ID>//<issuer of ID> 
 133   
 134          u'bill',                                                                # args: template for string replacement 
 135          u'bill_item'                                                    # args: template for string replacement 
 136  ] 
 137   
 138  default_placeholder_regex = r'\$<.+?>\$'                                # this one works (except that OOo cannot be non-greedy |-( ) 
 139   
 140  #_regex_parts = [ 
 141  #       r'\$<\w+::.*(?::)\d+>\$', 
 142  #       r'\$<\w+::.+(?!>\$)>\$', 
 143  #       r'\$<\w+?>\$' 
 144  #] 
 145  #default_placeholder_regex = r'|'.join(_regex_parts) 
 146   
 147  default_placeholder_start = u'$<' 
 148  default_placeholder_end = u'>$' 
 149  #===================================================================== 
150 -class gmPlaceholderHandler(gmBorg.cBorg):
151 """Returns values for placeholders. 152 153 - patient related placeholders operate on the currently active patient 154 - is passed to the forms handling code, for example 155 156 Return values when .debug is False: 157 - errors with placeholders return None 158 - placeholders failing to resolve to a value return an empty string 159 160 Return values when .debug is True: 161 - errors with placeholders return an error string 162 - placeholders failing to resolve to a value return a warning string 163 164 There are several types of placeholders: 165 166 simple static placeholders 167 - those are listed in known_placeholders 168 - they are used as-is 169 170 extended static placeholders 171 - those are, effectively, static placeholders 172 with a maximum length attached (after "::::") 173 174 injectable placeholders 175 - they must be set up before use by set_placeholder() 176 - they should be removed after use by unset_placeholder() 177 - the syntax is like extended static placeholders 178 - they are listed in _injectable_placeholders 179 180 variant placeholders 181 - those are listed in known_variant_placeholders 182 - they are parsed into placeholder, data, and maximum length 183 - the length is optional 184 - data is passed to the handler 185 186 Note that this cannot be called from a non-gui thread unless 187 wrapped in wx.CallAfter(). 188 """
189 - def __init__(self, *args, **kwargs):
190 191 self.pat = gmPerson.gmCurrentPatient() 192 self.debug = False 193 194 self.invalid_placeholder_template = _('invalid placeholder [%s]') 195 196 self.__cache = {}
197 #-------------------------------------------------------- 198 # external API 199 #--------------------------------------------------------
200 - def set_placeholder(self, key=None, value=None):
203 #--------------------------------------------------------
204 - def unset_placeholder(self, key=None):
207 #--------------------------------------------------------
208 - def set_cache_value(self, key=None, value=None):
209 self.__cache[key] = value
210 #--------------------------------------------------------
211 - def unset_cache_value(self, key=None):
212 del self.__cache[key]
213 #-------------------------------------------------------- 214 # __getitem__ API 215 #--------------------------------------------------------
216 - def __getitem__(self, placeholder):
217 """Map self['placeholder'] to self.placeholder. 218 219 This is useful for replacing placeholders parsed out 220 of documents as strings. 221 222 Unknown/invalid placeholders still deliver a result but 223 it will be glaringly obvious if debugging is enabled. 224 """ 225 _log.debug('replacing [%s]', placeholder) 226 227 original_placeholder = placeholder 228 229 if placeholder.startswith(default_placeholder_start): 230 placeholder = placeholder[len(default_placeholder_start):] 231 if placeholder.endswith(default_placeholder_end): 232 placeholder = placeholder[:-len(default_placeholder_end)] 233 else: 234 _log.debug('placeholder must either start with [%s] and end with [%s] or neither of both', default_placeholder_start, default_placeholder_end) 235 if self.debug: 236 return self.invalid_placeholder_template % original_placeholder 237 return None 238 239 # simple static placeholder ? 240 if placeholder in known_placeholders: 241 return getattr(self, placeholder) 242 243 # injectable placeholder ? 244 parts = placeholder.split('::::', 1) 245 if len(parts) == 2: 246 name, lng = parts 247 unknown_injectable = False 248 try: 249 val = _injectable_placeholders[name] 250 except KeyError: 251 unknown_injectable = True 252 except: 253 _log.exception('placeholder handling error: %s', original_placeholder) 254 if self.debug: 255 return self.invalid_placeholder_template % original_placeholder 256 return None 257 if not unknown_injectable: 258 if val is None: 259 if self.debug: 260 return u'injectable placeholder [%s]: no value available' % name 261 return placeholder 262 return val[:int(lng)] 263 264 # extended static placeholder ? 265 parts = placeholder.split('::::', 1) 266 if len(parts) == 2: 267 name, lng = parts 268 try: 269 return getattr(self, name)[:int(lng)] 270 except: 271 _log.exception('placeholder handling error: %s', original_placeholder) 272 if self.debug: 273 return self.invalid_placeholder_template % original_placeholder 274 return None 275 276 # variable placeholders 277 parts = placeholder.split('::') 278 279 if len(parts) == 1: 280 _log.warning('invalid placeholder layout: %s', original_placeholder) 281 if self.debug: 282 return self.invalid_placeholder_template % original_placeholder 283 return None 284 285 if len(parts) == 2: 286 name, data = parts 287 lng = None 288 289 if len(parts) == 3: 290 name, data, lng = parts 291 try: 292 lng = int(lng) 293 except (TypeError, ValueError): 294 _log.error('placeholder length definition error: %s, discarding length: >%s<', original_placeholder, lng) 295 lng = None 296 297 if len(parts) > 3: 298 _log.warning('invalid placeholder layout: %s', original_placeholder) 299 if self.debug: 300 return self.invalid_placeholder_template % original_placeholder 301 return None 302 303 handler = getattr(self, '_get_variant_%s' % name, None) 304 if handler is None: 305 _log.warning('no handler <_get_variant_%s> for placeholder %s', name, original_placeholder) 306 if self.debug: 307 return self.invalid_placeholder_template % original_placeholder 308 return None 309 310 try: 311 if lng is None: 312 return handler(data = data) 313 return handler(data = data)[:lng] 314 except: 315 _log.exception('placeholder handling error: %s', original_placeholder) 316 if self.debug: 317 return self.invalid_placeholder_template % original_placeholder 318 return None 319 320 _log.error('something went wrong, should never get here') 321 return None
322 #-------------------------------------------------------- 323 # properties actually handling placeholders 324 #-------------------------------------------------------- 325 # property helpers 326 #--------------------------------------------------------
327 - def _setter_noop(self, val):
328 """This does nothing, used as a NOOP properties setter.""" 329 pass
330 #--------------------------------------------------------
331 - def _get_lastname(self):
332 return self.pat.get_active_name()['lastnames']
333 #--------------------------------------------------------
334 - def _get_firstname(self):
335 return self.pat.get_active_name()['firstnames']
336 #--------------------------------------------------------
337 - def _get_title(self):
338 return gmTools.coalesce(self.pat.get_active_name()['title'], u'')
339 #--------------------------------------------------------
340 - def _get_dob(self):
341 return self._get_variant_date_of_birth(data='%x')
342 #--------------------------------------------------------
343 - def _get_progress_notes(self):
344 return self._get_variant_soap()
345 #--------------------------------------------------------
346 - def _get_soap_s(self):
347 return self._get_variant_soap(data = u's')
348 #--------------------------------------------------------
349 - def _get_soap_o(self):
350 return self._get_variant_soap(data = u'o')
351 #--------------------------------------------------------
352 - def _get_soap_a(self):
353 return self._get_variant_soap(data = u'a')
354 #--------------------------------------------------------
355 - def _get_soap_p(self):
356 return self._get_variant_soap(data = u'p')
357 #--------------------------------------------------------
358 - def _get_soap_u(self):
359 return self._get_variant_soap(data = u'u')
360 #--------------------------------------------------------
361 - def _get_soap_admin(self):
362 return self._get_variant_soap(soap_cats = None)
363 #--------------------------------------------------------
364 - def _get_client_version(self):
365 return gmTools.coalesce ( 366 _cfg.get(option = u'client_version'), 367 u'%s' % self.__class__.__name__ 368 )
369 #--------------------------------------------------------
371 prov = self.pat.primary_provider 372 if prov is None: 373 return self._get_current_provider() 374 375 title = gmTools.coalesce ( 376 prov['title'], 377 gmPerson.map_gender2salutation(prov['gender']) 378 ) 379 380 tmp = u'%s %s. %s' % ( 381 title, 382 prov['firstnames'][:1], 383 prov['lastnames'] 384 ) 385 386 return tmp
387 #--------------------------------------------------------
388 - def _get_current_provider(self):
389 prov = gmStaff.gmCurrentProvider() 390 391 title = gmTools.coalesce ( 392 prov['title'], 393 gmPerson.map_gender2salutation(prov['gender']) 394 ) 395 396 tmp = u'%s %s. %s' % ( 397 title, 398 prov['firstnames'][:1], 399 prov['lastnames'] 400 ) 401 402 return tmp
403 #--------------------------------------------------------
404 - def _get_allergy_state(self):
405 allg_state = self.pat.get_emr().allergy_state 406 407 if allg_state['last_confirmed'] is None: 408 date_confirmed = u'' 409 else: 410 date_confirmed = u' (%s)' % allg_state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding()) 411 412 tmp = u'%s%s' % ( 413 allg_state.state_string, 414 date_confirmed 415 ) 416 return tmp
417 #-------------------------------------------------------- 418 # property definitions for static placeholders 419 #-------------------------------------------------------- 420 placeholder_regex = property(lambda x: default_placeholder_regex, _setter_noop) 421 422 #-------------------------------------------------------- 423 424 # placeholders 425 lastname = property(_get_lastname, _setter_noop) 426 firstname = property(_get_firstname, _setter_noop) 427 title = property(_get_title, _setter_noop) 428 date_of_birth = property(_get_dob, _setter_noop) 429 430 progress_notes = property(_get_progress_notes, _setter_noop) 431 soap = property(_get_progress_notes, _setter_noop) 432 soap_s = property(_get_soap_s, _setter_noop) 433 soap_o = property(_get_soap_o, _setter_noop) 434 soap_a = property(_get_soap_a, _setter_noop) 435 soap_p = property(_get_soap_p, _setter_noop) 436 soap_u = property(_get_soap_u, _setter_noop) 437 soap_admin = property(_get_soap_admin, _setter_noop) 438 439 allergy_state = property(_get_allergy_state, _setter_noop) 440 441 client_version = property(_get_client_version, _setter_noop) 442 443 current_provider = property(_get_current_provider, _setter_noop) 444 primary_praxis_provider = property(_get_primary_praxis_provider, _setter_noop) 445 #-------------------------------------------------------- 446 # variant handlers 447 #--------------------------------------------------------
448 - def _get_variant_encounter_list(self, data=None):
449 450 encounters = gmEMRStructWidgets.select_encounters(single_selection = False) 451 if not encounters: 452 return u'' 453 454 template = data 455 456 lines = [] 457 for enc in encounters: 458 try: 459 lines.append(template % enc) 460 except: 461 lines.append(u'error formatting encounter') 462 _log.exception('problem formatting encounter list') 463 _log.error('template: %s', template) 464 _log.error('encounter: %s', encounter) 465 466 return u'\n'.join(lines)
467 #--------------------------------------------------------
468 - def _get_variant_soap_for_encounters(self, data=None):
469 """Select encounters from list and format SOAP thereof. 470 471 data: soap_cats (' ' -> None -> admin) // date format 472 """ 473 # defaults 474 cats = None 475 date_format = None 476 477 if data is not None: 478 data_parts = data.split('//') 479 480 # part[0]: categories 481 if len(data_parts[0]) > 0: 482 cats = [] 483 if u' ' in data_parts[0]: 484 cats.append(None) 485 data_parts[0] = data_parts[0].replace(u' ', u'') 486 cats.extend(list(data_parts[0])) 487 488 # part[1]: date format 489 if len(data_parts) > 1: 490 if len(data_parts[1]) > 0: 491 date_format = data_parts[1] 492 493 encounters = gmEMRStructWidgets.select_encounters(single_selection = False) 494 if not encounters: 495 return u'' 496 497 chunks = [] 498 for enc in encounters: 499 chunks.append(enc.format_latex ( 500 date_format = date_format, 501 soap_cats = cats, 502 soap_order = u'soap_rank, date' 503 )) 504 505 return u''.join(chunks)
506 #--------------------------------------------------------
507 - def _get_variant_emr_journal(self, data=None):
508 # default: all categories, neutral template 509 cats = list(u'soapu') 510 cats.append(None) 511 template = u'%s' 512 interactive = True 513 line_length = 9999 514 target_format = None 515 time_range = None 516 517 if data is not None: 518 data_parts = data.split('//') 519 520 # part[0]: categories 521 cats = [] 522 # ' ' -> None == admin 523 for c in list(data_parts[0]): 524 if c == u' ': 525 c = None 526 cats.append(c) 527 # '' -> SOAP + None 528 if cats == u'': 529 cats = list(u'soapu').append(None) 530 531 # part[1]: template 532 if len(data_parts) > 1: 533 template = data_parts[1] 534 535 # part[2]: line length 536 if len(data_parts) > 2: 537 try: 538 line_length = int(data_parts[2]) 539 except: 540 line_length = 9999 541 542 # part[3]: weeks going back in time 543 if len(data_parts) > 3: 544 try: 545 time_range = 7 * int(data_parts[3]) 546 except: 547 time_range = None 548 549 # part[4]: output format 550 if len(data_parts) > 4: 551 target_format = data_parts[4] 552 553 # FIXME: will need to be a generator later on 554 narr = self.pat.get_emr().get_as_journal(soap_cats = cats, time_range = time_range) 555 556 if len(narr) == 0: 557 return u'' 558 559 if target_format == u'tex': 560 keys = narr[0].keys() 561 lines = [] 562 line_dict = {} 563 for n in narr: 564 for key in keys: 565 if isinstance(n[key], basestring): 566 line_dict[key] = gmTools.tex_escape_string(text = n[key]) 567 continue 568 line_dict[key] = n[key] 569 try: 570 lines.append((template % line_dict)[:line_length]) 571 except KeyError: 572 return u'invalid key in template [%s], valid keys: %s]' % (template, str(keys)) 573 else: 574 try: 575 lines = [ (template % n)[:line_length] for n in narr ] 576 except KeyError: 577 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys())) 578 579 return u'\n'.join(lines)
580 #--------------------------------------------------------
581 - def _get_variant_progress_notes(self, data=None):
582 return self._get_variant_soap(data=data)
583 #--------------------------------------------------------
584 - def _get_variant_soap(self, data=None):
585 586 # default: all categories, neutral template 587 cats = list(u'soapu') 588 cats.append(None) 589 template = u'%s' 590 591 if data is not None: 592 data_parts = data.split('//') 593 594 # part[0]: categories 595 cats = [] 596 # ' ' -> None == admin 597 for cat in list(data_parts[0]): 598 if cat == u' ': 599 cat = None 600 cats.append(cat) 601 # '' -> SOAP + None 602 if cats == u'': 603 cats = list(u'soapu') 604 cats.append(None) 605 606 # part[1]: template 607 if len(data_parts) > 1: 608 template = data_parts[1] 609 610 #narr = gmNarrativeWidgets.select_narrative_from_episodes_new(soap_cats = cats) 611 narr = gmNarrativeWidgets.select_narrative_from_episodes(soap_cats = cats) 612 613 if narr is None: 614 return u'' 615 616 if len(narr) == 0: 617 return u'' 618 619 try: 620 narr = [ template % n['narrative'] for n in narr ] 621 except KeyError: 622 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys())) 623 624 return u'\n'.join(narr)
625 #--------------------------------------------------------
626 - def _get_variant_name(self, data=None):
627 if data is None: 628 return [_('template is missing')] 629 630 name = self.pat.get_active_name() 631 632 parts = { 633 'title': gmTools.coalesce(name['title'], u''), 634 'firstnames': name['firstnames'], 635 'lastnames': name['lastnames'], 636 'preferred': gmTools.coalesce ( 637 initial = name['preferred'], 638 instead = u' ', 639 template_initial = u' "%s" ' 640 ) 641 } 642 643 return data % parts
644 #--------------------------------------------------------
645 - def _get_variant_date_of_birth(self, data='%x'):
646 return self.pat.get_formatted_dob(format = str(data), encoding = gmI18N.get_encoding())
647 #-------------------------------------------------------- 648 # FIXME: extend to all supported genders
649 - def _get_variant_gender_mapper(self, data='male//female//other'):
650 values = data.split('//', 2) 651 652 if len(values) == 2: 653 male_value, female_value = values 654 other_value = u'<unkown gender>' 655 elif len(values) == 3: 656 male_value, female_value, other_value = values 657 else: 658 return _('invalid gender mapping layout: [%s]') % data 659 660 if self.pat['gender'] == u'm': 661 return male_value 662 663 if self.pat['gender'] == u'f': 664 return female_value 665 666 return other_value
667 #-------------------------------------------------------- 668 # address related placeholders 669 #--------------------------------------------------------
670 - def _get_variant_patient_address(self, data=u''):
671 672 data_parts = data.split(u'//') 673 674 # address type 675 adr_type = data_parts[0].strip() 676 orig_type = adr_type 677 if adr_type != u'': 678 adrs = self.pat.get_addresses(address_type = adr_type) 679 if len(adrs) == 0: 680 _log.warning('no address for type [%s]', adr_type) 681 adr_type = u'' 682 if adr_type == u'': 683 _log.debug('asking user for address type') 684 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat) 685 if adr is None: 686 if self.debug: 687 return _('no address type replacement selected') 688 return u'' 689 adr_type = adr['address_type'] 690 adr = self.pat.get_addresses(address_type = adr_type)[0] 691 692 # formatting template 693 template = _('%(street)s %(number)s, %(postcode)s %(urb)s, %(l10n_state)s, %(l10n_country)s') 694 if len(data_parts) > 1: 695 if data_parts[1].strip() != u'': 696 template = data_parts[1] 697 698 try: 699 return template % adr.fields_as_dict() 700 except StandardError: 701 _log.exception('error formatting address') 702 _log.error('template: %s', template) 703 704 return None
705 #--------------------------------------------------------
706 - def __get_variant_adr_part(self, data=u'?', part=None):
707 requested_type = data.strip() 708 cache_key = 'adr-type-%s' % requested_type 709 try: 710 type2use = self.__cache[cache_key] 711 _log.debug('cache hit (%s): [%s] -> [%s]', cache_key, requested_type, type2use) 712 except KeyError: 713 type2use = requested_type 714 if type2use != u'': 715 adrs = self.pat.get_addresses(address_type = type2use) 716 if len(adrs) == 0: 717 _log.warning('no address of type [%s] for <%s> field extraction', requested_type, part) 718 type2use = u'' 719 if type2use == u'': 720 _log.debug('asking user for replacement address type') 721 adr = gmPersonContactWidgets.select_address(missing = requested_type, person = self.pat) 722 if adr is None: 723 _log.debug('no replacement selected') 724 if self.debug: 725 return _('no address type replacement selected') 726 return u'' 727 type2use = adr['address_type'] 728 self.__cache[cache_key] = type2use 729 _log.debug('caching (%s): [%s] -> [%s]', cache_key, requested_type, type2use) 730 731 return self.pat.get_addresses(address_type = type2use)[0][part]
732 #--------------------------------------------------------
733 - def _get_variant_adr_street(self, data=u'?'):
734 return self.__get_variant_adr_part(data = data, part = 'street')
735 #--------------------------------------------------------
736 - def _get_variant_adr_number(self, data=u'?'):
737 return self.__get_variant_adr_part(data = data, part = 'number')
738 #--------------------------------------------------------
739 - def _get_variant_adr_subunit(self, data=u'?'):
740 return self.__get_variant_adr_part(data = data, part = 'subunit')
741 #--------------------------------------------------------
742 - def _get_variant_adr_location(self, data=u'?'):
743 return self.__get_variant_adr_part(data = data, part = 'urb')
744 #--------------------------------------------------------
745 - def _get_variant_adr_suburb(self, data=u'?'):
746 return self.__get_variant_adr_part(data = data, part = 'suburb')
747 #--------------------------------------------------------
748 - def _get_variant_adr_postcode(self, data=u'?'):
749 return self.__get_variant_adr_part(data = data, part = 'postcode')
750 #--------------------------------------------------------
751 - def _get_variant_adr_region(self, data=u'?'):
752 return self.__get_variant_adr_part(data = data, part = 'l10n_state')
753 #--------------------------------------------------------
754 - def _get_variant_adr_country(self, data=u'?'):
755 return self.__get_variant_adr_part(data = data, part = 'l10n_country')
756 #--------------------------------------------------------
757 - def _get_variant_patient_comm(self, data=u'?'):
758 comms = self.pat.get_comm_channels(comm_medium = data) 759 if len(comms) == 0: 760 if self.debug: 761 return _('no URL for comm channel [%s]') % data 762 return u'' 763 return comms[0]['url']
764 #--------------------------------------------------------
765 - def _get_variant_patient_tags(self, data=u'%s//\\n'):
766 if len(self.pat.tags) == 0: 767 if self.debug: 768 return _('no tags for this patient') 769 return u'' 770 771 tags = gmDemographicsWidgets.select_patient_tags(patient = self.pat) 772 773 if tags is None: 774 if self.debug: 775 return _('no patient tags selected for inclusion') % data 776 return u'' 777 778 template, separator = data.split('//', 2) 779 780 return separator.join([ template % t.fields_as_dict() for t in tags ])
781 # #-------------------------------------------------------- 782 # def _get_variant_patient_tags_table(self, data=u'?'): 783 # pass 784 #--------------------------------------------------------
785 - def _get_variant_current_provider_external_id(self, data=u''):
786 data_parts = data.split(u'//') 787 if len(data_parts) < 2: 788 return u'current provider external ID: template is missing' 789 790 id_type = data_parts[0].strip() 791 if id_type == u'': 792 return u'current provider external ID: type is missing' 793 794 issuer = data_parts[1].strip() 795 if issuer == u'': 796 return u'current provider external ID: issuer is missing' 797 798 prov = gmStaff.gmCurrentProvider() 799 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer) 800 801 if len(ids) == 0: 802 if self.debug: 803 return _('no external ID [%s] by [%s]') % (id_type, issuer) 804 return u'' 805 806 return ids[0]['value']
807 #--------------------------------------------------------
809 data_parts = data.split(u'//') 810 if len(data_parts) < 2: 811 return u'primary in-praxis provider external ID: template is missing' 812 813 id_type = data_parts[0].strip() 814 if id_type == u'': 815 return u'primary in-praxis provider external ID: type is missing' 816 817 issuer = data_parts[1].strip() 818 if issuer == u'': 819 return u'primary in-praxis provider external ID: issuer is missing' 820 821 prov = self.pat.primary_provider 822 if prov is None: 823 if self.debug: 824 return _('no primary in-praxis provider') 825 return u'' 826 827 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer) 828 829 if len(ids) == 0: 830 if self.debug: 831 return _('no external ID [%s] by [%s]') % (id_type, issuer) 832 return u'' 833 834 return ids[0]['value']
835 #--------------------------------------------------------
836 - def _get_variant_external_id(self, data=u''):
837 data_parts = data.split(u'//') 838 if len(data_parts) < 2: 839 return u'patient external ID: template is missing' 840 841 id_type = data_parts[0].strip() 842 if id_type == u'': 843 return u'patient external ID: type is missing' 844 845 issuer = data_parts[1].strip() 846 if issuer == u'': 847 return u'patient external ID: issuer is missing' 848 849 ids = self.pat.get_external_ids(id_type = id_type, issuer = issuer) 850 851 if len(ids) == 0: 852 if self.debug: 853 return _('no external ID [%s] by [%s]') % (id_type, issuer) 854 return u'' 855 856 return ids[0]['value']
857 #--------------------------------------------------------
858 - def _get_variant_allergy_list(self, data=None):
859 if data is None: 860 return [_('template is missing')] 861 862 template, separator = data.split('//', 2) 863 864 emr = self.pat.get_emr() 865 return separator.join([ template % a for a in emr.get_allergies() ])
866 #--------------------------------------------------------
867 - def _get_variant_allergies(self, data=None):
868 869 if data is None: 870 return [_('template is missing')] 871 872 emr = self.pat.get_emr() 873 return u'\n'.join([ data % a for a in emr.get_allergies() ])
874 #--------------------------------------------------------
875 - def _get_variant_current_meds(self, data=None):
876 877 if data is None: 878 return [_('template is missing')] 879 880 emr = self.pat.get_emr() 881 current_meds = emr.get_current_substance_intake ( 882 include_inactive = False, 883 include_unapproved = False, 884 order_by = u'brand, substance' 885 ) 886 887 return u'\n'.join([ data % m.fields_as_dict(date_format = '%Y %B %d') for m in current_meds ])
888 #--------------------------------------------------------
889 - def _get_variant_current_meds_table(self, data=None):
890 891 options = data.split('//') 892 893 if u'latex' in options: 894 return gmMedication.format_substance_intake ( 895 emr = self.pat.get_emr(), 896 output_format = u'latex', 897 table_type = u'by-brand' 898 ) 899 900 _log.error('no known current medications table formatting style in [%s]', data) 901 return _('unknown current medication table formatting style')
902 #--------------------------------------------------------
903 - def _get_variant_current_meds_notes(self, data=None):
904 905 options = data.split('//') 906 907 if u'latex' in options: 908 return gmMedication.format_substance_intake_notes ( 909 emr = self.pat.get_emr(), 910 output_format = u'latex', 911 table_type = u'by-brand' 912 ) 913 914 _log.error('no known current medications notes formatting style in [%s]', data) 915 return _('unknown current medication notes formatting style')
916 #--------------------------------------------------------
917 - def _get_variant_lab_table(self, data=None):
918 919 options = data.split('//') 920 921 emr = self.pat.get_emr() 922 923 if u'latex' in options: 924 return gmPathLab.format_test_results ( 925 results = emr.get_test_results_by_date(), 926 output_format = u'latex' 927 ) 928 929 _log.error('no known test results table formatting style in [%s]', data) 930 return _('unknown test results table formatting style [%s]') % data
931 #--------------------------------------------------------
932 - def _get_variant_latest_vaccs_table(self, data=None):
933 934 options = data.split('//') 935 936 emr = self.pat.get_emr() 937 938 if u'latex' in options: 939 return gmVaccination.format_latest_vaccinations(output_format = u'latex', emr = emr) 940 941 _log.error('no known vaccinations table formatting style in [%s]', data) 942 return _('unknown vaccinations table formatting style [%s]') % data
943 #--------------------------------------------------------
944 - def _get_variant_vaccination_history(self, data=None):
945 options = data.split('//') 946 template = options[0] 947 if len(options) > 1: 948 date_format = options[1] 949 else: 950 date_format = u'%Y %B %d' 951 952 emr = self.pat.get_emr() 953 vaccs = emr.get_vaccinations(order_by = u'date_given DESC, vaccine') 954 955 return u'\n'.join([ template % v.fields_as_dict(date_format = date_format) for v in vaccs ])
956 #--------------------------------------------------------
957 - def _get_variant_PHX(self, data=None):
958 959 if data is None: 960 if self.debug: 961 _log.error('PHX: missing placeholder arguments') 962 return _('PHX: Invalid placeholder options.') 963 return u'' 964 965 _log.debug('arguments: %s', data) 966 967 data_parts = data.split(u'//') 968 template = u'%s' 969 separator = u'\n' 970 date_format = '%Y %B %d' 971 esc_style = None 972 try: 973 template = data_parts[0] 974 separator = data_parts[1] 975 date_format = data_parts[2] 976 esc_style = data_parts[3] 977 except IndexError: 978 pass 979 980 phxs = gmEMRStructWidgets.select_health_issues(emr = self.pat.emr) 981 if phxs is None: 982 if self.debug: 983 return _('no PHX for this patient (available or selected)') 984 return u'' 985 986 return separator.join ([ 987 template % phx.fields_as_dict ( 988 date_format = date_format, 989 escape_style = esc_style, 990 bool_strings = (_('yes'), _('no')) 991 ) for phx in phxs 992 ])
993 #--------------------------------------------------------
994 - def _get_variant_problems(self, data=None):
995 996 if data is None: 997 return [_('template is missing')] 998 999 probs = self.pat.get_emr().get_problems() 1000 1001 return u'\n'.join([ data % p for p in probs ])
1002 #--------------------------------------------------------
1003 - def _get_variant_today(self, data='%x'):
1004 return gmDateTime.pydt_now_here().strftime(str(data)).decode(gmI18N.get_encoding())
1005 #--------------------------------------------------------
1006 - def _get_variant_tex_escape(self, data=None):
1007 return gmTools.tex_escape_string(text = data)
1008 #--------------------------------------------------------
1009 - def _get_variant_free_text(self, data=u'tex//'):
1010 # <data>: 1011 # format: tex (only, currently) 1012 # message: shown in input dialog, must not contain "//" or "::" 1013 1014 data_parts = data.split('//') 1015 format = data_parts[0] 1016 if len(data_parts) > 1: 1017 msg = data_parts[1] 1018 else: 1019 msg = _('generic text') 1020 1021 dlg = gmGuiHelpers.cMultilineTextEntryDlg ( 1022 None, 1023 -1, 1024 title = _('Replacing <free_text> placeholder'), 1025 msg = _('Below you can enter free text.\n\n [%s]') % msg 1026 ) 1027 dlg.enable_user_formatting = True 1028 decision = dlg.ShowModal() 1029 1030 if decision != wx.ID_SAVE: 1031 dlg.Destroy() 1032 if self.debug: 1033 return _('Text input cancelled by user.') 1034 return u'' 1035 1036 text = dlg.value.strip() 1037 if dlg.is_user_formatted: 1038 dlg.Destroy() 1039 return text 1040 1041 dlg.Destroy() 1042 1043 if format == u'tex': 1044 return gmTools.tex_escape_string(text = text) 1045 1046 return text
1047 #--------------------------------------------------------
1048 - def _get_variant_bill(self, data=None):
1049 try: 1050 bill = self.__cache['bill'] 1051 except KeyError: 1052 from Gnumed.wxpython import gmBillingWidgets 1053 bill = gmBillingWidgets.manage_bills(patient = self.pat) 1054 if bill is None: 1055 if self.debug: 1056 return _('no bill selected') 1057 return u'' 1058 self.__cache['bill'] = bill 1059 1060 return data % bill.fields_as_dict(date_format = '%Y %B %d')
1061 #--------------------------------------------------------
1062 - def _get_variant_bill_item(self, data=None):
1063 try: 1064 bill = self.__cache['bill'] 1065 except KeyError: 1066 from Gnumed.wxpython import gmBillingWidgets 1067 bill = gmBillingWidgets.manage_bills(patient = self.pat) 1068 if bill is None: 1069 if self.debug: 1070 return _('no bill selected') 1071 return u'' 1072 self.__cache['bill'] = bill 1073 1074 return u'\n'.join([ data % i.fields_as_dict(date_format = '%Y %B %d') for i in bill.bill_items ])
1075 #-------------------------------------------------------- 1076 # internal helpers 1077 #--------------------------------------------------------
1078 - def __let_user_select_comm_type(self, missing=None):
1079 pass
1080 #=====================================================================
1081 -class cMacroPrimitives:
1082 """Functions a macro can legally use. 1083 1084 An instance of this class is passed to the GNUmed scripting 1085 listener. Hence, all actions a macro can legally take must 1086 be defined in this class. Thus we achieve some screening for 1087 security and also thread safety handling. 1088 """ 1089 #-----------------------------------------------------------------
1090 - def __init__(self, personality = None):
1091 if personality is None: 1092 raise gmExceptions.ConstructorError, 'must specify personality' 1093 self.__personality = personality 1094 self.__attached = 0 1095 self._get_source_personality = None 1096 self.__user_done = False 1097 self.__user_answer = 'no answer yet' 1098 self.__pat = gmPerson.gmCurrentPatient() 1099 1100 self.__auth_cookie = str(random.random()) 1101 self.__pat_lock_cookie = str(random.random()) 1102 self.__lock_after_load_cookie = str(random.random()) 1103 1104 _log.info('slave mode personality is [%s]', personality)
1105 #----------------------------------------------------------------- 1106 # public API 1107 #-----------------------------------------------------------------
1108 - def attach(self, personality = None):
1109 if self.__attached: 1110 _log.error('attach with [%s] rejected, already serving a client', personality) 1111 return (0, _('attach rejected, already serving a client')) 1112 if personality != self.__personality: 1113 _log.error('rejecting attach to personality [%s], only servicing [%s]' % (personality, self.__personality)) 1114 return (0, _('attach to personality [%s] rejected') % personality) 1115 self.__attached = 1 1116 self.__auth_cookie = str(random.random()) 1117 return (1, self.__auth_cookie)
1118 #-----------------------------------------------------------------
1119 - def detach(self, auth_cookie=None):
1120 if not self.__attached: 1121 return 1 1122 if auth_cookie != self.__auth_cookie: 1123 _log.error('rejecting detach() with cookie [%s]' % auth_cookie) 1124 return 0 1125 self.__attached = 0 1126 return 1
1127 #-----------------------------------------------------------------
1128 - def force_detach(self):
1129 if not self.__attached: 1130 return 1 1131 self.__user_done = False 1132 # FIXME: use self.__sync_cookie for syncing with user interaction 1133 wx.CallAfter(self._force_detach) 1134 return 1
1135 #-----------------------------------------------------------------
1136 - def version(self):
1137 ver = _cfg.get(option = u'client_version') 1138 return "GNUmed %s, %s $Revision: 1.51 $" % (ver, self.__class__.__name__)
1139 #-----------------------------------------------------------------
1140 - def shutdown_gnumed(self, auth_cookie=None, forced=False):
1141 """Shuts down this client instance.""" 1142 if not self.__attached: 1143 return 0 1144 if auth_cookie != self.__auth_cookie: 1145 _log.error('non-authenticated shutdown_gnumed()') 1146 return 0 1147 wx.CallAfter(self._shutdown_gnumed, forced) 1148 return 1
1149 #-----------------------------------------------------------------
1150 - def raise_gnumed(self, auth_cookie = None):
1151 """Raise ourselves to the top of the desktop.""" 1152 if not self.__attached: 1153 return 0 1154 if auth_cookie != self.__auth_cookie: 1155 _log.error('non-authenticated raise_gnumed()') 1156 return 0 1157 return "cMacroPrimitives.raise_gnumed() not implemented"
1158 #-----------------------------------------------------------------
1159 - def get_loaded_plugins(self, auth_cookie = None):
1160 if not self.__attached: 1161 return 0 1162 if auth_cookie != self.__auth_cookie: 1163 _log.error('non-authenticated get_loaded_plugins()') 1164 return 0 1165 gb = gmGuiBroker.GuiBroker() 1166 return gb['horstspace.notebook.gui'].keys()
1167 #-----------------------------------------------------------------
1168 - def raise_notebook_plugin(self, auth_cookie = None, a_plugin = None):
1169 """Raise a notebook plugin within GNUmed.""" 1170 if not self.__attached: 1171 return 0 1172 if auth_cookie != self.__auth_cookie: 1173 _log.error('non-authenticated raise_notebook_plugin()') 1174 return 0 1175 # FIXME: use semaphore 1176 wx.CallAfter(gmPlugin.raise_notebook_plugin, a_plugin) 1177 return 1
1178 #-----------------------------------------------------------------
1179 - def load_patient_from_external_source(self, auth_cookie = None):
1180 """Load external patient, perhaps create it. 1181 1182 Callers must use get_user_answer() to get status information. 1183 It is unsafe to proceed without knowing the completion state as 1184 the controlled client may be waiting for user input from a 1185 patient selection list. 1186 """ 1187 if not self.__attached: 1188 return (0, _('request rejected, you are not attach()ed')) 1189 if auth_cookie != self.__auth_cookie: 1190 _log.error('non-authenticated load_patient_from_external_source()') 1191 return (0, _('rejected load_patient_from_external_source(), not authenticated')) 1192 if self.__pat.locked: 1193 _log.error('patient is locked, cannot load from external source') 1194 return (0, _('current patient is locked')) 1195 self.__user_done = False 1196 wx.CallAfter(self._load_patient_from_external_source) 1197 self.__lock_after_load_cookie = str(random.random()) 1198 return (1, self.__lock_after_load_cookie)
1199 #-----------------------------------------------------------------
1200 - def lock_loaded_patient(self, auth_cookie = None, lock_after_load_cookie = None):
1201 if not self.__attached: 1202 return (0, _('request rejected, you are not attach()ed')) 1203 if auth_cookie != self.__auth_cookie: 1204 _log.error('non-authenticated lock_load_patient()') 1205 return (0, _('rejected lock_load_patient(), not authenticated')) 1206 # FIXME: ask user what to do about wrong cookie 1207 if lock_after_load_cookie != self.__lock_after_load_cookie: 1208 _log.warning('patient lock-after-load request rejected due to wrong cookie [%s]' % lock_after_load_cookie) 1209 return (0, 'patient lock-after-load request rejected, wrong cookie provided') 1210 self.__pat.locked = True 1211 self.__pat_lock_cookie = str(random.random()) 1212 return (1, self.__pat_lock_cookie)
1213 #-----------------------------------------------------------------
1214 - def lock_into_patient(self, auth_cookie = None, search_params = None):
1215 if not self.__attached: 1216 return (0, _('request rejected, you are not attach()ed')) 1217 if auth_cookie != self.__auth_cookie: 1218 _log.error('non-authenticated lock_into_patient()') 1219 return (0, _('rejected lock_into_patient(), not authenticated')) 1220 if self.__pat.locked: 1221 _log.error('patient is already locked') 1222 return (0, _('already locked into a patient')) 1223 searcher = gmPersonSearch.cPatientSearcher_SQL() 1224 if type(search_params) == types.DictType: 1225 idents = searcher.get_identities(search_dict=search_params) 1226 raise StandardError("must use dto, not search_dict") 1227 else: 1228 idents = searcher.get_identities(search_term=search_params) 1229 if idents is None: 1230 return (0, _('error searching for patient with [%s]/%s') % (search_term, search_dict)) 1231 if len(idents) == 0: 1232 return (0, _('no patient found for [%s]/%s') % (search_term, search_dict)) 1233 # FIXME: let user select patient 1234 if len(idents) > 1: 1235 return (0, _('several matching patients found for [%s]/%s') % (search_term, search_dict)) 1236 if not gmPatSearchWidgets.set_active_patient(patient = idents[0]): 1237 return (0, _('cannot activate patient [%s] (%s/%s)') % (str(idents[0]), search_term, search_dict)) 1238 self.__pat.locked = True 1239 self.__pat_lock_cookie = str(random.random()) 1240 return (1, self.__pat_lock_cookie)
1241 #-----------------------------------------------------------------
1242 - def unlock_patient(self, auth_cookie = None, unlock_cookie = None):
1243 if not self.__attached: 1244 return (0, _('request rejected, you are not attach()ed')) 1245 if auth_cookie != self.__auth_cookie: 1246 _log.error('non-authenticated unlock_patient()') 1247 return (0, _('rejected unlock_patient, not authenticated')) 1248 # we ain't locked anyways, so succeed 1249 if not self.__pat.locked: 1250 return (1, '') 1251 # FIXME: ask user what to do about wrong cookie 1252 if unlock_cookie != self.__pat_lock_cookie: 1253 _log.warning('patient unlock request rejected due to wrong cookie [%s]' % unlock_cookie) 1254 return (0, 'patient unlock request rejected, wrong cookie provided') 1255 self.__pat.locked = False 1256 return (1, '')
1257 #-----------------------------------------------------------------
1258 - def assume_staff_identity(self, auth_cookie = None, staff_name = "Dr.Jekyll", staff_creds = None):
1259 if not self.__attached: 1260 return 0 1261 if auth_cookie != self.__auth_cookie: 1262 _log.error('non-authenticated select_identity()') 1263 return 0 1264 return "cMacroPrimitives.assume_staff_identity() not implemented"
1265 #-----------------------------------------------------------------
1266 - def get_user_answer(self):
1267 if not self.__user_done: 1268 return (0, 'still waiting') 1269 self.__user_done = False 1270 return (1, self.__user_answer)
1271 #----------------------------------------------------------------- 1272 # internal API 1273 #-----------------------------------------------------------------
1274 - def _force_detach(self):
1275 msg = _( 1276 'Someone tries to forcibly break the existing\n' 1277 'controlling connection. This may or may not\n' 1278 'have legitimate reasons.\n\n' 1279 'Do you want to allow breaking the connection ?' 1280 ) 1281 can_break_conn = gmGuiHelpers.gm_show_question ( 1282 aMessage = msg, 1283 aTitle = _('forced detach attempt') 1284 ) 1285 if can_break_conn: 1286 self.__user_answer = 1 1287 else: 1288 self.__user_answer = 0 1289 self.__user_done = True 1290 if can_break_conn: 1291 self.__pat.locked = False 1292 self.__attached = 0 1293 return 1
1294 #-----------------------------------------------------------------
1295 - def _shutdown_gnumed(self, forced=False):
1296 top_win = wx.GetApp().GetTopWindow() 1297 if forced: 1298 top_win.Destroy() 1299 else: 1300 top_win.Close()
1301 #-----------------------------------------------------------------
1303 patient = gmPatSearchWidgets.get_person_from_external_sources(search_immediately = True, activate_immediately = True) 1304 if patient is not None: 1305 self.__user_answer = 1 1306 else: 1307 self.__user_answer = 0 1308 self.__user_done = True 1309 return 1
1310 #===================================================================== 1311 # main 1312 #===================================================================== 1313 if __name__ == '__main__': 1314 1315 if len(sys.argv) < 2: 1316 sys.exit() 1317 1318 if sys.argv[1] != 'test': 1319 sys.exit() 1320 1321 gmI18N.activate_locale() 1322 gmI18N.install_domain() 1323 1324 #--------------------------------------------------------
1325 - def test_placeholders():
1326 handler = gmPlaceholderHandler() 1327 handler.debug = True 1328 1329 for placeholder in ['a', 'b']: 1330 print handler[placeholder] 1331 1332 pat = gmPersonSearch.ask_for_patient() 1333 if pat is None: 1334 return 1335 1336 gmPatSearchWidgets.set_active_patient(patient = pat) 1337 1338 print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 1339 1340 app = wx.PyWidgetTester(size = (200, 50)) 1341 for placeholder in known_placeholders: 1342 print placeholder, "=", handler[placeholder] 1343 1344 ph = 'progress_notes::ap' 1345 print '%s: %s' % (ph, handler[ph])
1346 #--------------------------------------------------------
1347 - def test_new_variant_placeholders():
1348 1349 tests = [ 1350 # should work: 1351 '$<lastname>$', 1352 '$<lastname::::3>$', 1353 '$<name::%(title)s %(firstnames)s%(preferred)s%(lastnames)s>$', 1354 1355 # should fail: 1356 'lastname', 1357 '$<lastname', 1358 '$<lastname::', 1359 '$<lastname::>$', 1360 '$<lastname::abc>$', 1361 '$<lastname::abc::>$', 1362 '$<lastname::abc::3>$', 1363 '$<lastname::abc::xyz>$', 1364 '$<lastname::::>$', 1365 '$<lastname::::xyz>$', 1366 1367 '$<date_of_birth::%Y-%m-%d>$', 1368 '$<date_of_birth::%Y-%m-%d::3>$', 1369 '$<date_of_birth::%Y-%m-%d::>$', 1370 1371 # should work: 1372 '$<adr_location::home::35>$', 1373 '$<gender_mapper::male//female//other::5>$', 1374 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\n::50>$', 1375 '$<allergy_list::%(descriptor)s, >$', 1376 '$<current_meds_table::latex//by-brand>$' 1377 1378 # 'firstname', 1379 # 'title', 1380 # 'date_of_birth', 1381 # 'progress_notes', 1382 # 'soap', 1383 # 'soap_s', 1384 # 'soap_o', 1385 # 'soap_a', 1386 # 'soap_p', 1387 1388 # 'soap', 1389 # 'progress_notes', 1390 # 'date_of_birth' 1391 ] 1392 1393 # tests = [ 1394 # '$<latest_vaccs_table::latex>$' 1395 # ] 1396 1397 pat = gmPersonSearch.ask_for_patient() 1398 if pat is None: 1399 return 1400 1401 gmPatSearchWidgets.set_active_patient(patient = pat) 1402 1403 handler = gmPlaceholderHandler() 1404 handler.debug = True 1405 1406 for placeholder in tests: 1407 print placeholder, "=>", handler[placeholder] 1408 print "--------------" 1409 raw_input()
1410 1411 # print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 1412 1413 # app = wx.PyWidgetTester(size = (200, 50)) 1414 # for placeholder in known_placeholders: 1415 # print placeholder, "=", handler[placeholder] 1416 1417 # ph = 'progress_notes::ap' 1418 # print '%s: %s' % (ph, handler[ph]) 1419 1420 #--------------------------------------------------------
1421 - def test_scripting():
1422 from Gnumed.pycommon import gmScriptingListener 1423 import xmlrpclib 1424 listener = gmScriptingListener.cScriptingListener(macro_executor = cMacroPrimitives(personality='unit test'), port=9999) 1425 1426 s = xmlrpclib.ServerProxy('http://localhost:9999') 1427 print "should fail:", s.attach() 1428 print "should fail:", s.attach('wrong cookie') 1429 print "should work:", s.version() 1430 print "should fail:", s.raise_gnumed() 1431 print "should fail:", s.raise_notebook_plugin('test plugin') 1432 print "should fail:", s.lock_into_patient('kirk, james') 1433 print "should fail:", s.unlock_patient() 1434 status, conn_auth = s.attach('unit test') 1435 print "should work:", status, conn_auth 1436 print "should work:", s.version() 1437 print "should work:", s.raise_gnumed(conn_auth) 1438 status, pat_auth = s.lock_into_patient(conn_auth, 'kirk, james') 1439 print "should work:", status, pat_auth 1440 print "should fail:", s.unlock_patient(conn_auth, 'bogus patient unlock cookie') 1441 print "should work", s.unlock_patient(conn_auth, pat_auth) 1442 data = {'firstname': 'jame', 'lastnames': 'Kirk', 'gender': 'm'} 1443 status, pat_auth = s.lock_into_patient(conn_auth, data) 1444 print "should work:", status, pat_auth 1445 print "should work", s.unlock_patient(conn_auth, pat_auth) 1446 print s.detach('bogus detach cookie') 1447 print s.detach(conn_auth) 1448 del s 1449 1450 listener.shutdown()
1451 #--------------------------------------------------------
1452 - def test_placeholder_regex():
1453 1454 import re as regex 1455 1456 tests = [ 1457 ' $<lastname>$ ', 1458 ' $<lastname::::3>$ ', 1459 1460 # should fail: 1461 '$<date_of_birth::%Y-%m-%d>$', 1462 '$<date_of_birth::%Y-%m-%d::3>$', 1463 '$<date_of_birth::%Y-%m-%d::>$', 1464 1465 '$<adr_location::home::35>$', 1466 '$<gender_mapper::male//female//other::5>$', 1467 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\\n::50>$', 1468 '$<allergy_list::%(descriptor)s, >$', 1469 1470 '\\noindent Patient: $<lastname>$, $<firstname>$', 1471 '$<allergies::%(descriptor)s & %(l10n_type)s & {\\footnotesize %(reaction)s} \tabularnewline \hline >$', 1472 '$<current_meds:: \item[%(substance)s] {\\footnotesize (%(brand)s)} %(preparation)s %(amount)s%(unit)s: %(schedule)s >$' 1473 ] 1474 1475 tests = [ 1476 1477 'junk $<lastname::::3>$ junk', 1478 'junk $<lastname::abc::3>$ junk', 1479 'junk $<lastname::abc>$ junk', 1480 'junk $<lastname>$ junk', 1481 1482 'junk $<lastname>$ junk $<firstname>$ junk', 1483 'junk $<lastname::abc>$ junk $<fiststname::abc>$ junk', 1484 'junk $<lastname::abc::3>$ junk $<firstname::abc::3>$ junk', 1485 'junk $<lastname::::3>$ junk $<firstname::::3>$ junk' 1486 1487 ] 1488 1489 print "testing placeholder regex:", default_placeholder_regex 1490 print "" 1491 1492 for t in tests: 1493 print 'line: "%s"' % t 1494 print "placeholders:" 1495 for p in regex.findall(default_placeholder_regex, t, regex.IGNORECASE): 1496 print ' => "%s"' % p 1497 print " "
1498 #--------------------------------------------------------
1499 - def test_placeholder():
1500 1501 phs = [ 1502 #u'emr_journal::soapu //%(clin_when)s %(modified_by)s %(soap_cat)s %(narrative)s//110::', 1503 #u'free_text::tex//placeholder test::9999', 1504 #u'soap_for_encounters:://::9999', 1505 #u'soap_a',, 1506 #u'encounter_list::%(started)s: %(assessment_of_encounter)s::30', 1507 #u'patient_comm::homephone::1234', 1508 #u'$<patient_address::work::1234>$', 1509 #u'adr_region::home::1234', 1510 #u'adr_country::fehlt::1234', 1511 #u'adr_subunit::fehlt::1234', 1512 #u'adr_suburb::fehlt-auch::1234', 1513 #u'external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 1514 #u'primary_praxis_provider', 1515 #u'current_provider', 1516 #u'current_provider_external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 1517 #u'current_provider_external_id::LANR//LÄK::1234' 1518 #u'primary_praxis_provider_external_id::LANR//LÄK::1234' 1519 #u'form_name_long::::1234', 1520 #u'form_name_long::::5', 1521 #u'form_name_long::::', 1522 #u'form_version::::5', 1523 #u'$<current_meds::\item %(brand)s %(preparation)s (%(substance)s) from %(started)s for %(duration)s as %(schedule)s until %(discontinued)s\\n::250>$', 1524 #u'$<vaccination_history::%(date_given)s: %(vaccine)s [%(batch_no)s] %(l10n_indications)s::250>$', 1525 #u'$<date_of_birth::%Y %B %d::20>$', 1526 #u'$<patient_tags::Tag "%(l10n_description)s": %(comment)s//\\n- ::250>$', 1527 u'$<PHX::%(description)s\n side: %(laterality)s, active: %(is_active)s, relevant: %(clinically_relevant)s, caused death: %(is_cause_of_death)s//\n//%Y %B %d//latex::250>$', 1528 ] 1529 1530 handler = gmPlaceholderHandler() 1531 handler.debug = True 1532 1533 gmStaff.set_current_provider_to_logged_on_user() 1534 pat = gmPersonSearch.ask_for_patient() 1535 if pat is None: 1536 return 1537 1538 gmPatSearchWidgets.set_active_patient(patient = pat) 1539 1540 app = wx.PyWidgetTester(size = (200, 50)) 1541 #handler.set_placeholder('form_name_long', 'ein Testformular') 1542 for ph in phs: 1543 print ph 1544 print "result:" 1545 print '%s' % handler[ph]
1546 #handler.unset_placeholder('form_name_long') 1547 #-------------------------------------------------------- 1548 1549 #test_placeholders() 1550 #test_new_variant_placeholders() 1551 #test_scripting() 1552 #test_placeholder_regex() 1553 test_placeholder() 1554 1555 #===================================================================== 1556