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

Source Code for Module Gnumed.wxpython.gmMacro

  1  """GNUmed macro primitives. 
  2   
  3  This module implements functions a macro can legally use. 
  4  """ 
  5  #===================================================================== 
  6  # $Source: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmMacro.py,v $ 
  7  __version__ = "$Revision: 1.50 $" 
  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, gmGuiBroker, gmExceptions, gmBorg, gmTools 
 19  from Gnumed.pycommon import gmCfg2, gmDateTime 
 20  from Gnumed.business import gmPerson, gmDemographicRecord 
 21  from Gnumed.wxpython import gmGuiHelpers, gmPlugin, gmPatSearchWidgets, gmNarrativeWidgets 
 22   
 23   
 24  _log = logging.getLogger('gm.scripting') 
 25  _cfg = gmCfg2.gmCfgData() 
 26   
 27  #===================================================================== 
 28  known_placeholders = [ 
 29          'lastname', 
 30          'firstname', 
 31          'title', 
 32          'date_of_birth', 
 33          'progress_notes', 
 34          'soap', 
 35          'soap_s', 
 36          'soap_o', 
 37          'soap_a', 
 38          'soap_p', 
 39          u'client_version', 
 40          u'current_provider', 
 41          u'allergy_state' 
 42  ] 
 43   
 44   
 45  # those must satisfy the pattern "$name::args::optional length$" when used 
 46  known_variant_placeholders = [ 
 47          u'soap', 
 48          u'progress_notes', 
 49          u'date_of_birth', 
 50          u'adr_street',                          # "data" holds: type of address 
 51          u'adr_number', 
 52          u'adr_location', 
 53          u'adr_postcode', 
 54          u'gender_mapper',                       # "data" holds: value for male // value for female 
 55          u'current_meds',                        # "data" holds: line template 
 56          u'today',                                       # "data" holds: strftime format 
 57          u'tex_escape',                          # "data" holds: string to escape 
 58          u'allergies',                           # "data" holds: line template, one allergy per line 
 59          u'allergy_list',                        # "data" holds: template per allergy, allergies on one line 
 60          u'problems'                                     # "data" holds: line template, one problem per line 
 61  ] 
 62   
 63  #default_placeholder_regex = r'$<.+(::.+){0,2}>$' 
 64  default_placeholder_regex = r'\$<.+?>\$' 
 65  default_placeholder_start = u'$<' 
 66  default_placeholder_end = u'>$' 
 67  #===================================================================== 
68 -class gmPlaceholderHandler(gmBorg.cBorg):
69 """Replaces placeholders in forms, fields, etc. 70 71 - patient related placeholders operate on the currently active patient 72 - is passed to the forms handling code, for example 73 74 Note that this cannot be called from a non-gui thread unless 75 wrapped in wx.CallAfter. 76 77 There are currently three types of placeholders: 78 79 simple static placeholders 80 - those are listed in known_placeholders 81 - they are used as-is 82 83 extended static placeholders 84 - those are like the static ones but have "::::<NUMBER>" appended 85 where <NUMBER> is the maximum length 86 87 variant placeholders 88 - those are listed in known_variant_placeholders 89 - they are parsed into placeholder, data, and maximum length 90 - the length is optional 91 - data is passed to the handler 92 """
93 - def __init__(self, *args, **kwargs):
94 95 self.pat = gmPerson.gmCurrentPatient() 96 self.debug = False 97 98 self.invalid_placeholder_template = _('invalid placeholder [%s]')
99 #-------------------------------------------------------- 100 # __getitem__ API 101 #--------------------------------------------------------
102 - def __getitem__(self, placeholder):
103 """Map self['placeholder'] to self.placeholder. 104 105 This is useful for replacing placeholders parsed out 106 of documents as strings. 107 108 Unknown/invalid placeholders still deliver a result but 109 it will be glaringly obvious if debugging is enabled. 110 """ 111 _log.debug('replacing [%s]', placeholder) 112 113 original_placeholder = placeholder 114 115 if placeholder.startswith(default_placeholder_start): 116 placeholder = placeholder[len(default_placeholder_start):] 117 if placeholder.endswith(default_placeholder_end): 118 placeholder = placeholder[:-len(default_placeholder_end)] 119 else: 120 _log.debug('placeholder must either start with [%s] and end with [%s] or neither of both', default_placeholder_start, default_placeholder_end) 121 if self.debug: 122 return self.invalid_placeholder_template % original_placeholder 123 return None 124 125 # simple static placeholder ? 126 if placeholder in known_placeholders: 127 return getattr(self, placeholder) 128 129 # extended static placeholder ? 130 parts = placeholder.split('::::', 1) 131 if len(parts) == 2: 132 name, lng = parts 133 try: 134 return getattr(self, name)[:int(lng)] 135 except: 136 _log.exception('placeholder handling error: %s', original_placeholder) 137 if self.debug: 138 return self.invalid_placeholder_template % original_placeholder 139 return None 140 141 # variable placeholders 142 parts = placeholder.split('::', 2) 143 if len(parts) == 2: 144 name, data = parts 145 lng = None 146 elif len(parts) == 3: 147 name, data, lng = parts 148 try: 149 lng = int(lng) 150 except: 151 _log.exception('placeholder length definition error: %s, discarding length', original_placeholder) 152 lng = None 153 else: 154 _log.warning('invalid placeholder layout: %s', original_placeholder) 155 if self.debug: 156 return self.invalid_placeholder_template % original_placeholder 157 return None 158 159 handler = getattr(self, '_get_variant_%s' % name, None) 160 if handler is None: 161 _log.warning('no handler <_get_variant_%s> for placeholder %s', name, original_placeholder) 162 if self.debug: 163 return self.invalid_placeholder_template % original_placeholder 164 return None 165 166 try: 167 if lng is None: 168 return handler(data = data) 169 return handler(data = data)[:lng] 170 except: 171 _log.exception('placeholder handling error: %s', original_placeholder) 172 if self.debug: 173 return self.invalid_placeholder_template % original_placeholder 174 return None 175 176 _log.error('something went wrong, should never get here') 177 return None
178 #-------------------------------------------------------- 179 # properties actually handling placeholders 180 #-------------------------------------------------------- 181 # property helpers 182 #--------------------------------------------------------
183 - def _setter_noop(self, val):
184 """This does nothing, used as a NOOP properties setter.""" 185 pass
186 #--------------------------------------------------------
187 - def _get_lastname(self):
188 return self.pat.get_active_name()['lastnames']
189 #--------------------------------------------------------
190 - def _get_firstname(self):
191 return self.pat.get_active_name()['firstnames']
192 #--------------------------------------------------------
193 - def _get_title(self):
194 return gmTools.coalesce(self.pat.get_active_name()['title'], u'')
195 #--------------------------------------------------------
196 - def _get_dob(self):
197 return self._get_variant_date_of_birth(data='%x')
198 #--------------------------------------------------------
199 - def _get_progress_notes(self):
200 return self._get_variant_soap()
201 #--------------------------------------------------------
202 - def _get_soap_s(self):
203 return self._get_variant_soap(data = u's')
204 #--------------------------------------------------------
205 - def _get_soap_o(self):
206 return self._get_variant_soap(data = u'o')
207 #--------------------------------------------------------
208 - def _get_soap_a(self):
209 return self._get_variant_soap(data = u'a')
210 #--------------------------------------------------------
211 - def _get_soap_p(self):
212 return self._get_variant_soap(data = u'p')
213 #--------------------------------------------------------
214 - def _get_soap_admin(self):
215 return self._get_variant_soap(soap_cats = None)
216 #--------------------------------------------------------
217 - def _get_client_version(self):
218 return gmTools.coalesce ( 219 _cfg.get(option = u'client_version'), 220 u'%s' % self.__class__.__name__ 221 )
222 #--------------------------------------------------------
223 - def _get_current_provider(self):
224 prov = gmPerson.gmCurrentProvider() 225 226 title = gmTools.coalesce ( 227 prov['title'], 228 gmPerson.map_gender2salutation(prov['gender']) 229 ) 230 231 tmp = u'%s %s. %s' % ( 232 title, 233 prov['firstnames'][:1], 234 prov['lastnames'] 235 ) 236 237 return tmp
238 #--------------------------------------------------------
239 - def _get_allergy_state(self):
240 allg_state = self.pat.get_emr().allergy_state 241 tmp = u'%s (%s)' % ( 242 allg_state.state_string, 243 allg_state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding()) 244 ) 245 return tmp
246 #-------------------------------------------------------- 247 # property definitions for static placeholders 248 #-------------------------------------------------------- 249 placeholder_regex = property(lambda x: default_placeholder_regex, _setter_noop) 250 251 # placeholders 252 lastname = property(_get_lastname, _setter_noop) 253 firstname = property(_get_firstname, _setter_noop) 254 title = property(_get_title, _setter_noop) 255 date_of_birth = property(_get_dob, _setter_noop) 256 257 progress_notes = property(_get_progress_notes, _setter_noop) 258 soap = property(_get_progress_notes, _setter_noop) 259 soap_s = property(_get_soap_s, _setter_noop) 260 soap_o = property(_get_soap_o, _setter_noop) 261 soap_a = property(_get_soap_a, _setter_noop) 262 soap_p = property(_get_soap_p, _setter_noop) 263 soap_admin = property(_get_soap_admin, _setter_noop) 264 265 allergy_state = property(_get_allergy_state, _setter_noop) 266 267 client_version = property(_get_client_version, _setter_noop) 268 269 current_provider = property(_get_current_provider, _setter_noop) 270 #-------------------------------------------------------- 271 # variant handlers 272 #--------------------------------------------------------
273 - def _get_variant_progress_notes(self, data=None):
274 return self._get_variant_soap(data=data)
275 #--------------------------------------------------------
276 - def _get_variant_soap(self, data=None):
277 if data is None: 278 cats = list(data) 279 template = u'%s' 280 else: 281 parts = data.split('//', 2) 282 if len(parts) == 1: 283 cats = list(parts) 284 template = u'%s' 285 else: 286 cats = list(parts[0]) 287 template = parts[1] 288 289 narr = gmNarrativeWidgets.select_narrative_from_episodes(soap_cats = cats) 290 291 if len(narr) == 0: 292 return u'' 293 294 narr = [ template % n['narrative'] for n in narr ] 295 296 return u'\n'.join(narr)
297 #--------------------------------------------------------
298 - def _get_variant_date_of_birth(self, data='%x'):
299 return self.pat['dob'].strftime(str(data)).decode(gmI18N.get_encoding())
300 #-------------------------------------------------------- 301 # FIXME: extend to all supported genders
302 - def _get_variant_gender_mapper(self, data='male//female//other'):
303 values = data.split('//', 2) 304 305 if len(values) == 2: 306 male_value, female_value = values 307 other_value = u'<unkown gender>' 308 elif len(values) == 3: 309 male_value, female_value, other_value = values 310 else: 311 return _('invalid gender mapping layout: [%s]') % data 312 313 if self.pat['gender'] == u'm': 314 return male_value 315 316 if self.pat['gender'] == u'f': 317 return female_value 318 319 return other_value
320 #--------------------------------------------------------
321 - def _get_variant_adr_street(self, data=u'?'):
322 # if data == u'?': 323 # types = xxxxxxxxxxx 324 adrs = self.pat.get_addresses(address_type=data) 325 if len(adrs) == 0: 326 return _('no street for address type [%s]') % data 327 return adrs[0]['street']
328 #--------------------------------------------------------
329 - def _get_variant_adr_number(self, data=u'?'):
330 adrs = self.pat.get_addresses(address_type=data) 331 if len(adrs) == 0: 332 return _('no number for address type [%s]') % data 333 return adrs[0]['number']
334 #--------------------------------------------------------
335 - def _get_variant_adr_location(self, data=u'?'):
336 adrs = self.pat.get_addresses(address_type=data) 337 if len(adrs) == 0: 338 return _('no location for address type [%s]') % data 339 return adrs[0]['urb']
340 #--------------------------------------------------------
341 - def _get_variant_adr_postcode(self, data=u'?'):
342 adrs = self.pat.get_addresses(address_type=data) 343 if len(adrs) == 0: 344 return _('no postcode for address type [%s]') % data 345 return adrs[0]['postcode']
346 #--------------------------------------------------------
347 - def _get_variant_allergy_list(self, data=None):
348 if data is None: 349 return [_('template is missing')] 350 351 template, separator = data.split('//', 2) 352 353 emr = self.pat.get_emr() 354 return separator.join([ template % a for a in emr.get_allergies() ])
355 #--------------------------------------------------------
356 - def _get_variant_allergies(self, data=None):
357 358 if data is None: 359 return [_('template is missing')] 360 361 emr = self.pat.get_emr() 362 return u'\n'.join([ data % a for a in emr.get_allergies() ])
363 #--------------------------------------------------------
364 - def _get_variant_current_meds(self, data=None):
365 366 if data is None: 367 return [_('template is missing')] 368 369 emr = self.pat.get_emr() 370 current_meds = emr.get_current_substance_intake ( 371 include_inactive = False, 372 include_unapproved = False, 373 order_by = u'brand, substance' 374 ) 375 376 return u'\n'.join([ data % m for m in current_meds ])
377 #--------------------------------------------------------
378 - def _get_variant_problems(self, data=None):
379 380 if data is None: 381 return [_('template is missing')] 382 383 probs = self.pat.get_emr().get_problems() 384 385 return u'\n'.join([ data % p for p in probs ])
386 #--------------------------------------------------------
387 - def _get_variant_today(self, data='%x'):
389 #--------------------------------------------------------
390 - def _get_variant_tex_escape(self, data=None):
391 return gmTools.tex_escape_string(text = data)
392 #-------------------------------------------------------- 393 # internal helpers 394 #-------------------------------------------------------- 395 396 #=====================================================================
397 -class cMacroPrimitives:
398 """Functions a macro can legally use. 399 400 An instance of this class is passed to the GNUmed scripting 401 listener. Hence, all actions a macro can legally take must 402 be defined in this class. Thus we achieve some screening for 403 security and also thread safety handling. 404 """ 405 #-----------------------------------------------------------------
406 - def __init__(self, personality = None):
407 if personality is None: 408 raise gmExceptions.ConstructorError, 'must specify personality' 409 self.__personality = personality 410 self.__attached = 0 411 self._get_source_personality = None 412 self.__user_done = False 413 self.__user_answer = 'no answer yet' 414 self.__pat = gmPerson.gmCurrentPatient() 415 416 self.__auth_cookie = str(random.random()) 417 self.__pat_lock_cookie = str(random.random()) 418 self.__lock_after_load_cookie = str(random.random()) 419 420 _log.info('slave mode personality is [%s]', personality)
421 #----------------------------------------------------------------- 422 # public API 423 #-----------------------------------------------------------------
424 - def attach(self, personality = None):
425 if self.__attached: 426 _log.error('attach with [%s] rejected, already serving a client', personality) 427 return (0, _('attach rejected, already serving a client')) 428 if personality != self.__personality: 429 _log.error('rejecting attach to personality [%s], only servicing [%s]' % (personality, self.__personality)) 430 return (0, _('attach to personality [%s] rejected') % personality) 431 self.__attached = 1 432 self.__auth_cookie = str(random.random()) 433 return (1, self.__auth_cookie)
434 #-----------------------------------------------------------------
435 - def detach(self, auth_cookie=None):
436 if not self.__attached: 437 return 1 438 if auth_cookie != self.__auth_cookie: 439 _log.error('rejecting detach() with cookie [%s]' % auth_cookie) 440 return 0 441 self.__attached = 0 442 return 1
443 #-----------------------------------------------------------------
444 - def force_detach(self):
445 if not self.__attached: 446 return 1 447 self.__user_done = False 448 # FIXME: use self.__sync_cookie for syncing with user interaction 449 wx.CallAfter(self._force_detach) 450 return 1
451 #-----------------------------------------------------------------
452 - def version(self):
453 ver = _cfg.get(option = u'client_version') 454 return "GNUmed %s, %s $Revision: 1.50 $" % (ver, self.__class__.__name__)
455 #-----------------------------------------------------------------
456 - def shutdown_gnumed(self, auth_cookie=None, forced=False):
457 """Shuts down this client instance.""" 458 if not self.__attached: 459 return 0 460 if auth_cookie != self.__auth_cookie: 461 _log.error('non-authenticated shutdown_gnumed()') 462 return 0 463 wx.CallAfter(self._shutdown_gnumed, forced) 464 return 1
465 #-----------------------------------------------------------------
466 - def raise_gnumed(self, auth_cookie = None):
467 """Raise ourselves to the top of the desktop.""" 468 if not self.__attached: 469 return 0 470 if auth_cookie != self.__auth_cookie: 471 _log.error('non-authenticated raise_gnumed()') 472 return 0 473 return "cMacroPrimitives.raise_gnumed() not implemented"
474 #-----------------------------------------------------------------
475 - def get_loaded_plugins(self, auth_cookie = None):
476 if not self.__attached: 477 return 0 478 if auth_cookie != self.__auth_cookie: 479 _log.error('non-authenticated get_loaded_plugins()') 480 return 0 481 gb = gmGuiBroker.GuiBroker() 482 return gb['horstspace.notebook.gui'].keys()
483 #-----------------------------------------------------------------
484 - def raise_notebook_plugin(self, auth_cookie = None, a_plugin = None):
485 """Raise a notebook plugin within GNUmed.""" 486 if not self.__attached: 487 return 0 488 if auth_cookie != self.__auth_cookie: 489 _log.error('non-authenticated raise_notebook_plugin()') 490 return 0 491 # FIXME: use semaphore 492 wx.CallAfter(gmPlugin.raise_notebook_plugin, a_plugin) 493 return 1
494 #-----------------------------------------------------------------
495 - def load_patient_from_external_source(self, auth_cookie = None):
496 """Load external patient, perhaps create it. 497 498 Callers must use get_user_answer() to get status information. 499 It is unsafe to proceed without knowing the completion state as 500 the controlled client may be waiting for user input from a 501 patient selection list. 502 """ 503 if not self.__attached: 504 return (0, _('request rejected, you are not attach()ed')) 505 if auth_cookie != self.__auth_cookie: 506 _log.error('non-authenticated load_patient_from_external_source()') 507 return (0, _('rejected load_patient_from_external_source(), not authenticated')) 508 if self.__pat.locked: 509 _log.error('patient is locked, cannot load from external source') 510 return (0, _('current patient is locked')) 511 self.__user_done = False 512 wx.CallAfter(self._load_patient_from_external_source) 513 self.__lock_after_load_cookie = str(random.random()) 514 return (1, self.__lock_after_load_cookie)
515 #-----------------------------------------------------------------
516 - def lock_loaded_patient(self, auth_cookie = None, lock_after_load_cookie = None):
517 if not self.__attached: 518 return (0, _('request rejected, you are not attach()ed')) 519 if auth_cookie != self.__auth_cookie: 520 _log.error('non-authenticated lock_load_patient()') 521 return (0, _('rejected lock_load_patient(), not authenticated')) 522 # FIXME: ask user what to do about wrong cookie 523 if lock_after_load_cookie != self.__lock_after_load_cookie: 524 _log.warning('patient lock-after-load request rejected due to wrong cookie [%s]' % lock_after_load_cookie) 525 return (0, 'patient lock-after-load request rejected, wrong cookie provided') 526 self.__pat.locked = True 527 self.__pat_lock_cookie = str(random.random()) 528 return (1, self.__pat_lock_cookie)
529 #-----------------------------------------------------------------
530 - def lock_into_patient(self, auth_cookie = None, search_params = None):
531 if not self.__attached: 532 return (0, _('request rejected, you are not attach()ed')) 533 if auth_cookie != self.__auth_cookie: 534 _log.error('non-authenticated lock_into_patient()') 535 return (0, _('rejected lock_into_patient(), not authenticated')) 536 if self.__pat.locked: 537 _log.error('patient is already locked') 538 return (0, _('already locked into a patient')) 539 searcher = gmPerson.cPatientSearcher_SQL() 540 if type(search_params) == types.DictType: 541 idents = searcher.get_identities(search_dict=search_params) 542 print "must use dto, not search_dict" 543 print xxxxxxxxxxxxxxxxx 544 else: 545 idents = searcher.get_identities(search_term=search_params) 546 if idents is None: 547 return (0, _('error searching for patient with [%s]/%s') % (search_term, search_dict)) 548 if len(idents) == 0: 549 return (0, _('no patient found for [%s]/%s') % (search_term, search_dict)) 550 # FIXME: let user select patient 551 if len(idents) > 1: 552 return (0, _('several matching patients found for [%s]/%s') % (search_term, search_dict)) 553 if not gmPatSearchWidgets.set_active_patient(patient = idents[0]): 554 return (0, _('cannot activate patient [%s] (%s/%s)') % (str(idents[0]), search_term, search_dict)) 555 self.__pat.locked = True 556 self.__pat_lock_cookie = str(random.random()) 557 return (1, self.__pat_lock_cookie)
558 #-----------------------------------------------------------------
559 - def unlock_patient(self, auth_cookie = None, unlock_cookie = None):
560 if not self.__attached: 561 return (0, _('request rejected, you are not attach()ed')) 562 if auth_cookie != self.__auth_cookie: 563 _log.error('non-authenticated unlock_patient()') 564 return (0, _('rejected unlock_patient, not authenticated')) 565 # we ain't locked anyways, so succeed 566 if not self.__pat.locked: 567 return (1, '') 568 # FIXME: ask user what to do about wrong cookie 569 if unlock_cookie != self.__pat_lock_cookie: 570 _log.warning('patient unlock request rejected due to wrong cookie [%s]' % unlock_cookie) 571 return (0, 'patient unlock request rejected, wrong cookie provided') 572 self.__pat.locked = False 573 return (1, '')
574 #-----------------------------------------------------------------
575 - def assume_staff_identity(self, auth_cookie = None, staff_name = "Dr.Jekyll", staff_creds = None):
576 if not self.__attached: 577 return 0 578 if auth_cookie != self.__auth_cookie: 579 _log.error('non-authenticated select_identity()') 580 return 0 581 return "cMacroPrimitives.assume_staff_identity() not implemented"
582 #-----------------------------------------------------------------
583 - def get_user_answer(self):
584 if not self.__user_done: 585 return (0, 'still waiting') 586 self.__user_done = False 587 return (1, self.__user_answer)
588 #----------------------------------------------------------------- 589 # internal API 590 #-----------------------------------------------------------------
591 - def _force_detach(self):
592 msg = _( 593 'Someone tries to forcibly break the existing\n' 594 'controlling connection. This may or may not\n' 595 'have legitimate reasons.\n\n' 596 'Do you want to allow breaking the connection ?' 597 ) 598 can_break_conn = gmGuiHelpers.gm_show_question ( 599 aMessage = msg, 600 aTitle = _('forced detach attempt') 601 ) 602 if can_break_conn: 603 self.__user_answer = 1 604 else: 605 self.__user_answer = 0 606 self.__user_done = True 607 if can_break_conn: 608 self.__pat.locked = False 609 self.__attached = 0 610 return 1
611 #-----------------------------------------------------------------
612 - def _shutdown_gnumed(self, forced=False):
613 top_win = wx.GetApp().GetTopWindow() 614 if forced: 615 top_win.Destroy() 616 else: 617 top_win.Close()
618 #-----------------------------------------------------------------
620 patient = gmPatSearchWidgets.get_person_from_external_sources(search_immediately = True, activate_immediately = True) 621 if patient is not None: 622 self.__user_answer = 1 623 else: 624 self.__user_answer = 0 625 self.__user_done = True 626 return 1
627 #===================================================================== 628 # main 629 #===================================================================== 630 if __name__ == '__main__': 631 632 gmI18N.activate_locale() 633 gmI18N.install_domain() 634 635 #--------------------------------------------------------
636 - def test_placeholders():
637 handler = gmPlaceholderHandler() 638 handler.debug = True 639 640 for placeholder in ['a', 'b']: 641 print handler[placeholder] 642 643 pat = gmPerson.ask_for_patient() 644 if pat is None: 645 return 646 647 gmPatSearchWidgets.set_active_patient(patient = pat) 648 649 print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 650 651 app = wx.PyWidgetTester(size = (200, 50)) 652 for placeholder in known_placeholders: 653 print placeholder, "=", handler[placeholder] 654 655 ph = 'progress_notes::ap' 656 print '%s: %s' % (ph, handler[ph])
657 #--------------------------------------------------------
658 - def test_new_variant_placeholders():
659 660 tests = [ 661 # should work: 662 '$<lastname>$', 663 '$<lastname::::3>$', 664 665 # should fail: 666 'lastname', 667 '$<lastname', 668 '$<lastname::', 669 '$<lastname::>$', 670 '$<lastname::abc>$', 671 '$<lastname::abc::>$', 672 '$<lastname::abc::3>$', 673 '$<lastname::abc::xyz>$', 674 '$<lastname::::>$', 675 '$<lastname::::xyz>$', 676 677 '$<date_of_birth::%Y-%m-%d>$', 678 '$<date_of_birth::%Y-%m-%d::3>$', 679 '$<date_of_birth::%Y-%m-%d::>$', 680 681 # should work: 682 '$<adr_location::home::35>$', 683 '$<gender_mapper::male//female//other::5>$', 684 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\n::50>$', 685 '$<allergy_list::%(descriptor)s, >$' 686 687 # 'firstname', 688 # 'title', 689 # 'date_of_birth', 690 # 'progress_notes', 691 # 'soap', 692 # 'soap_s', 693 # 'soap_o', 694 # 'soap_a', 695 # 'soap_p', 696 697 # 'soap', 698 # 'progress_notes', 699 # 'date_of_birth' 700 ] 701 702 pat = gmPerson.ask_for_patient() 703 if pat is None: 704 return 705 706 gmPatSearchWidgets.set_active_patient(patient = pat) 707 708 handler = gmPlaceholderHandler() 709 handler.debug = True 710 711 for placeholder in tests: 712 print placeholder, "=>", handler[placeholder] 713 print "--------------" 714 raw_input()
715 716 # print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 717 718 # app = wx.PyWidgetTester(size = (200, 50)) 719 # for placeholder in known_placeholders: 720 # print placeholder, "=", handler[placeholder] 721 722 # ph = 'progress_notes::ap' 723 # print '%s: %s' % (ph, handler[ph]) 724 725 #--------------------------------------------------------
726 - def test_scripting():
727 from Gnumed.pycommon import gmScriptingListener 728 import xmlrpclib 729 listener = gmScriptingListener.cScriptingListener(macro_executor = cMacroPrimitives(personality='unit test'), port=9999) 730 731 s = xmlrpclib.ServerProxy('http://localhost:9999') 732 print "should fail:", s.attach() 733 print "should fail:", s.attach('wrong cookie') 734 print "should work:", s.version() 735 print "should fail:", s.raise_gnumed() 736 print "should fail:", s.raise_notebook_plugin('test plugin') 737 print "should fail:", s.lock_into_patient('kirk, james') 738 print "should fail:", s.unlock_patient() 739 status, conn_auth = s.attach('unit test') 740 print "should work:", status, conn_auth 741 print "should work:", s.version() 742 print "should work:", s.raise_gnumed(conn_auth) 743 status, pat_auth = s.lock_into_patient(conn_auth, 'kirk, james') 744 print "should work:", status, pat_auth 745 print "should fail:", s.unlock_patient(conn_auth, 'bogus patient unlock cookie') 746 print "should work", s.unlock_patient(conn_auth, pat_auth) 747 data = {'firstname': 'jame', 'lastnames': 'Kirk', 'gender': 'm'} 748 status, pat_auth = s.lock_into_patient(conn_auth, data) 749 print "should work:", status, pat_auth 750 print "should work", s.unlock_patient(conn_auth, pat_auth) 751 print s.detach('bogus detach cookie') 752 print s.detach(conn_auth) 753 del s 754 755 listener.shutdown()
756 #-------------------------------------------------------- 757 758 if len(sys.argv) > 1 and sys.argv[1] == 'test': 759 #test_placeholders() 760 test_new_variant_placeholders() 761 #test_scripting() 762 763 #===================================================================== 764 # $Log: gmMacro.py,v $ 765 # Revision 1.50 2010/01/21 08:44:46 ncq 766 # - implement new placeholders, improve others 767 # 768 # Revision 1.49 2010/01/15 12:43:46 ncq 769 # - tex-escape placeholder 770 # - return safe substitute if real client version unavailable 771 # 772 # Revision 1.48 2009/12/22 12:01:58 ncq 773 # - escape dollar signs as they frequently mean something 774 # 775 # Revision 1.47 2009/12/21 20:28:02 ncq 776 # - allergies placeholder 777 # 778 # Revision 1.46 2009/12/21 15:11:30 ncq 779 # - client_version, current_provider, today, current_meds 780 # - placeholder regex must be non-greedy to support several per line 781 # - improved logging 782 # - don't throw exceptions on placeholder substitution, rather return hint 783 # 784 # Revision 1.45 2009/09/29 13:18:28 ncq 785 # - implement address placeholders 786 # - implement gender mapper placeholder 787 # 788 # Revision 1.44 2009/06/04 16:30:30 ncq 789 # - use set active patient from pat search widgets 790 # 791 # Revision 1.43 2009/03/10 14:23:32 ncq 792 # - support new style placeholders and test 793 # 794 # Revision 1.42 2009/01/15 11:40:20 ncq 795 # - better logging 796 # 797 # Revision 1.41 2008/03/20 15:30:37 ncq 798 # - fix misplaced % 799 # 800 # Revision 1.40 2008/03/09 20:16:32 ncq 801 # *** empty log message *** 802 # 803 # Revision 1.39 2008/03/05 22:30:14 ncq 804 # - new style logging 805 # 806 # Revision 1.38 2007/12/03 20:45:16 ncq 807 # - add variant placeholder handling ! :-) 808 # 809 # Revision 1.37 2007/11/05 12:10:21 ncq 810 # - support admin soap type 811 # 812 # Revision 1.36 2007/10/19 12:52:00 ncq 813 # - immediately search for patient matches 814 # 815 # Revision 1.35 2007/09/16 22:40:46 ncq 816 # - fix soap_* placeholder handling 817 # 818 # Revision 1.34 2007/09/09 19:17:44 ncq 819 # - add a bunch of placeholders regarding SOAP notes 820 # 821 # Revision 1.33 2007/08/29 22:09:32 ncq 822 # - narrative widgets factored out 823 # 824 # Revision 1.32 2007/08/13 21:59:54 ncq 825 # - add placeholder handler 826 # - add progress_notes placeholder 827 # - improved test suite 828 # 829 # Revision 1.31 2007/07/17 21:44:24 ncq 830 # - use patient.locked properly 831 # 832 # Revision 1.30 2007/07/11 21:09:54 ncq 833 # - use curr_pat.locked 834 # 835 # Revision 1.29 2007/07/03 16:00:56 ncq 836 # - remove unneeded import 837 # 838 # Revision 1.28 2007/01/21 12:21:38 ncq 839 # - comment on search_dict -> dto 840 # 841 # Revision 1.27 2006/12/25 22:54:44 ncq 842 # - comment fix 843 # 844 # Revision 1.26 2006/07/22 12:15:08 ncq 845 # - add missing import 846 # 847 # Revision 1.25 2006/07/22 10:04:51 ncq 848 # - cleanup 849 # - pre-init all attributes so connectors won't kill the GNUmed slave 850 # with stupid AttributeExceptions 851 # - add lock_loaded_patient() 852 # 853 # Revision 1.24 2006/07/21 14:47:19 ncq 854 # - cleanup 855 # - add (_)load_patient_from_external_source() 856 # - improve testing 857 # 858 # Revision 1.23 2006/05/04 09:49:20 ncq 859 # - get_clinical_record() -> get_emr() 860 # - adjust to changes in set_active_patient() 861 # - need explicit set_active_patient() after ask_for_patient() if wanted 862 # 863 # Revision 1.22 2005/11/28 23:07:34 ncq 864 # - add shutdown_gnumed() 865 # 866 # Revision 1.21 2005/11/27 22:08:38 ncq 867 # - patient searcher has somewhat changed so adapt 868 # 869 # Revision 1.20 2005/11/27 20:38:10 ncq 870 # - properly import wx 871 # 872 # Revision 1.19 2005/09/28 21:27:30 ncq 873 # - a lot of wx2.6-ification 874 # 875 # Revision 1.18 2005/09/28 15:57:48 ncq 876 # - a whole bunch of wx.Foo -> wx.Foo 877 # 878 # Revision 1.17 2005/09/27 20:44:59 ncq 879 # - wx.wx* -> wx.* 880 # 881 # Revision 1.16 2005/01/31 10:37:26 ncq 882 # - gmPatient.py -> gmPerson.py 883 # 884 # Revision 1.15 2004/09/13 09:38:29 ncq 885 # - allow to wait for user interaction in controlled GnuMed instance 886 # despite having to use wxCallAfter by waiting on a semaphore 887 # 888 # Revision 1.14 2004/07/24 17:13:25 ncq 889 # - main.plugins.gui now horstspace.notebook.gui 890 # 891 # Revision 1.13 2004/06/25 13:28:00 ncq 892 # - logically separate notebook and clinical window plugins completely 893 # 894 # Revision 1.12 2004/06/01 07:59:55 ncq 895 # - comments improved 896 # 897 # Revision 1.11 2004/03/20 19:48:07 ncq 898 # - adapt to flat id list from get_patient_ids 899 # 900 # Revision 1.10 2004/03/20 17:54:18 ncq 901 # - lock_into_patient now supports dicts and strings 902 # - fix unit test 903 # 904 # Revision 1.9 2004/03/12 13:22:38 ncq 905 # - comment on semaphore for GUI actions 906 # 907 # Revision 1.8 2004/03/05 11:22:35 ncq 908 # - import from Gnumed.<pkg> 909 # 910 # Revision 1.7 2004/02/25 09:46:22 ncq 911 # - import from pycommon now, not python-common 912 # 913 # Revision 1.6 2004/02/17 10:45:30 ncq 914 # - return authentication cookie from attach() 915 # - use that cookie in all RPCs 916 # - add assume_staff_identity() 917 # 918 # Revision 1.5 2004/02/12 23:57:22 ncq 919 # - now also use random cookie for attach/detach 920 # - add force_detach() with user feedback 921 # - add get_loaded_plugins() 922 # - implement raise_plugin() 923 # 924 # Revision 1.4 2004/02/05 23:52:05 ncq 925 # - remove spurious return 0 926 # 927 # Revision 1.3 2004/02/05 20:46:18 ncq 928 # - require attach() cookie for detach(), too 929 # 930 # Revision 1.2 2004/02/05 20:40:34 ncq 931 # - added attach() 932 # - only allow attach()ed clients to call methods 933 # - introduce patient locking/unlocking cookie 934 # - enhance unit test 935 # 936 # Revision 1.1 2004/02/05 18:10:44 ncq 937 # - actually minimally functional macro executor with test code 938 # 939