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