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 if parts[2].strip() != '': 2465 target_mime = parts[2].strip() 2466 if len(parts) > 3: 2467 if parts[3].strip() != '': 2468 target_ext = parts[3].strip() 2469 2470 expansion = gmKeywordExpansion.get_expansion ( 2471 keyword = keyword, 2472 textual_only = False, 2473 binary_only = True 2474 ) 2475 if expansion is None: 2476 if self.debug: 2477 return self._escape(_('no binary expansion found for keyword <%s>') % keyword) 2478 return '' 2479 2480 saved_fname = expansion.save_to_file() 2481 if saved_fname is None: 2482 if self.debug: 2483 return self._escape(_('cannot export data of binary expansion keyword <%s>') % keyword) 2484 return '' 2485 2486 if expansion['is_encrypted']: 2487 pwd = wx.GetPasswordFromUser ( 2488 message = _('Enter your GnuPG passphrase for decryption of [%s]') % expansion['keyword'], 2489 caption = _('GnuPG passphrase prompt'), 2490 default_value = '' 2491 ) 2492 saved_fname = gmCrypto.gpg_decrypt_file(filename = saved_fname, passphrase = pwd) 2493 if saved_fname is None: 2494 if self.debug: 2495 return self._escape(_('cannot decrypt data of binary expansion keyword <%s>') % keyword) 2496 return '' 2497 2498 if target_mime is None: 2499 return template % saved_fname 2500 2501 converted_fname = gmMimeLib.convert_file(filename = saved_fname, target_mime = target_mime, target_extension = target_ext) 2502 if converted_fname is None: 2503 if self.debug: 2504 return self._escape(_('cannot convert data of binary expansion keyword <%s>') % keyword) 2505 # hoping that the target can cope: 2506 return template % saved_fname 2507 2508 return template % converted_fname
2509 2510 #--------------------------------------------------------
2511 - def _get_variant_qrcode(self, data=None):
2512 options = data.split(self.__args_divider) 2513 if len(options) == 0: 2514 return None 2515 text4qr = options[0] 2516 if len(options) > 1: 2517 template = options[1] 2518 else: 2519 template = u'%s' 2520 qr_filename = gmTools.create_qrcode(text = text4qr) 2521 if qr_filename is None: 2522 return self._escape('cannot_create_QR_code') 2523 2524 return template % qr_filename
2525 2526 #--------------------------------------------------------
2527 - def _get_variant_range_of(self, data=None):
2528 if data is None: 2529 return None 2530 # wrapper code already takes care of actually 2531 # selecting the range so all we need to do here 2532 # is to return the data itself 2533 return data
2534 2535 #--------------------------------------------------------
2536 - def _get_variant_if_not_empty(self, data=None):
2537 if data is None: 2538 return None 2539 2540 parts = data.split(self.__args_divider) 2541 if len(parts) < 3: 2542 return 'IF_NOT_EMPTY lacks <instead> definition' 2543 txt = parts[0] 2544 template = parts[1] 2545 instead = parts[2] 2546 2547 if txt.strip() == '': 2548 return instead 2549 if '%s' in template: 2550 return template % txt 2551 return template
2552 2553 #--------------------------------------------------------
2554 - def _get_variant_if_debugging(self, data=None):
2555 2556 if data is None: 2557 return None 2558 parts = data.split(self.__args_divider) 2559 if len(parts) < 2: 2560 return self._escape(u'IF_DEBUGGING lacks proper definition') 2561 debug_str = parts[0] 2562 non_debug_str = parts[1] 2563 if self.debug: 2564 return debug_str 2565 return non_debug_str
2566 2567 #--------------------------------------------------------
2568 - def _get_variant_free_text(self, data=None):
2569 2570 if data is None: 2571 parts = [] 2572 msg = _('generic text') 2573 cache_key = 'free_text::%s' % datetime.datetime.now() 2574 else: 2575 parts = data.split(self.__args_divider) 2576 msg = parts[0] 2577 cache_key = 'free_text::%s' % msg 2578 2579 try: 2580 return self.__cache[cache_key] 2581 except KeyError: 2582 pass 2583 2584 if len(parts) > 1: 2585 preset = parts[1] 2586 else: 2587 preset = '' 2588 2589 dlg = gmGuiHelpers.cMultilineTextEntryDlg ( 2590 None, 2591 -1, 2592 title = _('Replacing <free_text> placeholder'), 2593 msg = _('Below you can enter free text.\n\n [%s]') % msg, 2594 text = preset 2595 ) 2596 dlg.enable_user_formatting = True 2597 decision = dlg.ShowModal() 2598 text = dlg.value.strip() 2599 is_user_formatted = dlg.is_user_formatted 2600 dlg.DestroyLater() 2601 2602 if decision != wx.ID_SAVE: 2603 if self.debug: 2604 return self._escape(_('Text input cancelled by user.')) 2605 return self._escape('') 2606 2607 # user knows "best" 2608 if is_user_formatted: 2609 self.__cache[cache_key] = text 2610 return text 2611 2612 text = self._escape(text) 2613 self.__cache[cache_key] = text 2614 return text
2615 2616 #--------------------------------------------------------
2617 - def _get_variant_bill(self, data=None):
2618 try: 2619 bill = self.__cache['bill'] 2620 except KeyError: 2621 from Gnumed.wxpython import gmBillingWidgets 2622 bill = gmBillingWidgets.manage_bills(patient = self.pat) 2623 if bill is None: 2624 if self.debug: 2625 return self._escape(_('no bill selected')) 2626 return '' 2627 self.__cache['bill'] = bill 2628 2629 parts = data.split(self.__args_divider) 2630 template = parts[0] 2631 if len(parts) > 1: 2632 date_format = parts[1] 2633 else: 2634 date_format = '%Y %B %d' 2635 2636 return template % bill.fields_as_dict(date_format = date_format, escape_style = self.__esc_style)
2637 2638 #--------------------------------------------------------
2639 - def _get_variant_bill_scan2pay(self, data=None):
2640 try: 2641 bill = self.__cache['bill'] 2642 except KeyError: 2643 from Gnumed.wxpython import gmBillingWidgets 2644 bill = gmBillingWidgets.manage_bills(patient = self.pat) 2645 if bill is None: 2646 if self.debug: 2647 return self._escape(_('no bill selected')) 2648 return '' 2649 self.__cache['bill'] = bill 2650 2651 format = 'qr' 2652 options = data.split(self.__args_divider) 2653 _log.debug('options: %s', options) 2654 for o in options: 2655 if o.strip().startswith('fmt='): 2656 format = o.strip()[4:] 2657 if format not in ['qr', 'txt']: 2658 return self._escape(_('praxis_scan2pay: invalid format (qr/txt)')) 2659 continue 2660 _log.debug('format: %s' % format) 2661 2662 from Gnumed.business import gmBilling 2663 data_str = gmBilling.get_scan2pay_data ( 2664 gmPraxis.gmCurrentPraxisBranch(), 2665 bill, 2666 provider = gmStaff.gmCurrentProvider() 2667 ) 2668 if data_str is None: 2669 if self.debug: 2670 return self._escape('bill_scan2pay-cannot_create_data_file') 2671 return '' 2672 2673 if format == 'txt': 2674 return self._escape(data_str) 2675 2676 if format == 'qr': 2677 qr_filename = gmTools.create_qrcode(text = data_str) 2678 if qr_filename is not None: 2679 return qr_filename 2680 if self.debug: 2681 return self._escape('bill_scan2pay-cannot_create_QR_code') 2682 return '' 2683 2684 return None
2685 2686 #--------------------------------------------------------
2687 - def _get_variant_bill_item(self, data=None):
2688 try: 2689 bill = self.__cache['bill'] 2690 except KeyError: 2691 from Gnumed.wxpython import gmBillingWidgets 2692 bill = gmBillingWidgets.manage_bills(patient = self.pat) 2693 if bill is None: 2694 if self.debug: 2695 return self._escape(_('no bill selected')) 2696 return '' 2697 self.__cache['bill'] = bill 2698 2699 parts = data.split(self.__args_divider) 2700 template = parts[0] 2701 if len(parts) > 1: 2702 date_format = parts[1] 2703 else: 2704 date_format = '%Y %B %d' 2705 2706 return '\n'.join([ template % i.fields_as_dict(date_format = date_format, escape_style = self.__esc_style) for i in bill.bill_items ])
2707 2708 #--------------------------------------------------------
2709 - def __get_variant_bill_adr_part(self, data=None, part=None):
2710 try: 2711 bill = self.__cache['bill'] 2712 except KeyError: 2713 from Gnumed.wxpython import gmBillingWidgets 2714 bill = gmBillingWidgets.manage_bills(patient = self.pat) 2715 if bill is None: 2716 if self.debug: 2717 return self._escape(_('no bill selected')) 2718 return '' 2719 self.__cache['bill'] = bill 2720 self.__cache['bill-adr'] = bill.address 2721 2722 try: 2723 bill_adr = self.__cache['bill-adr'] 2724 except KeyError: 2725 bill_adr = bill.address 2726 self.__cache['bill-adr'] = bill_adr 2727 2728 if bill_adr is None: 2729 if self.debug: 2730 return self._escape(_('[%s] bill has no address') % part) 2731 return '' 2732 2733 if bill_adr[part] is None: 2734 return self._escape('') 2735 2736 if data is None: 2737 return self._escape(bill_adr[part]) 2738 2739 if data == '': 2740 return self._escape(bill_adr[part]) 2741 2742 return data % self._escape(bill_adr[part])
2743 2744 #--------------------------------------------------------
2745 - def _get_variant_bill_adr_street(self, data='?'):
2746 return self.__get_variant_bill_adr_part(data = data, part = 'street')
2747 2748 #--------------------------------------------------------
2749 - def _get_variant_bill_adr_number(self, data='?'):
2750 return self.__get_variant_bill_adr_part(data = data, part = 'number')
2751 2752 #--------------------------------------------------------
2753 - def _get_variant_bill_adr_subunit(self, data='?'):
2754 return self.__get_variant_bill_adr_part(data = data, part = 'subunit')
2755 #--------------------------------------------------------
2756 - def _get_variant_bill_adr_location(self, data='?'):
2757 return self.__get_variant_bill_adr_part(data = data, part = 'urb')
2758 2759 #--------------------------------------------------------
2760 - def _get_variant_bill_adr_suburb(self, data='?'):
2761 return self.__get_variant_bill_adr_part(data = data, part = 'suburb')
2762 2763 #--------------------------------------------------------
2764 - def _get_variant_bill_adr_postcode(self, data='?'):
2765 return self.__get_variant_bill_adr_part(data = data, part = 'postcode')
2766 2767 #--------------------------------------------------------
2768 - def _get_variant_bill_adr_region(self, data='?'):
2769 return self.__get_variant_bill_adr_part(data = data, part = 'l10n_region')
2770 2771 #--------------------------------------------------------
2772 - def _get_variant_bill_adr_country(self, data='?'):
2773 return self.__get_variant_bill_adr_part(data = data, part = 'l10n_country')
2774 2775 #-------------------------------------------------------- 2776 # internal helpers 2777 #--------------------------------------------------------
2778 - def _escape(self, text=None):
2779 if self.__esc_func is None: 2780 return text 2781 assert (text is not None), 'text=None passed to _escape()' 2782 return self.__esc_func(text)
2783 2784 #--------------------------------------------------------
2785 - def _escape_dict(self, the_dict=None, date_format='%Y %b %d %H:%M', none_string='', bool_strings=None):
2786 if bool_strings is None: 2787 bools = {True: _('true'), False: _('false')} 2788 else: 2789 bools = {True: bool_strings[0], False: bool_strings[1]} 2790 data = {} 2791 for field in the_dict.keys(): 2792 # FIXME: harden against BYTEA fields 2793 #if type(self._payload[self._idx[field]]) == ... 2794 # data[field] = _('<%s bytes of binary data>') % len(self._payload[self._idx[field]]) 2795 # continue 2796 val = the_dict[field] 2797 if val is None: 2798 data[field] = none_string 2799 continue 2800 if isinstance(val, bool): 2801 data[field] = bools[val] 2802 continue 2803 if isinstance(val, datetime.datetime): 2804 data[field] = gmDateTime.pydt_strftime(val, format = date_format) 2805 if self.__esc_style in ['latex', 'tex']: 2806 data[field] = gmTools.tex_escape_string(data[field]) 2807 elif self.__esc_style in ['xetex', 'xelatex']: 2808 data[field] = gmTools.xetex_escape_string(data[field]) 2809 continue 2810 try: 2811 data[field] = str(val, encoding = 'utf8', errors = 'replace') 2812 except TypeError: 2813 try: 2814 data[field] = str(val) 2815 except (UnicodeDecodeError, TypeError): 2816 val = '%s' % str(val) 2817 data[field] = val.decode('utf8', 'replace') 2818 if self.__esc_style in ['latex', 'tex']: 2819 data[field] = gmTools.tex_escape_string(data[field]) 2820 elif self.__esc_style in ['xetex', 'xelatex']: 2821 data[field] = gmTools.xetex_escape_string(data[field]) 2822 return data
2823 2824 #---------------------------------------------------------------------
2825 -def test_placeholders():
2826 2827 _log.debug('testing for placeholders with pattern: %s', first_pass_placeholder_regex) 2828 2829 data_source = gmPlaceholderHandler() 2830 original_line = '' 2831 2832 while True: 2833 # get input from user 2834 line = wx.GetTextFromUser ( 2835 _('Enter some text containing a placeholder:'), 2836 _('Testing placeholders'), 2837 centre = True, 2838 default_value = original_line 2839 ) 2840 if line.strip() == '': 2841 break 2842 original_line = line 2843 # replace 2844 placeholders_in_line = regex.findall(first_pass_placeholder_regex, line, regex.IGNORECASE) 2845 if len(placeholders_in_line) == 0: 2846 continue 2847 for placeholder in placeholders_in_line: 2848 try: 2849 val = data_source[placeholder] 2850 except: 2851 val = _('error with placeholder [%s]') % placeholder 2852 if val is None: 2853 val = _('error with placeholder [%s]') % placeholder 2854 line = line.replace(placeholder, val) 2855 # show 2856 msg = _( 2857 'Input: %s\n' 2858 '\n' 2859 'Output:\n' 2860 '%s' 2861 ) % ( 2862 original_line, 2863 line 2864 ) 2865 gmGuiHelpers.gm_show_info ( 2866 title = _('Testing placeholders'), 2867 info = msg 2868 )
2869 2870 #=====================================================================
2871 -class cMacroPrimitives:
2872 """Functions a macro can legally use. 2873 2874 An instance of this class is passed to the GNUmed scripting 2875 listener. Hence, all actions a macro can legally take must 2876 be defined in this class. Thus we achieve some screening for 2877 security and also thread safety handling. 2878 """ 2879 #-----------------------------------------------------------------
2880 - def __init__(self, personality = None):
2881 if personality is None: 2882 raise gmExceptions.ConstructorError('must specify personality') 2883 self.__personality = personality 2884 self.__attached = 0 2885 self._get_source_personality = None 2886 self.__user_done = False 2887 self.__user_answer = 'no answer yet' 2888 self.__pat = gmPerson.gmCurrentPatient() 2889 2890 self.__auth_cookie = str(random.random()) 2891 self.__pat_lock_cookie = str(random.random()) 2892 self.__lock_after_load_cookie = str(random.random()) 2893 2894 _log.info('slave mode personality is [%s]', personality)
2895 #----------------------------------------------------------------- 2896 # public API 2897 #-----------------------------------------------------------------
2898 - def attach(self, personality = None):
2899 if self.__attached: 2900 _log.error('attach with [%s] rejected, already serving a client', personality) 2901 return (0, _('attach rejected, already serving a client')) 2902 if personality != self.__personality: 2903 _log.error('rejecting attach to personality [%s], only servicing [%s]' % (personality, self.__personality)) 2904 return (0, _('attach to personality [%s] rejected') % personality) 2905 self.__attached = 1 2906 self.__auth_cookie = str(random.random()) 2907 return (1, self.__auth_cookie)
2908 #-----------------------------------------------------------------
2909 - def detach(self, auth_cookie=None):
2910 if not self.__attached: 2911 return 1 2912 if auth_cookie != self.__auth_cookie: 2913 _log.error('rejecting detach() with cookie [%s]' % auth_cookie) 2914 return 0 2915 self.__attached = 0 2916 return 1
2917 #-----------------------------------------------------------------
2918 - def force_detach(self):
2919 if not self.__attached: 2920 return 1 2921 self.__user_done = False 2922 # FIXME: use self.__sync_cookie for syncing with user interaction 2923 wx.CallAfter(self._force_detach) 2924 return 1
2925 #-----------------------------------------------------------------
2926 - def version(self):
2927 ver = _cfg.get(option = 'client_version') 2928 return "GNUmed %s, %s $Revision: 1.51 $" % (ver, self.__class__.__name__)
2929 #-----------------------------------------------------------------
2930 - def shutdown_gnumed(self, auth_cookie=None, forced=False):
2931 """Shuts down this client instance.""" 2932 if not self.__attached: 2933 return 0 2934 if auth_cookie != self.__auth_cookie: 2935 _log.error('non-authenticated shutdown_gnumed()') 2936 return 0 2937 wx.CallAfter(self._shutdown_gnumed, forced) 2938 return 1
2939 #-----------------------------------------------------------------
2940 - def raise_gnumed(self, auth_cookie = None):
2941 """Raise ourselves to the top of the desktop.""" 2942 if not self.__attached: 2943 return 0 2944 if auth_cookie != self.__auth_cookie: 2945 _log.error('non-authenticated raise_gnumed()') 2946 return 0 2947 return "cMacroPrimitives.raise_gnumed() not implemented"
2948 #-----------------------------------------------------------------
2949 - def get_loaded_plugins(self, auth_cookie = None):
2950 if not self.__attached: 2951 return 0 2952 if auth_cookie != self.__auth_cookie: 2953 _log.error('non-authenticated get_loaded_plugins()') 2954 return 0 2955 gb = gmGuiBroker.GuiBroker() 2956 return gb['horstspace.notebook.gui'].keys()
2957 #-----------------------------------------------------------------
2958 - def raise_notebook_plugin(self, auth_cookie = None, a_plugin = None):
2959 """Raise a notebook plugin within GNUmed.""" 2960 if not self.__attached: 2961 return 0 2962 if auth_cookie != self.__auth_cookie: 2963 _log.error('non-authenticated raise_notebook_plugin()') 2964 return 0 2965 # FIXME: use semaphore 2966 wx.CallAfter(gmPlugin.raise_notebook_plugin, a_plugin) 2967 return 1
2968 #-----------------------------------------------------------------
2969 - def load_patient_from_external_source(self, auth_cookie = None):
2970 """Load external patient, perhaps create it. 2971 2972 Callers must use get_user_answer() to get status information. 2973 It is unsafe to proceed without knowing the completion state as 2974 the controlled client may be waiting for user input from a 2975 patient selection list. 2976 """ 2977 if not self.__attached: 2978 return (0, _('request rejected, you are not attach()ed')) 2979 if auth_cookie != self.__auth_cookie: 2980 _log.error('non-authenticated load_patient_from_external_source()') 2981 return (0, _('rejected load_patient_from_external_source(), not authenticated')) 2982 if self.__pat.locked: 2983 _log.error('patient is locked, cannot load from external source') 2984 return (0, _('current patient is locked')) 2985 self.__user_done = False 2986 wx.CallAfter(self._load_patient_from_external_source) 2987 self.__lock_after_load_cookie = str(random.random()) 2988 return (1, self.__lock_after_load_cookie)
2989 #-----------------------------------------------------------------
2990 - def lock_loaded_patient(self, auth_cookie = None, lock_after_load_cookie = None):
2991 if not self.__attached: 2992 return (0, _('request rejected, you are not attach()ed')) 2993 if auth_cookie != self.__auth_cookie: 2994 _log.error('non-authenticated lock_load_patient()') 2995 return (0, _('rejected lock_load_patient(), not authenticated')) 2996 # FIXME: ask user what to do about wrong cookie 2997 if lock_after_load_cookie != self.__lock_after_load_cookie: 2998 _log.warning('patient lock-after-load request rejected due to wrong cookie [%s]' % lock_after_load_cookie) 2999 return (0, 'patient lock-after-load request rejected, wrong cookie provided') 3000 self.__pat.locked = True 3001 self.__pat_lock_cookie = str(random.random()) 3002 return (1, self.__pat_lock_cookie)
3003 #-----------------------------------------------------------------
3004 - def lock_into_patient(self, auth_cookie = None, search_params = None):
3005 if not self.__attached: 3006 return (0, _('request rejected, you are not attach()ed')) 3007 if auth_cookie != self.__auth_cookie: 3008 _log.error('non-authenticated lock_into_patient()') 3009 return (0, _('rejected lock_into_patient(), not authenticated')) 3010 if self.__pat.locked: 3011 _log.error('patient is already locked') 3012 return (0, _('already locked into a patient')) 3013 searcher = gmPersonSearch.cPatientSearcher_SQL() 3014 if type(search_params) == dict: 3015 idents = searcher.get_identities(search_dict=search_params) 3016 raise Exception("must use dto, not search_dict") 3017 else: 3018 idents = searcher.get_identities(search_term=search_params) 3019 if idents is None: 3020 return (0, _('error searching for patient with [%s]/%s') % (search_term, search_dict)) 3021 if len(idents) == 0: 3022 return (0, _('no patient found for [%s]/%s') % (search_term, search_dict)) 3023 # FIXME: let user select patient 3024 if len(idents) > 1: 3025 return (0, _('several matching patients found for [%s]/%s') % (search_term, search_dict)) 3026 if not gmPatSearchWidgets.set_active_patient(patient = idents[0]): 3027 return (0, _('cannot activate patient [%s] (%s/%s)') % (str(idents[0]), search_term, search_dict)) 3028 self.__pat.locked = True 3029 self.__pat_lock_cookie = str(random.random()) 3030 return (1, self.__pat_lock_cookie)
3031 #-----------------------------------------------------------------
3032 - def unlock_patient(self, auth_cookie = None, unlock_cookie = None):
3033 if not self.__attached: 3034 return (0, _('request rejected, you are not attach()ed')) 3035 if auth_cookie != self.__auth_cookie: 3036 _log.error('non-authenticated unlock_patient()') 3037 return (0, _('rejected unlock_patient, not authenticated')) 3038 # we ain't locked anyways, so succeed 3039 if not self.__pat.locked: 3040 return (1, '') 3041 # FIXME: ask user what to do about wrong cookie 3042 if unlock_cookie != self.__pat_lock_cookie: 3043 _log.warning('patient unlock request rejected due to wrong cookie [%s]' % unlock_cookie) 3044 return (0, 'patient unlock request rejected, wrong cookie provided') 3045 self.__pat.locked = False 3046 return (1, '')
3047 #-----------------------------------------------------------------
3048 - def assume_staff_identity(self, auth_cookie = None, staff_name = "Dr.Jekyll", staff_creds = None):
3049 if not self.__attached: 3050 return 0 3051 if auth_cookie != self.__auth_cookie: 3052 _log.error('non-authenticated select_identity()') 3053 return 0 3054 return "cMacroPrimitives.assume_staff_identity() not implemented"
3055 #-----------------------------------------------------------------
3056 - def get_user_answer(self):
3057 if not self.__user_done: 3058 return (0, 'still waiting') 3059 self.__user_done = False 3060 return (1, self.__user_answer)
3061 #----------------------------------------------------------------- 3062 # internal API 3063 #-----------------------------------------------------------------
3064 - def _force_detach(self):
3065 msg = _( 3066 'Someone tries to forcibly break the existing\n' 3067 'controlling connection. This may or may not\n' 3068 'have legitimate reasons.\n\n' 3069 'Do you want to allow breaking the connection ?' 3070 ) 3071 can_break_conn = gmGuiHelpers.gm_show_question ( 3072 aMessage = msg, 3073 aTitle = _('forced detach attempt') 3074 ) 3075 if can_break_conn: 3076 self.__user_answer = 1 3077 else: 3078 self.__user_answer = 0 3079 self.__user_done = True 3080 if can_break_conn: 3081 self.__pat.locked = False 3082 self.__attached = 0 3083 return 1
3084 #-----------------------------------------------------------------
3085 - def _shutdown_gnumed(self, forced=False):
3086 top_win = wx.GetApp().GetTopWindow() 3087 if forced: 3088 top_win.DestroyLater() 3089 else: 3090 top_win.Close()
3091 #-----------------------------------------------------------------
3093 patient = gmPatSearchWidgets.get_person_from_external_sources(search_immediately = True, activate_immediately = True) 3094 if patient is not None: 3095 self.__user_answer = 1 3096 else: 3097 self.__user_answer = 0 3098 self.__user_done = True 3099 return 1
3100 #===================================================================== 3101 # main 3102 #===================================================================== 3103 if __name__ == '__main__': 3104 3105 if len(sys.argv) < 2: 3106 sys.exit() 3107 3108 if sys.argv[1] != 'test': 3109 sys.exit() 3110 3111 gmI18N.activate_locale() 3112 gmI18N.install_domain() 3113 3114 #--------------------------------------------------------
3115 - def test_placeholders():
3116 handler = gmPlaceholderHandler() 3117 handler.debug = True 3118 3119 for placeholder in ['a', 'b']: 3120 print(handler[placeholder]) 3121 3122 pat = gmPersonSearch.ask_for_patient() 3123 if pat is None: 3124 return 3125 3126 gmPatSearchWidgets.set_active_patient(patient = pat) 3127 3128 print('DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d']) 3129 3130 app = wx.PyWidgetTester(size = (200, 50)) 3131 3132 ph = 'progress_notes::ap' 3133 print('%s: %s' % (ph, handler[ph]))
3134 #--------------------------------------------------------
3135 - def test_new_variant_placeholders():
3136 3137 tests = [ 3138 # should work: 3139 '$<lastname>$', 3140 '$<lastname::::3>$', 3141 '$<name::%(title)s %(firstnames)s%(preferred)s%(lastnames)s>$', 3142 3143 # should fail: 3144 'lastname', 3145 '$<lastname', 3146 '$<lastname::', 3147 '$<lastname::>$', 3148 '$<lastname::abc>$', 3149 '$<lastname::abc::>$', 3150 '$<lastname::abc::3>$', 3151 '$<lastname::abc::xyz>$', 3152 '$<lastname::::>$', 3153 '$<lastname::::xyz>$', 3154 3155 '$<date_of_birth::%Y-%m-%d>$', 3156 '$<date_of_birth::%Y-%m-%d::3>$', 3157 '$<date_of_birth::%Y-%m-%d::>$', 3158 3159 # should work: 3160 '$<adr_location::home::35>$', 3161 '$<gender_mapper::male//female//other::5>$', 3162 '$<current_meds::==> %(product)s %(preparation)s (%(substance)s) <==\n::50>$', 3163 '$<allergy_list::%(descriptor)s, >$', 3164 '$<current_meds_table::latex//>$' 3165 3166 # 'firstname', 3167 # 'title', 3168 # 'date_of_birth', 3169 # 'progress_notes', 3170 # 'soap', 3171 # 'soap_s', 3172 # 'soap_o', 3173 # 'soap_a', 3174 # 'soap_p', 3175 3176 # 'soap', 3177 # 'progress_notes', 3178 # 'date_of_birth' 3179 ] 3180 3181 # tests = [ 3182 # '$<latest_vaccs_table::latex>$' 3183 # ] 3184 3185 pat = gmPersonSearch.ask_for_patient() 3186 if pat is None: 3187 return 3188 3189 gmPatSearchWidgets.set_active_patient(patient = pat) 3190 3191 handler = gmPlaceholderHandler() 3192 handler.debug = True 3193 3194 for placeholder in tests: 3195 print(placeholder, "=>", handler[placeholder]) 3196 print("--------------") 3197 input()
3198 3199 # print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d'] 3200 3201 # app = wx.PyWidgetTester(size = (200, 50)) 3202 3203 # ph = 'progress_notes::ap' 3204 # print '%s: %s' % (ph, handler[ph]) 3205 3206 #--------------------------------------------------------
3207 - def test_scripting():
3208 from Gnumed.pycommon import gmScriptingListener 3209 import xmlrpc.client 3210 3211 listener = gmScriptingListener.cScriptingListener(macro_executor = cMacroPrimitives(personality='unit test'), port=9999) 3212 3213 s = xmlrpc.client.ServerProxy('http://localhost:9999') 3214 print("should fail:", s.attach()) 3215 print("should fail:", s.attach('wrong cookie')) 3216 print("should work:", s.version()) 3217 print("should fail:", s.raise_gnumed()) 3218 print("should fail:", s.raise_notebook_plugin('test plugin')) 3219 print("should fail:", s.lock_into_patient('kirk, james')) 3220 print("should fail:", s.unlock_patient()) 3221 status, conn_auth = s.attach('unit test') 3222 print("should work:", status, conn_auth) 3223 print("should work:", s.version()) 3224 print("should work:", s.raise_gnumed(conn_auth)) 3225 status, pat_auth = s.lock_into_patient(conn_auth, 'kirk, james') 3226 print("should work:", status, pat_auth) 3227 print("should fail:", s.unlock_patient(conn_auth, 'bogus patient unlock cookie')) 3228 print("should work", s.unlock_patient(conn_auth, pat_auth)) 3229 data = {'firstname': 'jame', 'lastnames': 'Kirk', 'gender': 'm'} 3230 status, pat_auth = s.lock_into_patient(conn_auth, data) 3231 print("should work:", status, pat_auth) 3232 print("should work", s.unlock_patient(conn_auth, pat_auth)) 3233 print(s.detach('bogus detach cookie')) 3234 print(s.detach(conn_auth)) 3235 del s 3236 3237 listener.shutdown()
3238 #--------------------------------------------------------
3239 - def test_placeholder_regex():
3240 3241 import re as regex 3242 3243 tests = [ 3244 ' $<lastname>$ ', 3245 ' $<lastname::::3>$ ', 3246 3247 # should fail: 3248 '$<date_of_birth::%Y-%m-%d>$', 3249 '$<date_of_birth::%Y-%m-%d::3>$', 3250 '$<date_of_birth::%Y-%m-%d::>$', 3251 3252 '$<adr_location::home::35>$', 3253 '$<gender_mapper::male//female//other::5>$', 3254 '$<current_meds::==> %(product)s %(preparation)s (%(substance)s) <==\\n::50>$', 3255 '$<allergy_list::%(descriptor)s, >$', 3256 3257 '\\noindent Patient: $<lastname>$, $<firstname>$', 3258 '$<allergies::%(descriptor)s & %(l10n_type)s & {\\footnotesize %(reaction)s} \tabularnewline \hline >$', 3259 '$<current_meds:: \item[%(substance)s] {\\footnotesize (%(product)s)} %(preparation)s %(amount)s%(unit)s: %(schedule)s >$' 3260 ] 3261 3262 tests = [ 3263 3264 'junk $<lastname::::3>$ junk', 3265 'junk $<lastname::abc::3>$ junk', 3266 'junk $<lastname::abc>$ junk', 3267 'junk $<lastname>$ junk', 3268 3269 'junk $<lastname>$ junk $<firstname>$ junk', 3270 'junk $<lastname::abc>$ junk $<fiststname::abc>$ junk', 3271 'junk $<lastname::abc::3>$ junk $<firstname::abc::3>$ junk', 3272 'junk $<lastname::::3>$ junk $<firstname::::3>$ junk' 3273 3274 ] 3275 3276 tests = [ 3277 # u'junk $<<<date_of_birth::%Y %B %d $<inner placeholder::%Y %B %d::20>$::20>>>$ junk', 3278 # u'junk $<date_of_birth::%Y %B %d::20>$ $<date_of_birth::%Y %B %d::20>$', 3279 # u'junk $<date_of_birth::%Y %B %d::>$ $<date_of_birth::%Y %B %d::20>$ $<<date_of_birth::%Y %B %d::20>>$', 3280 # u'junk $<date_of_birth::::20>$', 3281 # u'junk $<date_of_birth::::>$', 3282 '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', 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::250>>>$ 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::3-4>>>$ junk', 3285 3286 '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', 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::3->>>$ 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::-4>>>$ should fail', 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::should_fail>>>$ junk', 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-4>>>$ 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::3-should_fail>>>$ 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::should_fail-should_fail>>>$ junk', 3295 ] 3296 3297 tests = [ 3298 'junk $<<<should pass::template::>>>$ junk', 3299 'junk $<<<should pass::template::10>>>$ junk', 3300 'junk $<<<should pass::template::10-20>>>$ junk', 3301 'junk $<<<should pass::template $<<dummy::template 2::10>>$::>>>$ junk', 3302 'junk $<<<should pass::template $<dummy::template 2::10>$::>>>$ junk', 3303 3304 'junk $<<<should pass::template::>>>$ junk $<<<should pass 2::template 2::>>>$ junk', 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 3308 'junk $<<<should fail::template $<<<dummy::template 2::10>>>$::>>>$ junk', 3309 3310 'junk $<<<should fail::template::10->>>$ junk', 3311 'junk $<<<should fail::template::10->>>$ junk', 3312 'junk $<<<should fail::template::10->>>$ junk', 3313 'junk $<<<should fail::template::10->>>$ junk', 3314 'junk $<first_pass::junk $<<<3rd_pass::template::20>>>$ junk::8-10>$ junk' 3315 ] 3316 3317 #print "testing placeholder regex:", first_pass_placeholder_regex 3318 ##print "testing placeholder regex:", second_pass_placeholder_regex 3319 ##print "testing placeholder regex:", third_pass_placeholder_regex 3320 #print "" 3321 #for t in tests: 3322 # print 'line: "%s"' % t 3323 # phs = regex.findall(first_pass_placeholder_regex, t, regex.IGNORECASE) 3324 # #phs = regex.findall(second_pass_placeholder_regex, t, regex.IGNORECASE) 3325 # #phs = regex.findall(third_pass_placeholder_regex, t, regex.IGNORECASE) 3326 # print " %s placeholders:" % len(phs) 3327 # for p in phs: 3328 # print ' => ', p 3329 # print " " 3330 3331 all_tests = { 3332 first_pass_placeholder_regex: [ 3333 # different lengths/regions 3334 ('junk $<first_level::template::>$ junk', ['$<first_level::template::>$']), 3335 ('junk $<first_level::template::10>$ junk', ['$<first_level::template::10>$']), 3336 ('junk $<first_level::template::10-12>$ junk', ['$<first_level::template::10-12>$']), 3337 3338 # inside is other-level: 3339 ('junk $<first_level::$<<insert::insert_template::0>>$::10-12>$ junk', ['$<first_level::$<<insert::insert_template::0>>$::10-12>$']), 3340 ('junk $<first_level::$<<<insert::insert_template::0>>>$::10-12>$ junk', ['$<first_level::$<<<insert::insert_template::0>>>$::10-12>$']), 3341 3342 # outside is other-level: 3343 ('junk $<<second_level::$<insert::insert_template::0>$::10-12>>$ junk', ['$<insert::insert_template::0>$']), 3344 ('junk $<<<third_level::$<insert::insert_template::0>$::10-12>>>$ junk', ['$<insert::insert_template::0>$']), 3345 3346 # other level on same line 3347 ('junk $<first_level 1::template 1::>$ junk $<<second_level 2::template 2::>>$ junk', ['$<first_level 1::template 1::>$']), 3348 ('junk $<first_level 1::template 1::>$ junk $<<<third_level 2::template 2::>>>$ junk', ['$<first_level 1::template 1::>$']), 3349 3350 # this should produce 2 matches 3351 ('junk $<first_level 1::template 1::>$ junk $<first_level 2::template 2::>$ junk', ['$<first_level 1::template 1::>$', '$<first_level 2::template 2::>$']), 3352 3353 # this will produce a mismatch, due to illegal nesting of same-level placeholders 3354 ('returns illegal match: junk $<first_level::$<insert::insert_template::0>$::10-12>$ junk', ['$<first_level::$<insert::insert_template::0>$::10-12>$']), 3355 ], 3356 second_pass_placeholder_regex: [ 3357 # different lengths/regions 3358 ('junk $<<second_level::template::>>$ junk', ['$<<second_level::template::>>$']), 3359 ('junk $<<second_level::template::10>>$ junk', ['$<<second_level::template::10>>$']), 3360 ('junk $<<second_level::template::10-12>>$ junk', ['$<<second_level::template::10-12>>$']), 3361 3362 # inside is other-level: 3363 ('junk $<<second_level::$<insert::insert_template::0>$::10-12>>$ junk', ['$<<second_level::$<insert::insert_template::0>$::10-12>>$']), 3364 ('junk $<<second_level::$<<<insert::insert_template::0>>>$::10-12>>$ junk', ['$<<second_level::$<<<insert::insert_template::0>>>$::10-12>>$']), 3365 3366 # outside is other-level: 3367 ('junk $<first_level::$<<insert::insert_template::0>>$::10-12>$ junk', ['$<<insert::insert_template::0>>$']), 3368 ('junk $<<<third_level::$<<insert::insert_template::0>>$::10-12>>>$ junk', ['$<<insert::insert_template::0>>$']), 3369 3370 # other level on same line 3371 ('junk $<first_level 1::template 1::>$ junk $<<second_level 2::template 2::>>$ junk', ['$<<second_level 2::template 2::>>$']), 3372 ('junk $<<second_level 1::template 1::>>$ junk $<<<third_level 2::template 2::>>>$ junk', ['$<<second_level 1::template 1::>>$']), 3373 3374 # this should produce 2 matches 3375 ('junk $<<second_level 1::template 1::>>$ junk $<<second_level 2::template 2::>>$ junk', ['$<<second_level 1::template 1::>>$', '$<<second_level 2::template 2::>>$']), 3376 3377 # this will produce a mismatch, due to illegal nesting of same-level placeholders 3378 ('returns illegal match: junk $<<second_level::$<<insert::insert_template::0>>$::10-12>>$ junk', ['$<<second_level::$<<insert::insert_template::0>>$::10-12>>$']), 3379 3380 ], 3381 third_pass_placeholder_regex: [ 3382 # different lengths/regions 3383 ('junk $<<<third_level::template::>>>$ junk', ['$<<<third_level::template::>>>$']), 3384 ('junk $<<<third_level::template::10>>>$ junk', ['$<<<third_level::template::10>>>$']), 3385 ('junk $<<<third_level::template::10-12>>>$ junk', ['$<<<third_level::template::10-12>>>$']), 3386 3387 # inside is other-level: 3388 ('junk $<<<third_level::$<<insert::insert_template::0>>$::10-12>>>$ junk', ['$<<<third_level::$<<insert::insert_template::0>>$::10-12>>>$']), 3389 ('junk $<<<third_level::$<insert::insert_template::0>$::10-12>>>$ junk', ['$<<<third_level::$<insert::insert_template::0>$::10-12>>>$']), 3390 3391 # outside is other-level: 3392 ('junk $<<second_level::$<<<insert::insert_template::0>>>$::10-12>>$ junk', ['$<<<insert::insert_template::0>>>$']), 3393 ('junk $<first_level::$<<<insert::insert_template::0>>>$::10-12>$ junk', ['$<<<insert::insert_template::0>>>$']), 3394 3395 # other level on same line 3396 ('junk $<first_level 1::template 1::>$ junk $<<<third_level 2::template 2::>>>$ junk', ['$<<<third_level 2::template 2::>>>$']), 3397 ('junk $<<second_level 1::template 1::>>$ junk $<<<third_level 2::template 2::>>>$ junk', ['$<<<third_level 2::template 2::>>>$']), 3398 3399 # this will produce a mismatch, due to illegal nesting of same-level placeholders 3400 ('returns illegal match: junk $<<<third_level::$<<<insert::insert_template::0>>>$::10-12>>>$ junk', ['$<<<third_level::$<<<insert::insert_template::0>>>$::10-12>>>$']), 3401 ] 3402 } 3403 3404 for pattern in [first_pass_placeholder_regex, second_pass_placeholder_regex, third_pass_placeholder_regex]: 3405 print("") 3406 print("-----------------------------") 3407 print("regex:", pattern) 3408 tests = all_tests[pattern] 3409 for t in tests: 3410 line, expected_results = t 3411 phs = regex.findall(pattern, line, regex.IGNORECASE) 3412 if len(phs) > 0: 3413 if phs == expected_results: 3414 continue 3415 3416 print("") 3417 print("failed") 3418 print("line:", line) 3419 3420 if len(phs) == 0: 3421 print("no match") 3422 continue 3423 3424 if len(phs) > 1: 3425 print("several matches") 3426 for r in expected_results: 3427 print("expected:", r) 3428 for p in phs: 3429 print("found:", p) 3430 continue 3431 3432 print("unexpected match") 3433 print("expected:", expected_results) 3434 print("found: ", phs)
3435 3436 #--------------------------------------------------------
3437 - def test_placeholder():
3438 3439 phs = [ 3440 #u'emr_journal::soapu //%(clin_when)s %(modified_by)s %(soap_cat)s %(narrative)s//1000 days::', 3441 #u'free_text::placeholder test//preset::9999', 3442 #u'soap_for_encounters:://::9999', 3443 #u'soap_p', 3444 #u'encounter_list::%(started)s: %(assessment_of_encounter)s::30', 3445 #u'patient_comm::homephone::1234', 3446 #u'$<patient_address::work::1234>$', 3447 #u'adr_region::home::1234', 3448 #u'adr_country::fehlt::1234', 3449 #u'adr_subunit::fehlt::1234', 3450 #u'adr_suburb::fehlt-auch::1234', 3451 #u'external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 3452 #u'primary_praxis_provider', 3453 #u'current_provider::::3-5', 3454 #u'current_provider_external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234', 3455 #u'current_provider_external_id::LANR//LÄK::1234' 3456 #u'$<current_provider_external_id::KV-LANR//KV::1234>$' 3457 #u'primary_praxis_provider_external_id::LANR//LÄK::1234' 3458 #u'form_name_long::::1234', 3459 #u'form_name_long::::5', 3460 #u'form_name_long::::', 3461 #u'form_version::::5', 3462 #u'$<current_meds::\item %(product)s %(preparation)s (%(substance)s) from %(started)s for %(duration)s as %(schedule)s until %(discontinued)s\\n::250>$', 3463 #u'$<vaccination_history::%(date_given)s: %(vaccine)s [%(batch_no)s] %(l10n_indications)s::250>$', 3464 #u'$<date_of_birth::%Y %B %d::20>$', 3465 #u'$<date_of_birth::%Y %B %d::>$', 3466 #u'$<date_of_birth::::20>$', 3467 #u'$<date_of_birth::::>$', 3468 #u'$<patient_tags::Tag "%(l10n_description)s": %(comment)s//\\n- ::250>$', 3469 #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>$', 3470 #u'$<patient_photo::\includegraphics[width=60mm]{%s}//image/png//.png::250>$', 3471 #u'$<data_snippet::binary_test_snippet//path=<%s>//image/png//.png::250>$', 3472 #u'$<data_snippet::autograph-LMcC//path=<%s>//image/jpg//.jpg::250>$', 3473 #u'$<current_meds::%s ($<lastname::::50>$)//select::>$', 3474 #u'$<current_meds::%s//select::>$', 3475 #u'$<soap_by_issue::soapu //%Y %b %d//%(narrative)s::1000>$', 3476 #u'$<soap_by_episode::soapu //%Y %b %d//%(narrative)s::1000>$', 3477 #u'$<documents::select//description//document %(clin_when)s: %(l10n_type)s// file: %(fullpath)s (<some path>/%(name)s)//~/gnumed/export/::>$', 3478 #u'$<soap::soapu //%s::9999>$', 3479 #u'$<soap::soapu //%(soap_cat)s: %(date)s | %(provider)s | %(narrative)s::9999>$' 3480 #u'$<test_results:://%c::>$' 3481 #u'$<test_results::%(unified_abbrev)s: %(unified_val)s %(val_unit)s//%c::>$' 3482 #u'$<reminders:://::>$' 3483 #u'$<current_meds_for_rx::%(product)s (%(contains)s): dispense %(amount2dispense)s ::>$' 3484 #u'$<praxis::%(branch)s (%(praxis)s)::>$' 3485 #u'$<praxis_address::::120>$' 3486 #u'$<praxis_id::::120>$' 3487 #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>$' 3488 3489 #u'$<receiver_name::%s::120>$', 3490 #u'$<receiver_street::%s//a::120>$', 3491 #u'$<receiver_number:: %s//a::120>$', 3492 #u'$<receiver_subunit:: %s::120>$', 3493 #u'$<receiver_postcode::%s//b::120>$', 3494 #u'$<receiver_location:: %s::120>$', 3495 #u'$<receiver_country::, %s::120>$', 3496 #u'$<external_care::%(issue)s: %(provider)s of %(unit)s@%(organization)s (%(comment)s)::1024>$', 3497 #u'$<url_escape::hello world ü::>$', 3498 #u'$<substance_abuse::%(substance)s (%(harmful_use_type)s) last=%(last_checked_when)s stop=%(discontinued)s // %(notes)s::>$', 3499 #u'bill_adr_region::region %s::1234', 3500 #u'bill_adr_country::%s::1234', 3501 #u'bill_adr_subunit::subunit: %s::1234', 3502 #u'bill_adr_suburb::-> %s::1234', 3503 #u'bill_adr_street::::1234', 3504 #u'bill_adr_number::%s::1234', 3505 #u'$<diagnoses::\listitem %s::>$' 3506 #u'$<patient_mcf::fmt=txt//card=%s::>$', 3507 #u'$<patient_mcf::fmt=mcf//mcf=%s::>$', 3508 #u'$<patient_mcf::fmt=qr//png=%s::>$' 3509 #u'$<praxis_scan2pay::fmt=txt::>$', 3510 #u'$<praxis_scan2pay::fmt=qr::>$' 3511 u'$<bill_scan2pay::fmt=txt::>$', 3512 u'$<bill_scan2pay::fmt=qr::>$' 3513 ] 3514 3515 handler = gmPlaceholderHandler() 3516 handler.debug = True 3517 3518 gmStaff.set_current_provider_to_logged_on_user() 3519 gmPraxisWidgets.set_active_praxis_branch(no_parent = True) 3520 pat = gmPersonSearch.ask_for_patient() 3521 if pat is None: 3522 return 3523 gmPatSearchWidgets.set_active_patient(patient = pat) 3524 3525 #app = wx.PyWidgetTester(size = (200, 50)) 3526 #handler.set_placeholder('form_name_long', 'ein Testformular') 3527 for ph in phs: 3528 print(ph) 3529 print(" result:") 3530 print(' %s' % handler[ph])
3531 #handler.unset_placeholder('form_name_long') 3532 3533 #--------------------------------------------------------
3534 - def test():
3535 pat = gmPersonSearch.ask_for_patient() 3536 if pat is None: 3537 sys.exit() 3538 gmPerson.set_active_patient(patient = pat) 3539 from Gnumed.wxpython import gmMedicationWidgets 3540 gmMedicationWidgets.manage_substance_intakes()
3541 3542 #--------------------------------------------------------
3543 - def test_show_phs():
3544 show_placeholders()
3545 3546 #-------------------------------------------------------- 3547 3548 app = wx.App() 3549 3550 #test_placeholders() 3551 #test_new_variant_placeholders() 3552 #test_scripting() 3553 #test_placeholder_regex() 3554 #test() 3555 test_placeholder() 3556 #test_show_phs() 3557