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