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