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