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  __author__ = "K.Hilbert <karsten.hilbert@gmx.net>" 
   8   
   9  import sys 
  10  import time 
  11  import random 
  12  import types 
  13  import logging 
  14  import os 
  15  import codecs 
  16   
  17   
  18  import wx 
  19   
  20   
  21  if __name__ == '__main__': 
  22          sys.path.insert(0, '../../') 
  23  from Gnumed.pycommon import gmI18N 
  24  if __name__ == '__main__': 
  25          gmI18N.activate_locale() 
  26          gmI18N.install_domain() 
  27  from Gnumed.pycommon import gmGuiBroker 
  28  from Gnumed.pycommon import gmTools 
  29  from Gnumed.pycommon import gmBorg 
  30  from Gnumed.pycommon import gmExceptions 
  31  from Gnumed.pycommon import gmCfg2 
  32  from Gnumed.pycommon import gmDateTime 
  33  from Gnumed.pycommon import gmMimeLib 
  34   
  35  from Gnumed.business import gmPerson 
  36  from Gnumed.business import gmStaff 
  37  from Gnumed.business import gmDemographicRecord 
  38  from Gnumed.business import gmMedication 
  39  from Gnumed.business import gmPathLab 
  40  from Gnumed.business import gmPersonSearch 
  41  from Gnumed.business import gmVaccination 
  42  from Gnumed.business import gmKeywordExpansion 
  43   
  44  from Gnumed.wxpython import gmGuiHelpers 
  45  from Gnumed.wxpython import gmNarrativeWidgets 
  46  from Gnumed.wxpython import gmPatSearchWidgets 
  47  from Gnumed.wxpython import gmPersonContactWidgets 
  48  from Gnumed.wxpython import gmPlugin 
  49  from Gnumed.wxpython import gmEMRStructWidgets 
  50  from Gnumed.wxpython import gmListWidgets 
  51  from Gnumed.wxpython import gmDemographicsWidgets 
  52  from Gnumed.wxpython import gmDocumentWidgets 
  53  from Gnumed.wxpython import gmKeywordExpansionWidgets 
  54  from Gnumed.wxpython import gmMeasurementWidgets 
  55   
  56   
  57  _log = logging.getLogger('gm.scripting') 
  58  _cfg = gmCfg2.gmCfgData() 
  59   
  60  #===================================================================== 
  61  # values for the following placeholders must be injected from the outside before 
  62  # using them, in use they must conform to the "placeholder::::max length" syntax, 
  63  # as long as they resolve to None they return themselves 
  64  _injectable_placeholders = { 
  65          u'form_name_long': None, 
  66          u'form_name_short': None, 
  67          u'form_version': None 
  68  } 
  69   
  70   
  71  # the following must satisfy the pattern "$<name::args::(optional) max string length>$" when used 
  72  known_variant_placeholders = [ 
  73          # generic: 
  74          u'free_text',                                                   # show a dialog for entering some free text 
  75                                                                                          # args: <message> 
  76          u'text_snippet',                                                # a text snippet, taken from the keyword expansion mechanism 
  77                                                                                          # args: <snippet name>//<template> 
  78          u'data_snippet',                                                # a binary snippet, taken from the keyword expansion mechanism 
  79                                                                                          # args: <snippet name>//<template>//<optional target mime type>//<optional target extension> 
  80                                                                                          # returns full path to an exported copy of the 
  81                                                                                          # data rather than the data itself, 
  82                                                                                          #       template: string template for outputting the path 
  83                                                                                          #       target mime type: a mime type into which to convert the image, no conversion if not given 
  84                                                                                          #       target extension: target file name extension, derived from target mime type if not given 
  85          u'tex_escape',                                                  # "args" holds: string to escape 
  86          u'today',                                                               # "args" holds: strftime format 
  87          u'gender_mapper',                                               # "args" holds: <value when person is male> // <is female> // <is other> 
  88                                                                                          #                               eg. "male//female//other" 
  89                                                                                          #                               or: "Lieber Patient//Liebe Patientin" 
  90          u'client_version',                                              # the version of the current client as a string (no "v" in front) 
  91   
  92          # patient demographics: 
  93          u'name',                                                                # args: template for name parts arrangement 
  94          u'date_of_birth',                                               # args: strftime date/time format directive 
  95   
  96          u'patient_address',                                             # args: <type of address>//<optional formatting template> 
  97          u'adr_street',                                                  # args: <type of address> 
  98          u'adr_number', 
  99          u'adr_subunit', 
 100          u'adr_location', 
 101          u'adr_suburb', 
 102          u'adr_postcode', 
 103          u'adr_region', 
 104          u'adr_country', 
 105   
 106          u'patient_comm',                                                # args: <comm channel type as per database>//<%(key)s-template> 
 107          u'patient_tags',                                                # "args" holds: <%(key)s-template>//<separator> 
 108  #       u'patient_tags_table',                                  # "args" holds: no args 
 109   
 110          u'patient_photo',                                               # args: <template>//<optional target mime type>//<optional target extension> 
 111                                                                                          # returns full path to an exported copy of the 
 112                                                                                          # image rather than the image data itself, 
 113                                                                                          # returns u'' if no mugshot available, 
 114                                                                                          #       template: string template for outputting the path 
 115                                                                                          #       target mime type: a mime type into which to convert the image, no conversion if not given 
 116                                                                                          #       target extension: target file name extension, derived from target mime type if not given 
 117   
 118          u'external_id',                                                 # args: <type of ID>//<issuer of ID> 
 119   
 120   
 121          # clinical record related: 
 122          u'soap', 
 123          u'soap_s', 
 124          u'soap_o', 
 125          u'soap_a', 
 126          u'soap_p', 
 127          u'soap_u', 
 128          u'soap_admin',                                                  # get all or subset of SOAPU/ADMIN, no template in args needed 
 129          u'progress_notes',                                              # "args" holds: categories//template 
 130                                                                                          #       categories: string with 'soapu '; ' ' == None == admin 
 131                                                                                          #       template:       u'something %s something'               (do not include // in template !) 
 132   
 133          u'soap_for_encounters',                                 # lets the user select a list of encounters for which 
 134                                                                                          # LaTeX formatted progress notes are emitted: 
 135                                                                                          # "args": soap categories // strftime date format 
 136   
 137          u'soap_by_issue',                                               # lets the user select a list of issues and 
 138                                                                                          # then SOAP entries from those issues 
 139                                                                                          # "args": soap categories // strftime date format // template 
 140   
 141          u'soap_by_episode',                                             # lets the user select a list of issues and 
 142                                                                                          # then SOAP entries from those issues 
 143                                                                                          # "args": soap categories // strftime date format // template 
 144   
 145          u'emr_journal',                                                 # "args" format:   <categories>//<template>//<line length>//<time range>//<target format> 
 146                                                                                          #       categories:        string with any of "s", "o", "a", "p", "u", " "; 
 147                                                                                          #                                  (" " == None == admin category) 
 148                                                                                          #       template:          something %s something else 
 149                                                                                          #                                  (Do not include // in the template !) 
 150                                                                                          #       line length:   the maximum length of individual lines, not the total placeholder length 
 151                                                                                          #       time range:             the number of weeks going back in time if given as a single number, 
 152                                                                                          #                                       or else it must be a valid PostgreSQL interval definition (w/o the ::interval) 
 153                                                                                          #       target format: "tex" or anything else, if "tex", data will be tex-escaped       (currently only "latex") 
 154   
 155          u'current_meds',                                                # "args" holds: line template//<select> 
 156                                                                                          #       <select>: if this is present the user will be asked which meds to export 
 157          u'current_meds_for_rx',                                 # formats substance intakes either by substance (non-brand intakes or by 
 158                                                                                          # brand (once per brand intake, even if multi-component) 
 159                                                                                          # args: <line template> 
 160                                                                                          #       <line_template>: template into which to insert each intake, keys from 
 161                                                                                          #                        clin.v_pat_substance_intake, special additional keys: 
 162                                                                                          #                        %(contains)s -- list of components 
 163                                                                                          #                        %(amount2dispense)s -- how much/many to dispense 
 164          u'current_meds_table',                                  # "args" holds: format, options 
 165          u'current_meds_notes',                                  # "args" holds: format, options 
 166   
 167          u'lab_table',                                                   # "args" holds: format (currently "latex" only) 
 168          u'test_results',                                                # "args":                       <%(key)s-template>//<date format>//<line separator (EOL)> 
 169   
 170          u'latest_vaccs_table',                                  # "args" holds: format, options 
 171          u'vaccination_history',                                 # "args": <%(key)s-template//date format> to format one vaccination per line 
 172   
 173          u'allergy_state',                                               # args: no args needed 
 174          u'allergies',                                                   # "args" holds: line template, one allergy per line 
 175          u'allergy_list',                                                # "args" holds: template per allergy, allergies on one line 
 176          u'problems',                                                    # "args" holds: line template, one problem per line 
 177          u'PHX',                                                                 # Past medical HiXtory, "args" holds: line template//separator//strftime date format//escape style (latex, currently) 
 178          u'encounter_list',                                              # "args" holds: per-encounter template, each ends up on one line 
 179   
 180          u'documents',                                                   # "args" format:        <select>//<description>//<template>//<path template>//<path> 
 181                                                                                          #       select:                 let user select which documents to include, 
 182                                                                                          #                                       optional, if not given: all documents included 
 183                                                                                          #       description:    whether to include descriptions, optional 
 184                                                                                          #       template:               something %(field)s something else, 
 185                                                                                          #                                       (do not include "//" itself in the template), 
 186                                                                                          #       path template:  the template for outputting the path to exported 
 187                                                                                          #                                       copies of the document pages, if not given no pages 
 188                                                                                          #                                       are exported, this template can contain "%(name)s" 
 189                                                                                          #                                       and/or "%(fullpath)s" which is replaced by the 
 190                                                                                          #                                       appropriate value for each exported file 
 191                                                                                          #       path:                   into which path to export copies of the document pages, 
 192                                                                                          #                                       temp dir if not given 
 193   
 194          u'reminders',                                                   # "args":                       <template>//<date format> 
 195                                                                                          #       template:               something %(field)s something else, 
 196                                                                                          #                                       (do not include "//" itself in the template) 
 197   
 198          # provider related: 
 199          u'current_provider',                                    # args: no args needed 
 200          u'current_provider_external_id',                # args: <type of ID>//<issuer of ID> 
 201          u'primary_praxis_provider',                             # primary provider for current patient in this praxis 
 202          u'primary_praxis_provider_external_id', # args: <type of ID>//<issuer of ID> 
 203   
 204          # billing related: 
 205          u'bill',                                                                # args: template for string replacement 
 206          u'bill_item'                                                    # args: template for string replacement 
 207  ] 
 208   
 209  # http://help.libreoffice.org/Common/List_of_Regular_Expressions 
 210  # except that OOo cannot be non-greedy |-( 
 211  #default_placeholder_regex = r'\$<.+?>\$'                               # previous working placeholder 
 212          # regex logic: 
 213          # starts with "$" 
 214          # followed by "<" 
 215          # followed by > 0 characters but NOT "<" but ONLY up to the NEXT ":" 
 216          # followed by "::" 
 217          # followed by any number of characters  but ONLY up to the NEXT ":" 
 218          # followed by "::" 
 219          # followed by any number of numbers 
 220          # followed by ">" 
 221          # followed by "$" 
 222  default_placeholder_regex = r'\$<[^<:]+::.*?::\d*?>\$'          # this one works [except that OOo cannot be non-greedy |-(    ] 
 223  first_order_placeholder_regex =   r'\$<<<[^<:]+?::.*::\d*?>>>\$' 
 224  second_order_placeholder_regex = r'\$<<[^<:]+?::.*::\d*?>>\$' 
 225  third_order_placeholder_regex =  r'\$<[^<:]+::.*?::\d*?>\$' 
 226   
 227   
 228  #default_placeholder_regex = r'\$<(?:(?!\$<).)+>\$'             # non-greedy equivalent, uses lookahead (but not supported by LO either |-o  ) 
 229   
 230  default_placeholder_start = u'$<' 
 231  default_placeholder_end = u'>$' 
 232  #===================================================================== 
233 -def show_placeholders():
234 fname = gmTools.get_unique_filename(prefix = 'gm-placeholders-', suffix = '.txt') 235 ph_file = codecs.open(filename = fname, mode = 'wb', encoding = 'utf8', errors = 'replace') 236 237 ph_file.write(u'Here you can find some more documentation on placeholder use:\n') 238 ph_file.write(u'\n http://wiki.gnumed.de/bin/view/Gnumed/GmManualLettersForms\n\n\n') 239 240 ph_file.write(u'Variable placeholders (use like: $<PLACEHOLDER_NAME::ARGUMENTS::MAX OUTPUT LENGTH>$):\n') 241 for ph in known_variant_placeholders: 242 ph_file.write(u' %s\n' % ph) 243 ph_file.write(u'\n') 244 245 ph_file.write(u'Injectable placeholders (use like: $<PLACEHOLDER_NAME::ARGUMENTS::MAX OUTPUT LENGTH>$):\n') 246 for ph in _injectable_placeholders: 247 ph_file.write(u' %s\n' % ph) 248 ph_file.write(u'\n') 249 250 ph_file.close() 251 gmMimeLib.call_viewer_on_file(aFile = fname, block = False)
252 #=====================================================================
253 -class gmPlaceholderHandler(gmBorg.cBorg):
254 """Returns values for placeholders. 255 256 - patient related placeholders operate on the currently active patient 257 - is passed to the forms handling code, for example 258 259 Return values when .debug is False: 260 - errors with placeholders return None 261 - placeholders failing to resolve to a value return an empty string 262 263 Return values when .debug is True: 264 - errors with placeholders return an error string 265 - placeholders failing to resolve to a value return a warning string 266 267 There are several types of placeholders: 268 269 injectable placeholders 270 - they must be set up before use by set_placeholder() 271 - they should be removed after use by unset_placeholder() 272 - the syntax is like extended static placeholders 273 - they are listed in _injectable_placeholders 274 275 variant placeholders 276 - those are listed in known_variant_placeholders 277 - they are parsed into placeholder, data, and maximum length 278 - the length is optional 279 - data is passed to the handler 280 281 Note that this cannot be called from a non-gui thread unless 282 wrapped in wx.CallAfter(). 283 """
284 - def __init__(self, *args, **kwargs):
285 286 self.pat = gmPerson.gmCurrentPatient() 287 self.debug = False 288 289 self.invalid_placeholder_template = _('invalid placeholder >>>>>%s<<<<<') 290 291 self.__cache = {} 292 293 self.__esc_style = None 294 self.__esc_func = lambda x:x
295 #-------------------------------------------------------- 296 # external API 297 #--------------------------------------------------------
298 - def set_placeholder(self, key=None, value=None):
301 #--------------------------------------------------------
302 - def unset_placeholder(self, key=None):
305 #--------------------------------------------------------
306 - def set_cache_value(self, key=None, value=None):
307 self.__cache[key] = value
308 #--------------------------------------------------------
309 - def unset_cache_value(self, key=None):
310 del self.__cache[key]
311 #--------------------------------------------------------
312 - def _set_escape_style(self, escape_style=None):
313 self.__esc_style = escape_style 314 return
315 316 escape_style = property(lambda x:x, _set_escape_style) 317 #--------------------------------------------------------
318 - def _set_escape_function(self, escape_function=None):
319 if escape_function is None: 320 self.__esc_func = lambda x:x 321 return 322 if not callable(escape_function): 323 raise ValueError(u'[%s._set_escape_function]: <%s> not callable' % (self.__class__.__name__, escape_function)) 324 self.__esc_func = escape_function 325 return
326 327 escape_function = property(lambda x:x, _set_escape_function) 328 #-------------------------------------------------------- 329 placeholder_regex = property(lambda x: default_placeholder_regex, lambda x:x) 330 331 first_order_placeholder_regex = property(lambda x: first_order_placeholder_regex, lambda x:x) 332 second_order_placeholder_regex = property(lambda x: second_order_placeholder_regex, lambda x:x) 333 third_order_placeholder_regex = property(lambda x: third_order_placeholder_regex, lambda x:x) 334 #-------------------------------------------------------- 335 # __getitem__ API 336 #--------------------------------------------------------
337 - def __getitem__(self, placeholder):
338 """Map self['placeholder'] to self.placeholder. 339 340 This is useful for replacing placeholders parsed out 341 of documents as strings. 342 343 Unknown/invalid placeholders still deliver a result but 344 it will be glaringly obvious if debugging is enabled. 345 """ 346 _log.debug('replacing [%s]', placeholder) 347 348 original_placeholder = placeholder 349 350 # remove leading/trailing '$<(<<)' and '(>>)>$' 351 if placeholder.startswith(default_placeholder_start): 352 placeholder = placeholder.lstrip('$').lstrip('<') 353 if placeholder.endswith(default_placeholder_end): 354 placeholder = placeholder.rstrip('$').rstrip('>') 355 else: 356 _log.error('placeholder must either start with [%s] and end with [%s] or neither of both', default_placeholder_start, default_placeholder_end) 357 if self.debug: 358 return self._escape(self.invalid_placeholder_template % original_placeholder) 359 return None 360 361 # injectable placeholder ? 362 parts = placeholder.split('::::', 1) 363 if len(parts) == 2: 364 name, lng = parts 365 is_an_injectable = True 366 try: 367 val = _injectable_placeholders[name] 368 except KeyError: 369 is_an_injectable = False 370 except: 371 _log.exception('injectable placeholder handling error: %s', original_placeholder) 372 if self.debug: 373 return self._escape(self.invalid_placeholder_template % original_placeholder) 374 return None 375 if is_an_injectable: 376 if val is None: 377 if self.debug: 378 return self._escape(u'injectable placeholder [%s]: no value available' % name) 379 return placeholder 380 try: 381 lng = int(lng) 382 except (TypeError, ValueError): 383 lng = len(val) 384 return val[:lng] 385 386 # variable placeholders 387 if len(placeholder.split('::', 2)) < 3: 388 _log.error('invalid placeholder structure: %s', original_placeholder) 389 if self.debug: 390 return self._escape(self.invalid_placeholder_template % original_placeholder) 391 return None 392 393 name, data = placeholder.split('::', 1) 394 data, lng_str = data.rsplit('::', 1) 395 _log.debug('placeholder parts: name=[%s]; length=[%s]; options=>>>%s<<<', name, lng_str, data) 396 try: 397 lng = int(lng_str) 398 except (TypeError, ValueError): 399 lng = None 400 401 handler = getattr(self, '_get_variant_%s' % name, None) 402 if handler is None: 403 _log.warning('no handler <_get_variant_%s> for placeholder %s', name, original_placeholder) 404 if self.debug: 405 return self._escape(self.invalid_placeholder_template % original_placeholder) 406 return None 407 408 try: 409 if lng is None: 410 return handler(data = data) 411 return handler(data = data)[:lng] 412 except: 413 _log.exception('placeholder handling error: %s', original_placeholder) 414 if self.debug: 415 return self._escape(self.invalid_placeholder_template % original_placeholder) 416 return None 417 418 _log.error('something went wrong, should never get here') 419 return None
420 #-------------------------------------------------------- 421 # placeholder handlers 422 #--------------------------------------------------------
423 - def _get_variant_client_version(self, data=None):
424 return self._escape ( 425 gmTools.coalesce ( 426 _cfg.get(option = u'client_version'), 427 u'%s' % self.__class__.__name__ 428 ) 429 )
430 #--------------------------------------------------------
431 - def _get_variant_reminders(self, data=None):
432 433 from Gnumed.wxpython import gmProviderInboxWidgets 434 435 template = _('due %(due_date)s: %(comment)s (%(interval_due)s)') 436 date_format = '%Y %b %d' 437 438 data_parts = data.split('//') 439 440 if len(data_parts) > 0: 441 if data_parts[0].strip() != u'': 442 template = data_parts[0] 443 444 if len(data_parts) > 1: 445 if data_parts[1].strip() != u'': 446 date_format = data_parts[1] 447 448 reminders = gmProviderInboxWidgets.manage_reminders(patient = self.pat.ID) 449 450 if reminders is None: 451 return u'' 452 453 if len(reminders) == 0: 454 return u'' 455 456 lines = [ template % r.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) for r in reminders ] 457 458 return u'\n'.join(lines)
459 #--------------------------------------------------------
460 - def _get_variant_documents(self, data=None):
461 462 select = False 463 include_descriptions = False 464 template = u'%s' 465 path_template = None 466 export_path = None 467 468 data_parts = data.split('//') 469 470 if u'select' in data_parts: 471 select = True 472 data_parts.remove(u'select') 473 474 if u'description' in data_parts: 475 include_descriptions = True 476 data_parts.remove(u'description') 477 478 template = data_parts[0] 479 480 if len(data_parts) > 1: 481 path_template = data_parts[1] 482 483 if len(data_parts) > 2: 484 export_path = data_parts[2] 485 486 # create path 487 if export_path is not None: 488 export_path = os.path.normcase(os.path.expanduser(export_path)) 489 gmTools.mkdir(export_path) 490 491 # select docs 492 if select: 493 docs = gmDocumentWidgets.manage_documents(msg = _('Select the patient documents to reference from the new document.'), single_selection = False) 494 else: 495 docs = self.pat.document_folder.documents 496 497 if docs is None: 498 return u'' 499 500 lines = [] 501 for doc in docs: 502 lines.append(template % doc.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style)) 503 if include_descriptions: 504 for desc in doc.get_descriptions(max_lng = None): 505 lines.append(self._escape(desc['text'] + u'\n')) 506 if path_template is not None: 507 for part_name in doc.export_parts_to_files(export_dir = export_path): 508 path, name = os.path.split(part_name) 509 lines.append(path_template % {'fullpath': part_name, 'name': name}) 510 511 return u'\n'.join(lines)
512 #--------------------------------------------------------
513 - def _get_variant_encounter_list(self, data=None):
514 515 encounters = gmEMRStructWidgets.select_encounters(single_selection = False) 516 if not encounters: 517 return u'' 518 519 template = data 520 521 lines = [] 522 for enc in encounters: 523 try: 524 lines.append(template % enc.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style)) 525 except: 526 lines.append(u'error formatting encounter') 527 _log.exception('problem formatting encounter list') 528 _log.error('template: %s', template) 529 _log.error('encounter: %s', encounter) 530 531 return u'\n'.join(lines)
532 #--------------------------------------------------------
533 - def _get_variant_soap_for_encounters(self, data=None):
534 """Select encounters from list and format SOAP thereof. 535 536 data: soap_cats (' ' -> None -> admin) // date format 537 """ 538 # defaults 539 cats = None 540 date_format = None 541 542 if data is not None: 543 data_parts = data.split('//') 544 545 # part[0]: categories 546 if len(data_parts[0]) > 0: 547 cats = [] 548 if u' ' in data_parts[0]: 549 cats.append(None) 550 data_parts[0] = data_parts[0].replace(u' ', u'') 551 cats.extend(list(data_parts[0])) 552 553 # part[1]: date format 554 if len(data_parts) > 1: 555 if len(data_parts[1]) > 0: 556 date_format = data_parts[1] 557 558 encounters = gmEMRStructWidgets.select_encounters(single_selection = False) 559 if not encounters: 560 return u'' 561 562 chunks = [] 563 for enc in encounters: 564 chunks.append(enc.format_latex ( 565 date_format = date_format, 566 soap_cats = cats, 567 soap_order = u'soap_rank, date' 568 )) 569 570 return u''.join(chunks)
571 #--------------------------------------------------------
572 - def _get_variant_emr_journal(self, data=None):
573 # default: all categories, neutral template 574 cats = list(u'soapu') 575 cats.append(None) 576 template = u'%s' 577 interactive = True 578 line_length = 9999 579 target_format = None 580 time_range = None 581 582 if data is not None: 583 data_parts = data.split('//') 584 585 # part[0]: categories 586 cats = [] 587 # ' ' -> None == admin 588 for c in list(data_parts[0]): 589 if c == u' ': 590 c = None 591 cats.append(c) 592 # '' -> SOAP + None 593 if cats == u'': 594 cats = list(u'soapu').append(None) 595 596 # part[1]: template 597 if len(data_parts) > 1: 598 template = data_parts[1] 599 600 # part[2]: line length 601 if len(data_parts) > 2: 602 try: 603 line_length = int(data_parts[2]) 604 except: 605 line_length = 9999 606 607 # part[3]: weeks going back in time 608 if len(data_parts) > 3: 609 try: 610 time_range = 7 * int(data_parts[3]) 611 except: 612 #time_range = None # infinite 613 # pass on literally, meaning it must be a valid PG interval string 614 time_range = data_parts[3] 615 616 # part[4]: output format 617 if len(data_parts) > 4: 618 target_format = data_parts[4] 619 620 # FIXME: will need to be a generator later on 621 narr = self.pat.emr.get_as_journal(soap_cats = cats, time_range = time_range) 622 623 if len(narr) == 0: 624 return u'' 625 626 keys = narr[0].keys() 627 lines = [] 628 line_dict = {} 629 for n in narr: 630 for key in keys: 631 if isinstance(n[key], basestring): 632 line_dict[key] = self._escape(text = n[key]) 633 continue 634 line_dict[key] = n[key] 635 try: 636 lines.append((template % line_dict)[:line_length]) 637 except KeyError: 638 return u'invalid key in template [%s], valid keys: %s]' % (template, str(keys)) 639 640 return u'\n'.join(lines)
641 #--------------------------------------------------------
642 - def _get_variant_soap_by_issue(self, data=None):
643 return self.__get_variant_soap_by_issue_or_episode(data = data, mode = u'issue')
644 #--------------------------------------------------------
645 - def _get_variant_soap_by_episode(self, data=None):
646 return self.__get_variant_soap_by_issue_or_episode(data = data, mode = u'episode')
647 #--------------------------------------------------------
648 - def __get_variant_soap_by_issue_or_episode(self, data=None, mode=None):
649 650 # default: all categories, neutral template 651 cats = list(u'soapu') 652 cats.append(None) 653 654 date_format = None 655 template = u'%s' 656 657 if data is not None: 658 data_parts = data.split('//') 659 660 # part[0]: categories 661 if len(data_parts[0]) > 0: 662 cats = [] 663 if u' ' in data_parts[0]: 664 cats.append(None) 665 cats.extend(list(data_parts[0].replace(u' ', u''))) 666 667 # part[1]: date format 668 if len(data_parts) > 1: 669 if len(data_parts[1]) > 0: 670 date_format = data_parts[1] 671 672 # part[2]: template 673 if len(data_parts) > 2: 674 if len(data_parts[2]) > 0: 675 template = data_parts[2] 676 677 if mode == u'issue': 678 narr = gmNarrativeWidgets.select_narrative_by_issue(soap_cats = cats) 679 else: 680 narr = gmNarrativeWidgets.select_narrative_by_episode(soap_cats = cats) 681 682 if narr is None: 683 return u'' 684 685 if len(narr) == 0: 686 return u'' 687 688 try: 689 narr = [ template % n.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) for n in narr ] 690 except KeyError: 691 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys())) 692 693 return u'\n'.join(narr)
694 #--------------------------------------------------------
695 - def _get_variant_progress_notes(self, data=None):
696 return self._get_variant_soap(data = data)
697 #--------------------------------------------------------
698 - def _get_variant_soap_s(self, data=None):
699 return self._get_variant_soap(data = u's')
700 #--------------------------------------------------------
701 - def _get_variant_soap_o(self, data=None):
702 return self._get_variant_soap(data = u'o')
703 #--------------------------------------------------------
704 - def _get_variant_soap_a(self, data=None):
705 return self._get_variant_soap(data = u'a')
706 #--------------------------------------------------------
707 - def _get_variant_soap_p(self, data=None):
708 return self._get_variant_soap(data = u'p')
709 #--------------------------------------------------------
710 - def _get_variant_soap_u(self, data=None):
711 return self._get_variant_soap(data = u'u')
712 #--------------------------------------------------------
713 - def _get_variant_soap_admin(self, data=None):
714 return self._get_variant_soap(data = u' ')
715 #--------------------------------------------------------
716 - def _get_variant_soap(self, data=None):
717 718 # default: all categories, neutral template 719 cats = list(u'soapu') 720 cats.append(None) 721 template = u'%(narrative)s' 722 723 if data is not None: 724 data_parts = data.split('//') 725 726 # part[0]: categories 727 cats = [] 728 # ' ' -> None == admin 729 for cat in list(data_parts[0]): 730 if cat == u' ': 731 cat = None 732 cats.append(cat) 733 # '' -> SOAP + None 734 if cats == u'': 735 cats = list(u'soapu') 736 cats.append(None) 737 738 # part[1]: template 739 if len(data_parts) > 1: 740 template = data_parts[1] 741 742 narr = gmNarrativeWidgets.select_narrative_from_episodes(soap_cats = cats) 743 744 if narr is None: 745 return u'' 746 747 if len(narr) == 0: 748 return u'' 749 750 # if any "%s" is in the template there cannot be any %(key)s 751 # and we also restrict the fields to .narrative (this is the 752 # old placeholder behaviour 753 if u'%s' in template: 754 narr = [ self._escape(n['narrative']) for n in narr ] 755 else: 756 narr = [ n.fields_as_dict(escape_style = self.__esc_style) for n in narr ] 757 758 try: 759 narr = [ template % n for n in narr ] 760 except KeyError: 761 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys())) 762 except TypeError: 763 return u'cannot mix "%%s" and "%%(key)s" in template [%s]' % template 764 765 return u'\n'.join(narr)
766 #--------------------------------------------------------
767 - def _get_variant_title(self, data=None):
768 return self._get_variant_name(data = u'%(title)s')
769 #--------------------------------------------------------
770 - def _get_variant_firstname(self, data=None):
771 return self._get_variant_name(data = u'%(firstnames)s')
772 #--------------------------------------------------------
773 - def _get_variant_lastname(self, data=None):
774 return self._get_variant_name(data = u'%(lastnames)s')
775 #--------------------------------------------------------
776 - def _get_variant_name(self, data=None):
777 if data is None: 778 return [_('template is missing')] 779 780 name = self.pat.get_active_name() 781 782 parts = { 783 'title': self._escape(gmTools.coalesce(name['title'], u'')), 784 'firstnames': self._escape(name['firstnames']), 785 'lastnames': self._escape(name['lastnames']), 786 'preferred': self._escape(gmTools.coalesce ( 787 initial = name['preferred'], 788 instead = u' ', 789 template_initial = u' "%s" ' 790 )) 791 } 792 793 return data % parts
794 #--------------------------------------------------------
795 - def _get_variant_date_of_birth(self, data='%Y %b %d'):
796 return self.pat.get_formatted_dob(format = str(data), encoding = gmI18N.get_encoding())
797 #-------------------------------------------------------- 798 # FIXME: extend to all supported genders
799 - def _get_variant_gender_mapper(self, data='male//female//other'):
800 801 values = data.split('//', 2) 802 803 if len(values) == 2: 804 male_value, female_value = values 805 other_value = u'<unkown gender>' 806 elif len(values) == 3: 807 male_value, female_value, other_value = values 808 else: 809 return _('invalid gender mapping layout: [%s]') % data 810 811 if self.pat['gender'] == u'm': 812 return self._escape(male_value) 813 814 if self.pat['gender'] == u'f': 815 return self._escape(female_value) 816 817 return self._escape(other_value)
818 #-------------------------------------------------------- 819 # address related placeholders 820 #--------------------------------------------------------
821 - def _get_variant_patient_address(self, data=u''):
822 823 data_parts = data.split(u'//') 824 825 # address type 826 adr_type = data_parts[0].strip() 827 orig_type = adr_type 828 if adr_type != u'': 829 adrs = self.pat.get_addresses(address_type = adr_type) 830 if len(adrs) == 0: 831 _log.warning('no address for type [%s]', adr_type) 832 adr_type = u'' 833 if adr_type == u'': 834 _log.debug('asking user for address type') 835 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat) 836 if adr is None: 837 if self.debug: 838 return _('no address type replacement selected') 839 return u'' 840 adr_type = adr['address_type'] 841 adr = self.pat.get_addresses(address_type = adr_type)[0] 842 843 # formatting template 844 template = _('%(street)s %(number)s, %(postcode)s %(urb)s, %(l10n_state)s, %(l10n_country)s') 845 if len(data_parts) > 1: 846 if data_parts[1].strip() != u'': 847 template = data_parts[1] 848 849 try: 850 return template % adr.fields_as_dict(escape_style = self.__esc_style) 851 except StandardError: 852 _log.exception('error formatting address') 853 _log.error('template: %s', template) 854 855 return None
856 #--------------------------------------------------------
857 - def __get_variant_adr_part(self, data=u'?', part=None):
858 requested_type = data.strip() 859 cache_key = 'adr-type-%s' % requested_type 860 try: 861 type2use = self.__cache[cache_key] 862 _log.debug('cache hit (%s): [%s] -> [%s]', cache_key, requested_type, type2use) 863 except KeyError: 864 type2use = requested_type 865 if type2use != u'': 866 adrs = self.pat.get_addresses(address_type = type2use) 867 if len(adrs) == 0: 868 _log.warning('no address of type [%s] for <%s> field extraction', requested_type, part) 869 type2use = u'' 870 if type2use == u'': 871 _log.debug('asking user for replacement address type') 872 adr = gmPersonContactWidgets.select_address(missing = requested_type, person = self.pat) 873 if adr is None: 874 _log.debug('no replacement selected') 875 if self.debug: 876 return self._escape(_('no address type replacement selected')) 877 return u'' 878 type2use = adr['address_type'] 879 self.__cache[cache_key] = type2use 880 _log.debug('caching (%s): [%s] -> [%s]', cache_key, requested_type, type2use) 881 882 return self._escape(self.pat.get_addresses(address_type = type2use)[0][part])
883 #--------------------------------------------------------
884 - def _get_variant_adr_street(self, data=u'?'):
885 return self.__get_variant_adr_part(data = data, part = 'street')
886 #--------------------------------------------------------
887 - def _get_variant_adr_number(self, data=u'?'):
888 return self.__get_variant_adr_part(data = data, part = 'number')
889 #--------------------------------------------------------
890 - def _get_variant_adr_subunit(self, data=u'?'):
891 return self.__get_variant_adr_part(data = data, part = 'subunit')
892 #--------------------------------------------------------
893 - def _get_variant_adr_location(self, data=u'?'):
894 return self.__get_variant_adr_part(data = data, part = 'urb')
895 #--------------------------------------------------------
896 - def _get_variant_adr_suburb(self, data=u'?'):
897 return self.__get_variant_adr_part(data = data, part = 'suburb')
898 #--------------------------------------------------------
899 - def _get_variant_adr_postcode(self, data=u'?'):
900 return self.__get_variant_adr_part(data = data, part = 'postcode')
901 #--------------------------------------------------------
902 - def _get_variant_adr_region(self, data=u'?'):
903 return self.__get_variant_adr_part(data = data, part = 'l10n_state')
904 #--------------------------------------------------------
905 - def _get_variant_adr_country(self, data=u'?'):
906 return self.__get_variant_adr_part(data = data, part = 'l10n_country')
907 #--------------------------------------------------------
908 - def _get_variant_patient_comm(self, data=None):
909 comm_type = None 910 template = u'%(url)s' 911 if data is not None: 912 data_parts = data.split(u'//') 913 if len(data_parts) > 0: 914 comm_type = data_parts[0] 915 if len(data_parts) > 1: 916 template = data_parts[1] 917 918 comms = self.pat.get_comm_channels(comm_medium = comm_type) 919 if len(comms) == 0: 920 if self.debug: 921 return template + u': ' + self._escape(_('no URL for comm channel [%s]') % data) 922 return u'' 923 924 return template % comms[0].fields_as_dict(escape_style = self.__esc_style)
925 # self._escape(comms[0]['url']) 926 #--------------------------------------------------------
927 - def _get_variant_patient_photo(self, data=None):
928 929 template = u'%s' 930 target_mime = None 931 target_ext = None 932 if data is not None: 933 parts = data.split(u'//') 934 template = parts[0] 935 if len(parts) > 1: 936 target_mime = parts[1].strip() 937 if len(parts) > 2: 938 target_ext = parts[2].strip() 939 if target_ext is None: 940 if target_mime is not None: 941 target_ext = gmMimeLib.guess_ext_by_mimetype(mimetype = target_mime) 942 943 mugshot = self.pat.document_folder.latest_mugshot 944 if mugshot is None: 945 if self.debug: 946 return self._escape(_('no mugshot available')) 947 return u'' 948 949 fname = mugshot.export_to_file ( 950 target_mime = target_mime, 951 target_extension = target_ext, 952 ignore_conversion_problems = True 953 ) 954 if fname is None: 955 if self.debug: 956 return self._escape(_('cannot export or convert latest mugshot')) 957 return u'' 958 959 return template % fname
960 #--------------------------------------------------------
961 - def _get_variant_patient_tags(self, data=u'%s//\\n'):
962 if len(self.pat.tags) == 0: 963 if self.debug: 964 return self._escape(_('no tags for this patient')) 965 return u'' 966 967 tags = gmDemographicsWidgets.select_patient_tags(patient = self.pat) 968 969 if tags is None: 970 if self.debug: 971 return self._escape(_('no patient tags selected for inclusion') % data) 972 return u'' 973 974 template, separator = data.split('//', 2) 975 976 return separator.join([ template % t.fields_as_dict(escape_style = self.__esc_style) for t in tags ])
977 # #-------------------------------------------------------- 978 # def _get_variant_patient_tags_table(self, data=u'?'): 979 # pass 980 #--------------------------------------------------------
981 - def _get_variant_current_provider(self, data=None):
982 prov = gmStaff.gmCurrentProvider() 983 984 title = gmTools.coalesce ( 985 prov['title'], 986 gmPerson.map_gender2salutation(prov['gender']) 987 ) 988 989 tmp = u'%s %s. %s' % ( 990 title, 991 prov['firstnames'][:1], 992 prov['lastnames'] 993 ) 994 return self._escape(tmp)
995 #--------------------------------------------------------
996 - def _get_variant_current_provider_external_id(self, data=u''):
997 data_parts = data.split(u'//') 998 if len(data_parts) < 2: 999 return self._escape(u'current provider external ID: template is missing') 1000 1001 id_type = data_parts[0].strip() 1002 if id_type == u'': 1003 return self._escape(u'current provider external ID: type is missing') 1004 1005 issuer = data_parts[1].strip() 1006 if issuer == u'': 1007 return self._escape(u'current provider external ID: issuer is missing') 1008 1009 prov = gmStaff.gmCurrentProvider() 1010 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer) 1011 1012 if len(ids) == 0: 1013 if self.debug: 1014 return self._escape(_('no external ID [%s] by [%s]') % (id_type, issuer)) 1015 return u'' 1016 1017 return self._escape(ids[0]['value'])
1018 #--------------------------------------------------------
1019 - def _get_variant_primary_praxis_provider(self, data=None):
1020 prov = self.pat.primary_provider 1021 if prov is None: 1022 return self._get_variant_current_provider() 1023 1024 title = gmTools.coalesce ( 1025 prov['title'], 1026 gmPerson.map_gender2salutation(prov['gender']) 1027 ) 1028 1029 tmp = u'%s %s. %s' % ( 1030 title, 1031 prov['firstnames'][:1], 1032 prov['lastnames'] 1033 ) 1034 return self._escape(tmp)
1035 #--------------------------------------------------------
1037 data_parts = data.split(u'//') 1038 if len(data_parts) < 2: 1039 return self._escape(u'primary in-praxis provider external ID: template is missing') 1040 1041 id_type = data_parts[0].strip() 1042 if id_type == u'': 1043 return self._escape(u'primary in-praxis provider external ID: type is missing') 1044 1045 issuer = data_parts[1].strip() 1046 if issuer == u'': 1047 return self._escape(u'primary in-praxis provider external ID: issuer is missing') 1048 1049 prov = self.pat.primary_provider 1050 if prov is None: 1051 if self.debug: 1052 return self._escape(_('no primary in-praxis provider')) 1053 return u'' 1054 1055 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer) 1056 1057 if len(ids) == 0: 1058 if self.debug: 1059 return self._escape(_('no external ID [%s] by [%s]') % (id_type, issuer)) 1060 return u'' 1061 1062 return self._escape(ids[0]['value'])
1063 #--------------------------------------------------------
1064 - def _get_variant_external_id(self, data=u''):
1065 data_parts = data.split(u'//') 1066 if len(data_parts) < 2: 1067 return self._escape(u'patient external ID: template is missing') 1068 1069 id_type = data_parts[0].strip() 1070 if id_type == u'': 1071 return self._escape(u'patient external ID: type is missing') 1072 1073 issuer = data_parts[1].strip() 1074 if issuer == u'': 1075 return self._escape(u'patient external ID: issuer is missing') 1076 1077 ids = self.pat.get_external_ids(id_type = id_type, issuer = issuer) 1078 1079 if len(ids) == 0: 1080 if self.debug: 1081 return self._escape(_('no external ID [%s] by [%s]') % (id_type, issuer)) 1082 return u'' 1083 1084 return self._escape(ids[0]['value'])
1085 #--------------------------------------------------------
1086 - def _get_variant_allergy_state(self, data=None):
1087 allg_state = self.pat.get_emr().allergy_state 1088 1089 if allg_state['last_confirmed'] is None: 1090 date_confirmed = u'' 1091 else: 1092 date_confirmed = u' (%s)' % gmDateTime.pydt_strftime ( 1093 allg_state['last_confirmed'], 1094 format = '%Y %B %d' 1095 ) 1096 1097 tmp = u'%s%s' % ( 1098 allg_state.state_string, 1099 date_confirmed 1100 ) 1101 return self._escape(tmp)
1102 #--------------------------------------------------------
1103 - def _get_variant_allergy_list(self, data=None):
1104 if data is None: 1105 return self._escape(_('template is missing')) 1106 1107 template, separator = data.split('//', 2) 1108 1109 return separator.join([ template % a.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style) for a in self.pat.emr.get_allergies() ])
1110 #--------------------------------------------------------
1111 - def _get_variant_allergies(self, data=None):
1112 1113 if data is None: 1114 return self._escape(_('template is missing')) 1115 1116 return u'\n'.join([ data % a.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style) for a in self.pat.emr.get_allergies() ])
1117 #--------------------------------------------------------
1118 - def _get_variant_current_meds_for_rx(self, data=None):
1119 if data is None: 1120 return self._escape(_('current_meds_for_rx: template is missing')) 1121 1122 emr = self.pat.get_emr() 1123 from Gnumed.wxpython import gmMedicationWidgets 1124 current_meds = gmMedicationWidgets.manage_substance_intakes(emr = emr) 1125 if current_meds is None: 1126 return u'' 1127 1128 intakes2show = {} 1129 for intake in current_meds: 1130 fields_dict = intake.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style) 1131 if intake['pk_brand'] is None: 1132 fields_dict['brand'] = self._escape(_('generic %s') % fields_dict['substance']) 1133 fields_dict['contains'] = self._escape(u'%s %s%s' % (fields_dict['substance'], fields_dict['amount'], fields_dict['unit'])) 1134 intakes2show[fields_dict['brand']] = fields_dict 1135 else: 1136 comps = [ c.split('::') for c in intake.containing_drug['components'] ] 1137 fields_dict['contains'] = self._escape(u'; '.join([ u'%s %s%s' % (c[0], c[1], c[2]) for c in comps ])) 1138 intakes2show[intake['brand']] = fields_dict # this will make multi-component drugs unique 1139 1140 intakes2dispense = {} 1141 for brand, intake in intakes2show.items(): 1142 msg = _('Dispense how much/many of "%(brand)s (%(contains)s)" ?') % intake 1143 amount2dispense = wx.GetTextFromUser(msg, _('Amount to dispense ?')) 1144 if amount2dispense == u'': 1145 continue 1146 intake['amount2dispense'] = amount2dispense 1147 intakes2dispense[brand] = intake 1148 1149 return u'\n'.join([ data % intake for intake in intakes2dispense.values() ])
1150 #--------------------------------------------------------
1151 - def _get_variant_current_meds(self, data=None):
1152 1153 if data is None: 1154 return self._escape(_('template is missing')) 1155 1156 parts = data.split(u'//') 1157 template = parts[0] 1158 ask_user = False 1159 if len(parts) > 1: 1160 ask_user = (parts[1] == u'select') 1161 1162 emr = self.pat.get_emr() 1163 if ask_user: 1164 from Gnumed.wxpython import gmMedicationWidgets 1165 current_meds = gmMedicationWidgets.manage_substance_intakes(emr = emr) 1166 if current_meds is None: 1167 return u'' 1168 else: 1169 current_meds = emr.get_current_substance_intake ( 1170 include_inactive = False, 1171 include_unapproved = True, 1172 order_by = u'brand, substance' 1173 ) 1174 if len(current_meds) == 0: 1175 return u'' 1176 1177 return u'\n'.join([ template % m.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style) for m in current_meds ])
1178 #--------------------------------------------------------
1179 - def _get_variant_current_meds_table(self, data=None):
1180 options = data.split('//') 1181 1182 return gmMedication.format_substance_intake ( 1183 emr = self.pat.emr, 1184 output_format = self.__esc_style, 1185 table_type = u'by-brand' 1186 )
1187 #--------------------------------------------------------
1188 - def _get_variant_current_meds_notes(self, data=None):
1189 options = data.split('//') 1190 1191 return gmMedication.format_substance_intake_notes ( 1192 emr = self.pat.get_emr(), 1193 output_format = self.__esc_style, 1194 table_type = u'by-brand' 1195 )
1196 #--------------------------------------------------------
1197 - def _get_variant_lab_table(self, data=None):
1198 options = data.split('//') 1199 1200 return gmPathLab.format_test_results ( 1201 results = self.pat.emr.get_test_results_by_date(), 1202 output_format = self.__esc_style 1203 )
1204 #--------------------------------------------------------
1205 - def _get_variant_test_results(self, data=None):
1206 1207 template = u'' 1208 date_format = '%Y %b %d %H:%M' 1209 separator = u'\n' 1210 1211 options = data.split(u'//') 1212 try: 1213 template = options[0].strip() 1214 date_format = options[1] 1215 separator = options[2] 1216 except IndexError: 1217 pass 1218 1219 if date_format.strip() == u'': 1220 date_format = '%Y %b %d %H:%M' 1221 if separator.strip() == u'': 1222 separator = u'\n' 1223 1224 results = gmMeasurementWidgets.manage_measurements(single_selection = False, emr = self.pat.emr) 1225 if results is None: 1226 if self.debug: 1227 return self._escape(_('no results for this patient (available or selected)')) 1228 return u'' 1229 1230 if template == u'': 1231 return (separator + separator).join([ self._escape(r.format(date_format = date_format)) for r in results ]) 1232 1233 return separator.join([ template % r.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) for r in results ])
1234 #--------------------------------------------------------
1235 - def _get_variant_latest_vaccs_table(self, data=None):
1236 options = data.split('//') 1237 1238 return gmVaccination.format_latest_vaccinations ( 1239 output_format = self.__esc_style, 1240 emr = self.pat.emr 1241 )
1242 #--------------------------------------------------------
1243 - def _get_variant_vaccination_history(self, data=None):
1244 options = data.split('//') 1245 template = options[0] 1246 if len(options) > 1: 1247 date_format = options[1] 1248 else: 1249 date_format = u'%Y %b %d' 1250 1251 vaccs = self.pat.emr.get_vaccinations(order_by = u'date_given DESC, vaccine') 1252 1253 return u'\n'.join([ template % v.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) for v in vaccs ])
1254 #--------------------------------------------------------
1255 - def _get_variant_PHX(self, data=None):
1256 1257 if data is None: 1258 if self.debug: 1259 _log.error('PHX: missing placeholder arguments') 1260 return self._escape(_('PHX: Invalid placeholder options.')) 1261 return u'' 1262 1263 _log.debug('arguments: %s', data) 1264 1265 data_parts = data.split(u'//') 1266 template = u'%s' 1267 separator = u'\n' 1268 date_format = '%Y %b %d' 1269 esc_style = None 1270 try: 1271 template = data_parts[0] 1272 separator = data_parts[1] 1273 date_format = data_parts[2] 1274 esc_style = data_parts[3] 1275 except IndexError: 1276 pass 1277 1278 phxs = gmEMRStructWidgets.select_health_issues(emr = self.pat.emr) 1279 if phxs is None: 1280 if self.debug: 1281 return self._escape(_('no PHX for this patient (available or selected)')) 1282 return u'' 1283 1284 return separator.join ([ 1285 template % phx.fields_as_dict ( 1286 date_format = date_format, 1287 #escape_style = esc_style, 1288 escape_style = self.__esc_style, 1289 bool_strings = (self._escape(_('yes')), self._escape(_('no'))) 1290 ) for phx in phxs 1291 ])
1292 #--------------------------------------------------------
1293 - def _get_variant_problems(self, data=None):
1294 1295 if data is None: 1296 return self._escape(_('template is missing')) 1297 1298 probs = self.pat.emr.get_problems() 1299 1300 return u'\n'.join([ data % p.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style) for p in probs ])
1301 #--------------------------------------------------------
1302 - def _get_variant_today(self, data='%Y %b %d'):
1303 return self._escape(gmDateTime.pydt_now_here().strftime(str(data)).decode(gmI18N.get_encoding()))
1304 #--------------------------------------------------------
1305 - def _get_variant_tex_escape(self, data=None):
1306 return gmTools.tex_escape_string(text = data)
1307 #--------------------------------------------------------
1308 - def _get_variant_text_snippet(self, data=None):
1309 data_parts = data.split(u'//') 1310 keyword = data_parts[0] 1311 template = u'%s' 1312 if len(data_parts) > 1: 1313 template = data_parts[1] 1314 1315 expansion = gmKeywordExpansionWidgets.expand_keyword(keyword = keyword, show_list = True) 1316 1317 if expansion is None: 1318 if self.debug: 1319 return self._escape(_('no textual expansion found for keyword <%s>') % keyword) 1320 return u'' 1321 1322 #return template % self._escape(expansion) 1323 return template % expansion
1324 #--------------------------------------------------------
1325 - def _get_variant_data_snippet(self, data=None):
1326 parts = data.split(u'//') 1327 keyword = parts[0] 1328 template = u'%s' 1329 target_mime = None 1330 target_ext = None 1331 if len(parts) > 1: 1332 template = parts[1] 1333 if len(parts) > 2: 1334 target_mime = parts[2].strip() 1335 if len(parts) > 3: 1336 target_ext = parts[3].strip() 1337 if target_ext is None: 1338 if target_mime is not None: 1339 target_ext = gmMimeLib.guess_ext_by_mimetype(mimetype = target_mime) 1340 1341 expansion = gmKeywordExpansion.get_expansion ( 1342 keyword = keyword, 1343 textual_only = False, 1344 binary_only = True 1345 ) 1346 if expansion is None: 1347 if self.debug: 1348 return self._escape(_('no binary expansion found for keyword <%s>') % keyword) 1349 return u'' 1350 1351 filename = expansion.export_to_file() 1352 if filename is None: 1353 if self.debug: 1354 return self._escape(_('cannot export data of binary expansion keyword <%s>') % keyword) 1355 return u'' 1356 1357 if expansion['is_encrypted']: 1358 pwd = wx.GetPasswordFromUser ( 1359 message = _('Enter your GnuPG passphrase for decryption of [%s]') % expansion['keyword'], 1360 caption = _('GnuPG passphrase prompt'), 1361 default_value = u'' 1362 ) 1363 filename = gmTools.gpg_decrypt_file(filename = filename, passphrase = pwd) 1364 if filename is None: 1365 if self.debug: 1366 return self._escape(_('cannot decrypt data of binary expansion keyword <%s>') % keyword) 1367 return u'' 1368 1369 target_fname = gmTools.get_unique_filename ( 1370 prefix = '%s-converted-' % os.path.splitext(filename)[0], 1371 suffix = target_ext 1372 ) 1373 if not gmMimeLib.convert_file(filename = filename, target_mime = target_mime, target_filename = target_fname): 1374 if self.debug: 1375 return self._escape(_('cannot convert data of binary expansion keyword <%s>') % keyword) 1376 # hoping that the target can cope: 1377 return template % filename 1378 1379 return template % target_fname
1380 #--------------------------------------------------------
1381 - def _get_variant_free_text(self, data=None):
1382 1383 if data is None: 1384 msg = _('generic text') 1385 else: 1386 msg = data 1387 1388 dlg = gmGuiHelpers.cMultilineTextEntryDlg ( 1389 None, 1390 -1, 1391 title = _('Replacing <free_text> placeholder'), 1392 msg = _('Below you can enter free text.\n\n [%s]') % msg 1393 ) 1394 dlg.enable_user_formatting = True 1395 decision = dlg.ShowModal() 1396 1397 if decision != wx.ID_SAVE: 1398 dlg.Destroy() 1399 if self.debug: 1400 return self._escape(_('Text input cancelled by user.')) 1401 return u'' 1402 1403 text = dlg.value.strip() 1404 if dlg.is_user_formatted: 1405 dlg.Destroy() 1406 return text 1407 1408 dlg.Destroy() 1409 1410 return self._escape(text)
1411 #--------------------------------------------------------
1412 - def _get_variant_bill(self, data=None):
1413 try: 1414 bill = self.__cache['bill'] 1415 except KeyError: 1416 from Gnumed.wxpython import gmBillingWidgets 1417 bill = gmBillingWidgets.manage_bills(patient = self.pat) 1418 if bill is None: 1419 if self.debug: 1420 return self._escape(_('no bill selected')) 1421 return u'' 1422 self.__cache['bill'] = bill 1423 1424 return data % bill.fields_as_dict(date_format = '%Y %B %d', escape_style = self.__esc_style)
1425 #--------------------------------------------------------
1426 - def _get_variant_bill_item(self, data=None):
1427 try: 1428 bill = self.__cache['bill'] 1429 except KeyError: 1430 from Gnumed.wxpython import gmBillingWidgets 1431 bill = gmBillingWidgets.manage_bills(patient = self.pat) 1432 if bill is None: 1433 if self.debug: 1434 return self._escape(_('no bill selected')) 1435 return u'' 1436 self.__cache['bill'] = bill 1437 1438 return u'\n'.join([ data % i.fields_as_dict(date_format = '%Y %B %d', escape_style = self.__esc_style) for i in bill.bill_items ])
1439 #-------------------------------------------------------- 1440 # internal helpers 1441 #--------------------------------------------------------
1442 - def _escape(self, text=None):
1443 if self.__esc_func is None: 1444 return text 1445 return self.__esc_func(text)
1446 #=====================================================================
1447 -class cMacroPrimitives:
1448 """Functions a macro can legally use. 1449 1450 An instance of this class is passed to the GNUmed scripting 1451 listener. Hence, all actions a macro can legally take must 1452 be defined in this class. Thus we achieve some screening for 1453 security and also thread safety handling. 1454 """ 1455 #-----------------------------------------------------------------
1456 - def __init__(self, personality = None):
1457 if personality is None: 1458 raise gmExceptions.ConstructorError, 'must specify personality' 1459 self.__personality = personality 1460 self.__attached = 0 1461 self._get_source_personality = None 1462 self.__user_done = False 1463 self.__user_answer = 'no answer yet' 1464 self.__pat = gmPerson.gmCurrentPatient() 1465 1466 self.__auth_cookie = str(random.random()) 1467 self.__pat_lock_cookie = str(random.random()) 1468 self.__lock_after_load_cookie = str(random.random()) 1469 1470 _log.info('slave mode personality is [%s]', personality)
1471 #----------------------------------------------------------------- 1472 # public API 1473 #-----------------------------------------------------------------
1474 - def attach(self, personality = None):
1475 if self.__attached: 1476 _log.error('attach with [%s] rejected, already serving a client', personality) 1477 return (0, _('attach rejected, already serving a client')) 1478 if personality != self.__personality: 1479 _log.error('rejecting attach to personality [%s], only servicing [%s]' % (personality, self.__personality)) 1480 return (0, _('attach to personality [%s] rejected') % personality) 1481 self.__attached = 1 1482 self.__auth_cookie = str(random.random()) 1483 return (1, self.__auth_cookie)
1484 #-----------------------------------------------------------------
1485 - def detach(self, auth_cookie=None):
1486 if not self.__attached: 1487 return 1 1488 if auth_cookie != self.__auth_cookie: 1489 _log.error('rejecting detach() with cookie [%s]' % auth_cookie) 1490 return 0 1491 self.__attached = 0 1492 return 1
1493 #-----------------------------------------------------------------
1494 - def force_detach(self):
1495 if not self.__attached: 1496 return 1 1497 self.__user_done = False 1498 # FIXME: use self.__sync_cookie for syncing with user interaction 1499 wx.CallAfter(self._force_detach) 1500 return 1
1501 #-----------------------------------------------------------------
1502 - def version(self):
1503 ver = _cfg.get(option = u'client_version') 1504 return "GNUmed %s, %s $Revision: 1.51 $" % (ver, self.__class__.__name__)
1505 #-----------------------------------------------------------------
1506 - def shutdown_gnumed(self, auth_cookie=None, forced=False):
1507 """Shuts down this client instance.""" 1508 if not self.__attached: 1509 return 0 1510 if auth_cookie != self.__auth_cookie: 1511 _log.error('non-authenticated shutdown_gnumed()') 1512 return 0 1513 wx.CallAfter(self._shutdown_gnumed, forced) 1514 return 1
1515 #-----------------------------------------------------------------
1516 - def raise_gnumed(self, auth_cookie = None):
1517 """Raise ourselves to the top of the desktop.""" 1518 if not self.__attached: 1519 return 0 1520 if auth_cookie != self.__auth_cookie: 1521 _log.error('non-authenticated raise_gnumed()') 1522 return 0 1523 return "cMacroPrimitives.raise_gnumed() not implemented"
1524 #-----------------------------------------------------------------
1525 - def get_loaded_plugins(self, auth_cookie = None):
1526 if not self.__attached: 1527 return 0 1528 if auth_cookie != self.__auth_cookie: 1529 _log.error('non-authenticated get_loaded_plugins()') 1530 return 0 1531 gb = gmGuiBroker.GuiBroker() 1532 return gb['horstspace.notebook.gui'].keys()
1533 #-----------------------------------------------------------------
1534 - def raise_notebook_plugin(self, auth_cookie = None, a_plugin = None):
1535 """Raise a notebook plugin within GNUmed.""" 1536 if not self.__attached: 1537 return 0 1538 if auth_cookie != self.__auth_cookie: 1539 _log.error('non-authenticated raise_notebook_plugin()') 1540 return 0 1541 # FIXME: use semaphore 1542 wx.CallAfter(gmPlugin.raise_notebook_plugin, a_plugin) 1543 return 1
1544 #-----------------------------------------------------------------
1545 - def load_patient_from_external_source(self, auth_cookie = None):
1546 """Load external patient, perhaps create it. 1547 1548 Callers must use get_user_answer() to get status information. 1549 It is unsafe to proceed without knowing the completion state as 1550 the controlled client may be waiting for user input from a 1551 patient selection list. 1552 """ 1553 if not self.__attached: 1554 return (0, _('request rejected, you are not attach()ed')) 1555 if auth_cookie != self.__auth_cookie: 1556 _log.error('non-authenticated load_patient_from_external_source()') 1557 return (0, _('rejected load_patient_from_external_source(), not authenticated')) 1558 if self.__pat.locked: 1559 _log.error('patient is locked, cannot load from external source') 1560 return (0, _('current patient is locked')) 1561 self.__user_done = False 1562 wx.CallAfter(self._load_patient_from_external_source) 1563 self.__lock_after_load_cookie = str(random.random()) 1564 return (1, self.__lock_after_load_cookie)
1565 #-----------------------------------------------------------------
1566 - def lock_loaded_patient(self, auth_cookie = None, lock_after_load_cookie = None):
1567 if not self.__attached: 1568 return (0, _('request rejected, you are not attach()ed')) 1569 if auth_cookie != self.__auth_cookie: 1570 _log.error('non-authenticated lock_load_patient()') 1571 return (0, _('rejected lock_load_patient(), not authenticated')) 1572 # FIXME: ask user what to do about wrong cookie 1573 if lock_after_load_cookie != self.__lock_after_load_cookie: 1574 _log.warning('patient lock-after-load request rejected due to wrong cookie [%s]' % lock_after_load_cookie) 1575 return (0, 'patient lock-after-load request rejected, wrong cookie provided') 1576 self.__pat.locked = True 1577 self.__pat_lock_cookie = str(random.random()) 1578 return (1, self.__pat_lock_cookie)
1579 #-----------------------------------------------------------------
1580 - def lock_into_patient(self, auth_cookie = None, search_params = None):
1581 if not self.__attached: 1582 return (0, _('request rejected, you are not attach()ed')) 1583 if auth_cookie != self.__auth_cookie: 1584 _log.error('non-authenticated lock_into_patient()') 1585 return (0, _('rejected lock_into_patient(), not authenticated')) 1586 if self.__pat.locked: 1587 _log.error('patient is already locked') 1588 return (0, _('already locked into a patient')) 1589 searcher = gmPersonSearch.cPatientSearcher_SQL() 1590 if type(search_params) == types.DictType: 1591 idents = searcher.get_identities(search_dict=search_params) 1592 raise StandardError("must use dto, not search_dict") 1593 else: 1594 idents = searcher.get_identities(search_term=search_params) 1595 if idents is None: 1596 return (0, _('error searching for patient with [%s]/%s') % (search_term, search_dict)) 1597 if len(idents) == 0: 1598 return (0, _('no patient found for [%s]/%s') % (search_term, search_dict)) 1599 # FIXME: let user select patient 1600 if len(idents) > 1: 1601 return (0, _('several matching patients found for [%s]/%s') % (search_term, search_dict)) 1602 if not gmPatSearchWidgets.set_active_patient(patient = idents[0]): 1603 return (0, _('cannot activate patient [%s] (%s/%s)') % (str(idents[0]), search_term, search_dict)) 1604 self.__pat.locked = True 1605 self.__pat_lock_cookie = str(random.random()) 1606 return (1, self.__pat_lock_cookie)
1607 #-----------------------------------------------------------------
1608 - def unlock_patient(self, auth_cookie = None, unlock_cookie = None):
1609 if not self.__attached: 1610 return (0, _('request rejected, you are not attach()ed')) 1611 if auth_cookie != self.__auth_cookie: 1612 _log.error('non-authenticated unlock_patient()') 1613 return (0, _('rejected unlock_patient, not authenticated')) 1614 # we ain't locked anyways, so succeed 1615 if not self.__pat.locked: 1616 return (1, '') 1617 # FIXME: ask user what to do about wrong cookie 1618 if unlock_cookie != self.__pat_lock_cookie: 1619 _log.warning('patient unlock request rejected due to wrong cookie [%s]' % unlock_cookie) 1620 return (0, 'patient unlock request rejected, wrong cookie provided') 1621 self.__pat.locked = False 1622 return (1, '')
1623 #-----------------------------------------------------------------
1624 - def assume_staff_identity(self, auth_cookie = None, staff_name = "Dr.Jekyll", staff_creds = None):
1625 if not self.__attached: 1626 return 0 1627 if auth_cookie != self.__auth_cookie: 1628 _log.error('non-authenticated select_identity()') 1629 return 0 1630 return "cMacroPrimitives.assume_staff_identity() not implemented"
1631 #-----------------------------------------------------------------
1632 - def get_user_answer(self):
1633 if not self.__user_done: 1634 return (0, 'still waiting') 1635 self.__user_done = False 1636 return (1, self.__user_answer)
1637 #----------------------------------------------------------------- 1638 # internal API 1639 #-----------------------------------------------------------------
1640 - def _force_detach(self):
1641 msg = _( 1642 'Someone tries to forcibly break the existing\n' 1643 'controlling connection. This may or may not\n' 1644 'have legitimate reasons.\n\n' 1645 'Do you want to allow breaking the connection ?' 1646 ) 1647 can_break_conn = gmGuiHelpers.gm_show_question ( 1648 aMessage = msg, 1649 aTitle = _('forced detach attempt') 1650 ) 1651 if can_break_conn: 1652 self.__user_answer = 1 1653 else: 1654 self.__user_answer = 0 1655 self.__user_done = True 1656 if can_break_conn: 1657 self.__pat.locked = False 1658 self.__attached = 0 1659 return 1
1660 #-----------------------------------------------------------------
1661 - def _shutdown_gnumed(self, forced=False):
1662 top_win = wx.GetApp().GetTopWindow() 1663 if forced: 1664 top_win.Destroy() 1665 else: 1666 top_win.Close()
1667 #-----------------------------------------------------------------
1669 patient = gmPatSearchWidgets.get_person_from_external_sources(search_immediately = True, activate_immediately = True) 1670 if patient is not None: 1671 self.__user_answer = 1 1672 else: 1673 self.__user_answer = 0 1674 self.__user_done = True 1675 return 1
1676 #===================================================================== 1677 # main 1678 #===================================================================== 1679 if __name__ == '__main__': 1680 1681 if len(sys.argv) < 2: 1682 sys.exit() 1683 1684 if sys.argv[1] != 'test': 1685 sys.exit() 1686 1687 gmI18N.activate_locale() 1688 gmI18N.install_domain() 1689 1690 #--------------------------------------------------------
1691 - def test_placeholders():
1692 handler = gmPlaceholderHandler() 1693 handler.debug = True 1694 1695 for placeholder in ['a', 'b']: 1696 print handler[placeholder] 1697 1698 pat = gmPersonSearch.ask_for_patient() 1699 if pat is None: 1700 return 1701 1702 gmPatSearchWidgets.set_active_patient(patient = pat) 1703 1704 print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 1705 1706 app = wx.PyWidgetTester(size = (200, 50)) 1707 1708 ph = 'progress_notes::ap' 1709 print '%s: %s' % (ph, handler[ph])
1710 #--------------------------------------------------------
1711 - def test_new_variant_placeholders():
1712 1713 tests = [ 1714 # should work: 1715 '$<lastname>$', 1716 '$<lastname::::3>$', 1717 '$<name::%(title)s %(firstnames)s%(preferred)s%(lastnames)s>$', 1718 1719 # should fail: 1720 'lastname', 1721 '$<lastname', 1722 '$<lastname::', 1723 '$<lastname::>$', 1724 '$<lastname::abc>$', 1725 '$<lastname::abc::>$', 1726 '$<lastname::abc::3>$', 1727 '$<lastname::abc::xyz>$', 1728 '$<lastname::::>$', 1729 '$<lastname::::xyz>$', 1730 1731 '$<date_of_birth::%Y-%m-%d>$', 1732 '$<date_of_birth::%Y-%m-%d::3>$', 1733 '$<date_of_birth::%Y-%m-%d::>$', 1734 1735 # should work: 1736 '$<adr_location::home::35>$', 1737 '$<gender_mapper::male//female//other::5>$', 1738 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\n::50>$', 1739 '$<allergy_list::%(descriptor)s, >$', 1740 '$<current_meds_table::latex//by-brand>$' 1741 1742 # 'firstname', 1743 # 'title', 1744 # 'date_of_birth', 1745 # 'progress_notes', 1746 # 'soap', 1747 # 'soap_s', 1748 # 'soap_o', 1749 # 'soap_a', 1750 # 'soap_p', 1751 1752 # 'soap', 1753 # 'progress_notes', 1754 # 'date_of_birth' 1755 ] 1756 1757 # tests = [ 1758 # '$<latest_vaccs_table::latex>$' 1759 # ] 1760 1761 pat = gmPersonSearch.ask_for_patient() 1762 if pat is None: 1763 return 1764 1765 gmPatSearchWidgets.set_active_patient(patient = pat) 1766 1767 handler = gmPlaceholderHandler() 1768 handler.debug = True 1769 1770 for placeholder in tests: 1771 print placeholder, "=>", handler[placeholder] 1772 print "--------------" 1773 raw_input()
1774 1775 # print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 1776 1777 # app = wx.PyWidgetTester(size = (200, 50)) 1778 1779 # ph = 'progress_notes::ap' 1780 # print '%s: %s' % (ph, handler[ph]) 1781 1782 #--------------------------------------------------------
1783 - def test_scripting():
1784 from Gnumed.pycommon import gmScriptingListener 1785 import xmlrpclib 1786 listener = gmScriptingListener.cScriptingListener(macro_executor = cMacroPrimitives(personality='unit test'), port=9999) 1787 1788 s = xmlrpclib.ServerProxy('http://localhost:9999') 1789 print "should fail:", s.attach() 1790 print "should fail:", s.attach('wrong cookie') 1791 print "should work:", s.version() 1792 print "should fail:", s.raise_gnumed() 1793 print "should fail:", s.raise_notebook_plugin('test plugin') 1794 print "should fail:", s.lock_into_patient('kirk, james') 1795 print "should fail:", s.unlock_patient() 1796 status, conn_auth = s.attach('unit test') 1797 print "should work:", status, conn_auth 1798 print "should work:", s.version() 1799 print "should work:", s.raise_gnumed(conn_auth) 1800 status, pat_auth = s.lock_into_patient(conn_auth, 'kirk, james') 1801 print "should work:", status, pat_auth 1802 print "should fail:", s.unlock_patient(conn_auth, 'bogus patient unlock cookie') 1803 print "should work", s.unlock_patient(conn_auth, pat_auth) 1804 data = {'firstname': 'jame', 'lastnames': 'Kirk', 'gender': 'm'} 1805 status, pat_auth = s.lock_into_patient(conn_auth, data) 1806 print "should work:", status, pat_auth 1807 print "should work", s.unlock_patient(conn_auth, pat_auth) 1808 print s.detach('bogus detach cookie') 1809 print s.detach(conn_auth) 1810 del s 1811 1812 listener.shutdown()
1813 #--------------------------------------------------------
1814 - def test_placeholder_regex():
1815 1816 import re as regex 1817 1818 tests = [ 1819 ' $<lastname>$ ', 1820 ' $<lastname::::3>$ ', 1821 1822 # should fail: 1823 '$<date_of_birth::%Y-%m-%d>$', 1824 '$<date_of_birth::%Y-%m-%d::3>$', 1825 '$<date_of_birth::%Y-%m-%d::>$', 1826 1827 '$<adr_location::home::35>$', 1828 '$<gender_mapper::male//female//other::5>$', 1829 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\\n::50>$', 1830 '$<allergy_list::%(descriptor)s, >$', 1831 1832 '\\noindent Patient: $<lastname>$, $<firstname>$', 1833 '$<allergies::%(descriptor)s & %(l10n_type)s & {\\footnotesize %(reaction)s} \tabularnewline \hline >$', 1834 '$<current_meds:: \item[%(substance)s] {\\footnotesize (%(brand)s)} %(preparation)s %(amount)s%(unit)s: %(schedule)s >$' 1835 ] 1836 1837 tests = [ 1838 1839 'junk $<lastname::::3>$ junk', 1840 'junk $<lastname::abc::3>$ junk', 1841 'junk $<lastname::abc>$ junk', 1842 'junk $<lastname>$ junk', 1843 1844 'junk $<lastname>$ junk $<firstname>$ junk', 1845 'junk $<lastname::abc>$ junk $<fiststname::abc>$ junk', 1846 'junk $<lastname::abc::3>$ junk $<firstname::abc::3>$ junk', 1847 'junk $<lastname::::3>$ junk $<firstname::::3>$ junk' 1848 1849 ] 1850 1851 tests = [ 1852 # u'junk $<<<date_of_birth::%Y %B %d $<inner placeholder::%Y %B %d::20>$::20>>>$ junk', 1853 # u'junk $<date_of_birth::%Y %B %d::20>$ $<date_of_birth::%Y %B %d::20>$', 1854 # u'junk $<date_of_birth::%Y %B %d::>$ $<date_of_birth::%Y %B %d::20>$ $<<date_of_birth::%Y %B %d::20>>$', 1855 # u'junk $<date_of_birth::::20>$', 1856 # u'junk $<date_of_birth::::>$', 1857 u'$<<<current_meds::%(brand)s (%(substance)s): Dispense $<free_text::Dispense how many of %(brand)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::250>>>$', 1858 ] 1859 1860 print "testing placeholder regex:", first_order_placeholder_regex 1861 print "" 1862 1863 for t in tests: 1864 print 'line: "%s"' % t 1865 phs = regex.findall(first_order_placeholder_regex, t, regex.IGNORECASE) 1866 print " %s placeholders:" % len(phs) 1867 for p in phs: 1868 print ' => "%s"' % p 1869 print " "
1870 #--------------------------------------------------------
1871 - def test_placeholder():
1872 1873 phs = [ 1874 #u'emr_journal::soapu //%(clin_when)s %(modified_by)s %(soap_cat)s %(narrative)s//1000 days::', 1875 #u'free_text::tex//placeholder test::9999', 1876 #u'soap_for_encounters:://::9999', 1877 #u'soap_p', 1878 #u'encounter_list::%(started)s: %(assessment_of_encounter)s::30', 1879 #u'patient_comm::homephone::1234', 1880 #u'$<patient_address::work::1234>$', 1881 #u'adr_region::home::1234', 1882 #u'adr_country::fehlt::1234', 1883 #u'adr_subunit::fehlt::1234', 1884 #u'adr_suburb::fehlt-auch::1234', 1885 #u'external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 1886 #u'primary_praxis_provider', 1887 #u'current_provider', 1888 #u'current_provider_external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 1889 #u'current_provider_external_id::LANR//LÄK::1234' 1890 #u'primary_praxis_provider_external_id::LANR//LÄK::1234' 1891 #u'form_name_long::::1234', 1892 #u'form_name_long::::5', 1893 #u'form_name_long::::', 1894 #u'form_version::::5', 1895 #u'$<current_meds::\item %(brand)s %(preparation)s (%(substance)s) from %(started)s for %(duration)s as %(schedule)s until %(discontinued)s\\n::250>$', 1896 #u'$<vaccination_history::%(date_given)s: %(vaccine)s [%(batch_no)s] %(l10n_indications)s::250>$', 1897 #u'$<date_of_birth::%Y %B %d::20>$', 1898 #u'$<date_of_birth::%Y %B %d::>$', 1899 #u'$<date_of_birth::::20>$', 1900 #u'$<date_of_birth::::>$', 1901 #u'$<patient_tags::Tag "%(l10n_description)s": %(comment)s//\\n- ::250>$', 1902 #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>$', 1903 #u'$<patient_photo::\includegraphics[width=60mm]{%s}//image/png//.png::250>$', 1904 #u'$<data_snippet::binary_test_snippet//path=<%s>//image/png//.png::250>$', 1905 #u'$<data_snippet::autograph-LMcC//path=<%s>//image/jpg//.jpg::250>$', 1906 #u'$<current_meds::%s ($<lastname::::50>$)//select::>$', 1907 #u'$<current_meds::%s//select::>$', 1908 #u'$<soap_by_issue::soapu //%Y %b %d//%s::>$', 1909 #u'$<soap_by_episode::soapu //%Y %b %d//%s::>$', 1910 #u'$<documents::select//description//document %(clin_when)s: %(l10n_type)s// file: %(fullpath)s (<some path>/%(name)s)//~/gnumed/export/::>$', 1911 #u'$<soap::soapu //%s::9999>$', 1912 #u'$<soap::soapu //%(soap_cat)s: %(date)s | %(provider)s | %(narrative)s::9999>$' 1913 #u'$<test_results:://%c::>$' 1914 #u'$<test_results::%(unified_abbrev)s: %(unified_val)s %(val_unit)s//%c::>$' 1915 #u'$<reminders:://::>$' 1916 u'$<current_meds_for_rx::%(brand)s (%(contains)s): dispense %(amount2dispense)s ::>$' 1917 ] 1918 1919 handler = gmPlaceholderHandler() 1920 handler.debug = True 1921 1922 gmStaff.set_current_provider_to_logged_on_user() 1923 pat = gmPersonSearch.ask_for_patient() 1924 if pat is None: 1925 return 1926 1927 gmPatSearchWidgets.set_active_patient(patient = pat) 1928 1929 app = wx.PyWidgetTester(size = (200, 50)) 1930 #handler.set_placeholder('form_name_long', 'ein Testformular') 1931 for ph in phs: 1932 print ph 1933 print " result:" 1934 print ' %s' % handler[ph]
1935 #handler.unset_placeholder('form_name_long') 1936 #--------------------------------------------------------
1937 - def test():
1938 pat = gmPersonSearch.ask_for_patient() 1939 if pat is None: 1940 sys.exit() 1941 gmPerson.set_active_patient(patient = pat) 1942 from Gnumed.wxpython import gmMedicationWidgets 1943 gmMedicationWidgets.manage_substance_intakes()
1944 #--------------------------------------------------------
1945 - def test_show_phs():
1946 show_placeholders()
1947 #-------------------------------------------------------- 1948 1949 #test_placeholders() 1950 #test_new_variant_placeholders() 1951 #test_scripting() 1952 #test_placeholder_regex() 1953 #test() 1954 test_placeholder() 1955 #test_show_phs() 1956 1957 #===================================================================== 1958