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