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