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