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 # placeholders 394 lastname = property(_get_lastname, _setter_noop) 395 firstname = property(_get_firstname, _setter_noop) 396 title = property(_get_title, _setter_noop) 397 date_of_birth = property(_get_dob, _setter_noop) 398 399 progress_notes = property(_get_progress_notes, _setter_noop) 400 soap = property(_get_progress_notes, _setter_noop) 401 soap_s = property(_get_soap_s, _setter_noop) 402 soap_o = property(_get_soap_o, _setter_noop) 403 soap_a = property(_get_soap_a, _setter_noop) 404 soap_p = property(_get_soap_p, _setter_noop) 405 soap_u = property(_get_soap_u, _setter_noop) 406 soap_admin = property(_get_soap_admin, _setter_noop) 407 408 allergy_state = property(_get_allergy_state, _setter_noop) 409 410 client_version = property(_get_client_version, _setter_noop) 411 412 current_provider = property(_get_current_provider, _setter_noop) 413 primary_praxis_provider = property(_get_primary_praxis_provider, _setter_noop) 414 #-------------------------------------------------------- 415 # variant handlers 416 #--------------------------------------------------------
417 - def _get_variant_encounter_list(self, data=None):
418 419 encounters = gmEMRStructWidgets.select_encounters(single_selection = False) 420 if not encounters: 421 return u'' 422 423 template = data 424 425 lines = [] 426 for enc in encounters: 427 try: 428 lines.append(template % enc) 429 except: 430 lines.append(u'error formatting encounter') 431 _log.exception('problem formatting encounter list') 432 _log.error('template: %s', template) 433 _log.error('encounter: %s', encounter) 434 435 return u'\n'.join(lines)
436 #--------------------------------------------------------
437 - def _get_variant_soap_for_encounters(self, data=None):
438 """Select encounters from list and format SOAP thereof. 439 440 data: soap_cats (' ' -> None -> admin) // date format 441 """ 442 # defaults 443 cats = None 444 date_format = None 445 446 if data is not None: 447 data_parts = data.split('//') 448 449 # part[0]: categories 450 if len(data_parts[0]) > 0: 451 cats = [] 452 if u' ' in data_parts[0]: 453 cats.append(None) 454 data_parts[0] = data_parts[0].replace(u' ', u'') 455 cats.extend(list(data_parts[0])) 456 457 # part[1]: date format 458 if len(data_parts) > 1: 459 if len(data_parts[1]) > 0: 460 date_format = data_parts[1] 461 462 encounters = gmEMRStructWidgets.select_encounters(single_selection = False) 463 if not encounters: 464 return u'' 465 466 chunks = [] 467 for enc in encounters: 468 chunks.append(enc.format_latex ( 469 date_format = date_format, 470 soap_cats = cats, 471 soap_order = u'soap_rank, date' 472 )) 473 474 return u''.join(chunks)
475 #--------------------------------------------------------
476 - def _get_variant_emr_journal(self, data=None):
477 # default: all categories, neutral template 478 cats = list(u'soapu') 479 cats.append(None) 480 template = u'%s' 481 interactive = True 482 line_length = 9999 483 target_format = None 484 time_range = None 485 486 if data is not None: 487 data_parts = data.split('//') 488 489 # part[0]: categories 490 cats = [] 491 # ' ' -> None == admin 492 for c in list(data_parts[0]): 493 if c == u' ': 494 c = None 495 cats.append(c) 496 # '' -> SOAP + None 497 if cats == u'': 498 cats = list(u'soapu').append(None) 499 500 # part[1]: template 501 if len(data_parts) > 1: 502 template = data_parts[1] 503 504 # part[2]: line length 505 if len(data_parts) > 2: 506 try: 507 line_length = int(data_parts[2]) 508 except: 509 line_length = 9999 510 511 # part[3]: weeks going back in time 512 if len(data_parts) > 3: 513 try: 514 time_range = 7 * int(data_parts[3]) 515 except: 516 time_range = None 517 518 # part[4]: output format 519 if len(data_parts) > 4: 520 target_format = data_parts[4] 521 522 # FIXME: will need to be a generator later on 523 narr = self.pat.get_emr().get_as_journal(soap_cats = cats, time_range = time_range) 524 525 if len(narr) == 0: 526 return u'' 527 528 if target_format == u'tex': 529 keys = narr[0].keys() 530 lines = [] 531 line_dict = {} 532 for n in narr: 533 for key in keys: 534 if isinstance(n[key], basestring): 535 line_dict[key] = gmTools.tex_escape_string(text = n[key]) 536 continue 537 line_dict[key] = n[key] 538 try: 539 lines.append((template % line_dict)[:line_length]) 540 except KeyError: 541 return u'invalid key in template [%s], valid keys: %s]' % (template, str(keys)) 542 else: 543 try: 544 lines = [ (template % n)[:line_length] for n in narr ] 545 except KeyError: 546 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys())) 547 548 return u'\n'.join(lines)
549 #--------------------------------------------------------
550 - def _get_variant_progress_notes(self, data=None):
551 return self._get_variant_soap(data=data)
552 #--------------------------------------------------------
553 - def _get_variant_soap(self, data=None):
554 555 # default: all categories, neutral template 556 cats = list(u'soapu') 557 cats.append(None) 558 template = u'%s' 559 560 if data is not None: 561 data_parts = data.split('//') 562 563 # part[0]: categories 564 cats = [] 565 # ' ' -> None == admin 566 for cat in list(data_parts[0]): 567 if cat == u' ': 568 cat = None 569 cats.append(cat) 570 # '' -> SOAP + None 571 if cats == u'': 572 cats = list(u'soapu') 573 cats.append(None) 574 575 # part[1]: template 576 if len(data_parts) > 1: 577 template = data_parts[1] 578 579 #narr = gmNarrativeWidgets.select_narrative_from_episodes_new(soap_cats = cats) 580 narr = gmNarrativeWidgets.select_narrative_from_episodes(soap_cats = cats) 581 582 if narr is None: 583 return u'' 584 585 if len(narr) == 0: 586 return u'' 587 588 try: 589 narr = [ template % n['narrative'] for n in narr ] 590 except KeyError: 591 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys())) 592 593 return u'\n'.join(narr)
594 #--------------------------------------------------------
595 - def _get_variant_name(self, data=None):
596 if data is None: 597 return [_('template is missing')] 598 599 name = self.pat.get_active_name() 600 601 parts = { 602 'title': gmTools.coalesce(name['title'], u''), 603 'firstnames': name['firstnames'], 604 'lastnames': name['lastnames'], 605 'preferred': gmTools.coalesce ( 606 initial = name['preferred'], 607 instead = u' ', 608 template_initial = u' "%s" ' 609 ) 610 } 611 612 return data % parts
613 #--------------------------------------------------------
614 - def _get_variant_date_of_birth(self, data='%x'):
615 return self.pat.get_formatted_dob(format = str(data), encoding = gmI18N.get_encoding())
616 #-------------------------------------------------------- 617 # FIXME: extend to all supported genders
618 - def _get_variant_gender_mapper(self, data='male//female//other'):
619 values = data.split('//', 2) 620 621 if len(values) == 2: 622 male_value, female_value = values 623 other_value = u'<unkown gender>' 624 elif len(values) == 3: 625 male_value, female_value, other_value = values 626 else: 627 return _('invalid gender mapping layout: [%s]') % data 628 629 if self.pat['gender'] == u'm': 630 return male_value 631 632 if self.pat['gender'] == u'f': 633 return female_value 634 635 return other_value
636 #-------------------------------------------------------- 637 # address related placeholders 638 #--------------------------------------------------------
639 - def _get_variant_patient_address(self, data=u''):
640 641 data_parts = data.split(u'//') 642 643 if data_parts[0].strip() == u'': 644 adr_type = u'home' 645 else: 646 adr_type = data_parts[0] 647 648 template = _('%(street)s %(number)s, %(postcode)s %(urb)s, %(l10n_state)s, %(l10n_country)s') 649 if len(data_parts) > 1: 650 if data_parts[1].strip() != u'': 651 template = data_parts[1] 652 653 adrs = self.pat.get_addresses(address_type = adr_type) 654 if len(adrs) == 0: 655 if self.debug: 656 return _('no address for type [%s]') % adr_type 657 return u'' 658 659 adr = adrs[0] 660 data = { 661 'street': adr['street'], 662 'notes_street': gmTools.coalesce(adr['notes_street'], u''), 663 'postcode': adr['postcode'], 664 'number': adr['number'], 665 'subunit': gmTools.coalesce(adr['subunit'], u''), 666 'notes_subunit': gmTools.coalesce(adr['notes_subunit'], u''), 667 'urb': adr['urb'], 668 'suburb': gmTools.coalesce(adr['suburb'], u''), 669 'l10n_state': adr['l10n_state'], 670 'l10n_country': adr['l10n_country'], 671 'code_state': adr['code_state'], 672 'code_country': adr['code_country'] 673 } 674 675 try: 676 return template % data 677 except StandardError: 678 _log.exception('error formatting address') 679 _log.error('template: %s', template) 680 681 return None
682 #--------------------------------------------------------
683 - def _get_variant_adr_street(self, data=u'?'):
684 adrs = self.pat.get_addresses(address_type=data) 685 if len(adrs) == 0: 686 if self.debug: 687 return _('no street for address type [%s]') % data 688 return u'' 689 return adrs[0]['street']
690 #--------------------------------------------------------
691 - def _get_variant_adr_number(self, data=u'?'):
692 adrs = self.pat.get_addresses(address_type=data) 693 if len(adrs) == 0: 694 if self.debug: 695 return _('no number for address type [%s]') % data 696 return u'' 697 return adrs[0]['number']
698 #--------------------------------------------------------
699 - def _get_variant_adr_location(self, data=u'?'):
700 adrs = self.pat.get_addresses(address_type=data) 701 if len(adrs) == 0: 702 if self.debug: 703 return _('no location for address type [%s]') % data 704 return u'' 705 return adrs[0]['urb']
706 #--------------------------------------------------------
707 - def _get_variant_adr_postcode(self, data=u'?'):
708 adrs = self.pat.get_addresses(address_type = data) 709 if len(adrs) == 0: 710 if self.debug: 711 return _('no postcode for address type [%s]') % data 712 return u'' 713 return adrs[0]['postcode']
714 #--------------------------------------------------------
715 - def _get_variant_adr_region(self, data=u'?'):
716 adrs = self.pat.get_addresses(address_type = data) 717 if len(adrs) == 0: 718 if self.debug: 719 return _('no region for address type [%s]') % data 720 return u'' 721 return adrs[0]['l10n_state']
722 #--------------------------------------------------------
723 - def _get_variant_adr_country(self, data=u'?'):
724 adrs = self.pat.get_addresses(address_type = data) 725 if len(adrs) == 0: 726 if self.debug: 727 return _('no country for address type [%s]') % data 728 return u'' 729 return adrs[0]['l10n_country']
730 #--------------------------------------------------------
731 - def _get_variant_patient_comm(self, data=u'?'):
732 comms = self.pat.get_comm_channels(comm_medium = data) 733 if len(comms) == 0: 734 if self.debug: 735 return _('no URL for comm channel [%s]') % data 736 return u'' 737 return comms[0]['url']
738 #--------------------------------------------------------
739 - def _get_variant_current_provider_external_id(self, data=u''):
740 data_parts = data.split(u'//') 741 if len(data_parts) < 2: 742 return u'current provider external ID: template is missing' 743 744 id_type = data_parts[0].strip() 745 if id_type == u'': 746 return u'current provider external ID: type is missing' 747 748 issuer = data_parts[1].strip() 749 if issuer == u'': 750 return u'current provider external ID: issuer is missing' 751 752 prov = gmStaff.gmCurrentProvider() 753 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer) 754 755 if len(ids) == 0: 756 if self.debug: 757 return _('no external ID [%s] by [%s]') % (id_type, issuer) 758 return u'' 759 760 return ids[0]['value']
761 #--------------------------------------------------------
763 data_parts = data.split(u'//') 764 if len(data_parts) < 2: 765 return u'primary in-praxis provider external ID: template is missing' 766 767 id_type = data_parts[0].strip() 768 if id_type == u'': 769 return u'primary in-praxis provider external ID: type is missing' 770 771 issuer = data_parts[1].strip() 772 if issuer == u'': 773 return u'primary in-praxis provider external ID: issuer is missing' 774 775 prov = self.pat.primary_provider 776 if prov is None: 777 if self.debug: 778 return _('no primary in-praxis provider') 779 return u'' 780 781 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer) 782 783 if len(ids) == 0: 784 if self.debug: 785 return _('no external ID [%s] by [%s]') % (id_type, issuer) 786 return u'' 787 788 return ids[0]['value']
789 #--------------------------------------------------------
790 - def _get_variant_external_id(self, data=u''):
791 data_parts = data.split(u'//') 792 if len(data_parts) < 2: 793 return u'patient external ID: template is missing' 794 795 id_type = data_parts[0].strip() 796 if id_type == u'': 797 return u'patient external ID: type is missing' 798 799 issuer = data_parts[1].strip() 800 if issuer == u'': 801 return u'patient external ID: issuer is missing' 802 803 ids = self.pat.get_external_ids(id_type = id_type, issuer = issuer) 804 805 if len(ids) == 0: 806 if self.debug: 807 return _('no external ID [%s] by [%s]') % (id_type, issuer) 808 return u'' 809 810 return ids[0]['value']
811 #--------------------------------------------------------
812 - def _get_variant_allergy_list(self, data=None):
813 if data is None: 814 return [_('template is missing')] 815 816 template, separator = data.split('//', 2) 817 818 emr = self.pat.get_emr() 819 return separator.join([ template % a for a in emr.get_allergies() ])
820 #--------------------------------------------------------
821 - def _get_variant_allergies(self, data=None):
822 823 if data is None: 824 return [_('template is missing')] 825 826 emr = self.pat.get_emr() 827 return u'\n'.join([ data % a for a in emr.get_allergies() ])
828 #--------------------------------------------------------
829 - def _get_variant_current_meds(self, data=None):
830 831 if data is None: 832 return [_('template is missing')] 833 834 emr = self.pat.get_emr() 835 current_meds = emr.get_current_substance_intake ( 836 include_inactive = False, 837 include_unapproved = False, 838 order_by = u'brand, substance' 839 ) 840 841 return u'\n'.join([ data % m.fields_as_dict(date_format = '%Y %B %d') for m in current_meds ])
842 #--------------------------------------------------------
843 - def _get_variant_current_meds_table(self, data=None):
844 845 options = data.split('//') 846 847 if u'latex' in options: 848 return gmMedication.format_substance_intake ( 849 emr = self.pat.get_emr(), 850 output_format = u'latex', 851 table_type = u'by-brand' 852 ) 853 854 _log.error('no known current medications table formatting style in [%s]', data) 855 return _('unknown current medication table formatting style')
856 #--------------------------------------------------------
857 - def _get_variant_current_meds_notes(self, data=None):
858 859 options = data.split('//') 860 861 if u'latex' in options: 862 return gmMedication.format_substance_intake_notes ( 863 emr = self.pat.get_emr(), 864 output_format = u'latex', 865 table_type = u'by-brand' 866 ) 867 868 _log.error('no known current medications notes formatting style in [%s]', data) 869 return _('unknown current medication notes formatting style')
870 #--------------------------------------------------------
871 - def _get_variant_lab_table(self, data=None):
872 873 options = data.split('//') 874 875 emr = self.pat.get_emr() 876 877 if u'latex' in options: 878 return gmPathLab.format_test_results ( 879 results = emr.get_test_results_by_date(), 880 output_format = u'latex' 881 ) 882 883 _log.error('no known test results table formatting style in [%s]', data) 884 return _('unknown test results table formatting style [%s]') % data
885 #--------------------------------------------------------
886 - def _get_variant_latest_vaccs_table(self, data=None):
887 888 options = data.split('//') 889 890 emr = self.pat.get_emr() 891 892 if u'latex' in options: 893 return gmVaccination.format_latest_vaccinations(output_format = u'latex', emr = emr) 894 895 _log.error('no known vaccinations table formatting style in [%s]', data) 896 return _('unknown vaccinations table formatting style [%s]') % data
897 #--------------------------------------------------------
898 - def _get_variant_problems(self, data=None):
899 900 if data is None: 901 return [_('template is missing')] 902 903 probs = self.pat.get_emr().get_problems() 904 905 return u'\n'.join([ data % p for p in probs ])
906 #--------------------------------------------------------
907 - def _get_variant_today(self, data='%x'):
908 return gmDateTime.pydt_now_here().strftime(str(data)).decode(gmI18N.get_encoding())
909 #--------------------------------------------------------
910 - def _get_variant_tex_escape(self, data=None):
911 return gmTools.tex_escape_string(text = data)
912 #--------------------------------------------------------
913 - def _get_variant_free_text(self, data=u'tex//'):
914 # <data>: 915 # format: tex (only, currently) 916 # message: shown in input dialog, must not contain "//" or "::" 917 918 data_parts = data.split('//') 919 format = data_parts[0] 920 if len(data_parts) > 1: 921 msg = data_parts[1] 922 else: 923 msg = _('generic text') 924 925 dlg = gmGuiHelpers.cMultilineTextEntryDlg ( 926 None, 927 -1, 928 title = _('Replacing <free_text> placeholder'), 929 msg = _('Below you can enter free text.\n\n [%s]') % msg 930 ) 931 dlg.enable_user_formatting = True 932 decision = dlg.ShowModal() 933 934 if decision != wx.ID_SAVE: 935 dlg.Destroy() 936 if self.debug: 937 return _('Text input cancelled by user.') 938 return u'' 939 940 text = dlg.value.strip() 941 if dlg.is_user_formatted: 942 dlg.Destroy() 943 return text 944 945 dlg.Destroy() 946 947 if format == u'tex': 948 return gmTools.tex_escape_string(text = text) 949 950 return text
951 #-------------------------------------------------------- 952 # internal helpers 953 #-------------------------------------------------------- 954 955 #=====================================================================
956 -class cMacroPrimitives:
957 """Functions a macro can legally use. 958 959 An instance of this class is passed to the GNUmed scripting 960 listener. Hence, all actions a macro can legally take must 961 be defined in this class. Thus we achieve some screening for 962 security and also thread safety handling. 963 """ 964 #-----------------------------------------------------------------
965 - def __init__(self, personality = None):
966 if personality is None: 967 raise gmExceptions.ConstructorError, 'must specify personality' 968 self.__personality = personality 969 self.__attached = 0 970 self._get_source_personality = None 971 self.__user_done = False 972 self.__user_answer = 'no answer yet' 973 self.__pat = gmPerson.gmCurrentPatient() 974 975 self.__auth_cookie = str(random.random()) 976 self.__pat_lock_cookie = str(random.random()) 977 self.__lock_after_load_cookie = str(random.random()) 978 979 _log.info('slave mode personality is [%s]', personality)
980 #----------------------------------------------------------------- 981 # public API 982 #-----------------------------------------------------------------
983 - def attach(self, personality = None):
984 if self.__attached: 985 _log.error('attach with [%s] rejected, already serving a client', personality) 986 return (0, _('attach rejected, already serving a client')) 987 if personality != self.__personality: 988 _log.error('rejecting attach to personality [%s], only servicing [%s]' % (personality, self.__personality)) 989 return (0, _('attach to personality [%s] rejected') % personality) 990 self.__attached = 1 991 self.__auth_cookie = str(random.random()) 992 return (1, self.__auth_cookie)
993 #-----------------------------------------------------------------
994 - def detach(self, auth_cookie=None):
995 if not self.__attached: 996 return 1 997 if auth_cookie != self.__auth_cookie: 998 _log.error('rejecting detach() with cookie [%s]' % auth_cookie) 999 return 0 1000 self.__attached = 0 1001 return 1
1002 #-----------------------------------------------------------------
1003 - def force_detach(self):
1004 if not self.__attached: 1005 return 1 1006 self.__user_done = False 1007 # FIXME: use self.__sync_cookie for syncing with user interaction 1008 wx.CallAfter(self._force_detach) 1009 return 1
1010 #-----------------------------------------------------------------
1011 - def version(self):
1012 ver = _cfg.get(option = u'client_version') 1013 return "GNUmed %s, %s $Revision: 1.51 $" % (ver, self.__class__.__name__)
1014 #-----------------------------------------------------------------
1015 - def shutdown_gnumed(self, auth_cookie=None, forced=False):
1016 """Shuts down this client instance.""" 1017 if not self.__attached: 1018 return 0 1019 if auth_cookie != self.__auth_cookie: 1020 _log.error('non-authenticated shutdown_gnumed()') 1021 return 0 1022 wx.CallAfter(self._shutdown_gnumed, forced) 1023 return 1
1024 #-----------------------------------------------------------------
1025 - def raise_gnumed(self, auth_cookie = None):
1026 """Raise ourselves to the top of the desktop.""" 1027 if not self.__attached: 1028 return 0 1029 if auth_cookie != self.__auth_cookie: 1030 _log.error('non-authenticated raise_gnumed()') 1031 return 0 1032 return "cMacroPrimitives.raise_gnumed() not implemented"
1033 #-----------------------------------------------------------------
1034 - def get_loaded_plugins(self, auth_cookie = None):
1035 if not self.__attached: 1036 return 0 1037 if auth_cookie != self.__auth_cookie: 1038 _log.error('non-authenticated get_loaded_plugins()') 1039 return 0 1040 gb = gmGuiBroker.GuiBroker() 1041 return gb['horstspace.notebook.gui'].keys()
1042 #-----------------------------------------------------------------
1043 - def raise_notebook_plugin(self, auth_cookie = None, a_plugin = None):
1044 """Raise a notebook plugin within GNUmed.""" 1045 if not self.__attached: 1046 return 0 1047 if auth_cookie != self.__auth_cookie: 1048 _log.error('non-authenticated raise_notebook_plugin()') 1049 return 0 1050 # FIXME: use semaphore 1051 wx.CallAfter(gmPlugin.raise_notebook_plugin, a_plugin) 1052 return 1
1053 #-----------------------------------------------------------------
1054 - def load_patient_from_external_source(self, auth_cookie = None):
1055 """Load external patient, perhaps create it. 1056 1057 Callers must use get_user_answer() to get status information. 1058 It is unsafe to proceed without knowing the completion state as 1059 the controlled client may be waiting for user input from a 1060 patient selection list. 1061 """ 1062 if not self.__attached: 1063 return (0, _('request rejected, you are not attach()ed')) 1064 if auth_cookie != self.__auth_cookie: 1065 _log.error('non-authenticated load_patient_from_external_source()') 1066 return (0, _('rejected load_patient_from_external_source(), not authenticated')) 1067 if self.__pat.locked: 1068 _log.error('patient is locked, cannot load from external source') 1069 return (0, _('current patient is locked')) 1070 self.__user_done = False 1071 wx.CallAfter(self._load_patient_from_external_source) 1072 self.__lock_after_load_cookie = str(random.random()) 1073 return (1, self.__lock_after_load_cookie)
1074 #-----------------------------------------------------------------
1075 - def lock_loaded_patient(self, auth_cookie = None, lock_after_load_cookie = None):
1076 if not self.__attached: 1077 return (0, _('request rejected, you are not attach()ed')) 1078 if auth_cookie != self.__auth_cookie: 1079 _log.error('non-authenticated lock_load_patient()') 1080 return (0, _('rejected lock_load_patient(), not authenticated')) 1081 # FIXME: ask user what to do about wrong cookie 1082 if lock_after_load_cookie != self.__lock_after_load_cookie: 1083 _log.warning('patient lock-after-load request rejected due to wrong cookie [%s]' % lock_after_load_cookie) 1084 return (0, 'patient lock-after-load request rejected, wrong cookie provided') 1085 self.__pat.locked = True 1086 self.__pat_lock_cookie = str(random.random()) 1087 return (1, self.__pat_lock_cookie)
1088 #-----------------------------------------------------------------
1089 - def lock_into_patient(self, auth_cookie = None, search_params = None):
1090 if not self.__attached: 1091 return (0, _('request rejected, you are not attach()ed')) 1092 if auth_cookie != self.__auth_cookie: 1093 _log.error('non-authenticated lock_into_patient()') 1094 return (0, _('rejected lock_into_patient(), not authenticated')) 1095 if self.__pat.locked: 1096 _log.error('patient is already locked') 1097 return (0, _('already locked into a patient')) 1098 searcher = gmPersonSearch.cPatientSearcher_SQL() 1099 if type(search_params) == types.DictType: 1100 idents = searcher.get_identities(search_dict=search_params) 1101 raise StandardError("must use dto, not search_dict") 1102 else: 1103 idents = searcher.get_identities(search_term=search_params) 1104 if idents is None: 1105 return (0, _('error searching for patient with [%s]/%s') % (search_term, search_dict)) 1106 if len(idents) == 0: 1107 return (0, _('no patient found for [%s]/%s') % (search_term, search_dict)) 1108 # FIXME: let user select patient 1109 if len(idents) > 1: 1110 return (0, _('several matching patients found for [%s]/%s') % (search_term, search_dict)) 1111 if not gmPatSearchWidgets.set_active_patient(patient = idents[0]): 1112 return (0, _('cannot activate patient [%s] (%s/%s)') % (str(idents[0]), search_term, search_dict)) 1113 self.__pat.locked = True 1114 self.__pat_lock_cookie = str(random.random()) 1115 return (1, self.__pat_lock_cookie)
1116 #-----------------------------------------------------------------
1117 - def unlock_patient(self, auth_cookie = None, unlock_cookie = None):
1118 if not self.__attached: 1119 return (0, _('request rejected, you are not attach()ed')) 1120 if auth_cookie != self.__auth_cookie: 1121 _log.error('non-authenticated unlock_patient()') 1122 return (0, _('rejected unlock_patient, not authenticated')) 1123 # we ain't locked anyways, so succeed 1124 if not self.__pat.locked: 1125 return (1, '') 1126 # FIXME: ask user what to do about wrong cookie 1127 if unlock_cookie != self.__pat_lock_cookie: 1128 _log.warning('patient unlock request rejected due to wrong cookie [%s]' % unlock_cookie) 1129 return (0, 'patient unlock request rejected, wrong cookie provided') 1130 self.__pat.locked = False 1131 return (1, '')
1132 #-----------------------------------------------------------------
1133 - def assume_staff_identity(self, auth_cookie = None, staff_name = "Dr.Jekyll", staff_creds = None):
1134 if not self.__attached: 1135 return 0 1136 if auth_cookie != self.__auth_cookie: 1137 _log.error('non-authenticated select_identity()') 1138 return 0 1139 return "cMacroPrimitives.assume_staff_identity() not implemented"
1140 #-----------------------------------------------------------------
1141 - def get_user_answer(self):
1142 if not self.__user_done: 1143 return (0, 'still waiting') 1144 self.__user_done = False 1145 return (1, self.__user_answer)
1146 #----------------------------------------------------------------- 1147 # internal API 1148 #-----------------------------------------------------------------
1149 - def _force_detach(self):
1150 msg = _( 1151 'Someone tries to forcibly break the existing\n' 1152 'controlling connection. This may or may not\n' 1153 'have legitimate reasons.\n\n' 1154 'Do you want to allow breaking the connection ?' 1155 ) 1156 can_break_conn = gmGuiHelpers.gm_show_question ( 1157 aMessage = msg, 1158 aTitle = _('forced detach attempt') 1159 ) 1160 if can_break_conn: 1161 self.__user_answer = 1 1162 else: 1163 self.__user_answer = 0 1164 self.__user_done = True 1165 if can_break_conn: 1166 self.__pat.locked = False 1167 self.__attached = 0 1168 return 1
1169 #-----------------------------------------------------------------
1170 - def _shutdown_gnumed(self, forced=False):
1171 top_win = wx.GetApp().GetTopWindow() 1172 if forced: 1173 top_win.Destroy() 1174 else: 1175 top_win.Close()
1176 #-----------------------------------------------------------------
1178 patient = gmPatSearchWidgets.get_person_from_external_sources(search_immediately = True, activate_immediately = True) 1179 if patient is not None: 1180 self.__user_answer = 1 1181 else: 1182 self.__user_answer = 0 1183 self.__user_done = True 1184 return 1
1185 #===================================================================== 1186 # main 1187 #===================================================================== 1188 if __name__ == '__main__': 1189 1190 if len(sys.argv) < 2: 1191 sys.exit() 1192 1193 if sys.argv[1] != 'test': 1194 sys.exit() 1195 1196 gmI18N.activate_locale() 1197 gmI18N.install_domain() 1198 1199 #--------------------------------------------------------
1200 - def test_placeholders():
1201 handler = gmPlaceholderHandler() 1202 handler.debug = True 1203 1204 for placeholder in ['a', 'b']: 1205 print handler[placeholder] 1206 1207 pat = gmPersonSearch.ask_for_patient() 1208 if pat is None: 1209 return 1210 1211 gmPatSearchWidgets.set_active_patient(patient = pat) 1212 1213 print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 1214 1215 app = wx.PyWidgetTester(size = (200, 50)) 1216 for placeholder in known_placeholders: 1217 print placeholder, "=", handler[placeholder] 1218 1219 ph = 'progress_notes::ap' 1220 print '%s: %s' % (ph, handler[ph])
1221 #--------------------------------------------------------
1222 - def test_new_variant_placeholders():
1223 1224 tests = [ 1225 # should work: 1226 '$<lastname>$', 1227 '$<lastname::::3>$', 1228 '$<name::%(title)s %(firstnames)s%(preferred)s%(lastnames)s>$', 1229 1230 # should fail: 1231 'lastname', 1232 '$<lastname', 1233 '$<lastname::', 1234 '$<lastname::>$', 1235 '$<lastname::abc>$', 1236 '$<lastname::abc::>$', 1237 '$<lastname::abc::3>$', 1238 '$<lastname::abc::xyz>$', 1239 '$<lastname::::>$', 1240 '$<lastname::::xyz>$', 1241 1242 '$<date_of_birth::%Y-%m-%d>$', 1243 '$<date_of_birth::%Y-%m-%d::3>$', 1244 '$<date_of_birth::%Y-%m-%d::>$', 1245 1246 # should work: 1247 '$<adr_location::home::35>$', 1248 '$<gender_mapper::male//female//other::5>$', 1249 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\n::50>$', 1250 '$<allergy_list::%(descriptor)s, >$', 1251 '$<current_meds_table::latex//by-brand>$' 1252 1253 # 'firstname', 1254 # 'title', 1255 # 'date_of_birth', 1256 # 'progress_notes', 1257 # 'soap', 1258 # 'soap_s', 1259 # 'soap_o', 1260 # 'soap_a', 1261 # 'soap_p', 1262 1263 # 'soap', 1264 # 'progress_notes', 1265 # 'date_of_birth' 1266 ] 1267 1268 tests = [ 1269 '$<latest_vaccs_table::latex>$' 1270 ] 1271 1272 pat = gmPersonSearch.ask_for_patient() 1273 if pat is None: 1274 return 1275 1276 gmPatSearchWidgets.set_active_patient(patient = pat) 1277 1278 handler = gmPlaceholderHandler() 1279 handler.debug = True 1280 1281 for placeholder in tests: 1282 print placeholder, "=>", handler[placeholder] 1283 print "--------------" 1284 raw_input()
1285 1286 # print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 1287 1288 # app = wx.PyWidgetTester(size = (200, 50)) 1289 # for placeholder in known_placeholders: 1290 # print placeholder, "=", handler[placeholder] 1291 1292 # ph = 'progress_notes::ap' 1293 # print '%s: %s' % (ph, handler[ph]) 1294 1295 #--------------------------------------------------------
1296 - def test_scripting():
1297 from Gnumed.pycommon import gmScriptingListener 1298 import xmlrpclib 1299 listener = gmScriptingListener.cScriptingListener(macro_executor = cMacroPrimitives(personality='unit test'), port=9999) 1300 1301 s = xmlrpclib.ServerProxy('http://localhost:9999') 1302 print "should fail:", s.attach() 1303 print "should fail:", s.attach('wrong cookie') 1304 print "should work:", s.version() 1305 print "should fail:", s.raise_gnumed() 1306 print "should fail:", s.raise_notebook_plugin('test plugin') 1307 print "should fail:", s.lock_into_patient('kirk, james') 1308 print "should fail:", s.unlock_patient() 1309 status, conn_auth = s.attach('unit test') 1310 print "should work:", status, conn_auth 1311 print "should work:", s.version() 1312 print "should work:", s.raise_gnumed(conn_auth) 1313 status, pat_auth = s.lock_into_patient(conn_auth, 'kirk, james') 1314 print "should work:", status, pat_auth 1315 print "should fail:", s.unlock_patient(conn_auth, 'bogus patient unlock cookie') 1316 print "should work", s.unlock_patient(conn_auth, pat_auth) 1317 data = {'firstname': 'jame', 'lastnames': 'Kirk', 'gender': 'm'} 1318 status, pat_auth = s.lock_into_patient(conn_auth, data) 1319 print "should work:", status, pat_auth 1320 print "should work", s.unlock_patient(conn_auth, pat_auth) 1321 print s.detach('bogus detach cookie') 1322 print s.detach(conn_auth) 1323 del s 1324 1325 listener.shutdown()
1326 #--------------------------------------------------------
1327 - def test_placeholder_regex():
1328 1329 import re as regex 1330 1331 tests = [ 1332 ' $<lastname>$ ', 1333 ' $<lastname::::3>$ ', 1334 1335 # should fail: 1336 '$<date_of_birth::%Y-%m-%d>$', 1337 '$<date_of_birth::%Y-%m-%d::3>$', 1338 '$<date_of_birth::%Y-%m-%d::>$', 1339 1340 '$<adr_location::home::35>$', 1341 '$<gender_mapper::male//female//other::5>$', 1342 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\\n::50>$', 1343 '$<allergy_list::%(descriptor)s, >$', 1344 1345 '\\noindent Patient: $<lastname>$, $<firstname>$', 1346 '$<allergies::%(descriptor)s & %(l10n_type)s & {\\footnotesize %(reaction)s} \tabularnewline \hline >$', 1347 '$<current_meds:: \item[%(substance)s] {\\footnotesize (%(brand)s)} %(preparation)s %(amount)s%(unit)s: %(schedule)s >$' 1348 ] 1349 1350 tests = [ 1351 1352 'junk $<lastname::::3>$ junk', 1353 'junk $<lastname::abc::3>$ junk', 1354 'junk $<lastname::abc>$ junk', 1355 'junk $<lastname>$ junk', 1356 1357 'junk $<lastname>$ junk $<firstname>$ junk', 1358 'junk $<lastname::abc>$ junk $<fiststname::abc>$ junk', 1359 'junk $<lastname::abc::3>$ junk $<firstname::abc::3>$ junk', 1360 'junk $<lastname::::3>$ junk $<firstname::::3>$ junk' 1361 1362 ] 1363 1364 print "testing placeholder regex:", default_placeholder_regex 1365 print "" 1366 1367 for t in tests: 1368 print 'line: "%s"' % t 1369 print "placeholders:" 1370 for p in regex.findall(default_placeholder_regex, t, regex.IGNORECASE): 1371 print ' => "%s"' % p 1372 print " "
1373 #--------------------------------------------------------
1374 - def test_placeholder():
1375 1376 phs = [ 1377 #u'emr_journal::soapu //%(date)s %(modified_by)s %(soap_cat)s %(narrative)s//30::', 1378 #u'free_text::tex//placeholder test::9999', 1379 #u'soap_for_encounters:://::9999', 1380 #u'soap_a',, 1381 #u'encounter_list::%(started)s: %(assessment_of_encounter)s::30', 1382 #u'patient_comm::homephone::1234', 1383 #u'patient_address::home//::1234', 1384 #u'adr_region::home::1234', 1385 #u'adr_country::home::1234', 1386 #u'external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 1387 #u'primary_praxis_provider', 1388 #u'current_provider', 1389 #u'current_provider_external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 1390 #u'current_provider_external_id::LANR//LÄK::1234' 1391 #u'primary_praxis_provider_external_id::LANR//LÄK::1234' 1392 #u'form_name_long::::1234', 1393 #u'form_name_long::::5', 1394 #u'form_version::::5', 1395 u'$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) from %(started)s for %(duration)s as %(schedule)s until %(discontinued)s <==\\n::250>$' 1396 ] 1397 1398 handler = gmPlaceholderHandler() 1399 handler.debug = True 1400 1401 gmStaff.set_current_provider_to_logged_on_user() 1402 pat = gmPersonSearch.ask_for_patient() 1403 if pat is None: 1404 return 1405 1406 gmPatSearchWidgets.set_active_patient(patient = pat) 1407 1408 app = wx.PyWidgetTester(size = (200, 50)) 1409 #handler.set_placeholder('form_name_long', 'ein Testformular') 1410 for ph in phs: 1411 print ph 1412 print ' => %s' % handler[ph]
1413 #handler.unset_placeholder('form_name_long') 1414 #-------------------------------------------------------- 1415 1416 #test_placeholders() 1417 #test_new_variant_placeholders() 1418 #test_scripting() 1419 #test_placeholder_regex() 1420 test_placeholder() 1421 1422 #===================================================================== 1423