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

Source Code for Module Gnumed.wxpython.gmMacro

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