Package Gnumed :: Package wxpython :: Module gmMacro
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmMacro

   1  # -*- coding: utf-8 -*- 
   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 io 
  16  import datetime 
  17  import urllib.parse 
  18  import codecs 
  19  import re as regex 
  20   
  21   
  22  import wx 
  23   
  24   
  25  if __name__ == '__main__': 
  26          sys.path.insert(0, '../../') 
  27  from Gnumed.pycommon import gmI18N 
  28  if __name__ == '__main__': 
  29          gmI18N.activate_locale() 
  30          gmI18N.install_domain() 
  31  from Gnumed.pycommon import gmGuiBroker 
  32  from Gnumed.pycommon import gmTools 
  33  from Gnumed.pycommon import gmBorg 
  34  from Gnumed.pycommon import gmExceptions 
  35  from Gnumed.pycommon import gmCfg2 
  36  from Gnumed.pycommon import gmDateTime 
  37  from Gnumed.pycommon import gmMimeLib 
  38  from Gnumed.pycommon import gmShellAPI 
  39  from Gnumed.pycommon import gmCrypto 
  40   
  41  from Gnumed.business import gmPerson 
  42  from Gnumed.business import gmStaff 
  43  from Gnumed.business import gmDemographicRecord 
  44  from Gnumed.business import gmMedication 
  45  from Gnumed.business import gmPathLab 
  46  from Gnumed.business import gmPersonSearch 
  47  from Gnumed.business import gmVaccination 
  48  from Gnumed.business import gmKeywordExpansion 
  49  from Gnumed.business import gmPraxis 
  50   
  51  from Gnumed.wxpython import gmGuiHelpers 
  52  from Gnumed.wxpython import gmNarrativeWorkflows 
  53  from Gnumed.wxpython import gmPatSearchWidgets 
  54  from Gnumed.wxpython import gmPersonContactWidgets 
  55  from Gnumed.wxpython import gmPlugin 
  56  from Gnumed.wxpython import gmEMRStructWidgets 
  57  from Gnumed.wxpython import gmEncounterWidgets 
  58  from Gnumed.wxpython import gmListWidgets 
  59  from Gnumed.wxpython import gmDemographicsWidgets 
  60  from Gnumed.wxpython import gmDocumentWidgets 
  61  from Gnumed.wxpython import gmKeywordExpansionWidgets 
  62  from Gnumed.wxpython import gmPraxisWidgets 
  63  from Gnumed.wxpython import gmAddressWidgets 
  64   
  65   
  66  _log = logging.getLogger('gm.scripting') 
  67  _cfg = gmCfg2.gmCfgData() 
  68   
  69  #===================================================================== 
  70  # values for the following placeholders must be injected from the outside before 
  71  # using them, in use they must conform to the "placeholder::::max length" syntax, 
  72  # as long as they resolve to None they return their respective names so the 
  73  # developers can know which placeholder was not set 
  74  known_injectable_placeholders = [ 
  75          'form_name_long', 
  76          'form_name_short', 
  77          'form_version', 
  78          'form_version_internal', 
  79          'form_last_modified' 
  80  ] 
  81   
  82  # the following must satisfy the pattern "$<name::args::(optional) max string length>$" when used 
  83  __known_variant_placeholders = { 
  84          # generic: 
  85          'free_text': u"""show a dialog for entering some free text: 
  86                  args: <message>//<preset> 
  87                          <message>: shown in input dialog, must not contain either 
  88                                  of '::' and whatever the arguments divider is 
  89                                  set to (default '//'), 
  90                          <preset>: whatever to initially show inside the input field, 
  91                  caches input per <message>""", 
  92   
  93          'text_snippet': """a text snippet, taken from the keyword expansion mechanism: 
  94                  args: <snippet name>//<template>""", 
  95   
  96          'data_snippet': """a binary snippet, taken from the keyword expansion mechanism: 
  97                  args: <snippet name>//<template>//<optional target mime type>//<optional target extension> 
  98                  returns full path to an exported copy of the 
  99                  data rather than the data itself, 
 100                  template: string template for outputting the path 
 101                  target mime type: a mime type into which to convert the image, no conversion if not given 
 102                  target extension: target file name extension, derived from target mime type if not given 
 103          """, 
 104          u'qrcode': u"""generate QR code file for a text snippet: 
 105                  returns: path to QR code png file 
 106                  args: <text>//<template> 
 107                          text: utf8 text, will always be encoded in 'binary' mode with utf8 as encoding 
 108                          template: %s-template into which to insert the QR code png file path 
 109          """, 
 110   
 111          # text manipulation 
 112          'range_of': """select range of enclosed text (note that this cannot take into account non-length characters such as enclosed LaTeX code 
 113                  args: <enclosed text> 
 114          """, 
 115          'if_not_empty': """format text based on template if not empty 
 116                  args: <possibly-empty-text>//<template-if-not-empty>//<alternative-text-if-empty> 
 117          """, 
 118   
 119          u'tex_escape': u"args: string to escape, mostly obsolete now", 
 120   
 121          u'url_escape': u"""Escapes a string suitable for use as _data_ in an URL 
 122                  args: text to escape 
 123          """, 
 124   
 125          # internal state 
 126          'ph_cfg': u"""Set placeholder handler options. 
 127                  args: option name//option value//macro return string 
 128                  option names: 
 129                          ellipsis: what to use as ellipsis (if anything) when 
 130                                  shortening strings or string regions, setting the 
 131                                  value to NONE will switch off ellipis handling, 
 132                                  default is switched off 
 133                          argumentsdivider: what to use as divider when splitting 
 134                                  an argument string into parts, default is '//', 
 135                                  note that the 'config' placeholder will ALWAYS 
 136                                  use '//' to split its argument string, regardless 
 137                                  of which setting of <argumentsdivider> is in effect, 
 138                                  use DEFAULT to reset this setting back to the 
 139                                  default '//' 
 140                          encoding: the encoding in which data emitted by GNUmed 
 141                                  as placeholder replacement needs to be valid in, 
 142                                  note that GNUmed will still emit unicode to replacement 
 143                                  consumers but it will ensure the data emitted _can_ 
 144                                  be encoded by this target encoding (by roundtripping 
 145                                  unicode-encoding-unicode) 
 146                                  valid from where this placeholder is located at 
 147                                  until further change, 
 148                                  use DEFAULT to reset encoding back to the default 
 149                                  which is to not ensure compatibility, 
 150                                  if the encoding ends in '-strict' then the placeholder 
 151                                  replacement will fail if the roundtrip fails 
 152          """, 
 153          u'if_debugging': u"""set text based on whether debugging is active 
 154                  args: <text-if-debugging>//<text-if-not-debugging> 
 155          """, 
 156   
 157          'today': "args: strftime format", 
 158   
 159          'gender_mapper': """maps gender of patient to a string: 
 160                  args: <value when person is male> // <is female> // <is other> 
 161                  eg. 'male//female//other' 
 162                  or: 'Lieber Patient//Liebe Patientin'""", 
 163          'client_version': "the version of the current client as a string (no 'v' in front)", 
 164   
 165          'gen_adr_street': """part of a generic address, cached, selected from database: 
 166                  args: optional template//optional selection message//optional cache ID 
 167                  template: %s-style formatting template 
 168                  message: text message shown in address selection list 
 169                  cache ID: used to differentiate separate cached invocations of this placeholder 
 170          """, 
 171          'gen_adr_number': """part of a generic address, cached, selected from database: 
 172                  args: optional template//optional selection message//optional cache ID 
 173                  template: %s-style formatting template 
 174                  message: text message shown in address selection list 
 175                  cache ID: used to differentiate separate cached invocations of this placeholder 
 176          """, 
 177          'gen_adr_subunit': """part of a generic address, cached, selected from database: 
 178                  args: optional template//optional selection message//optional cache ID 
 179                  template: %s-style formatting template 
 180                  message: text message shown in address selection list 
 181                  cache ID: used to differentiate separate cached invocations of this placeholder 
 182          """, 
 183          'gen_adr_location': """part of a generic address, cached, selected from database: 
 184                  args: optional template//optional selection message//optional cache ID 
 185                  template: %s-style formatting template 
 186                  message: text message shown in address selection list 
 187                  cache ID: used to differentiate separate cached invocations of this placeholder 
 188          """, 
 189          'gen_adr_suburb': """part of a generic address, cached, selected from database: 
 190                  args: optional template//optional selection message//optional cache ID 
 191                  template: %s-style formatting template 
 192                  message: text message shown in address selection list 
 193                  cache ID: used to differentiate separate cached invocations of this placeholder 
 194          """, 
 195          'gen_adr_postcode': """part of a generic address, cached, selected from database: 
 196                  args: optional template//optional selection message//optional cache ID 
 197                  template: %s-style formatting template 
 198                  message: text message shown in address selection list 
 199                  cache ID: used to differentiate separate cached invocations of this placeholder 
 200          """, 
 201          'gen_adr_region': """part of a generic address, cached, selected from database: 
 202                  args: optional template//optional selection message//optional cache ID 
 203                  template: %s-style formatting template 
 204                  message: text message shown in address selection list 
 205                  cache ID: used to differentiate separate cached invocations of this placeholder 
 206          """, 
 207          'gen_adr_country': """part of a generic address, cached, selected from database: 
 208                  args: optional template//optional selection message//optional cache ID 
 209                  template: %s-style formatting template 
 210                  message: text message shown in address selection list 
 211                  cache ID: used to differentiate separate cached invocations of this placeholder 
 212          """, 
 213   
 214          'receiver_name': """the receiver name, cached, selected from database: 
 215                  receivers are presented for selection from people/addresses related 
 216                  to the patient in some way or other, 
 217                  args: optional template//optional cache ID 
 218                  template: %s-style formatting template 
 219                  cache ID: used to differentiate separate cached invocations of this placeholder 
 220          """, 
 221          'receiver_street': """part of a receiver address, cached, selected from database: 
 222                  receivers are presented for selection from people/addresses related 
 223                  to the patient in some way or other, 
 224                  args: optional template//optional cache ID 
 225                  template: %s-style formatting template 
 226                  cache ID: used to differentiate separate cached invocations of this placeholder 
 227          """, 
 228          'receiver_number': """part of a receiver address, cached, selected from database: 
 229                  receivers are presented for selection from people/addresses related 
 230                  to the patient in some way or other, 
 231                  args: optional template//optional cache ID 
 232                  template: %s-style formatting template 
 233                  cache ID: used to differentiate separate cached invocations of this placeholder 
 234          """, 
 235          'receiver_subunit': """part of a receiver address, cached, selected from database: 
 236                  receivers are presented for selection from people/addresses related 
 237                  to the patient in some way or other, 
 238                  args: optional template//optional cache ID 
 239                  template: %s-style formatting template 
 240                  cache ID: used to differentiate separate cached invocations of this placeholder 
 241          """, 
 242          'receiver_location': """part of a receiver address, cached, selected from database: 
 243                  receivers are presented for selection from people/addresses related 
 244                  to the patient in some way or other, 
 245                  args: optional template//optional cache ID 
 246                  template: %s-style formatting template 
 247                  cache ID: used to differentiate separate cached invocations of this placeholder 
 248          """, 
 249          'receiver_suburb': """part of a receiver address, cached, selected from database: 
 250                  receivers are presented for selection from people/addresses related 
 251                  to the patient in some way or other, 
 252                  args: optional template//optional cache ID 
 253                  template: %s-style formatting template 
 254                  cache ID: used to differentiate separate cached invocations of this placeholder 
 255          """, 
 256          'receiver_postcode': """part of a receiver address, cached, selected from database: 
 257                  receivers are presented for selection from people/addresses related 
 258                  to the patient in some way or other, 
 259                  args: optional template//optional cache ID 
 260                  template: %s-style formatting template 
 261                  cache ID: used to differentiate separate cached invocations of this placeholder 
 262          """, 
 263          'receiver_region': """part of a receiver address, cached, selected from database: 
 264                  receivers are presented for selection from people/addresses related 
 265                  to the patient in some way or other, 
 266                  args: optional template//optional cache ID 
 267                  template: %s-style formatting template 
 268                  cache ID: used to differentiate separate cached invocations of this placeholder 
 269          """, 
 270          'receiver_country': """part of a receiver address, cached, selected from database: 
 271                  receivers are presented for selection from people/addresses related 
 272                  to the patient in some way or other, 
 273                  args: optional template//optional cache ID 
 274                  template: %s-style formatting template 
 275                  cache ID: used to differentiate separate cached invocations of this placeholder 
 276          """, 
 277   
 278          # patient demographics: 
 279          'name': "args: template for name parts arrangement", 
 280          'date_of_birth': "args: strftime date/time format directive", 
 281   
 282          'patient_address': "args: <type of address>//<optional formatting template>", 
 283          'adr_street': "args: <type of address>, cached per type", 
 284          'adr_number': "args: <type of address>, cached per type", 
 285          'adr_subunit': "args: <type of address>, cached per type", 
 286          'adr_location': "args: <type of address>, cached per type", 
 287          'adr_suburb': "args: <type of address>, cached per type", 
 288          'adr_postcode': "args: <type of address>, cached per type", 
 289          'adr_region': "args: <type of address>, cached per type", 
 290          'adr_country': "args: <type of address>, cached per type", 
 291   
 292          'patient_comm': "args: <comm channel type as per database>//<%(field)s-template>", 
 293   
 294          'patient_vcf': """returns path to VCF for current patient 
 295                  args: <template> 
 296                  template: %s-template for path 
 297          """, 
 298          'patient_gdt': """returns path to GDT for current patient 
 299                  args: <template> 
 300                  template: %s-template for path 
 301          """, 
 302          u'patient_mcf': u"""returns MECARD for current patient 
 303                  args: <format>//<template> 
 304                  format: fmt=qr|mcf|txt 
 305                          qr: QR code png file path, 
 306                          mcf: MECARD .mcf file path, 
 307                          txt: MECARD string, 
 308                          default - if omitted - is "txt", 
 309                  template: tmpl=<%s-template string>, "%s" if omitted 
 310          """, 
 311   
 312          'patient_tags': "args: <%(field)s-template>//<separator>", 
 313          #u'patient_tags_table': u"no args", 
 314          'patient_photo': """outputs URL to exported patient photo (cached per mime type and extension): 
 315                  args: <template>//<optional target mime type>//<optional target extension>, 
 316                  returns full path to an exported copy of the 
 317                  image rather than the image data itself, 
 318                  returns u'' if no mugshot available, 
 319                  template: string template for outputting the path 
 320                  target mime type: a mime type into which to convert the image, no conversion if not given 
 321                  target extension: target file name extension, derived from target mime type if not given""", 
 322          'external_id': "args: <type of ID>//<issuer of ID>", 
 323   
 324   
 325          # clinical record related: 
 326          'soap': "get all of SOAPU/ADMIN, no template in args needed", 
 327          'soap_s': "get subset of SOAPU/ADMIN, no template in args needed", 
 328          'soap_o': "get subset of SOAPU/ADMIN, no template in args needed", 
 329          'soap_a': "get subset of SOAPU/ADMIN, no template in args needed", 
 330          'soap_p': "get subset of SOAPU/ADMIN, no template in args needed", 
 331          'soap_u': "get subset of SOAPU/ADMIN, no template in args needed", 
 332          'soap_admin': "get subset of SOAPU/ADMIN, no template in args needed", 
 333   
 334          'progress_notes': """get progress notes: 
 335                  args: categories//template 
 336                  categories: string with 'soapu '; ' ' == None == admin 
 337                  template:       u'something %s something'               (do not include // in template !)""", 
 338   
 339          'soap_for_encounters': """lets the user select a list of encounters for which: 
 340                  LaTeX formatted progress notes are emitted, 
 341                  args: soap categories // strftime date format""", 
 342   
 343          'soap_by_issue': """lets the user select a list of issues and then SOAP entries from those issues: 
 344                  args: soap categories // strftime date format // template""", 
 345   
 346          'soap_by_episode': """lets the user select a list of episodes and then SOAP entries from those episodes: 
 347                  args: soap categories // strftime date format // template""", 
 348   
 349          'emr_journal': """returns EMR journal view entries: 
 350                  args format:   <categories>//<template>//<line length>//<time range>//<target format> 
 351                  categories:        string with any of "s", "o", "a", "p", "u", " "; (" " == None == admin category) 
 352                  template:          something %s something else (Do not include // in the template !) 
 353                  line length:   the maximum length of individual lines, not the total placeholder length 
 354                  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)""", 
 355   
 356          'substance_abuse': """returns substance abuse entries: 
 357                  args: line template 
 358          """, 
 359   
 360          'current_meds': """returns current medications: 
 361                  args: line template//<select> 
 362                  <select>: if this is present the user will be asked which meds to export""", 
 363   
 364          'current_meds_for_rx': """formats substance intakes either by substance (non-product intakes) or by producdt (once per product intake, even if multi-component): 
 365                  args: <line template> 
 366                  <line_template>: template into which to insert each intake, keys from 
 367                  clin.v_substance_intakes, special additional keys: 
 368                          %(contains)s -- list of components 
 369                          %(amount2dispense)s -- how much/many to dispense""", 
 370   
 371          'current_meds_AMTS': """emit LaTeX longtable lines with appropriate page breaks: 
 372                  also creates per-page AMTS QR codes and sets the 
 373                  following internal placeholders: 
 374                          amts_png_file_1 
 375                          amts_png_file_2 
 376                          amts_png_file_3 
 377                          amts_data_file_1 
 378                          amts_data_file_2 
 379                          amts_data_file_3 
 380                          amts_png_file_current_page 
 381                          amts_data_file_utf8 
 382                          amts_png_file_utf8 
 383                  the last of which contains the LaTeX command \\thepage (such that 
 384                  LaTeX can use this in, say, page headers) but omitting the .png 
 385                  (for which LaTeX will look by itself), 
 386                  note that you will have to use the 2nd- or 3rd-pass placeholder 
 387                  format if you plan to insert the above because they will only be 
 388                  available by first (or second) pass processing of the initial 
 389                  placeholder "current_meds_AMTS" 
 390          """, 
 391          'current_meds_AMTS_enhanced': """emit LaTeX longtable lines with appropriate page breaks: 
 392                  this returns the same content as current_meds_AMTS except that 
 393                  it does not truncate output data whenever possible 
 394          """, 
 395   
 396          'current_meds_table': "emits a LaTeX table, no arguments", 
 397          'current_meds_notes': "emits a LaTeX table, no arguments", 
 398          'lab_table': "emits a LaTeX table, no arguments", 
 399          'test_results': "args: <%(field)s-template>//<date format>//<line separator (EOL)>", 
 400          'latest_vaccs_table': "emits a LaTeX table, no arguments", 
 401          'vaccination_history': "args: <%(field)s-template//date format> to format one vaccination per line", 
 402          'allergy_state': "no arguments", 
 403          'allergies': "args: line template, one allergy per line", 
 404          'allergy_list': "args holds: template per allergy, all allergies on one line", 
 405          'problems': "args holds: line template, one problem per line", 
 406          'diagnoses': 'args: line template, one diagnosis per line', 
 407          'PHX': "Past medical HiXtory; args: line template//separator//strftime date format", 
 408          'encounter_list': "args: per-encounter template, each ends up on one line", 
 409   
 410          'documents': """retrieves documents from the archive: 
 411                  args:   <select>//<description>//<template>//<path template>//<path> 
 412                  select: let user select which documents to include, optional, if not given: all documents included 
 413                  description:    whether to include descriptions, optional 
 414                  template:       something %(field)s something else (do not include '//' or '::' itself in the template) 
 415                  path template:  the template for outputting the path to exported 
 416                          copies of the document pages, if not given no pages are exported, 
 417                          this template can contain "%(name)s" and/or "%(fullpath)s" which  
 418                          is replaced by the appropriate value for each exported file 
 419                  path:   into which path to export copies of the document pages, temp dir if not given""", 
 420   
 421          'reminders': """patient reminders: 
 422                  args:   <template>//<date format> 
 423                  template:       something %(field)s something else (do not include '//' or '::' itself in the template)""", 
 424   
 425          'external_care': """External care entries: 
 426                  args:   <template> 
 427                  template:       something %(field)s something else (do not include '//' or '::' itself in the template)""", 
 428   
 429          # provider related: 
 430          'current_provider': "no arguments", 
 431          'current_provider_name': """formatted name of current provider: 
 432                  args: <template>, 
 433                  template:       something %(field)s something else (do not include '//' or '::' itself in the template) 
 434          """, 
 435          'current_provider_title': """formatted name of current provider: 
 436                  args: <optional template>, 
 437                  template:       something %(title)s something else (do not include '//' or '::' itself in the template) 
 438          """, 
 439          'current_provider_firstnames': """formatted name of current provider: 
 440                  args: <optional template>, 
 441                  template:       something %(firstnames)s something else (do not include '//' or '::' itself in the template) 
 442          """, 
 443          'current_provider_lastnames': """formatted name of current provider: 
 444                  args: <optional template>, 
 445                  template:       something %(lastnames)s something else (do not include '//' or '::' itself in the template) 
 446          """, 
 447          'current_provider_external_id': "args: <type of ID>//<issuer of ID>", 
 448          'primary_praxis_provider': "primary provider for current patient in this praxis", 
 449          'primary_praxis_provider_external_id': "args: <type of ID>//<issuer of ID>", 
 450   
 451   
 452          # praxis related: 
 453          'praxis': """retrieve current branch of your praxis: 
 454                  args: <template>//select 
 455                  template:               something %(field)s something else (do not include '//' or '::' itself in the template) 
 456                  select:                 if this is present allow selection of the branch rather than using the current branch""", 
 457   
 458          'praxis_address': "args: <optional formatting template>", 
 459          'praxis_comm': "args: type//<optional formatting template>", 
 460          'praxis_id': "args: <type of ID>//<issuer of ID>//<optional formatting template>", 
 461          'praxis_vcf': """returns path to VCF for current praxis branch 
 462                  args: <template> 
 463                  template: %s-template for path 
 464          """, 
 465          u'praxis_mcf': u"""returns MECARD for current praxis branch 
 466                  args: <format>//<template> 
 467                  format: fmt=qr|mcf|txt 
 468                          qr: QR code png file path, 
 469                          mcf: MECARD .mcf file path, 
 470                          txt: MECARD string, 
 471                          default - if omitted - is "txt", 
 472                  template: tmpl=<%s-template string>, "%s" if omitted 
 473          """, 
 474          u'praxis_scan2pay': u"""return scan2pay data or QR code for current praxis 
 475                  args: <format>, 
 476                          format: fmt=qr|txt 
 477                                  qr: QR code png file path, 
 478                                  txt: scan2pay data string, 
 479                                  default - if omitted: qr 
 480          """, 
 481   
 482          # billing related: 
 483          'bill': """retrieve a bill 
 484                  args: <template>//<date format> 
 485                  template:               something %(field)s something else (do not include '//' or '::' itself in the template) 
 486                  date format:    strftime date format""", 
 487          'bill_scan2pay': u"""return scan2pay data or QR code for a bill 
 488                  args: <format>, 
 489                          format: fmt=qr|txt 
 490                                  qr: QR code png file path, 
 491                                  txt: scan2pay data string, 
 492                                  default - if omitted: qr 
 493          """, 
 494          'bill_item': """retrieve the items of a previously retrieved (and therefore cached until the next retrieval) bill 
 495                  args: <template>//<date format> 
 496                  template:               something %(field)s something else (do not include '//' or '::' itself in the template) 
 497                  date format:    strftime date format""", 
 498          'bill_adr_street': "args: optional template (%s-style formatting template); cached per bill", 
 499          'bill_adr_number': "args: optional template (%s-style formatting template); cached per bill", 
 500          'bill_adr_subunit': "args: optional template (%s-style formatting template); cached per bill", 
 501          'bill_adr_location': "args: optional template (%s-style formatting template); cached per bill", 
 502          'bill_adr_suburb': "args: optional template (%s-style formatting template); cached per bill", 
 503          'bill_adr_postcode': "args: optional template (%s-style formatting template); cached per bill", 
 504          'bill_adr_region': "args: optional template (%s-style formatting template); cached per bill", 
 505          'bill_adr_country': "args: optional template (%s-style formatting template); cached per bill" 
 506   
 507  } 
 508   
 509  known_variant_placeholders = __known_variant_placeholders.keys() 
 510  #known_variant_placeholders.sort() 
 511   
 512   
 513  # http://help.libreoffice.org/Common/List_of_Regular_Expressions 
 514  # except that OOo cannot be non-greedy |-( 
 515  #default_placeholder_regex = r'\$<.+?>\$'                               # previous working placeholder 
 516          # regex logic: 
 517          # starts with "$" 
 518          # followed by "<" 
 519          # followed by > 0 characters but NOT "<" but ONLY up to the NEXT ":" 
 520          # followed by "::" 
 521          # followed by any number of characters  but ONLY up to the NEXT ":" 
 522          # followed by "::" 
 523          # followed by any number of numbers 
 524          # followed by ">" 
 525          # followed by "$" 
 526   
 527  # previous: 
 528  default_placeholder_regex = r'\$<[^<:]+::.*?::\d*?>\$|\$<[^<:]+::.*?::\d+-\d+>\$'         # this one works [except that OOo cannot be non-greedy |-(    ] 
 529  first_pass_placeholder_regex = r'|'.join ([ 
 530          r'\$<[^<:]+::.*?(?=::\d*?>\$)::\d*?>\$', 
 531          r'\$<[^<:]+::.*?(?=::\d+-\d+>\$)::\d+-\d+>\$' 
 532  ]) 
 533  second_pass_placeholder_regex = r'|'.join ([ 
 534          r'\$<<[^<:]+?::.*?(?=::\d*?>>\$)::\d*?>>\$', 
 535          r'\$<<[^<:]+?::.*?(?=::\d+-\d+>>\$)::\d+-\d+>>\$' 
 536  ]) 
 537  third_pass_placeholder_regex = r'|'.join ([ 
 538          r'\$<<<[^<:]+?::.*?(?=::\d*?>>>\$)::\d*?>>>\$', 
 539          r'\$<<<[^<:]+?::.*?(?=::\d+-\d+>>>\$)::\d+-\d+>>>\$' 
 540  ]) 
 541   
 542  default_placeholder_start = '$<' 
 543  default_placeholder_end = '>$' 
 544   
 545  #===================================================================== 
546 -def show_placeholders():
547 548 fname = gmTools.get_unique_filename(prefix = 'gm-placeholders-', suffix = '.txt') 549 ph_file = io.open(fname, mode = 'wt', encoding = 'utf8', errors = 'replace') 550 551 ph_file.write('Here you can find some more documentation on placeholder use:\n') 552 ph_file.write('\n http://wiki.gnumed.de/bin/view/Gnumed/GmManualLettersForms\n\n\n') 553 554 ph_file.write('Variable placeholders:\n') 555 ph_file.write('Usage: $<PLACEHOLDER_NAME::ARGUMENTS::REGION_DEFINITION>$)\n') 556 ph_file.write(' REGION_DEFINITION:\n') 557 ph_file.write('* a single number specifying the maximum output length or\n') 558 ph_file.write('* a number, a "-", followed by a second number specifying the region of the string to return\n') 559 ph_file.write('ARGUMENTS:\n') 560 ph_file.write('* depend on the actual placeholder (see there)\n') 561 ph_file.write('* if a template is supported it will be used to %-format the output\n') 562 ph_file.write('* templates may be either %s-style or %(name)s-style\n') 563 ph_file.write('* templates cannot contain "::"\n') 564 ph_file.write('* templates cannot contain whatever the arguments divider is set to (default "//")\n') 565 for ph in known_variant_placeholders: 566 txt = __known_variant_placeholders[ph] 567 ph_file.write('\n') 568 ph_file.write(' ---=== %s ===---\n' % ph) 569 ph_file.write('\n') 570 ph_file.write(txt) 571 ph_file.write('\n\n') 572 ph_file.write('\n') 573 574 ph_file.write('Known injectable placeholders (use like: $<PLACEHOLDER_NAME::ARGUMENTS::MAX OUTPUT LENGTH>$):\n') 575 for ph in known_injectable_placeholders: 576 ph_file.write(' %s\n' % ph) 577 ph_file.write('\n') 578 579 ph_file.close() 580 gmMimeLib.call_viewer_on_file(aFile = fname, block = False)
581 582 #=====================================================================
583 -class gmPlaceholderHandler(gmBorg.cBorg):
584 """Returns values for placeholders. 585 586 - patient related placeholders operate on the currently active patient 587 - is passed to the forms handling code, for example 588 589 Return values when .debug is False: 590 - errors with placeholders return None 591 - placeholders failing to resolve to a value return an empty string 592 593 Return values when .debug is True: 594 - errors with placeholders return an error string 595 - placeholders failing to resolve to a value return a warning string 596 597 There are several types of placeholders: 598 599 injectable placeholders 600 - they must be set up before use by set_placeholder() 601 - they should be removed after use by unset_placeholder() 602 - the syntax is like extended static placeholders 603 - known ones are listed in known_injectable_placeholders 604 - per-form ones can be used but must exist before 605 the form is processed 606 607 variant placeholders 608 - those are listed in known_variant_placeholders 609 - they are parsed into placeholder, data, and maximum length 610 - the length is optional 611 - data is passed to the handler 612 613 Note that this cannot be called from a non-gui thread unless 614 wrapped in wx.CallAfter(). 615 """
616 - def __init__(self, *args, **kwargs):
617 618 self.pat = gmPerson.gmCurrentPatient() 619 self.debug = False 620 621 self.invalid_placeholder_template = _('invalid placeholder >>>>>%s<<<<<') 622 623 self.__injected_placeholders = {} 624 self.__cache = {} 625 626 self.__esc_style = None 627 self.__esc_func = lambda x:x 628 629 self.__ellipsis = None 630 self.__args_divider = '//' 631 self.__data_encoding = None 632 self.__data_encoding_strict = False
633 634 #-------------------------------------------------------- 635 # external API 636 #--------------------------------------------------------
637 - def set_placeholder(self, key=None, value=None, known_only=True):
638 _log.debug('setting [%s]', key) 639 try: 640 known_injectable_placeholders.index(key) 641 except ValueError: 642 _log.debug('injectable placeholder [%s] unknown', key) 643 if known_only: 644 raise 645 self.__injected_placeholders[key] = value
646 647 #--------------------------------------------------------
648 - def unset_placeholder(self, key=None):
649 _log.debug('unsetting [%s]', key) 650 try: 651 del self.__injected_placeholders[key] 652 except KeyError: 653 _log.debug('injectable placeholder [%s] unknown', key)
654 655 #--------------------------------------------------------
656 - def set_cache_value(self, key=None, value=None):
657 self.__cache[key] = value
658 #--------------------------------------------------------
659 - def unset_cache_value(self, key=None):
660 del self.__cache[key]
661 662 #--------------------------------------------------------
663 - def _set_escape_style(self, escape_style=None):
664 self.__esc_style = escape_style 665 return
666 667 escape_style = property(lambda x:x, _set_escape_style) 668 669 #--------------------------------------------------------
670 - def _set_escape_function(self, escape_function=None):
671 if escape_function is None: 672 self.__esc_func = lambda x:x 673 return 674 if not callable(escape_function): 675 raise ValueError('[%s._set_escape_function]: <%s> not callable' % (self.__class__.__name__, escape_function)) 676 self.__esc_func = escape_function 677 return
678 679 escape_function = property(lambda x:x, _set_escape_function) 680 681 #--------------------------------------------------------
682 - def _set_ellipsis(self, ellipsis):
683 if ellipsis == 'NONE': 684 ellipsis = None 685 self.__ellipsis = ellipsis
686 687 ellipsis = property(lambda x: self.__ellipsis, _set_ellipsis) 688 689 #--------------------------------------------------------
690 - def _set_arguments_divider(self, divider):
691 if divider == 'DEFAULT': 692 divider = '//' 693 self.__args_divider = divider
694 695 arguments_divider = property(lambda x: self.__args_divider, _set_arguments_divider) 696 697 #--------------------------------------------------------
698 - def _set_data_encoding(self, encoding):
699 if encoding == 'NONE': 700 self.__data_encoding = None 701 self.__data_encoding_strict = False 702 703 self.__data_encoding_strict = False 704 if encoding.endswith('-strict'): 705 self.__data_encoding_strict = True 706 encoding = encoding[:-7] 707 try: 708 codecs.lookup(encoding) 709 self.__data_encoding = encoding 710 except LookupError: 711 _log.error('<codecs> module can NOT handle encoding [%s]' % enc)
712 713 data_encoding = property(lambda x: self.__data_encoding, _set_data_encoding) 714 715 #-------------------------------------------------------- 716 placeholder_regex = property(lambda x: default_placeholder_regex, lambda x:x) 717 718 first_pass_placeholder_regex = property(lambda x: first_pass_placeholder_regex, lambda x:x) 719 second_pass_placeholder_regex = property(lambda x: second_pass_placeholder_regex, lambda x:x) 720 third_pass_placeholder_regex = property(lambda x: third_pass_placeholder_regex, lambda x:x) 721 722 #--------------------------------------------------------
723 - def __parse_region_definition(self, region_str):
724 region_str = region_str.strip() 725 726 if region_str == '': 727 return None, None 728 729 try: 730 pos_last_char = int(region_str) 731 return 0, pos_last_char 732 except (TypeError, ValueError): 733 _log.debug('region definition not a simple length') 734 735 # note that we only check for "legality", not for reasonable bounds 736 first_last = region_str.split('-') 737 if len(first_last) != 2: 738 _log.error('invalid placeholder region definition: %s', region_str) 739 raise ValueError 740 741 try: 742 pos_first_char = int(first_last[0].strip()) 743 pos_last_char = int(first_last[1].strip()) 744 except (TypeError, ValueError): 745 _log.error('invalid placeholder region definition: %s', region_str) 746 raise ValueError 747 748 # user says 1,2,... (= character position in string), Python needs 0,1,... (indexes 0-based) 749 if pos_first_char > 0: 750 pos_first_char -= 1 751 752 return pos_first_char, pos_last_char
753 754 #--------------------------------------------------------
755 - def __make_compatible_with_encoding(self, data_str):
756 if self.__data_encoding is None: 757 return data_str 758 759 try: 760 codecs.encode(data_str, self.__data_encoding, 'strict') 761 return data_str 762 except UnicodeEncodeError: 763 _log.error('cannot strict-encode string into [%s]: %s', self.__data_encoding, data_str) 764 765 if self.__data_encoding_strict: 766 return 'not compatible with encoding [%s]: %s' % (self.__data_encoding, data_str) 767 768 try: 769 import unidecode 770 except ImportError: 771 _log.debug('cannot transliterate, <unidecode> module not installed') 772 return codecs.encode(data_str, self.__data_encoding, 'replace').decode(self.__data_encoding) 773 774 return unidecode.unidecode(data_str).decode('utf8')
775 776 #-------------------------------------------------------- 777 # __getitem__ API 778 #--------------------------------------------------------
779 - def __getitem__(self, placeholder):
780 """Map self['placeholder'] to self.placeholder. 781 782 This is useful for replacing placeholders parsed out 783 of documents as strings. 784 785 Unknown/invalid placeholders still deliver a result but 786 it will be glaringly obvious if debugging is enabled. 787 """ 788 _log.debug('replacing [%s]', placeholder) 789 790 original_placeholder_def = placeholder 791 792 # remove leading/trailing '$<(<<)' and '(>>)>$' 793 if placeholder.startswith(default_placeholder_start): 794 placeholder = placeholder.lstrip('$').lstrip('<') 795 if placeholder.endswith(default_placeholder_end): 796 placeholder = placeholder.rstrip('$').rstrip('>') 797 else: 798 _log.error('placeholder must either start with [%s] and end with [%s] or neither of both', default_placeholder_start, default_placeholder_end) 799 if self.debug: 800 return self._escape(self.invalid_placeholder_template % original_placeholder_def) 801 return None 802 803 # injectable placeholder ? 804 parts = placeholder.split('::::', 1) 805 if len(parts) == 2: 806 ph_name, region_str = parts 807 is_an_injectable = True 808 try: 809 val = self.__injected_placeholders[ph_name] 810 except KeyError: 811 is_an_injectable = False 812 except: 813 _log.exception('injectable placeholder handling error: %s', original_placeholder_def) 814 if self.debug: 815 return self._escape(self.invalid_placeholder_template % original_placeholder_def) 816 return None 817 if is_an_injectable: 818 if val is None: 819 if self.debug: 820 return self._escape('injectable placeholder [%s]: no value available' % ph_name) 821 return placeholder 822 try: 823 pos_first_char, pos_last_char = self.__parse_region_definition(region_str) 824 except ValueError: 825 if self.debug: 826 return self._escape(self.invalid_placeholder_template % original_placeholder_def) 827 return None 828 if pos_last_char is None: 829 return self.__make_compatible_with_encoding(val) 830 # ellipsis needed ? 831 if len(val) > (pos_last_char - pos_first_char): 832 # ellipsis wanted ? 833 if self.__ellipsis is not None: 834 return self.__make_compatible_with_encoding(val[pos_first_char:(pos_last_char-len(self.__ellipsis))] + self.__ellipsis) 835 return self.__make_compatible_with_encoding(val[pos_first_char:pos_last_char]) 836 837 # variable placeholders 838 if len(placeholder.split('::', 2)) < 3: 839 _log.error('invalid placeholder structure: %s', original_placeholder_def) 840 if self.debug: 841 return self._escape(self.invalid_placeholder_template % original_placeholder_def) 842 return None 843 844 ph_name, data_and_lng = placeholder.split('::', 1) # note: split _is_ lsplit 845 options, region_str = data_and_lng.rsplit('::', 1) 846 _log.debug('placeholder parts: name=[%s]; region_def=[%s]; options=>>>%s<<<', ph_name, region_str, options) 847 try: 848 pos_first_char, pos_last_char = self.__parse_region_definition(region_str) 849 except ValueError: 850 if self.debug: 851 return self._escape(self.invalid_placeholder_template % original_placeholder_def) 852 return None 853 854 handler = getattr(self, '_get_variant_%s' % ph_name, None) 855 if handler is None: 856 _log.warning('no handler <_get_variant_%s> for placeholder %s', ph_name, original_placeholder_def) 857 if self.debug: 858 return self._escape(self.invalid_placeholder_template % original_placeholder_def) 859 return None 860 861 try: 862 val = handler(data = options) 863 if pos_last_char is None: 864 return self.__make_compatible_with_encoding(val) 865 # ellipsis needed ? 866 if len(val) > (pos_last_char - pos_first_char): 867 # ellipsis wanted ? 868 if self.__ellipsis is not None: 869 return self.__make_compatible_with_encoding(val[pos_first_char:(pos_last_char-len(self.__ellipsis))] + self.__ellipsis) 870 return self.__make_compatible_with_encoding(val[pos_first_char:pos_last_char]) 871 except: 872 _log.exception('placeholder handling error: %s', original_placeholder_def) 873 if self.debug: 874 return self._escape(self.invalid_placeholder_template % original_placeholder_def) 875 return None 876 877 _log.error('something went wrong, should never get here') 878 return None
879 880 #-------------------------------------------------------- 881 # placeholder handlers 882 #--------------------------------------------------------
883 - def _get_variant_ph_cfg(self, data=None):
884 options = data.split('//') # ALWAYS use '//' for splitting, regardless of self.__args_divider 885 name = options[0] 886 val = options[1] 887 if name == 'ellipsis': 888 self.ellipsis = val 889 elif name == 'argumentsdivider': 890 self.arguments_divider = val 891 elif name == 'encoding': 892 self.data_encoding = val 893 if len(options) > 2: 894 return options[2] % {'name': name, 'value': val} 895 return ''
896 #--------------------------------------------------------
897 - def _get_variant_client_version(self, data=None):
898 return self._escape ( 899 gmTools.coalesce ( 900 _cfg.get(option = 'client_version'), 901 '%s' % self.__class__.__name__ 902 ) 903 )
904 #--------------------------------------------------------
905 - def _get_variant_reminders(self, data=None):
906 907 from Gnumed.wxpython import gmProviderInboxWidgets 908 909 template = _('due %(due_date)s: %(comment)s (%(interval_due)s)') 910 date_format = '%Y %b %d' 911 912 data_parts = data.split(self.__args_divider) 913 914 if len(data_parts) > 0: 915 if data_parts[0].strip() != '': 916 template = data_parts[0] 917 918 if len(data_parts) > 1: 919 if data_parts[1].strip() != '': 920 date_format = data_parts[1] 921 922 reminders = gmProviderInboxWidgets.manage_reminders(patient = self.pat.ID) 923 924 if reminders is None: 925 return '' 926 927 if len(reminders) == 0: 928 return '' 929 930 lines = [ template % r.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) for r in reminders ] 931 932 return '\n'.join(lines)
933 #--------------------------------------------------------
934 - def _get_variant_external_care(self, data=None):
935 936 from Gnumed.wxpython import gmExternalCareWidgets 937 external_cares = gmExternalCareWidgets.manage_external_care() 938 939 if external_cares is None: 940 return '' 941 942 if len(external_cares) == 0: 943 return '' 944 945 template = data 946 lines = [ template % ext.fields_as_dict(escape_style = self.__esc_style) for ext in external_cares ] 947 948 return '\n'.join(lines)
949 #--------------------------------------------------------
950 - def _get_variant_documents(self, data=None):
951 952 select = False 953 include_descriptions = False 954 template = '%s' 955 path_template = None 956 export_path = None 957 958 data_parts = data.split(self.__args_divider) 959 960 if 'select' in data_parts: 961 select = True 962 data_parts.remove('select') 963 964 if 'description' in data_parts: 965 include_descriptions = True 966 data_parts.remove('description') 967 968 template = data_parts[0] 969 970 if len(data_parts) > 1: 971 path_template = data_parts[1] 972 973 if len(data_parts) > 2: 974 export_path = data_parts[2] 975 976 # create path 977 if export_path is not None: 978 export_path = os.path.normcase(os.path.expanduser(export_path)) 979 gmTools.mkdir(export_path) 980 981 # select docs 982 if select: 983 docs = gmDocumentWidgets.manage_documents(msg = _('Select the patient documents to reference from the new document.'), single_selection = False) 984 else: 985 docs = self.pat.document_folder.documents 986 987 if docs is None: 988 return '' 989 990 lines = [] 991 for doc in docs: 992 lines.append(template % doc.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style)) 993 if include_descriptions: 994 for desc in doc.get_descriptions(max_lng = None): 995 lines.append(self._escape(desc['text'] + '\n')) 996 if path_template is not None: 997 for part_name in doc.save_parts_to_files(export_dir = export_path): 998 path, name = os.path.split(part_name) 999 lines.append(path_template % {'fullpath': part_name, 'name': name}) 1000 1001 return '\n'.join(lines)
1002 #--------------------------------------------------------
1003 - def _get_variant_encounter_list(self, data=None):
1004 1005 encounters = gmEncounterWidgets.select_encounters(single_selection = False) 1006 if not encounters: 1007 return '' 1008 1009 template = data 1010 1011 lines = [] 1012 for enc in encounters: 1013 try: 1014 lines.append(template % enc.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style)) 1015 except: 1016 lines.append('error formatting encounter') 1017 _log.exception('problem formatting encounter list') 1018 _log.error('template: %s', template) 1019 _log.error('encounter: %s', encounter) 1020 1021 return '\n'.join(lines)
1022 #--------------------------------------------------------
1023 - def _get_variant_soap_for_encounters(self, data=None):
1024 """Select encounters from list and format SOAP thereof. 1025 1026 data: soap_cats (' ' -> None -> admin) // date format 1027 """ 1028 # defaults 1029 cats = None 1030 date_format = None 1031 1032 if data is not None: 1033 data_parts = data.split(self.__args_divider) 1034 1035 # part[0]: categories 1036 if len(data_parts[0]) > 0: 1037 cats = [] 1038 if ' ' in data_parts[0]: 1039 cats.append(None) 1040 data_parts[0] = data_parts[0].replace(' ', '') 1041 cats.extend(list(data_parts[0])) 1042 1043 # part[1]: date format 1044 if len(data_parts) > 1: 1045 if len(data_parts[1]) > 0: 1046 date_format = data_parts[1] 1047 1048 encounters = gmEncounterWidgets.select_encounters(single_selection = False) 1049 if not encounters: 1050 return '' 1051 1052 chunks = [] 1053 for enc in encounters: 1054 chunks.append(enc.format_latex ( 1055 date_format = date_format, 1056 soap_cats = cats, 1057 soap_order = 'soap_rank, date' 1058 )) 1059 1060 return ''.join(chunks)
1061 #--------------------------------------------------------
1062 - def _get_variant_emr_journal(self, data=None):
1063 # default: all categories, neutral template 1064 cats = list('soapu') 1065 cats.append(None) 1066 template = '%s' 1067 interactive = True 1068 line_length = 9999 1069 time_range = None 1070 1071 if data is not None: 1072 data_parts = data.split(self.__args_divider) 1073 1074 # part[0]: categories 1075 cats = [] 1076 # ' ' -> None == admin 1077 for c in list(data_parts[0]): 1078 if c == ' ': 1079 c = None 1080 cats.append(c) 1081 # '' -> SOAP + None 1082 if cats == '': 1083 cats = list('soapu').append(None) 1084 1085 # part[1]: template 1086 if len(data_parts) > 1: 1087 template = data_parts[1] 1088 1089 # part[2]: line length 1090 if len(data_parts) > 2: 1091 try: 1092 line_length = int(data_parts[2]) 1093 except: 1094 line_length = 9999 1095 1096 # part[3]: weeks going back in time 1097 if len(data_parts) > 3: 1098 try: 1099 time_range = 7 * int(data_parts[3]) 1100 except: 1101 #time_range = None # infinite 1102 # pass on literally, meaning it must be a valid PG interval string 1103 time_range = data_parts[3] 1104 1105 # FIXME: will need to be a generator later on 1106 narr = self.pat.emr.get_as_journal(soap_cats = cats, time_range = time_range) 1107 1108 if len(narr) == 0: 1109 return '' 1110 1111 keys = narr[0].keys() 1112 lines = [] 1113 line_dict = {} 1114 for n in narr: 1115 for key in keys: 1116 if isinstance(n[key], str): 1117 line_dict[key] = self._escape(text = n[key]) 1118 continue 1119 line_dict[key] = n[key] 1120 try: 1121 lines.append((template % line_dict)[:line_length]) 1122 except KeyError: 1123 return 'invalid key in template [%s], valid keys: %s]' % (template, str(keys)) 1124 1125 return '\n'.join(lines)
1126 #--------------------------------------------------------
1127 - def _get_variant_soap_by_issue(self, data=None):
1128 return self.__get_variant_soap_by_issue_or_episode(data = data, mode = 'issue')
1129 #--------------------------------------------------------
1130 - def _get_variant_soap_by_episode(self, data=None):
1131 return self.__get_variant_soap_by_issue_or_episode(data = data, mode = 'episode')
1132 #--------------------------------------------------------
1133 - def __get_variant_soap_by_issue_or_episode(self, data=None, mode=None):
1134 1135 # default: all categories, neutral template 1136 cats = list('soapu') 1137 cats.append(None) 1138 1139 date_format = None 1140 template = '%s' 1141 1142 if data is not None: 1143 data_parts = data.split(self.__args_divider) 1144 1145 # part[0]: categories 1146 if len(data_parts[0]) > 0: 1147 cats = [] 1148 if ' ' in data_parts[0]: 1149 cats.append(None) 1150 cats.extend(list(data_parts[0].replace(' ', ''))) 1151 1152 # part[1]: date format 1153 if len(data_parts) > 1: 1154 if len(data_parts[1]) > 0: 1155 date_format = data_parts[1] 1156 1157 # part[2]: template 1158 if len(data_parts) > 2: 1159 if len(data_parts[2]) > 0: 1160 template = data_parts[2] 1161 1162 if mode == 'issue': 1163 narr = gmNarrativeWorkflows.select_narrative_by_issue(soap_cats = cats) 1164 else: 1165 narr = gmNarrativeWorkflows.select_narrative_by_episode(soap_cats = cats) 1166 1167 if narr is None: 1168 return '' 1169 1170 if len(narr) == 0: 1171 return '' 1172 1173 try: 1174 narr = [ template % n.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) for n in narr ] 1175 except KeyError: 1176 return 'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys())) 1177 1178 return '\n'.join(narr)
1179 #--------------------------------------------------------
1180 - def _get_variant_progress_notes(self, data=None):
1181 return self._get_variant_soap(data = data)
1182 #--------------------------------------------------------
1183 - def _get_variant_soap_s(self, data=None):
1184 return self._get_variant_soap(data = 's')
1185 #--------------------------------------------------------
1186 - def _get_variant_soap_o(self, data=None):
1187 return self._get_variant_soap(data = 'o')
1188 #--------------------------------------------------------
1189 - def _get_variant_soap_a(self, data=None):
1190 return self._get_variant_soap(data = 'a')
1191 #--------------------------------------------------------
1192 - def _get_variant_soap_p(self, data=None):
1193 return self._get_variant_soap(data = 'p')
1194 #--------------------------------------------------------
1195 - def _get_variant_soap_u(self, data=None):
1196 return self._get_variant_soap(data = 'u')
1197 #--------------------------------------------------------
1198 - def _get_variant_soap_admin(self, data=None):
1199 return self._get_variant_soap(data = ' ')
1200 #--------------------------------------------------------
1201 - def _get_variant_soap(self, data=None):
1202 1203 # default: all categories, neutral template 1204 cats = list('soapu') 1205 cats.append(None) 1206 template = '%(narrative)s' 1207 1208 if data is not None: 1209 data_parts = data.split(self.__args_divider) 1210 1211 # part[0]: categories 1212 cats = [] 1213 # ' ' -> None == admin 1214 for cat in list(data_parts[0]): 1215 if cat == ' ': 1216 cat = None 1217 cats.append(cat) 1218 # '' -> SOAP + None 1219 if cats == '': 1220 cats = list('soapu') 1221 cats.append(None) 1222 1223 # part[1]: template 1224 if len(data_parts) > 1: 1225 template = data_parts[1] 1226 1227 #narr = gmNarrativeWorkflows.select_narrative_from_episodes(soap_cats = cats) 1228 narr = gmNarrativeWorkflows.select_narrative(soap_cats = cats) 1229 1230 if narr is None: 1231 return '' 1232 1233 if len(narr) == 0: 1234 return '' 1235 1236 # if any "%s" is in the template there cannot be any %(field)s 1237 # and we also restrict the fields to .narrative (this is the 1238 # old placeholder behaviour 1239 if '%s' in template: 1240 narr = [ self._escape(n['narrative']) for n in narr ] 1241 else: 1242 narr = [ n.fields_as_dict(escape_style = self.__esc_style) for n in narr ] 1243 1244 try: 1245 narr = [ template % n for n in narr ] 1246 except KeyError: 1247 return 'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys())) 1248 except TypeError: 1249 return 'cannot mix "%%s" and "%%(field)s" in template [%s]' % template 1250 1251 return '\n'.join(narr)
1252 1253 #--------------------------------------------------------
1254 - def _get_variant_title(self, data=None):
1255 return self._get_variant_name(data = '%(title)s')
1256 #--------------------------------------------------------
1257 - def _get_variant_firstname(self, data=None):
1258 return self._get_variant_name(data = '%(firstnames)s')
1259 #--------------------------------------------------------
1260 - def _get_variant_lastname(self, data=None):
1261 return self._get_variant_name(data = '%(lastnames)s')
1262 #--------------------------------------------------------
1263 - def _get_variant_name(self, data=None):
1264 if data is None: 1265 return [_('template is missing')] 1266 1267 name = self.pat.get_active_name() 1268 1269 parts = { 1270 'title': self._escape(gmTools.coalesce(name['title'], '')), 1271 'firstnames': self._escape(name['firstnames']), 1272 'lastnames': self._escape(name['lastnames']), 1273 'preferred': self._escape(gmTools.coalesce ( 1274 value2test = name['preferred'], 1275 return_instead = ' ', 1276 template4value = ' "%s" ' 1277 )) 1278 } 1279 1280 return data % parts
1281 1282 #--------------------------------------------------------
1283 - def _get_variant_date_of_birth(self, data='%Y %b %d'):
1284 return self.pat.get_formatted_dob(format = data)
1285 1286 #-------------------------------------------------------- 1287 # FIXME: extend to all supported genders
1288 - def _get_variant_gender_mapper(self, data='male//female//other'):
1289 1290 values = data.split('//', 2) 1291 1292 if len(values) == 2: 1293 male_value, female_value = values 1294 other_value = '<unkown gender>' 1295 elif len(values) == 3: 1296 male_value, female_value, other_value = values 1297 else: 1298 return _('invalid gender mapping layout: [%s]') % data 1299 1300 if self.pat['gender'] == 'm': 1301 return self._escape(male_value) 1302 1303 if self.pat['gender'] == 'f': 1304 return self._escape(female_value) 1305 1306 return self._escape(other_value)
1307 #-------------------------------------------------------- 1308 # address related placeholders 1309 #--------------------------------------------------------
1310 - def __get_variant_gen_adr_part(self, data='?', part=None):
1311 1312 template = '%s' 1313 msg = _('Select the address you want to use !') 1314 cache_id = '' 1315 options = data.split('//', 4) 1316 if len(options) > 0: 1317 template = options[0] 1318 if template.strip() == '': 1319 template = '%s' 1320 if len(options) > 1: 1321 msg = options[1] 1322 if len(options) > 2: 1323 cache_id = options[2] 1324 1325 cache_key = 'generic_address::' + cache_id 1326 try: 1327 adr2use = self.__cache[cache_key] 1328 _log.debug('cache hit (%s): [%s]', cache_key, adr2use) 1329 except KeyError: 1330 adr2use = None 1331 1332 if adr2use is None: 1333 dlg = gmAddressWidgets.cAddressSelectionDlg(None, -1) 1334 dlg.message = msg 1335 choice = dlg.ShowModal() 1336 adr2use = dlg.address 1337 dlg.DestroyLater() 1338 if choice == wx.ID_CANCEL: 1339 return '' 1340 self.__cache[cache_key] = adr2use 1341 1342 return template % self._escape(adr2use[part])
1343 #--------------------------------------------------------
1344 - def _get_variant_gen_adr_street(self, data='?'):
1345 return self.__get_variant_gen_adr_part(data = data, part = 'street')
1346 #--------------------------------------------------------
1347 - def _get_variant_gen_adr_number(self, data='?'):
1348 return self.__get_variant_gen_adr_part(data = data, part = 'number')
1349 #--------------------------------------------------------
1350 - def _get_variant_gen_adr_subunit(self, data='?'):
1351 return self.__get_variant_gen_adr_part(data = data, part = 'subunit')
1352 #--------------------------------------------------------
1353 - def _get_variant_gen_adr_location(self, data='?'):
1354 return self.__get_variant_gen_adr_part(data = data, part = 'urb')
1355 #--------------------------------------------------------
1356 - def _get_variant_gen_adr_suburb(self, data='?'):
1357 return self.__get_variant_gen_adr_part(data = data, part = 'suburb')
1358 #--------------------------------------------------------
1359 - def _get_variant_gen_adr_postcode(self, data='?'):
1360 return self.__get_variant_gen_adr_part(data = data, part = 'postcode')
1361 #--------------------------------------------------------
1362 - def _get_variant_gen_adr_region(self, data='?'):
1363 return self.__get_variant_gen_adr_part(data = data, part = 'l10n_region')
1364 #--------------------------------------------------------
1365 - def _get_variant_gen_adr_country(self, data='?'):
1366 return self.__get_variant_gen_adr_part(data = data, part = 'l10n_country')
1367 #--------------------------------------------------------
1368 - def __get_variant_receiver_part(self, data='%s', part=None):
1369 1370 template = '%s' 1371 cache_id = '' 1372 options = data.split('//', 3) 1373 if len(options) > 0: 1374 template = options[0] 1375 if template.strip() == '': 1376 template = '%s' 1377 if len(options) > 1: 1378 cache_id = options[1] 1379 1380 cache_key = 'receiver::' + cache_id 1381 try: 1382 name, adr = self.__cache[cache_key] 1383 _log.debug('cache hit (%s): [%s:%s]', cache_key, name, adr) 1384 except KeyError: 1385 name = None 1386 adr = None 1387 1388 if name is None: 1389 from Gnumed.wxpython import gmFormWidgets 1390 dlg = gmFormWidgets.cReceiverSelectionDlg(None, -1) 1391 dlg.patient = self.pat 1392 choice = dlg.ShowModal() 1393 name = dlg.name 1394 adr = dlg.address 1395 dlg.DestroyLater() 1396 if choice == wx.ID_CANCEL: 1397 return '' 1398 self.__cache[cache_key] = (name, adr) 1399 1400 if part == 'name': 1401 return template % self._escape(name) 1402 1403 return template % self._escape(gmTools.coalesce(adr[part], ''))
1404 #--------------------------------------------------------
1405 - def _get_variant_receiver_name(self, data='%s'):
1406 return self.__get_variant_receiver_part(data = data, part = 'name')
1407 #--------------------------------------------------------
1408 - def _get_variant_receiver_street(self, data='%s'):
1409 return self.__get_variant_receiver_part(data = data, part = 'street')
1410 #--------------------------------------------------------
1411 - def _get_variant_receiver_number(self, data='%s'):
1412 return self.__get_variant_receiver_part(data = data, part = 'number')
1413 #--------------------------------------------------------
1414 - def _get_variant_receiver_subunit(self, data='%s'):
1415 return self.__get_variant_receiver_part(data = data, part = 'subunit')
1416 #--------------------------------------------------------
1417 - def _get_variant_receiver_location(self, data='%s'):
1418 return self.__get_variant_receiver_part(data = data, part = 'urb')
1419 #--------------------------------------------------------
1420 - def _get_variant_receiver_suburb(self, data='%s'):
1421 return self.__get_variant_receiver_part(data = data, part = 'suburb')
1422 #--------------------------------------------------------
1423 - def _get_variant_receiver_postcode(self, data='%s'):
1424 return self.__get_variant_receiver_part(data = data, part = 'postcode')
1425 #--------------------------------------------------------
1426 - def _get_variant_receiver_region(self, data='%s'):
1427 return self.__get_variant_receiver_part(data = data, part = 'l10n_region')
1428 #--------------------------------------------------------
1429 - def _get_variant_receiver_country(self, data='%s'):
1430 return self.__get_variant_receiver_part(data = data, part = 'l10n_country')
1431 #--------------------------------------------------------
1432 - def _get_variant_patient_address(self, data=''):
1433 1434 data_parts = data.split(self.__args_divider) 1435 1436 # address type 1437 adr_type = data_parts[0].strip() 1438 orig_type = adr_type 1439 if adr_type != '': 1440 adrs = self.pat.get_addresses(address_type = adr_type) 1441 if len(adrs) == 0: 1442 _log.warning('no address for type [%s]', adr_type) 1443 adr_type = '' 1444 if adr_type == '': 1445 _log.debug('asking user for address type') 1446 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat) 1447 if adr is None: 1448 if self.debug: 1449 return _('no address type replacement selected') 1450 return '' 1451 adr_type = adr['address_type'] 1452 adr = self.pat.get_addresses(address_type = adr_type)[0] 1453 1454 # formatting template 1455 template = _('%(street)s %(number)s, %(postcode)s %(urb)s, %(l10n_region)s, %(l10n_country)s') 1456 if len(data_parts) > 1: 1457 if data_parts[1].strip() != '': 1458 template = data_parts[1] 1459 1460 try: 1461 return template % adr.fields_as_dict(escape_style = self.__esc_style) 1462 except Exception: 1463 _log.exception('error formatting address') 1464 _log.error('template: %s', template) 1465 1466 return None
1467 #--------------------------------------------------------
1468 - def __get_variant_adr_part(self, data='?', part=None):
1469 requested_type = data.strip() 1470 cache_key = 'adr-type-%s' % requested_type 1471 try: 1472 type2use = self.__cache[cache_key] 1473 _log.debug('cache hit (%s): [%s] -> [%s]', cache_key, requested_type, type2use) 1474 except KeyError: 1475 type2use = requested_type 1476 if type2use != '': 1477 adrs = self.pat.get_addresses(address_type = type2use) 1478 if len(adrs) == 0: 1479 _log.warning('no address of type [%s] for <%s> field extraction', requested_type, part) 1480 type2use = '' 1481 if type2use == '': 1482 _log.debug('asking user for replacement address type') 1483 adr = gmPersonContactWidgets.select_address(missing = requested_type, person = self.pat) 1484 if adr is None: 1485 _log.debug('no replacement selected') 1486 if self.debug: 1487 return self._escape(_('no address type replacement selected')) 1488 return '' 1489 type2use = adr['address_type'] 1490 self.__cache[cache_key] = type2use 1491 _log.debug('caching (%s): [%s] -> [%s]', cache_key, requested_type, type2use) 1492 1493 part_data = self.pat.get_addresses(address_type = type2use)[0][part] 1494 if part_data is None: 1495 part_data = '' # do escape empty string since we never know what target formats need 1496 return self._escape(part_data)
1497 1498 #--------------------------------------------------------
1499 - def _get_variant_adr_street(self, data='?'):
1500 return self.__get_variant_adr_part(data = data, part = 'street')
1501 #--------------------------------------------------------
1502 - def _get_variant_adr_number(self, data='?'):
1503 return self.__get_variant_adr_part(data = data, part = 'number')
1504 #--------------------------------------------------------
1505 - def _get_variant_adr_subunit(self, data='?'):
1506 return self.__get_variant_adr_part(data = data, part = 'subunit')
1507 #--------------------------------------------------------
1508 - def _get_variant_adr_location(self, data='?'):
1509 return self.__get_variant_adr_part(data = data, part = 'urb')
1510 #--------------------------------------------------------
1511 - def _get_variant_adr_suburb(self, data='?'):
1512 return self.__get_variant_adr_part(data = data, part = 'suburb')
1513 #--------------------------------------------------------
1514 - def _get_variant_adr_postcode(self, data='?'):
1515 return self.__get_variant_adr_part(data = data, part = 'postcode')
1516 #--------------------------------------------------------
1517 - def _get_variant_adr_region(self, data='?'):
1518 return self.__get_variant_adr_part(data = data, part = 'l10n_region')
1519 #--------------------------------------------------------
1520 - def _get_variant_adr_country(self, data='?'):
1521 return self.__get_variant_adr_part(data = data, part = 'l10n_country')
1522 #--------------------------------------------------------
1523 - def _get_variant_patient_comm(self, data=None):
1524 comm_type = None 1525 template = '%(url)s' 1526 if data is not None: 1527 data_parts = data.split(self.__args_divider) 1528 if len(data_parts) > 0: 1529 comm_type = data_parts[0] 1530 if len(data_parts) > 1: 1531 template = data_parts[1] 1532 1533 comms = self.pat.get_comm_channels(comm_medium = comm_type) 1534 if len(comms) == 0: 1535 if self.debug: 1536 return self._escape(_('no URL for comm channel [%s]') % data) 1537 return '' 1538 1539 return template % comms[0].fields_as_dict(escape_style = self.__esc_style)
1540 #--------------------------------------------------------
1541 - def _get_variant_patient_photo(self, data=None):
1542 1543 template = '%s' 1544 target_mime = None 1545 target_ext = None 1546 if data is not None: 1547 parts = data.split(self.__args_divider) 1548 template = parts[0] 1549 if len(parts) > 1: 1550 target_mime = parts[1].strip() 1551 if len(parts) > 2: 1552 target_ext = parts[2].strip() 1553 if target_ext is None: 1554 if target_mime is not None: 1555 target_ext = gmMimeLib.guess_ext_by_mimetype(mimetype = target_mime) 1556 1557 cache_key = 'patient_photo_path::%s::%s' % (target_mime, target_ext) 1558 try: 1559 fname = self.__cache[cache_key] 1560 _log.debug('cache hit on [%s]: %s', cache_key, fname) 1561 except KeyError: 1562 mugshot = self.pat.document_folder.latest_mugshot 1563 if mugshot is None: 1564 if self.debug: 1565 return self._escape(_('no mugshot available')) 1566 return '' 1567 fname = mugshot.save_to_file ( 1568 target_mime = target_mime, 1569 target_extension = target_ext, 1570 ignore_conversion_problems = True 1571 ) 1572 if fname is None: 1573 if self.debug: 1574 return self._escape(_('cannot export or convert latest mugshot')) 1575 return '' 1576 self.__cache[cache_key] = fname 1577 1578 return template % fname
1579 1580 #--------------------------------------------------------
1581 - def _get_variant_patient_vcf(self, data):
1582 options = data.split(self.__args_divider) 1583 template = options[0].strip() 1584 if template == '': 1585 template = '%s' 1586 1587 return template % self.pat.export_as_vcard()
1588 1589 #--------------------------------------------------------
1590 - def _get_variant_patient_mcf(self, data):
1591 template = u'%s' 1592 format = 'txt' 1593 options = data.split(self.__args_divider) 1594 _log.debug('options: %s', options) 1595 for o in options: 1596 if o.strip().startswith('fmt='): 1597 format = o.strip()[4:] 1598 if format not in ['qr', 'mcf', 'txt']: 1599 return self._escape(_('patient_mcf: invalid format (qr/mcf/txt)')) 1600 continue 1601 if o.strip().startswith('tmpl='): 1602 template = o.strip()[5:] 1603 continue 1604 _log.debug('template: %s' % template) 1605 _log.debug('format: %s' % format) 1606 1607 if format == 'txt': 1608 return template % self._escape(self.pat.MECARD) 1609 1610 if format == 'mcf': 1611 return template % self.pat.export_as_mecard() 1612 1613 if format == 'qr': 1614 qr_filename = gmTools.create_qrcode(text = self.pat.MECARD) 1615 if qr_filename is None: 1616 return self._escape('patient_mcf-cannot_create_QR_code') 1617 return template % qr_filename 1618 1619 return None
1620 1621 #--------------------------------------------------------
1622 - def _get_variant_patient_gdt(self, data):
1623 options = data.split(self.__args_divider) 1624 template = options[0].strip() 1625 if template == '': 1626 template = '%s' 1627 1628 return template % self.pat.export_as_gdt()
1629 1630 #--------------------------------------------------------
1631 - def _get_variant_patient_tags(self, data='%s//\\n'):
1632 if len(self.pat.tags) == 0: 1633 if self.debug: 1634 return self._escape(_('no tags for this patient')) 1635 return '' 1636 1637 tags = gmDemographicsWidgets.select_patient_tags(patient = self.pat) 1638 1639 if tags is None: 1640 if self.debug: 1641 return self._escape(_('no patient tags selected for inclusion') % data) 1642 return '' 1643 1644 template, separator = data.split('//', 2) 1645 1646 return separator.join([ template % t.fields_as_dict(escape_style = self.__esc_style) for t in tags ])
1647 # #-------------------------------------------------------- 1648 # def _get_variant_patient_tags_table(self, data=u'?'): 1649 # pass 1650 #-------------------------------------------------------- 1651 # praxis related placeholders 1652 #--------------------------------------------------------
1653 - def _get_variant_praxis(self, data=None):
1654 options = data.split(self.__args_divider) 1655 1656 if 'select' in options: 1657 options.remove('select') 1658 branch = 'select branch' 1659 else: 1660 branch = gmPraxis.cPraxisBranch(aPK_obj = gmPraxis.gmCurrentPraxisBranch()['pk_praxis_branch']) 1661 1662 template = '%s' 1663 if len(options) > 0: 1664 template = options[0] 1665 if template.strip() == '': 1666 template = '%s' 1667 1668 return template % branch.fields_as_dict(escape_style = self.__esc_style)
1669 1670 #--------------------------------------------------------
1671 - def _get_variant_praxis_vcf(self, data=None):
1672 1673 cache_key = 'current_branch_vcf_path' 1674 try: 1675 vcf_name = self.__cache[cache_key] 1676 _log.debug('cache hit (%s): [%s]', cache_key, vcf_name) 1677 except KeyError: 1678 vcf_name = gmPraxis.gmCurrentPraxisBranch().vcf 1679 self.__cache[cache_key] = vcf_name 1680 1681 template = '%s' 1682 if data.strip() != '': 1683 template = data 1684 1685 return template % vcf_name
1686 1687 #--------------------------------------------------------
1688 - def _get_variant_praxis_mcf(self, data):
1689 template = u'%s' 1690 format = 'txt' 1691 options = data.split(self.__args_divider) 1692 _log.debug('options: %s', options) 1693 for o in options: 1694 if o.strip().startswith('fmt='): 1695 format = o.strip()[4:] 1696 if format not in ['qr', 'mcf', 'txt']: 1697 return self._escape(_('praxis_mcf: invalid format (qr/mcf/txt)')) 1698 continue 1699 if o.strip().startswith('tmpl='): 1700 template = o.strip()[5:] 1701 continue 1702 _log.debug('template: %s' % template) 1703 _log.debug('format: %s' % format) 1704 1705 if format == 'txt': 1706 return template % self._escape(gmPraxis.gmCurrentPraxisBranch().MECARD) 1707 1708 if format == 'mcf': 1709 return template % gmPraxis.gmCurrentPraxisBranch().export_as_mecard() 1710 1711 if format == 'qr': 1712 qr_filename = gmTools.create_qrcode(text = gmPraxis.gmCurrentPraxisBranch().MECARD) 1713 if qr_filename is None: 1714 return self._escape('praxis_mcf-cannot_create_QR_code') 1715 return template % qr_filename 1716 1717 return None
1718 1719 #--------------------------------------------------------
1720 - def _get_variant_praxis_scan2pay(self, data):
1721 #template = u'%s' 1722 format = 'qr' 1723 options = data.split(self.__args_divider) 1724 _log.debug('options: %s', options) 1725 for o in options: 1726 if o.strip().startswith('fmt='): 1727 format = o.strip()[4:] 1728 if format not in ['qr', 'txt']: 1729 return self._escape(_('praxis_scan2pay: invalid format (qr/txt)')) 1730 continue 1731 # if o.strip().startswith('tmpl='): 1732 # template = o.strip()[5:] 1733 # continue 1734 # _log.debug('template: %s' % template) 1735 _log.debug('format: %s' % format) 1736 1737 data_str = gmPraxis.gmCurrentPraxisBranch().scan2pay_data 1738 if data_str is None: 1739 if self.debug: 1740 return self._escape('praxis_scan2pay-cannot_create_data_file') 1741 return '' 1742 1743 if format == 'txt': 1744 return self._escape(data_str) 1745 #return template % self._escape(gmPraxis.gmCurrentPraxisBranch().MECARD) 1746 1747 if format == 'qr': 1748 qr_filename = gmTools.create_qrcode(text = data_str) 1749 if qr_filename is None: 1750 if self.debug: 1751 return self._escape('praxis_scan2pay-cannot_create_QR_code') 1752 return '' 1753 #return template % qr_filename 1754 return qr_filename 1755 1756 return None
1757 1758 #--------------------------------------------------------
1759 - def _get_variant_praxis_address(self, data=''):
1760 options = data.split(self.__args_divider) 1761 1762 # formatting template 1763 template = _('%(street)s %(number)s, %(postcode)s %(urb)s, %(l10n_region)s, %(l10n_country)s') 1764 if len(options) > 0: 1765 if options[0].strip() != '': 1766 template = options[0] 1767 1768 adr = gmPraxis.gmCurrentPraxisBranch().address 1769 if adr is None: 1770 if self.debug: 1771 return _('no address recorded') 1772 return '' 1773 try: 1774 return template % adr.fields_as_dict(escape_style = self.__esc_style) 1775 except Exception: 1776 _log.exception('error formatting address') 1777 _log.error('template: %s', template) 1778 1779 return None
1780 1781 #--------------------------------------------------------
1782 - def _get_variant_praxis_comm(self, data=None):
1783 options = data.split(self.__args_divider) 1784 comm_type = options[0] 1785 template = '%(url)s' 1786 if len(options) > 1: 1787 template = options[1] 1788 1789 comms = gmPraxis.gmCurrentPraxisBranch().get_comm_channels(comm_medium = comm_type) 1790 if len(comms) == 0: 1791 if self.debug: 1792 return self._escape(_('no URL for comm channel [%s]') % data) 1793 return '' 1794 1795 return template % comms[0].fields_as_dict(escape_style = self.__esc_style)
1796 1797 #--------------------------------------------------------
1798 - def _get_variant_praxis_id(self, data=None):
1799 options = data.split(self.__args_divider) 1800 id_type = options[0].strip() 1801 if id_type == '': 1802 return self._escape('praxis external ID: type is missing') 1803 1804 if len(options) > 1: 1805 issuer = options[1].strip() 1806 if issuer == '': 1807 issue = None 1808 else: 1809 issuer = None 1810 1811 if len(options) > 2: 1812 template = options[2] 1813 else: 1814 template = '%(name)s: %(value)s (%(issuer)s)' 1815 1816 ids = gmPraxis.gmCurrentPraxisBranch().get_external_ids(id_type = id_type, issuer = issuer) 1817 if len(ids) == 0: 1818 if self.debug: 1819 return self._escape(_('no external ID [%s] by [%s]') % (id_type, issuer)) 1820 return '' 1821 1822 return template % self._escape_dict(the_dict = ids[0], none_string = '')
1823 1824 #-------------------------------------------------------- 1825 # provider related placeholders 1826 #--------------------------------------------------------
1827 - def _get_variant_current_provider(self, data=None):
1828 prov = gmStaff.gmCurrentProvider() 1829 1830 tmp = '%s%s. %s' % ( 1831 gmTools.coalesce(prov['title'], '', '%s '), 1832 prov['firstnames'][:1], 1833 prov['lastnames'] 1834 ) 1835 return self._escape(tmp)
1836 1837 #--------------------------------------------------------
1838 - def _get_variant_current_provider_title(self, data=None):
1839 if data is None: 1840 template = u'%(title)s' 1841 elif data.strip() == u'': 1842 data = u'%(title)s' 1843 return self._get_variant_current_provider_name(data = data)
1844 1845 #--------------------------------------------------------
1846 - def _get_variant_current_provider_firstnames(self, data=None):
1847 if data is None: 1848 data = u'%(firstnames)s' 1849 elif data.strip() == u'': 1850 data = u'%(firstnames)s' 1851 return self._get_variant_current_provider_name(data = data)
1852 1853 #--------------------------------------------------------
1854 - def _get_variant_current_provider_lastnames(self, data=None):
1855 if data is None: 1856 data = u'%(lastnames)s' 1857 elif data.strip() == u'': 1858 data = u'%(lastnames)s' 1859 return self._get_variant_current_provider_name(data = data)
1860 1861 #--------------------------------------------------------
1862 - def _get_variant_current_provider_name(self, data=None):
1863 if data is None: 1864 return [_('template is missing')] 1865 if data.strip() == '': 1866 return [_('template is empty')] 1867 name = gmStaff.gmCurrentProvider().identity.get_active_name() 1868 parts = { 1869 'title': self._escape(gmTools.coalesce(name['title'], '')), 1870 'firstnames': self._escape(name['firstnames']), 1871 'lastnames': self._escape(name['lastnames']), 1872 'preferred': self._escape(gmTools.coalesce(name['preferred'], '')) 1873 } 1874 return data % parts
1875 1876 #--------------------------------------------------------
1878 data_parts = data.split(self.__args_divider) 1879 if len(data_parts) < 2: 1880 return self._escape('current provider external ID: template is missing') 1881 1882 id_type = data_parts[0].strip() 1883 if id_type == '': 1884 return self._escape('current provider external ID: type is missing') 1885 1886 issuer = data_parts[1].strip() 1887 if issuer == '': 1888 return self._escape('current provider external ID: issuer is missing') 1889 1890 prov = gmStaff.gmCurrentProvider() 1891 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer) 1892 1893 if len(ids) == 0: 1894 if self.debug: 1895 return self._escape(_('no external ID [%s] by [%s]') % (id_type, issuer)) 1896 return '' 1897 1898 return self._escape(ids[0]['value'])
1899 1900 #--------------------------------------------------------
1901 - def _get_variant_primary_praxis_provider(self, data=None):
1902 prov = self.pat.primary_provider 1903 if prov is None: 1904 return self._get_variant_current_provider() 1905 1906 title = gmTools.coalesce ( 1907 prov['title'], 1908 gmPerson.map_gender2salutation(prov['gender']) 1909 ) 1910 1911 tmp = '%s %s. %s' % ( 1912 title, 1913 prov['firstnames'][:1], 1914 prov['lastnames'] 1915 ) 1916 return self._escape(tmp)
1917 1918 #--------------------------------------------------------
1920 data_parts = data.split(self.__args_divider) 1921 if len(data_parts) < 2: 1922 return self._escape('primary in-praxis provider external ID: template is missing') 1923 1924 id_type = data_parts[0].strip() 1925 if id_type == '': 1926 return self._escape('primary in-praxis provider external ID: type is missing') 1927 1928 issuer = data_parts[1].strip() 1929 if issuer == '': 1930 return self._escape('primary in-praxis provider external ID: issuer is missing') 1931 1932 prov = self.pat.primary_provider 1933 if prov is None: 1934 if self.debug: 1935 return self._escape(_('no primary in-praxis provider')) 1936 return '' 1937 1938 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer) 1939 1940 if len(ids) == 0: 1941 if self.debug: 1942 return self._escape(_('no external ID [%s] by [%s]') % (id_type, issuer)) 1943 return '' 1944 1945 return self._escape(ids[0]['value'])
1946 1947 #--------------------------------------------------------
1948 - def _get_variant_external_id(self, data=''):
1949 data_parts = data.split(self.__args_divider) 1950 if len(data_parts) < 2: 1951 return self._escape('patient external ID: template is missing') 1952 1953 id_type = data_parts[0].strip() 1954 if id_type == '': 1955 return self._escape('patient external ID: type is missing') 1956 1957 issuer = data_parts[1].strip() 1958 if issuer == '': 1959 return self._escape('patient external ID: issuer is missing') 1960 1961 ids = self.pat.get_external_ids(id_type = id_type, issuer = issuer) 1962 1963 if len(ids) == 0: 1964 if self.debug: 1965 return self._escape(_('no external ID [%s] by [%s]') % (id_type, issuer)) 1966 return '' 1967 1968 return self._escape(ids[0]['value'])
1969 1970 #--------------------------------------------------------
1971 - def _get_variant_allergy_state(self, data=None):
1972 allg_state = self.pat.emr.allergy_state 1973 1974 if allg_state['last_confirmed'] is None: 1975 date_confirmed = '' 1976 else: 1977 date_confirmed = ' (%s)' % gmDateTime.pydt_strftime ( 1978 allg_state['last_confirmed'], 1979 format = '%Y %B %d' 1980 ) 1981 1982 tmp = '%s%s' % ( 1983 allg_state.state_string, 1984 date_confirmed 1985 ) 1986 return self._escape(tmp)
1987 1988 #--------------------------------------------------------
1989 - def _get_variant_allergy_list(self, data=None):
1990 if data is None: 1991 return self._escape(_('template is missing')) 1992 1993 template, separator = data.split('//', 2) 1994 1995 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() ])
1996 1997 #--------------------------------------------------------
1998 - def _get_variant_allergies(self, data=None):
1999 2000 if data is None: 2001 return self._escape(_('template is missing')) 2002 2003 return '\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() ])
2004 2005 #--------------------------------------------------------
2006 - def _get_variant_current_meds_AMTS_enhanced(self, data=None):
2007 return self._get_variant_current_meds_AMTS(data=data, strict=False)
2008 2009 #--------------------------------------------------------
2010 - def _get_variant_current_meds_AMTS(self, data=None, strict=True):
2011 2012 # select intakes 2013 emr = self.pat.emr 2014 from Gnumed.wxpython import gmMedicationWidgets 2015 intakes2export = gmMedicationWidgets.manage_substance_intakes(emr = emr) 2016 if intakes2export is None: 2017 return '' 2018 if len(intakes2export) == 0: 2019 return '' 2020 2021 # make them unique: 2022 unique_intakes = {} 2023 for intake in intakes2export: 2024 if intake['pk_drug_product'] is None: 2025 unique_intakes[intake['pk_substance']] = intake 2026 else: 2027 unique_intakes[intake['product']] = intake 2028 del intakes2export 2029 unique_intakes = unique_intakes.values() 2030 2031 # create data files / datamatrix code files 2032 self.__create_amts_datamatrix_files(intakes = unique_intakes) 2033 2034 # create AMTS-LaTeX per intake 2035 intake_as_latex_rows = [] 2036 for intake in unique_intakes: 2037 intake_as_latex_rows.append(intake._get_as_amts_latex(strict = strict)) 2038 del unique_intakes 2039 2040 # append allergy information 2041 # - state 2042 intake_as_latex_rows.extend(emr.allergy_state._get_as_amts_latex(strict = strict)) 2043 # - allergies 2044 for allg in emr.get_allergies(): 2045 intake_as_latex_rows.append(allg._get_as_amts_latex(strict = strict)) 2046 2047 # insert \newpage after each group of 15 rows 2048 table_rows = intake_as_latex_rows[:15] 2049 if len(intake_as_latex_rows) > 15: 2050 table_rows.append('\\newpage') 2051 table_rows.extend(intake_as_latex_rows[15:30]) 2052 if len(intake_as_latex_rows) > 30: 2053 table_rows.append('\\newpage') 2054 table_rows.extend(intake_as_latex_rows[30:45]) 2055 2056 if strict: 2057 return '\n'.join(table_rows) 2058 2059 # allow two more pages in enhanced mode 2060 if len(intake_as_latex_rows) > 45: 2061 table_rows.append('\\newpage') 2062 table_rows.extend(intake_as_latex_rows[30:45]) 2063 2064 if len(intake_as_latex_rows) > 60: 2065 table_rows.append('\\newpage') 2066 table_rows.extend(intake_as_latex_rows[30:45]) 2067 2068 return '\n'.join(table_rows)
2069 2070 #--------------------------------------------------------
2071 - def __create_amts_datamatrix_files(self, intakes=None):
2072 2073 # setup dummy files 2074 for idx in [1,2,3]: 2075 self.set_placeholder(key = 'amts_data_file_%s' % idx, value = './missing-file.txt', known_only = False) 2076 self.set_placeholder(key = 'amts_png_file_%s' % idx, value = './missing-file.png', known_only = False) 2077 self.set_placeholder(key = 'amts_png_file_current_page', value = './missing-file-current-page.png', known_only = False) 2078 self.set_placeholder(key = 'amts_png_file_utf8', value = './missing-file-utf8.png', known_only = False) 2079 self.set_placeholder(key = 'amts_data_file_utf8', value = './missing-file-utf8.txt', known_only = False) 2080 2081 # find processor 2082 found, dmtx_creator = gmShellAPI.detect_external_binary(binary = 'gm-create_datamatrix') 2083 _log.debug(dmtx_creator) 2084 if not found: 2085 _log.error('gm-create_datamatrix(.bat/.exe) not found') 2086 return 2087 2088 png_dir = gmTools.mk_sandbox_dir() 2089 _log.debug('sandboxing AMTS datamatrix PNGs in: %s', png_dir) 2090 2091 from Gnumed.business import gmForms 2092 2093 # generate GNUmed-enhanced non-conformant data file and datamatrix 2094 # for embedding (utf8, unabridged data fields) 2095 amts_data_template_def_file = gmMedication.generate_amts_data_template_definition_file(strict = False) 2096 _log.debug('amts data template definition file: %s', amts_data_template_def_file) 2097 form = gmForms.cTextForm(template_file = amts_data_template_def_file) 2098 # <S>ection with intakes</S> 2099 amts_sections = '<S>%s</S>' % ''.join ([ 2100 i._get_as_amts_data(strict = False) for i in intakes 2101 ]) 2102 # <S>ection with allergy data</S> 2103 emr = self.pat.emr 2104 amts_sections += emr.allergy_state._get_as_amts_data(strict = False) % ''.join ([ 2105 a._get_as_amts_data(strict = False) for a in emr.get_allergies() 2106 ]) 2107 self.set_placeholder(key = 'amts_intakes_as_data_enhanced', value = amts_sections, known_only = False) 2108 # self.set_placeholder(key = u'amts_check_symbol', value = gmMedication.calculate_amts_data_check_symbol(intakes = intakes), known_only = False) 2109 self.set_placeholder(key = 'amts_total_pages', value = '1', known_only = False) 2110 success = form.substitute_placeholders(data_source = self) 2111 self.unset_placeholder(key = 'amts_intakes_as_data_enhanced') 2112 # self.unset_placeholder(key = u'amts_check_symbol') 2113 self.unset_placeholder(key = 'amts_total_pages') 2114 if not success: 2115 _log.error('cannot substitute into amts data file form template') 2116 return 2117 data_file = form.re_editable_filenames[0] 2118 png_file = os.path.join(png_dir, 'gm4amts-datamatrix-utf8.png') 2119 cmd = '%s %s %s' % (dmtx_creator, data_file, png_file) 2120 success = gmShellAPI.run_command_in_shell(command = cmd, blocking = True) 2121 if not success: 2122 _log.error('error running [%s]' % cmd) 2123 return 2124 self.set_placeholder(key = 'amts_data_file_utf8', value = data_file, known_only = False) 2125 self.set_placeholder(key = 'amts_png_file_utf8', value = png_file, known_only = False) 2126 2127 # generate conformant per-page files: 2128 total_pages = (len(intakes) / 15.0) 2129 if total_pages > int(total_pages): 2130 total_pages += 1 2131 total_pages = int(total_pages) 2132 _log.debug('total pages: %s', total_pages) 2133 2134 png_file_base = os.path.join(png_dir, 'gm4amts-datamatrix-page-') 2135 for this_page in range(1,total_pages+1): 2136 intakes_this_page = intakes[(this_page-1)*15:this_page*15] 2137 amts_data_template_def_file = gmMedication.generate_amts_data_template_definition_file(strict = True) 2138 _log.debug('amts data template definition file: %s', amts_data_template_def_file) 2139 form = gmForms.cTextForm(template_file = amts_data_template_def_file) 2140 # <S>ection with intakes</S> 2141 amts_sections = '<S>%s</S>' % ''.join ([ 2142 i._get_as_amts_data(strict = False) for i in intakes_this_page 2143 ]) 2144 if this_page == total_pages: 2145 # <S>ection with allergy data</S> 2146 emr = self.pat.emr 2147 amts_sections += emr.allergy_state._get_as_amts_data(strict = False) % ''.join ([ 2148 a._get_as_amts_data(strict = False) for a in emr.get_allergies() 2149 ]) 2150 self.set_placeholder(key = 'amts_intakes_as_data', value = amts_sections, known_only = False) 2151 # self.set_placeholder(key = u'amts_check_symbol', value = gmMedication.calculate_amts_data_check_symbol(intakes = intakes_this_page), known_only = False) 2152 if total_pages == 1: 2153 pg_idx = '' 2154 else: 2155 pg_idx = '%s' % this_page 2156 self.set_placeholder(key = 'amts_page_idx', value = pg_idx, known_only = False) 2157 self.set_placeholder(key = 'amts_total_pages', value = '%s' % total_pages, known_only = False) 2158 success = form.substitute_placeholders(data_source = self) 2159 self.unset_placeholder(key = 'amts_intakes_as_data') 2160 # self.unset_placeholder(key = u'amts_check_symbol') 2161 self.unset_placeholder(key = 'amts_page_idx') 2162 self.unset_placeholder(key = 'amts_total_pages') 2163 if not success: 2164 _log.error('cannot substitute into amts data file form template') 2165 return 2166 2167 data_file = form.re_editable_filenames[0] 2168 png_file = '%s%s.png' % (png_file_base, this_page) 2169 latin1_data_file = gmTools.recode_file ( 2170 source_file = data_file, 2171 source_encoding = 'utf8', 2172 target_encoding = 'latin1', 2173 base_dir = os.path.split(data_file)[0] 2174 ) 2175 cmd = '%s %s %s' % (dmtx_creator, latin1_data_file, png_file) 2176 success = gmShellAPI.run_command_in_shell(command = cmd, blocking = True) 2177 if not success: 2178 _log.error('error running [%s]' % cmd) 2179 return 2180 2181 # cache file names for later use in \embedfile 2182 self.set_placeholder(key = 'amts_data_file_%s' % this_page, value = latin1_data_file, known_only = False) 2183 self.set_placeholder(key = 'amts_png_file_%s' % this_page, value = png_file, known_only = False) 2184 2185 self.set_placeholder(key = 'amts_png_file_current_page', value = png_file_base + '\\thepage', known_only = False)
2186 2187 #--------------------------------------------------------
2188 - def _get_variant_current_meds_for_rx(self, data=None):
2189 if data is None: 2190 return self._escape(_('current_meds_for_rx: template is missing')) 2191 2192 emr = self.pat.emr 2193 from Gnumed.wxpython import gmMedicationWidgets 2194 current_meds = gmMedicationWidgets.manage_substance_intakes(emr = emr) 2195 if current_meds is None: 2196 return '' 2197 2198 intakes2show = {} 2199 for intake in current_meds: 2200 fields_dict = intake.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style) 2201 fields_dict['medically_formatted_start'] = self._escape(intake.medically_formatted_start) 2202 if intake['pk_drug_product'] is None: 2203 fields_dict['product'] = self._escape(_('generic %s') % fields_dict['substance']) 2204 fields_dict['contains'] = self._escape('%s %s%s' % (fields_dict['substance'], fields_dict['amount'], fields_dict['unit'])) 2205 intakes2show[fields_dict['product']] = fields_dict 2206 else: 2207 comps = [ c.split('::') for c in intake.containing_drug['components'] ] 2208 fields_dict['contains'] = self._escape('; '.join([ '%s %s%s' % (c[0], c[1], c[2]) for c in comps ])) 2209 intakes2show[intake['product']] = fields_dict # this will make multi-component drugs unique 2210 2211 intakes2dispense = {} 2212 for product, intake in intakes2show.items(): 2213 msg = _('Dispense how much/many of "%(product)s (%(contains)s)" ?') % intake 2214 amount2dispense = wx.GetTextFromUser(msg, _('Amount to dispense ?')) 2215 if amount2dispense == '': 2216 continue 2217 intake['amount2dispense'] = amount2dispense 2218 intakes2dispense[product] = intake 2219 2220 return '\n'.join([ data % intake for intake in intakes2dispense.values() ])
2221 2222 #--------------------------------------------------------
2223 - def _get_variant_substance_abuse(self, data=None):
2224 if data is None: 2225 return self._escape(_('template is missing')) 2226 template = data 2227 from Gnumed.wxpython import gmHabitWidgets 2228 abuses = gmHabitWidgets.manage_substance_abuse(patient = self.pat) 2229 if abuses is None: 2230 return '' 2231 lines = [] 2232 for a in abuses: 2233 fields = a.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style) 2234 fields['harmful_use_type'] = a.harmful_use_type_string 2235 lines.append(template % fields) 2236 return '\n'.join(lines)
2237 2238 #--------------------------------------------------------
2239 - def _get_variant_current_meds(self, data=None):
2240 2241 if data is None: 2242 return self._escape(_('template is missing')) 2243 2244 parts = data.split(self.__args_divider) 2245 template = parts[0] 2246 ask_user = False 2247 if len(parts) > 1: 2248 ask_user = (parts[1] == 'select') 2249 2250 emr = self.pat.emr 2251 if ask_user: 2252 from Gnumed.wxpython import gmMedicationWidgets 2253 current_meds = gmMedicationWidgets.manage_substance_intakes(emr = emr) 2254 if current_meds is None: 2255 return '' 2256 else: 2257 current_meds = emr.get_current_medications ( 2258 include_inactive = False, 2259 include_unapproved = True, 2260 order_by = 'product, substance' 2261 ) 2262 if len(current_meds) == 0: 2263 return '' 2264 2265 lines = [] 2266 for m in current_meds: 2267 data = m.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style) 2268 data['medically_formatted_start'] = self._escape(m.medically_formatted_start) 2269 lines.append(template % data) 2270 2271 return '\n'.join(lines)
2272 #--------------------------------------------------------
2273 - def _get_variant_current_meds_table(self, data=None):
2274 return gmMedication.format_substance_intake ( 2275 emr = self.pat.emr, 2276 output_format = self.__esc_style, 2277 table_type = 'by-product' 2278 )
2279 #--------------------------------------------------------
2280 - def _get_variant_current_meds_notes(self, data=None):
2281 return gmMedication.format_substance_intake_notes ( 2282 emr = self.pat.emr, 2283 output_format = self.__esc_style, 2284 table_type = 'by-product' 2285 )
2286 #--------------------------------------------------------
2287 - def _get_variant_lab_table(self, data=None):
2288 return gmPathLab.format_test_results ( 2289 results = self.pat.emr.get_test_results_by_date(), 2290 output_format = self.__esc_style 2291 )
2292 #--------------------------------------------------------
2293 - def _get_variant_test_results(self, data=None):
2294 2295 template = '' 2296 date_format = '%Y %b %d %H:%M' 2297 separator = '\n' 2298 2299 options = data.split(self.__args_divider) 2300 try: 2301 template = options[0].strip() 2302 date_format = options[1] 2303 separator = options[2] 2304 except IndexError: 2305 pass 2306 2307 if date_format.strip() == '': 2308 date_format = '%Y %b %d %H:%M' 2309 if separator.strip() == '': 2310 separator = '\n' 2311 2312 #results = gmMeasurementWidgets.manage_measurements(single_selection = False, emr = self.pat.emr) 2313 from Gnumed.wxpython.gmMeasurementWidgets import manage_measurements 2314 results = manage_measurements(single_selection = False, emr = self.pat.emr) 2315 if results is None: 2316 if self.debug: 2317 return self._escape(_('no results for this patient (available or selected)')) 2318 return '' 2319 2320 if template == '': 2321 return (separator + separator).join([ self._escape(r.format(date_format = date_format)) for r in results ]) 2322 2323 return separator.join([ template % r.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) for r in results ])
2324 #--------------------------------------------------------
2325 - def _get_variant_latest_vaccs_table(self, data=None):
2326 return gmVaccination.format_latest_vaccinations ( 2327 output_format = self.__esc_style, 2328 emr = self.pat.emr 2329 )
2330 #--------------------------------------------------------
2331 - def _get_variant_vaccination_history(self, data=None):
2332 options = data.split(self.__args_divider) 2333 template = options[0] 2334 if len(options) > 1: 2335 date_format = options[1] 2336 else: 2337 date_format = '%Y %b %d' 2338 vaccinations_as_dict = [] 2339 for v in self.pat.emr.get_vaccinations(order_by = 'date_given DESC, vaccine'): 2340 v_as_dict = v.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) 2341 v_as_dict['l10n_indications'] = [ ind['l10n_indication'] for ind in v['indications'] ] 2342 vaccinations_as_dict.append(v_as_dict) 2343 2344 return u'\n'.join([ template % v for v in vaccinations_as_dict ])
2345 2346 #--------------------------------------------------------
2347 - def _get_variant_PHX(self, data=None):
2348 2349 if data is None: 2350 if self.debug: 2351 _log.error('PHX: missing placeholder arguments') 2352 return self._escape(_('PHX: Invalid placeholder options.')) 2353 return '' 2354 2355 _log.debug('arguments: %s', data) 2356 2357 data_parts = data.split(self.__args_divider) 2358 template = '%s' 2359 separator = '\n' 2360 date_format = '%Y %b %d' 2361 try: 2362 template = data_parts[0] 2363 separator = data_parts[1] 2364 date_format = data_parts[2] 2365 except IndexError: 2366 pass 2367 2368 phxs = gmEMRStructWidgets.select_health_issues(emr = self.pat.emr) 2369 if phxs is None: 2370 if self.debug: 2371 return self._escape(_('no PHX for this patient (available or selected)')) 2372 return '' 2373 2374 return separator.join ([ 2375 template % phx.fields_as_dict ( 2376 date_format = date_format, 2377 escape_style = self.__esc_style, 2378 bool_strings = (self._escape(_('yes')), self._escape(_('no'))) 2379 ) for phx in phxs 2380 ])
2381 2382 #--------------------------------------------------------
2383 - def _get_variant_problems(self, data=None):
2384 2385 if data is None: 2386 return self._escape(_('template is missing')) 2387 probs = self.pat.emr.get_problems() 2388 return '\n'.join([ data % p.fields_as_dict(date_format = '%Y %b %d', escape_style = self.__esc_style) for p in probs ])
2389 2390 #--------------------------------------------------------
2391 - def _get_variant_diagnoses(self, data=None):
2392 2393 if data is None: 2394 return self._escape(_('template is missing')) 2395 template = data 2396 dxs = self.pat.emr.candidate_diagnoses 2397 if len(dxs) == 0: 2398 _log.debug('no diagnoses available') 2399 return '' 2400 selected = gmListWidgets.get_choices_from_list ( 2401 msg = _('Select the relevant diagnoses:'), 2402 caption = _('Diagnosis selection'), 2403 columns = [ _('Diagnosis'), _('Marked confidential'), _('Certainty'), _('Source') ], 2404 choices = [[ 2405 dx['diagnosis'], 2406 gmTools.bool2subst(dx['explicitely_confidential'], _('yes'), _('no'), _('unknown')), 2407 gmTools.coalesce(dx['diagnostic_certainty_classification'], ''), 2408 dx['source'] 2409 ] for dx in dxs 2410 ], 2411 data = dxs, 2412 single_selection = False, 2413 can_return_empty = True 2414 ) 2415 if selected is None: 2416 _log.debug('user did not select any diagnoses') 2417 return '' 2418 if len(selected) == 0: 2419 _log.debug('user did not select any diagnoses') 2420 return '' 2421 #return template % {'diagnosis': u'', 'diagnostic_certainty_classification': u''} 2422 return '\n'.join(template % self._escape_dict(dx, none_string = '?', bool_strings = [_('yes'), _('no')]) for dx in selected)
2423 2424 #--------------------------------------------------------
2425 - def _get_variant_today(self, data='%Y %b %d'):
2426 return self._escape(gmDateTime.pydt_now_here().strftime(data))
2427 2428 #--------------------------------------------------------
2429 - def _get_variant_tex_escape(self, data=None):
2431 2432 #--------------------------------------------------------
2433 - def _get_variant_url_escape(self, data=None):
2434 return self._escape(urllib.parse.quote(data.encode('utf8')))
2435 2436 #--------------------------------------------------------
2437 - def _get_variant_text_snippet(self, data=None):
2438 data_parts = data.split(self.__args_divider) 2439 keyword = data_parts[0] 2440 template = '%s' 2441 if len(data_parts) > 1: 2442 template = data_parts[1] 2443 2444 expansion = gmKeywordExpansionWidgets.expand_keyword(keyword = keyword, show_list_if_needed = True) 2445 2446 if expansion is None: 2447 if self.debug: 2448 return self._escape(_('no textual expansion found for keyword <%s>') % keyword) 2449 return '' 2450 2451 #return template % self._escape(expansion) 2452 return template % expansion
2453 2454 #--------------------------------------------------------
2455 - def _get_variant_data_snippet(self, data=None):
2456 parts = data.split(self.__args_divider) 2457 keyword = parts[0] 2458 template = '%s' 2459 target_mime = None 2460 target_ext = None 2461 if len(parts) > 1: 2462 template = parts[1] 2463 if len(parts) > 2: 2464 target_mime = parts[2].strip() 2465 if len(parts) > 3: 2466 target_ext = parts[3].strip() 2467 if target_ext is None: 2468 if target_mime is not None: 2469 target_ext = gmMimeLib.guess_ext_by_mimetype(mimetype = target_mime) 2470 2471 expansion = gmKeywordExpansion.get_expansion ( 2472 keyword = keyword, 2473 textual_only = False, 2474 binary_only = True 2475 ) 2476 if expansion is None: 2477 if self.debug: 2478 return self._escape(_('no binary expansion found for keyword <%s>') % keyword) 2479 return '' 2480 2481 filename = expansion.save_to_file() 2482 if filename is None: 2483 if self.debug: 2484 return self._escape(_('cannot export data of binary expansion keyword <%s>') % keyword) 2485 return '' 2486 2487 if expansion['is_encrypted']: 2488 pwd = wx.GetPasswordFromUser ( 2489 message = _('Enter your GnuPG passphrase for decryption of [%s]') % expansion['keyword'], 2490 caption = _('GnuPG passphrase prompt'), 2491 default_value = '' 2492 ) 2493 filename = gmCrypto.gpg_decrypt_file(filename = filename, passphrase = pwd) 2494 if filename is None: 2495 if self.debug: 2496 return self._escape(_('cannot decrypt data of binary expansion keyword <%s>') % keyword) 2497 return '' 2498 2499 target_fname = gmTools.get_unique_filename ( 2500 prefix = '%s-converted-' % os.path.splitext(filename)[0], 2501 suffix = target_ext 2502 ) 2503 if not gmMimeLib.convert_file(filename = filename, target_mime = target_mime, target_filename = target_fname): 2504 if self.debug: 2505 return self._escape(_('cannot convert data of binary expansion keyword <%s>') % keyword) 2506 # hoping that the target can cope: 2507 return template % filename 2508 2509 return template % target_fname
2510 2511 #--------------------------------------------------------
2512 - def _get_variant_qrcode(self, data=None):
2513 options = data.split(self.__args_divider) 2514 if len(options) == 0: 2515 return None 2516 text4qr = options[0] 2517 if len(options) > 1: 2518 template = options[1] 2519 else: 2520 template = u'%s' 2521 qr_filename = gmTools.create_qrcode(text = text4qr) 2522 if qr_filename is None: 2523 return self._escape('cannot_create_QR_code') 2524 2525 return template % qr_filename
2526 2527 #--------------------------------------------------------
2528 - def _get_variant_range_of(self, data=None):
2529 if data is None: 2530 return None 2531 # wrapper code already takes care of actually 2532 # selecting the range so all we need to do here 2533 # is to return the data itself 2534 return data
2535 2536 #--------------------------------------------------------
2537 - def _get_variant_if_not_empty(self, data=None):
2538 if data is None: 2539 return None 2540 2541 parts = data.split(self.__args_divider) 2542 if len(parts) < 3: 2543 return 'IF_NOT_EMPTY lacks <instead> definition' 2544 txt = parts[0] 2545 template = parts[1] 2546 instead = parts[2] 2547 2548 if txt.strip() == '': 2549 return instead 2550 if '%s' in template: 2551 return template % txt 2552 return template
2553 2554 #--------------------------------------------------------
2555 - def _get_variant_if_debugging(self, data=None):
2556 2557 if data is None: 2558 return None 2559 parts = data.split(self.__args_divider) 2560 if len(parts) < 2: 2561 return self._escape(u'IF_DEBUGGING lacks proper definition') 2562 debug_str = parts[0] 2563 non_debug_str = parts[1] 2564 if self.debug: 2565 return debug_str 2566 return non_debug_str
2567 2568 #--------------------------------------------------------
2569 - def _get_variant_free_text(self, data=None):
2570 2571 if data is None: 2572 parts = [] 2573 msg = _('generic text') 2574 cache_key = 'free_text::%s' % datetime.datetime.now() 2575 else: 2576 parts = data.split(self.__args_divider) 2577 msg = parts[0] 2578 cache_key = 'free_text::%s' % msg 2579 2580 try: 2581 return self.__cache[cache_key] 2582 except KeyError: 2583 pass 2584 2585 if len(parts) > 1: 2586 preset = parts[1] 2587 else: 2588 preset = '' 2589 2590 dlg = gmGuiHelpers.cMultilineTextEntryDlg ( 2591 None, 2592 -1, 2593 title = _('Replacing <free_text> placeholder'), 2594 msg = _('Below you can enter free text.\n\n [%s]') % msg, 2595 text = preset 2596 ) 2597 dlg.enable_user_formatting = True 2598 decision = dlg.ShowModal() 2599 text = dlg.value.strip() 2600 is_user_formatted = dlg.is_user_formatted 2601 dlg.DestroyLater() 2602 2603 if decision != wx.ID_SAVE: 2604 if self.debug: 2605 return self._escape(_('Text input cancelled by user.')) 2606 return self._escape('') 2607 2608 # user knows "best" 2609 if is_user_formatted: 2610 self.__cache[cache_key] = text 2611 return text 2612 2613 text = self._escape(text) 2614 self.__cache[cache_key] = text 2615 return text
2616 2617 #--------------------------------------------------------
2618 - def _get_variant_bill(self, data=None):
2619 try: 2620 bill = self.__cache['bill'] 2621 except KeyError: 2622 from Gnumed.wxpython import gmBillingWidgets 2623 bill = gmBillingWidgets.manage_bills(patient = self.pat) 2624 if bill is None: 2625 if self.debug: 2626 return self._escape(_('no bill selected')) 2627 return '' 2628 self.__cache['bill'] = bill 2629 2630 parts = data.split(self.__args_divider) 2631 template = parts[0] 2632 if len(parts) > 1: 2633 date_format = parts[1] 2634 else: 2635 date_format = '%Y %B %d' 2636 2637 return template % bill.fields_as_dict(date_format = date_format, escape_style = self.__esc_style)
2638 2639 #--------------------------------------------------------
2640 - def _get_variant_bill_scan2pay(self, data=None):
2641 try: 2642 bill = self.__cache['bill'] 2643 except KeyError: 2644 from Gnumed.wxpython import gmBillingWidgets 2645 bill = gmBillingWidgets.manage_bills(patient = self.pat) 2646 if bill is None: 2647 if self.debug: 2648 return self._escape(_('no bill selected')) 2649 return '' 2650 self.__cache['bill'] = bill 2651 2652 format = 'qr' 2653 options = data.split(self.__args_divider) 2654 _log.debug('options: %s', options) 2655 for o in options: 2656 if o.strip().startswith('fmt='): 2657 format = o.strip()[4:] 2658 if format not in ['qr', 'txt']: 2659 return self._escape(_('praxis_scan2pay: invalid format (qr/txt)')) 2660 continue 2661 _log.debug('format: %s' % format) 2662 2663 from Gnumed.business import gmBilling 2664 data_str = gmBilling.get_scan2pay_data ( 2665 gmPraxis.gmCurrentPraxisBranch(), 2666 bill, 2667 provider = gmStaff.gmCurrentProvider() 2668 ) 2669 if data_str is None: 2670 if self.debug: 2671 return self._escape('bill_scan2pay-cannot_create_data_file') 2672 return '' 2673 2674 if format == 'txt': 2675 return self._escape(data_str) 2676 2677 if format == 'qr': 2678 qr_filename = gmTools.create_qrcode(text = data_str) 2679 if qr_filename is not None: 2680 return qr_filename 2681 if self.debug: 2682 return self._escape('bill_scan2pay-cannot_create_QR_code') 2683 return '' 2684 2685 return None
2686 2687 #--------------------------------------------------------
2688 - def _get_variant_bill_item(self, data=None):
2689 try: 2690 bill = self.__cache['bill'] 2691 except KeyError: 2692 from Gnumed.wxpython import gmBillingWidgets 2693 bill = gmBillingWidgets.manage_bills(patient = self.pat) 2694 if bill is None: 2695 if self.debug: 2696 return self._escape(_('no bill selected')) 2697 return '' 2698 self.__cache['bill'] = bill 2699 2700 parts = data.split(self.__args_divider) 2701 template = parts[0] 2702 if len(parts) > 1: 2703 date_format = parts[1] 2704 else: 2705 date_format = '%Y %B %d' 2706 2707 return '\n'.join([ template % i.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) for i in bill.bill_items ])
2708 2709 #--------------------------------------------------------
2710 - def __get_variant_bill_adr_part(self, data=None, part=None):
2711 try: 2712 bill = self.__cache['bill'] 2713 except KeyError: 2714 from Gnumed.wxpython import gmBillingWidgets 2715 bill = gmBillingWidgets.manage_bills(patient = self.pat) 2716 if bill is None: 2717 if self.debug: 2718 return self._escape(_('no bill selected')) 2719 return '' 2720 self.__cache['bill'] = bill 2721 self.__cache['bill-adr'] = bill.address 2722 2723 try: 2724 bill_adr = self.__cache['bill-adr'] 2725 except KeyError: 2726 bill_adr = bill.address 2727 self.__cache['bill-adr'] = bill_adr 2728 2729 if bill_adr is None: 2730 if self.debug: 2731 return self._escape(_('[%s] bill has no address') % part) 2732 return '' 2733 2734 if bill_adr[part] is None: 2735 return self._escape('') 2736 2737 if data is None: 2738 return self._escape(bill_adr[part]) 2739 2740 if data == '': 2741 return self._escape(bill_adr[part]) 2742 2743 return data % self._escape(bill_adr[part])
2744 2745 #--------------------------------------------------------
2746 - def _get_variant_bill_adr_street(self, data='?'):
2747 return self.__get_variant_bill_adr_part(data = data, part = 'street')
2748 2749 #--------------------------------------------------------
2750 - def _get_variant_bill_adr_number(self, data='?'):
2751 return self.__get_variant_bill_adr_part(data = data, part = 'number')
2752 2753 #--------------------------------------------------------
2754 - def _get_variant_bill_adr_subunit(self, data='?'):
2755 return self.__get_variant_bill_adr_part(data = data, part = 'subunit')
2756 #--------------------------------------------------------
2757 - def _get_variant_bill_adr_location(self, data='?'):
2758 return self.__get_variant_bill_adr_part(data = data, part = 'urb')
2759 2760 #--------------------------------------------------------
2761 - def _get_variant_bill_adr_suburb(self, data='?'):
2762 return self.__get_variant_bill_adr_part(data = data, part = 'suburb')
2763 2764 #--------------------------------------------------------
2765 - def _get_variant_bill_adr_postcode(self, data='?'):
2766 return self.__get_variant_bill_adr_part(data = data, part = 'postcode')
2767 2768 #--------------------------------------------------------
2769 - def _get_variant_bill_adr_region(self, data='?'):
2770 return self.__get_variant_bill_adr_part(data = data, part = 'l10n_region')
2771 2772 #--------------------------------------------------------
2773 - def _get_variant_bill_adr_country(self, data='?'):
2774 return self.__get_variant_bill_adr_part(data = data, part = 'l10n_country')
2775 2776 #-------------------------------------------------------- 2777 # internal helpers 2778 #--------------------------------------------------------
2779 - def _escape(self, text=None):
2780 if self.__esc_func is None: 2781 return text 2782 assert (text is not None), 'text=None passed to _escape()' 2783 return self.__esc_func(text)
2784 2785 #--------------------------------------------------------
2786 - def _escape_dict(self, the_dict=None, date_format='%Y %b %d %H:%M', none_string='', bool_strings=None):
2787 if bool_strings is None: 2788 bools = {True: _('true'), False: _('false')} 2789 else: 2790 bools = {True: bool_strings[0], False: bool_strings[1]} 2791 data = {} 2792 for field in the_dict.keys(): 2793 # FIXME: harden against BYTEA fields 2794 #if type(self._payload[self._idx[field]]) == ... 2795 # data[field] = _('<%s bytes of binary data>') % len(self._payload[self._idx[field]]) 2796 # continue 2797 val = the_dict[field] 2798 if val is None: 2799 data[field] = none_string 2800 continue 2801 if isinstance(val, bool): 2802 data[field] = bools[val] 2803 continue 2804 if isinstance(val, datetime.datetime): 2805 data[field] = gmDateTime.pydt_strftime(val, format = date_format) 2806 if self.__esc_style in ['latex', 'tex']: 2807 data[field] = gmTools.tex_escape_string(data[field]) 2808 elif self.__esc_style in ['xetex', 'xelatex']: 2809 data[field] = gmTools.xetex_escape_string(data[field]) 2810 continue 2811 try: 2812 data[field] = str(val, encoding = 'utf8', errors = 'replace') 2813 except TypeError: 2814 try: 2815 data[field] = str(val) 2816 except (UnicodeDecodeError, TypeError): 2817 val = '%s' % str(val) 2818 data[field] = val.decode('utf8', 'replace') 2819 if self.__esc_style in ['latex', 'tex']: 2820 data[field] = gmTools.tex_escape_string(data[field]) 2821 elif self.__esc_style in ['xetex', 'xelatex']: 2822 data[field] = gmTools.xetex_escape_string(data[field]) 2823 return data
2824 2825 #---------------------------------------------------------------------
2826 -def test_placeholders():
2827 2828 _log.debug('testing for placeholders with pattern: %s', first_pass_placeholder_regex) 2829 2830 data_source = gmPlaceholderHandler() 2831 original_line = '' 2832 2833 while True: 2834 # get input from user 2835 line = wx.GetTextFromUser ( 2836 _('Enter some text containing a placeholder:'), 2837 _('Testing placeholders'), 2838 centre = True, 2839 default_value = original_line 2840 ) 2841 if line.strip() == '': 2842 break 2843 original_line = line 2844 # replace 2845 placeholders_in_line = regex.findall(first_pass_placeholder_regex, line, regex.IGNORECASE) 2846 if len(placeholders_in_line) == 0: 2847 continue 2848 for placeholder in placeholders_in_line: 2849 try: 2850 val = data_source[placeholder] 2851 except: 2852 val = _('error with placeholder [%s]') % placeholder 2853 if val is None: 2854 val = _('error with placeholder [%s]') % placeholder 2855 line = line.replace(placeholder, val) 2856 # show 2857 msg = _( 2858 'Input: %s\n' 2859 '\n' 2860 'Output:\n' 2861 '%s' 2862 ) % ( 2863 original_line, 2864 line 2865 ) 2866 gmGuiHelpers.gm_show_info ( 2867 title = _('Testing placeholders'), 2868 info = msg 2869 )
2870 2871 #=====================================================================
2872 -class cMacroPrimitives:
2873 """Functions a macro can legally use. 2874 2875 An instance of this class is passed to the GNUmed scripting 2876 listener. Hence, all actions a macro can legally take must 2877 be defined in this class. Thus we achieve some screening for 2878 security and also thread safety handling. 2879 """ 2880 #-----------------------------------------------------------------
2881 - def __init__(self, personality = None):
2882 if personality is None: 2883 raise gmExceptions.ConstructorError('must specify personality') 2884 self.__personality = personality 2885 self.__attached = 0 2886 self._get_source_personality = None 2887 self.__user_done = False 2888 self.__user_answer = 'no answer yet' 2889 self.__pat = gmPerson.gmCurrentPatient() 2890 2891 self.__auth_cookie = str(random.random()) 2892 self.__pat_lock_cookie = str(random.random()) 2893 self.__lock_after_load_cookie = str(random.random()) 2894 2895 _log.info('slave mode personality is [%s]', personality)
2896 #----------------------------------------------------------------- 2897 # public API 2898 #-----------------------------------------------------------------
2899 - def attach(self, personality = None):
2900 if self.__attached: 2901 _log.error('attach with [%s] rejected, already serving a client', personality) 2902 return (0, _('attach rejected, already serving a client')) 2903 if personality != self.__personality: 2904 _log.error('rejecting attach to personality [%s], only servicing [%s]' % (personality, self.__personality)) 2905 return (0, _('attach to personality [%s] rejected') % personality) 2906 self.__attached = 1 2907 self.__auth_cookie = str(random.random()) 2908 return (1, self.__auth_cookie)
2909 #-----------------------------------------------------------------
2910 - def detach(self, auth_cookie=None):
2911 if not self.__attached: 2912 return 1 2913 if auth_cookie != self.__auth_cookie: 2914 _log.error('rejecting detach() with cookie [%s]' % auth_cookie) 2915 return 0 2916 self.__attached = 0 2917 return 1
2918 #-----------------------------------------------------------------
2919 - def force_detach(self):
2920 if not self.__attached: 2921 return 1 2922 self.__user_done = False 2923 # FIXME: use self.__sync_cookie for syncing with user interaction 2924 wx.CallAfter(self._force_detach) 2925 return 1
2926 #-----------------------------------------------------------------
2927 - def version(self):
2928 ver = _cfg.get(option = 'client_version') 2929 return "GNUmed %s, %s $Revision: 1.51 $" % (ver, self.__class__.__name__)
2930 #-----------------------------------------------------------------
2931 - def shutdown_gnumed(self, auth_cookie=None, forced=False):
2932 """Shuts down this client instance.""" 2933 if not self.__attached: 2934 return 0 2935 if auth_cookie != self.__auth_cookie: 2936 _log.error('non-authenticated shutdown_gnumed()') 2937 return 0 2938 wx.CallAfter(self._shutdown_gnumed, forced) 2939 return 1
2940 #-----------------------------------------------------------------
2941 - def raise_gnumed(self, auth_cookie = None):
2942 """Raise ourselves to the top of the desktop.""" 2943 if not self.__attached: 2944 return 0 2945 if auth_cookie != self.__auth_cookie: 2946 _log.error('non-authenticated raise_gnumed()') 2947 return 0 2948 return "cMacroPrimitives.raise_gnumed() not implemented"
2949 #-----------------------------------------------------------------
2950 - def get_loaded_plugins(self, auth_cookie = None):
2951 if not self.__attached: 2952 return 0 2953 if auth_cookie != self.__auth_cookie: 2954 _log.error('non-authenticated get_loaded_plugins()') 2955 return 0 2956 gb = gmGuiBroker.GuiBroker() 2957 return gb['horstspace.notebook.gui'].keys()
2958 #-----------------------------------------------------------------
2959 - def raise_notebook_plugin(self, auth_cookie = None, a_plugin = None):
2960 """Raise a notebook plugin within GNUmed.""" 2961 if not self.__attached: 2962 return 0 2963 if auth_cookie != self.__auth_cookie: 2964 _log.error('non-authenticated raise_notebook_plugin()') 2965 return 0 2966 # FIXME: use semaphore 2967 wx.CallAfter(gmPlugin.raise_notebook_plugin, a_plugin) 2968 return 1
2969 #-----------------------------------------------------------------
2970 - def load_patient_from_external_source(self, auth_cookie = None):
2971 """Load external patient, perhaps create it. 2972 2973 Callers must use get_user_answer() to get status information. 2974 It is unsafe to proceed without knowing the completion state as 2975 the controlled client may be waiting for user input from a 2976 patient selection list. 2977 """ 2978 if not self.__attached: 2979 return (0, _('request rejected, you are not attach()ed')) 2980 if auth_cookie != self.__auth_cookie: 2981 _log.error('non-authenticated load_patient_from_external_source()') 2982 return (0, _('rejected load_patient_from_external_source(), not authenticated')) 2983 if self.__pat.locked: 2984 _log.error('patient is locked, cannot load from external source') 2985 return (0, _('current patient is locked')) 2986 self.__user_done = False 2987 wx.CallAfter(self._load_patient_from_external_source) 2988 self.__lock_after_load_cookie = str(random.random()) 2989 return (1, self.__lock_after_load_cookie)
2990 #-----------------------------------------------------------------
2991 - def lock_loaded_patient(self, auth_cookie = None, lock_after_load_cookie = None):
2992 if not self.__attached: 2993 return (0, _('request rejected, you are not attach()ed')) 2994 if auth_cookie != self.__auth_cookie: 2995 _log.error('non-authenticated lock_load_patient()') 2996 return (0, _('rejected lock_load_patient(), not authenticated')) 2997 # FIXME: ask user what to do about wrong cookie 2998 if lock_after_load_cookie != self.__lock_after_load_cookie: 2999 _log.warning('patient lock-after-load request rejected due to wrong cookie [%s]' % lock_after_load_cookie) 3000 return (0, 'patient lock-after-load request rejected, wrong cookie provided') 3001 self.__pat.locked = True 3002 self.__pat_lock_cookie = str(random.random()) 3003 return (1, self.__pat_lock_cookie)
3004 #-----------------------------------------------------------------
3005 - def lock_into_patient(self, auth_cookie = None, search_params = None):
3006 if not self.__attached: 3007 return (0, _('request rejected, you are not attach()ed')) 3008 if auth_cookie != self.__auth_cookie: 3009 _log.error('non-authenticated lock_into_patient()') 3010 return (0, _('rejected lock_into_patient(), not authenticated')) 3011 if self.__pat.locked: 3012 _log.error('patient is already locked') 3013 return (0, _('already locked into a patient')) 3014 searcher = gmPersonSearch.cPatientSearcher_SQL() 3015 if type(search_params) == dict: 3016 idents = searcher.get_identities(search_dict=search_params) 3017 raise Exception("must use dto, not search_dict") 3018 else: 3019 idents = searcher.get_identities(search_term=search_params) 3020 if idents is None: 3021 return (0, _('error searching for patient with [%s]/%s') % (search_term, search_dict)) 3022 if len(idents) == 0: 3023 return (0, _('no patient found for [%s]/%s') % (search_term, search_dict)) 3024 # FIXME: let user select patient 3025 if len(idents) > 1: 3026 return (0, _('several matching patients found for [%s]/%s') % (search_term, search_dict)) 3027 if not gmPatSearchWidgets.set_active_patient(patient = idents[0]): 3028 return (0, _('cannot activate patient [%s] (%s/%s)') % (str(idents[0]), search_term, search_dict)) 3029 self.__pat.locked = True 3030 self.__pat_lock_cookie = str(random.random()) 3031 return (1, self.__pat_lock_cookie)
3032 #-----------------------------------------------------------------
3033 - def unlock_patient(self, auth_cookie = None, unlock_cookie = None):
3034 if not self.__attached: 3035 return (0, _('request rejected, you are not attach()ed')) 3036 if auth_cookie != self.__auth_cookie: 3037 _log.error('non-authenticated unlock_patient()') 3038 return (0, _('rejected unlock_patient, not authenticated')) 3039 # we ain't locked anyways, so succeed 3040 if not self.__pat.locked: 3041 return (1, '') 3042 # FIXME: ask user what to do about wrong cookie 3043 if unlock_cookie != self.__pat_lock_cookie: 3044 _log.warning('patient unlock request rejected due to wrong cookie [%s]' % unlock_cookie) 3045 return (0, 'patient unlock request rejected, wrong cookie provided') 3046 self.__pat.locked = False 3047 return (1, '')
3048 #-----------------------------------------------------------------
3049 - def assume_staff_identity(self, auth_cookie = None, staff_name = "Dr.Jekyll", staff_creds = None):
3050 if not self.__attached: 3051 return 0 3052 if auth_cookie != self.__auth_cookie: 3053 _log.error('non-authenticated select_identity()') 3054 return 0 3055 return "cMacroPrimitives.assume_staff_identity() not implemented"
3056 #-----------------------------------------------------------------
3057 - def get_user_answer(self):
3058 if not self.__user_done: 3059 return (0, 'still waiting') 3060 self.__user_done = False 3061 return (1, self.__user_answer)
3062 #----------------------------------------------------------------- 3063 # internal API 3064 #-----------------------------------------------------------------
3065 - def _force_detach(self):
3066 msg = _( 3067 'Someone tries to forcibly break the existing\n' 3068 'controlling connection. This may or may not\n' 3069 'have legitimate reasons.\n\n' 3070 'Do you want to allow breaking the connection ?' 3071 ) 3072 can_break_conn = gmGuiHelpers.gm_show_question ( 3073 aMessage = msg, 3074 aTitle = _('forced detach attempt') 3075 ) 3076 if can_break_conn: 3077 self.__user_answer = 1 3078 else: 3079 self.__user_answer = 0 3080 self.__user_done = True 3081 if can_break_conn: 3082 self.__pat.locked = False 3083 self.__attached = 0 3084 return 1
3085 #-----------------------------------------------------------------
3086 - def _shutdown_gnumed(self, forced=False):
3087 top_win = wx.GetApp().GetTopWindow() 3088 if forced: 3089 top_win.DestroyLater() 3090 else: 3091 top_win.Close()
3092 #-----------------------------------------------------------------
3094 patient = gmPatSearchWidgets.get_person_from_external_sources(search_immediately = True, activate_immediately = True) 3095 if patient is not None: 3096 self.__user_answer = 1 3097 else: 3098 self.__user_answer = 0 3099 self.__user_done = True 3100 return 1
3101 #===================================================================== 3102 # main 3103 #===================================================================== 3104 if __name__ == '__main__': 3105 3106 if len(sys.argv) < 2: 3107 sys.exit() 3108 3109 if sys.argv[1] != 'test': 3110 sys.exit() 3111 3112 gmI18N.activate_locale() 3113 gmI18N.install_domain() 3114 3115 #--------------------------------------------------------
3116 - def test_placeholders():
3117 handler = gmPlaceholderHandler() 3118 handler.debug = True 3119 3120 for placeholder in ['a', 'b']: 3121 print(handler[placeholder]) 3122 3123 pat = gmPersonSearch.ask_for_patient() 3124 if pat is None: 3125 return 3126 3127 gmPatSearchWidgets.set_active_patient(patient = pat) 3128 3129 print('DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d']) 3130 3131 app = wx.PyWidgetTester(size = (200, 50)) 3132 3133 ph = 'progress_notes::ap' 3134 print('%s: %s' % (ph, handler[ph]))
3135 #--------------------------------------------------------
3136 - def test_new_variant_placeholders():
3137 3138 tests = [ 3139 # should work: 3140 '$<lastname>$', 3141 '$<lastname::::3>$', 3142 '$<name::%(title)s %(firstnames)s%(preferred)s%(lastnames)s>$', 3143 3144 # should fail: 3145 'lastname', 3146 '$<lastname', 3147 '$<lastname::', 3148 '$<lastname::>$', 3149 '$<lastname::abc>$', 3150 '$<lastname::abc::>$', 3151 '$<lastname::abc::3>$', 3152 '$<lastname::abc::xyz>$', 3153 '$<lastname::::>$', 3154 '$<lastname::::xyz>$', 3155 3156 '$<date_of_birth::%Y-%m-%d>$', 3157 '$<date_of_birth::%Y-%m-%d::3>$', 3158 '$<date_of_birth::%Y-%m-%d::>$', 3159 3160 # should work: 3161 '$<adr_location::home::35>$', 3162 '$<gender_mapper::male//female//other::5>$', 3163 '$<current_meds::==> %(product)s %(preparation)s (%(substance)s) <==\n::50>$', 3164 '$<allergy_list::%(descriptor)s, >$', 3165 '$<current_meds_table::latex//>$' 3166 3167 # 'firstname', 3168 # 'title', 3169 # 'date_of_birth', 3170 # 'progress_notes', 3171 # 'soap', 3172 # 'soap_s', 3173 # 'soap_o', 3174 # 'soap_a', 3175 # 'soap_p', 3176 3177 # 'soap', 3178 # 'progress_notes', 3179 # 'date_of_birth' 3180 ] 3181 3182 # tests = [ 3183 # '$<latest_vaccs_table::latex>$' 3184 # ] 3185 3186 pat = gmPersonSearch.ask_for_patient() 3187 if pat is None: 3188 return 3189 3190 gmPatSearchWidgets.set_active_patient(patient = pat) 3191 3192 handler = gmPlaceholderHandler() 3193 handler.debug = True 3194 3195 for placeholder in tests: 3196 print(placeholder, "=>", handler[placeholder]) 3197 print("--------------") 3198 input()
3199 3200 # print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 3201 3202 # app = wx.PyWidgetTester(size = (200, 50)) 3203 3204 # ph = 'progress_notes::ap' 3205 # print '%s: %s' % (ph, handler[ph]) 3206 3207 #--------------------------------------------------------
3208 - def test_scripting():
3209 from Gnumed.pycommon import gmScriptingListener 3210 import xmlrpc.client 3211 3212 listener = gmScriptingListener.cScriptingListener(macro_executor = cMacroPrimitives(personality='unit test'), port=9999) 3213 3214 s = xmlrpc.client.ServerProxy('http://localhost:9999') 3215 print("should fail:", s.attach()) 3216 print("should fail:", s.attach('wrong cookie')) 3217 print("should work:", s.version()) 3218 print("should fail:", s.raise_gnumed()) 3219 print("should fail:", s.raise_notebook_plugin('test plugin')) 3220 print("should fail:", s.lock_into_patient('kirk, james')) 3221 print("should fail:", s.unlock_patient()) 3222 status, conn_auth = s.attach('unit test') 3223 print("should work:", status, conn_auth) 3224 print("should work:", s.version()) 3225 print("should work:", s.raise_gnumed(conn_auth)) 3226 status, pat_auth = s.lock_into_patient(conn_auth, 'kirk, james') 3227 print("should work:", status, pat_auth) 3228 print("should fail:", s.unlock_patient(conn_auth, 'bogus patient unlock cookie')) 3229 print("should work", s.unlock_patient(conn_auth, pat_auth)) 3230 data = {'firstname': 'jame', 'lastnames': 'Kirk', 'gender': 'm'} 3231 status, pat_auth = s.lock_into_patient(conn_auth, data) 3232 print("should work:", status, pat_auth) 3233 print("should work", s.unlock_patient(conn_auth, pat_auth)) 3234 print(s.detach('bogus detach cookie')) 3235 print(s.detach(conn_auth)) 3236 del s 3237 3238 listener.shutdown()
3239 #--------------------------------------------------------
3240 - def test_placeholder_regex():
3241 3242 import re as regex 3243 3244 tests = [ 3245 ' $<lastname>$ ', 3246 ' $<lastname::::3>$ ', 3247 3248 # should fail: 3249 '$<date_of_birth::%Y-%m-%d>$', 3250 '$<date_of_birth::%Y-%m-%d::3>$', 3251 '$<date_of_birth::%Y-%m-%d::>$', 3252 3253 '$<adr_location::home::35>$', 3254 '$<gender_mapper::male//female//other::5>$', 3255 '$<current_meds::==> %(product)s %(preparation)s (%(substance)s) <==\\n::50>$', 3256 '$<allergy_list::%(descriptor)s, >$', 3257 3258 '\\noindent Patient: $<lastname>$, $<firstname>$', 3259 '$<allergies::%(descriptor)s & %(l10n_type)s & {\\footnotesize %(reaction)s} \tabularnewline \hline >$', 3260 '$<current_meds:: \item[%(substance)s] {\\footnotesize (%(product)s)} %(preparation)s %(amount)s%(unit)s: %(schedule)s >$' 3261 ] 3262 3263 tests = [ 3264 3265 'junk $<lastname::::3>$ junk', 3266 'junk $<lastname::abc::3>$ junk', 3267 'junk $<lastname::abc>$ junk', 3268 'junk $<lastname>$ junk', 3269 3270 'junk $<lastname>$ junk $<firstname>$ junk', 3271 'junk $<lastname::abc>$ junk $<fiststname::abc>$ junk', 3272 'junk $<lastname::abc::3>$ junk $<firstname::abc::3>$ junk', 3273 'junk $<lastname::::3>$ junk $<firstname::::3>$ junk' 3274 3275 ] 3276 3277 tests = [ 3278 # u'junk $<<<date_of_birth::%Y %B %d $<inner placeholder::%Y %B %d::20>$::20>>>$ junk', 3279 # u'junk $<date_of_birth::%Y %B %d::20>$ $<date_of_birth::%Y %B %d::20>$', 3280 # u'junk $<date_of_birth::%Y %B %d::>$ $<date_of_birth::%Y %B %d::20>$ $<<date_of_birth::%Y %B %d::20>>$', 3281 # u'junk $<date_of_birth::::20>$', 3282 # u'junk $<date_of_birth::::>$', 3283 'junk $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::>>>$ junk', 3284 'junk $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::250>>>$ junk', 3285 'junk $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::3-4>>>$ junk', 3286 3287 'should fail $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::->>>$ junk', 3288 'should fail $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::3->>>$ junk', 3289 'should fail $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::-4>>>$ should fail', 3290 'should fail $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::should_fail>>>$ junk', 3291 'should fail $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::should_fail->>>$ junk', 3292 'should fail $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::-should_fail>>>$ junk', 3293 'should fail $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::should_fail-4>>>$ junk', 3294 'should fail $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::3-should_fail>>>$ junk', 3295 'should fail $<<<current_meds::%(product)s (%(substance)s): Dispense $<free_text::Dispense how many of %(product)s %(preparation)s (%(substance)s) ?::20>$ (%(preparation)s) \\n::should_fail-should_fail>>>$ junk', 3296 ] 3297 3298 tests = [ 3299 'junk $<<<should pass::template::>>>$ junk', 3300 'junk $<<<should pass::template::10>>>$ junk', 3301 'junk $<<<should pass::template::10-20>>>$ junk', 3302 'junk $<<<should pass::template $<<dummy::template 2::10>>$::>>>$ junk', 3303 'junk $<<<should pass::template $<dummy::template 2::10>$::>>>$ junk', 3304 3305 'junk $<<<should pass::template::>>>$ junk $<<<should pass 2::template 2::>>>$ junk', 3306 'junk $<<<should pass::template::>>>$ junk $<<should pass 2::template 2::>>$ junk', 3307 'junk $<<<should pass::template::>>>$ junk $<should pass 2::template 2::>$ junk', 3308 3309 'junk $<<<should fail::template $<<<dummy::template 2::10>>>$::>>>$ junk', 3310 3311 'junk $<<<should fail::template::10->>>$ junk', 3312 'junk $<<<should fail::template::10->>>$ junk', 3313 'junk $<<<should fail::template::10->>>$ junk', 3314 'junk $<<<should fail::template::10->>>$ junk', 3315 'junk $<first_pass::junk $<<<3rd_pass::template::20>>>$ junk::8-10>$ junk' 3316 ] 3317 3318 #print "testing placeholder regex:", first_pass_placeholder_regex 3319 ##print "testing placeholder regex:", second_pass_placeholder_regex 3320 ##print "testing placeholder regex:", third_pass_placeholder_regex 3321 #print "" 3322 #for t in tests: 3323 # print 'line: "%s"' % t 3324 # phs = regex.findall(first_pass_placeholder_regex, t, regex.IGNORECASE) 3325 # #phs = regex.findall(second_pass_placeholder_regex, t, regex.IGNORECASE) 3326 # #phs = regex.findall(third_pass_placeholder_regex, t, regex.IGNORECASE) 3327 # print " %s placeholders:" % len(phs) 3328 # for p in phs: 3329 # print ' => ', p 3330 # print " " 3331 3332 all_tests = { 3333 first_pass_placeholder_regex: [ 3334 # different lengths/regions 3335 ('junk $<first_level::template::>$ junk', ['$<first_level::template::>$']), 3336 ('junk $<first_level::template::10>$ junk', ['$<first_level::template::10>$']), 3337 ('junk $<first_level::template::10-12>$ junk', ['$<first_level::template::10-12>$']), 3338 3339 # inside is other-level: 3340 ('junk $<first_level::$<<insert::insert_template::0>>$::10-12>$ junk', ['$<first_level::$<<insert::insert_template::0>>$::10-12>$']), 3341 ('junk $<first_level::$<<<insert::insert_template::0>>>$::10-12>$ junk', ['$<first_level::$<<<insert::insert_template::0>>>$::10-12>$']), 3342 3343 # outside is other-level: 3344 ('junk $<<second_level::$<insert::insert_template::0>$::10-12>>$ junk', ['$<insert::insert_template::0>$']), 3345 ('junk $<<<third_level::$<insert::insert_template::0>$::10-12>>>$ junk', ['$<insert::insert_template::0>$']), 3346 3347 # other level on same line 3348 ('junk $<first_level 1::template 1::>$ junk $<<second_level 2::template 2::>>$ junk', ['$<first_level 1::template 1::>$']), 3349 ('junk $<first_level 1::template 1::>$ junk $<<<third_level 2::template 2::>>>$ junk', ['$<first_level 1::template 1::>$']), 3350 3351 # this should produce 2 matches 3352 ('junk $<first_level 1::template 1::>$ junk $<first_level 2::template 2::>$ junk', ['$<first_level 1::template 1::>$', '$<first_level 2::template 2::>$']), 3353 3354 # this will produce a mismatch, due to illegal nesting of same-level placeholders 3355 ('returns illegal match: junk $<first_level::$<insert::insert_template::0>$::10-12>$ junk', ['$<first_level::$<insert::insert_template::0>$::10-12>$']), 3356 ], 3357 second_pass_placeholder_regex: [ 3358 # different lengths/regions 3359 ('junk $<<second_level::template::>>$ junk', ['$<<second_level::template::>>$']), 3360 ('junk $<<second_level::template::10>>$ junk', ['$<<second_level::template::10>>$']), 3361 ('junk $<<second_level::template::10-12>>$ junk', ['$<<second_level::template::10-12>>$']), 3362 3363 # inside is other-level: 3364 ('junk $<<second_level::$<insert::insert_template::0>$::10-12>>$ junk', ['$<<second_level::$<insert::insert_template::0>$::10-12>>$']), 3365 ('junk $<<second_level::$<<<insert::insert_template::0>>>$::10-12>>$ junk', ['$<<second_level::$<<<insert::insert_template::0>>>$::10-12>>$']), 3366 3367 # outside is other-level: 3368 ('junk $<first_level::$<<insert::insert_template::0>>$::10-12>$ junk', ['$<<insert::insert_template::0>>$']), 3369 ('junk $<<<third_level::$<<insert::insert_template::0>>$::10-12>>>$ junk', ['$<<insert::insert_template::0>>$']), 3370 3371 # other level on same line 3372 ('junk $<first_level 1::template 1::>$ junk $<<second_level 2::template 2::>>$ junk', ['$<<second_level 2::template 2::>>$']), 3373 ('junk $<<second_level 1::template 1::>>$ junk $<<<third_level 2::template 2::>>>$ junk', ['$<<second_level 1::template 1::>>$']), 3374 3375 # this should produce 2 matches 3376 ('junk $<<second_level 1::template 1::>>$ junk $<<second_level 2::template 2::>>$ junk', ['$<<second_level 1::template 1::>>$', '$<<second_level 2::template 2::>>$']), 3377 3378 # this will produce a mismatch, due to illegal nesting of same-level placeholders 3379 ('returns illegal match: junk $<<second_level::$<<insert::insert_template::0>>$::10-12>>$ junk', ['$<<second_level::$<<insert::insert_template::0>>$::10-12>>$']), 3380 3381 ], 3382 third_pass_placeholder_regex: [ 3383 # different lengths/regions 3384 ('junk $<<<third_level::template::>>>$ junk', ['$<<<third_level::template::>>>$']), 3385 ('junk $<<<third_level::template::10>>>$ junk', ['$<<<third_level::template::10>>>$']), 3386 ('junk $<<<third_level::template::10-12>>>$ junk', ['$<<<third_level::template::10-12>>>$']), 3387 3388 # inside is other-level: 3389 ('junk $<<<third_level::$<<insert::insert_template::0>>$::10-12>>>$ junk', ['$<<<third_level::$<<insert::insert_template::0>>$::10-12>>>$']), 3390 ('junk $<<<third_level::$<insert::insert_template::0>$::10-12>>>$ junk', ['$<<<third_level::$<insert::insert_template::0>$::10-12>>>$']), 3391 3392 # outside is other-level: 3393 ('junk $<<second_level::$<<<insert::insert_template::0>>>$::10-12>>$ junk', ['$<<<insert::insert_template::0>>>$']), 3394 ('junk $<first_level::$<<<insert::insert_template::0>>>$::10-12>$ junk', ['$<<<insert::insert_template::0>>>$']), 3395 3396 # other level on same line 3397 ('junk $<first_level 1::template 1::>$ junk $<<<third_level 2::template 2::>>>$ junk', ['$<<<third_level 2::template 2::>>>$']), 3398 ('junk $<<second_level 1::template 1::>>$ junk $<<<third_level 2::template 2::>>>$ junk', ['$<<<third_level 2::template 2::>>>$']), 3399 3400 # this will produce a mismatch, due to illegal nesting of same-level placeholders 3401 ('returns illegal match: junk $<<<third_level::$<<<insert::insert_template::0>>>$::10-12>>>$ junk', ['$<<<third_level::$<<<insert::insert_template::0>>>$::10-12>>>$']), 3402 ] 3403 } 3404 3405 for pattern in [first_pass_placeholder_regex, second_pass_placeholder_regex, third_pass_placeholder_regex]: 3406 print("") 3407 print("-----------------------------") 3408 print("regex:", pattern) 3409 tests = all_tests[pattern] 3410 for t in tests: 3411 line, expected_results = t 3412 phs = regex.findall(pattern, line, regex.IGNORECASE) 3413 if len(phs) > 0: 3414 if phs == expected_results: 3415 continue 3416 3417 print("") 3418 print("failed") 3419 print("line:", line) 3420 3421 if len(phs) == 0: 3422 print("no match") 3423 continue 3424 3425 if len(phs) > 1: 3426 print("several matches") 3427 for r in expected_results: 3428 print("expected:", r) 3429 for p in phs: 3430 print("found:", p) 3431 continue 3432 3433 print("unexpected match") 3434 print("expected:", expected_results) 3435 print("found: ", phs)
3436 3437 #--------------------------------------------------------
3438 - def test_placeholder():
3439 3440 phs = [ 3441 #u'emr_journal::soapu //%(clin_when)s %(modified_by)s %(soap_cat)s %(narrative)s//1000 days::', 3442 #u'free_text::placeholder test//preset::9999', 3443 #u'soap_for_encounters:://::9999', 3444 #u'soap_p', 3445 #u'encounter_list::%(started)s: %(assessment_of_encounter)s::30', 3446 #u'patient_comm::homephone::1234', 3447 #u'$<patient_address::work::1234>$', 3448 #u'adr_region::home::1234', 3449 #u'adr_country::fehlt::1234', 3450 #u'adr_subunit::fehlt::1234', 3451 #u'adr_suburb::fehlt-auch::1234', 3452 #u'external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 3453 #u'primary_praxis_provider', 3454 #u'current_provider::::3-5', 3455 #u'current_provider_external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 3456 #u'current_provider_external_id::LANR//LÄK::1234' 3457 #u'$<current_provider_external_id::KV-LANR//KV::1234>$' 3458 #u'primary_praxis_provider_external_id::LANR//LÄK::1234' 3459 #u'form_name_long::::1234', 3460 #u'form_name_long::::5', 3461 #u'form_name_long::::', 3462 #u'form_version::::5', 3463 #u'$<current_meds::\item %(product)s %(preparation)s (%(substance)s) from %(started)s for %(duration)s as %(schedule)s until %(discontinued)s\\n::250>$', 3464 #u'$<vaccination_history::%(date_given)s: %(vaccine)s [%(batch_no)s] %(l10n_indications)s::250>$', 3465 #u'$<date_of_birth::%Y %B %d::20>$', 3466 #u'$<date_of_birth::%Y %B %d::>$', 3467 #u'$<date_of_birth::::20>$', 3468 #u'$<date_of_birth::::>$', 3469 #u'$<patient_tags::Tag "%(l10n_description)s": %(comment)s//\\n- ::250>$', 3470 #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>$', 3471 #u'$<patient_photo::\includegraphics[width=60mm]{%s}//image/png//.png::250>$', 3472 #u'$<data_snippet::binary_test_snippet//path=<%s>//image/png//.png::250>$', 3473 #u'$<data_snippet::autograph-LMcC//path=<%s>//image/jpg//.jpg::250>$', 3474 #u'$<current_meds::%s ($<lastname::::50>$)//select::>$', 3475 #u'$<current_meds::%s//select::>$', 3476 #u'$<soap_by_issue::soapu //%Y %b %d//%(narrative)s::1000>$', 3477 #u'$<soap_by_episode::soapu //%Y %b %d//%(narrative)s::1000>$', 3478 #u'$<documents::select//description//document %(clin_when)s: %(l10n_type)s// file: %(fullpath)s (<some path>/%(name)s)//~/gnumed/export/::>$', 3479 #u'$<soap::soapu //%s::9999>$', 3480 #u'$<soap::soapu //%(soap_cat)s: %(date)s | %(provider)s | %(narrative)s::9999>$' 3481 #u'$<test_results:://%c::>$' 3482 #u'$<test_results::%(unified_abbrev)s: %(unified_val)s %(val_unit)s//%c::>$' 3483 #u'$<reminders:://::>$' 3484 #u'$<current_meds_for_rx::%(product)s (%(contains)s): dispense %(amount2dispense)s ::>$' 3485 #u'$<praxis::%(branch)s (%(praxis)s)::>$' 3486 #u'$<praxis_address::::120>$' 3487 #u'$<praxis_id::::120>$' 3488 #u'$<gen_adr_street::Street = %s//Wählen Sie die Empfängeradresse !::120>$', u'$<gen_adr_location::Ort = %s::120>$', u'$<gen_adr_country::::120>$' 3489 3490 #u'$<receiver_name::%s::120>$', 3491 #u'$<receiver_street::%s//a::120>$', 3492 #u'$<receiver_number:: %s//a::120>$', 3493 #u'$<receiver_subunit:: %s::120>$', 3494 #u'$<receiver_postcode::%s//b::120>$', 3495 #u'$<receiver_location:: %s::120>$', 3496 #u'$<receiver_country::, %s::120>$', 3497 #u'$<external_care::%(issue)s: %(provider)s of %(unit)s@%(organization)s (%(comment)s)::1024>$', 3498 #u'$<url_escape::hello world ü::>$', 3499 #u'$<substance_abuse::%(substance)s (%(harmful_use_type)s) last=%(last_checked_when)s stop=%(discontinued)s // %(notes)s::>$', 3500 #u'bill_adr_region::region %s::1234', 3501 #u'bill_adr_country::%s::1234', 3502 #u'bill_adr_subunit::subunit: %s::1234', 3503 #u'bill_adr_suburb::-> %s::1234', 3504 #u'bill_adr_street::::1234', 3505 #u'bill_adr_number::%s::1234', 3506 #u'$<diagnoses::\listitem %s::>$' 3507 #u'$<patient_mcf::fmt=txt//card=%s::>$', 3508 #u'$<patient_mcf::fmt=mcf//mcf=%s::>$', 3509 #u'$<patient_mcf::fmt=qr//png=%s::>$' 3510 #u'$<praxis_scan2pay::fmt=txt::>$', 3511 #u'$<praxis_scan2pay::fmt=qr::>$' 3512 u'$<bill_scan2pay::fmt=txt::>$', 3513 u'$<bill_scan2pay::fmt=qr::>$' 3514 ] 3515 3516 handler = gmPlaceholderHandler() 3517 handler.debug = True 3518 3519 gmStaff.set_current_provider_to_logged_on_user() 3520 gmPraxisWidgets.set_active_praxis_branch(no_parent = True) 3521 pat = gmPersonSearch.ask_for_patient() 3522 if pat is None: 3523 return 3524 gmPatSearchWidgets.set_active_patient(patient = pat) 3525 3526 #app = wx.PyWidgetTester(size = (200, 50)) 3527 #handler.set_placeholder('form_name_long', 'ein Testformular') 3528 for ph in phs: 3529 print(ph) 3530 print(" result:") 3531 print(' %s' % handler[ph])
3532 #handler.unset_placeholder('form_name_long') 3533 3534 #--------------------------------------------------------
3535 - def test():
3536 pat = gmPersonSearch.ask_for_patient() 3537 if pat is None: 3538 sys.exit() 3539 gmPerson.set_active_patient(patient = pat) 3540 from Gnumed.wxpython import gmMedicationWidgets 3541 gmMedicationWidgets.manage_substance_intakes()
3542 3543 #--------------------------------------------------------
3544 - def test_show_phs():
3545 show_placeholders()
3546 3547 #-------------------------------------------------------- 3548 3549 app = wx.App() 3550 3551 #test_placeholders() 3552 #test_new_variant_placeholders() 3553 #test_scripting() 3554 #test_placeholder_regex() 3555 #test() 3556 test_placeholder() 3557 #test_show_phs() 3558