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