| Home | Trees | Indices | Help |
|
|---|
|
|
1 # coding: utf8
2 """GNUmed macro primitives.
3
4 This module implements functions a macro can legally use.
5 """
6 #=====================================================================
7 __version__ = "$Revision: 1.51 $"
8 __author__ = "K.Hilbert <karsten.hilbert@gmx.net>"
9
10 import sys, time, random, types, logging
11
12
13 import wx
14
15
16 if __name__ == '__main__':
17 sys.path.insert(0, '../../')
18 from Gnumed.pycommon import gmI18N
19 if __name__ == '__main__':
20 gmI18N.activate_locale()
21 gmI18N.install_domain()
22 from Gnumed.pycommon import gmGuiBroker
23 from Gnumed.pycommon import gmTools
24 from Gnumed.pycommon import gmBorg
25 from Gnumed.pycommon import gmExceptions
26 from Gnumed.pycommon import gmCfg2
27 from Gnumed.pycommon import gmDateTime
28
29 from Gnumed.business import gmPerson
30 from Gnumed.business import gmStaff
31 from Gnumed.business import gmDemographicRecord
32 from Gnumed.business import gmMedication
33 from Gnumed.business import gmPathLab
34 from Gnumed.business import gmPersonSearch
35 from Gnumed.business import gmVaccination
36 from Gnumed.business import gmPersonSearch
37
38 from Gnumed.wxpython import gmGuiHelpers
39 from Gnumed.wxpython import gmNarrativeWidgets
40 from Gnumed.wxpython import gmPatSearchWidgets
41 from Gnumed.wxpython import gmPersonContactWidgets
42 from Gnumed.wxpython import gmPlugin
43 from Gnumed.wxpython import gmEMRStructWidgets
44 from Gnumed.wxpython import gmListWidgets
45
46
47 _log = logging.getLogger('gm.scripting')
48 _cfg = gmCfg2.gmCfgData()
49
50 #=====================================================================
51 known_placeholders = [
52 'lastname',
53 'firstname',
54 'title',
55 'date_of_birth',
56 'progress_notes',
57 'soap',
58 'soap_s',
59 'soap_o',
60 'soap_a',
61 'soap_p',
62 'soap_u',
63 u'client_version',
64 u'current_provider',
65 u'primary_praxis_provider', # primary provider for current patient in this praxis
66 u'allergy_state'
67 ]
68
69
70 # values for those placeholders must be injected from the outside before using them,
71 # in use they must conform to the "placeholder::::length" syntax,
72 # as long as they resolve to None they return themselves
73 _injectable_placeholders = {
74 u'form_name_long': None,
75 u'form_name_short': None,
76 u'form_version': None
77 }
78
79
80 # those must satisfy the pattern "$<name::args::(optional) max string length>$" when used
81 known_variant_placeholders = [
82 u'soap',
83 u'progress_notes', # "args" holds: categories//template
84 # categories: string with 'soapu '; ' ' == None == admin
85 # template: u'something %s something' (do not include // in template !)
86 u'emr_journal', # "args" format: <categories>//<template>//<line length>//<time range>//<target format>
87 # categories: string with any of "s", "o", "a", "p", "u", " ";
88 # (" " == None == admin category)
89 # template: something %s something else
90 # (Do not include // in the template !)
91 # line length: the length of individual lines, not the total placeholder length
92 # time range: the number of weeks going back in time
93 # target format: "tex" or anything else, if "tex", data will be tex-escaped
94 u'date_of_birth',
95
96 u'patient_address', # "args": <type of address>//<optional formatting template>
97 u'adr_street', # "args" holds: type of address
98 u'adr_number',
99 u'adr_location',
100 u'adr_postcode',
101 u'adr_region',
102 u'adr_country',
103
104 u'patient_comm', # args: comm channel type as per database
105 u'external_id', # args: <type of ID>//<issuer of ID>
106 u'gender_mapper', # "args" holds: <value when person is male> // <is female> // <is other>
107 # eg. "male//female//other"
108 # or: "Lieber Patient//Liebe Patientin"
109 u'current_meds', # "args" holds: line template
110 u'current_meds_table', # "args" holds: format, options
111 # currently only "latex"
112 u'current_meds_notes', # "args" holds: format, options
113 u'lab_table', # "args" holds: format (currently "latex" only)
114 u'latest_vaccs_table', # "args" holds: format, options
115 u'vaccination_history', # "args": <%(key)s-template//date format> to format one vaccination per line
116 u'today', # "args" holds: strftime format
117 u'tex_escape', # "args" holds: string to escape
118 u'allergies', # "args" holds: line template, one allergy per line
119 u'allergy_list', # "args" holds: template per allergy, allergies on one line
120 u'problems', # "args" holds: line template, one problem per line
121 u'name', # "args" holds: template for name parts arrangement
122 u'free_text', # show a dialog for entering some free text
123 u'soap_for_encounters', # "args" holds: soap cats // strftime date format
124 u'encounter_list', # "args" holds: per-encounter template, each ends up on one line
125 u'current_provider_external_id', # args: <type of ID>//<issuer of ID>
126 u'primary_praxis_provider_external_id', # args: <type of ID>//<issuer of ID>
127
128 u'bill', # args: template for string replacement
129 u'bill_item' # args: template for string replacement
130 ]
131
132 default_placeholder_regex = r'\$<.+?>\$' # this one works (except that OOo cannot be non-greedy |-( )
133
134 #_regex_parts = [
135 # r'\$<\w+::.*(?::)\d+>\$',
136 # r'\$<\w+::.+(?!>\$)>\$',
137 # r'\$<\w+?>\$'
138 #]
139 #default_placeholder_regex = r'|'.join(_regex_parts)
140
141 default_placeholder_start = u'$<'
142 default_placeholder_end = u'>$'
143 #=====================================================================
145 """Returns values for placeholders.
146
147 - patient related placeholders operate on the currently active patient
148 - is passed to the forms handling code, for example
149
150 Return values when .debug is False:
151 - errors with placeholders return None
152 - placeholders failing to resolve to a value return an empty string
153
154 Return values when .debug is True:
155 - errors with placeholders return an error string
156 - placeholders failing to resolve to a value return a warning string
157
158 There are several types of placeholders:
159
160 simple static placeholders
161 - those are listed in known_placeholders
162 - they are used as-is
163
164 extended static placeholders
165 - those are, effectively, static placeholders
166 with a maximum length attached (after "::::")
167
168 injectable placeholders
169 - they must be set up before use by set_placeholder()
170 - they should be removed after use by unset_placeholder()
171 - the syntax is like extended static placeholders
172 - they are listed in _injectable_placeholders
173
174 variant placeholders
175 - those are listed in known_variant_placeholders
176 - they are parsed into placeholder, data, and maximum length
177 - the length is optional
178 - data is passed to the handler
179
180 Note that this cannot be called from a non-gui thread unless
181 wrapped in wx.CallAfter().
182 """
184
185 self.pat = gmPerson.gmCurrentPatient()
186 self.debug = False
187
188 self.invalid_placeholder_template = _('invalid placeholder [%s]')
189
190 self.__cache = {}
191 #--------------------------------------------------------
192 # external API
193 #--------------------------------------------------------
197 #--------------------------------------------------------
201 #--------------------------------------------------------
203 self.__cache[key] = value
204 #--------------------------------------------------------
207 #--------------------------------------------------------
208 # __getitem__ API
209 #--------------------------------------------------------
211 """Map self['placeholder'] to self.placeholder.
212
213 This is useful for replacing placeholders parsed out
214 of documents as strings.
215
216 Unknown/invalid placeholders still deliver a result but
217 it will be glaringly obvious if debugging is enabled.
218 """
219 _log.debug('replacing [%s]', placeholder)
220
221 original_placeholder = placeholder
222
223 if placeholder.startswith(default_placeholder_start):
224 placeholder = placeholder[len(default_placeholder_start):]
225 if placeholder.endswith(default_placeholder_end):
226 placeholder = placeholder[:-len(default_placeholder_end)]
227 else:
228 _log.debug('placeholder must either start with [%s] and end with [%s] or neither of both', default_placeholder_start, default_placeholder_end)
229 if self.debug:
230 return self.invalid_placeholder_template % original_placeholder
231 return None
232
233 # simple static placeholder ?
234 if placeholder in known_placeholders:
235 return getattr(self, placeholder)
236
237 # injectable placeholder ?
238 parts = placeholder.split('::::', 1)
239 if len(parts) == 2:
240 name, lng = parts
241 unknown_injectable = False
242 try:
243 val = _injectable_placeholders[name]
244 except KeyError:
245 unknown_injectable = True
246 except:
247 _log.exception('placeholder handling error: %s', original_placeholder)
248 if self.debug:
249 return self.invalid_placeholder_template % original_placeholder
250 return None
251 if not unknown_injectable:
252 if val is None:
253 if self.debug:
254 return u'injectable placeholder [%s]: no value available' % name
255 return placeholder
256 return val[:int(lng)]
257
258 # extended static placeholder ?
259 parts = placeholder.split('::::', 1)
260 if len(parts) == 2:
261 name, lng = parts
262 try:
263 return getattr(self, name)[:int(lng)]
264 except:
265 _log.exception('placeholder handling error: %s', original_placeholder)
266 if self.debug:
267 return self.invalid_placeholder_template % original_placeholder
268 return None
269
270 # variable placeholders
271 parts = placeholder.split('::')
272
273 if len(parts) == 1:
274 _log.warning('invalid placeholder layout: %s', original_placeholder)
275 if self.debug:
276 return self.invalid_placeholder_template % original_placeholder
277 return None
278
279 if len(parts) == 2:
280 name, data = parts
281 lng = None
282
283 if len(parts) == 3:
284 name, data, lng = parts
285 try:
286 lng = int(lng)
287 except (TypeError, ValueError):
288 _log.error('placeholder length definition error: %s, discarding length: >%s<', original_placeholder, lng)
289 lng = None
290
291 if len(parts) > 3:
292 _log.warning('invalid placeholder layout: %s', original_placeholder)
293 if self.debug:
294 return self.invalid_placeholder_template % original_placeholder
295 return None
296
297 handler = getattr(self, '_get_variant_%s' % name, None)
298 if handler is None:
299 _log.warning('no handler <_get_variant_%s> for placeholder %s', name, original_placeholder)
300 if self.debug:
301 return self.invalid_placeholder_template % original_placeholder
302 return None
303
304 try:
305 if lng is None:
306 return handler(data = data)
307 return handler(data = data)[:lng]
308 except:
309 _log.exception('placeholder handling error: %s', original_placeholder)
310 if self.debug:
311 return self.invalid_placeholder_template % original_placeholder
312 return None
313
314 _log.error('something went wrong, should never get here')
315 return None
316 #--------------------------------------------------------
317 # properties actually handling placeholders
318 #--------------------------------------------------------
319 # property helpers
320 #--------------------------------------------------------
324 #--------------------------------------------------------
326 return self.pat.get_active_name()['lastnames']
327 #--------------------------------------------------------
329 return self.pat.get_active_name()['firstnames']
330 #--------------------------------------------------------
333 #--------------------------------------------------------
335 return self._get_variant_date_of_birth(data='%x')
336 #--------------------------------------------------------
339 #--------------------------------------------------------
341 return self._get_variant_soap(data = u's')
342 #--------------------------------------------------------
344 return self._get_variant_soap(data = u'o')
345 #--------------------------------------------------------
347 return self._get_variant_soap(data = u'a')
348 #--------------------------------------------------------
350 return self._get_variant_soap(data = u'p')
351 #--------------------------------------------------------
353 return self._get_variant_soap(data = u'u')
354 #--------------------------------------------------------
357 #--------------------------------------------------------
359 return gmTools.coalesce (
360 _cfg.get(option = u'client_version'),
361 u'%s' % self.__class__.__name__
362 )
363 #--------------------------------------------------------
365 prov = self.pat.primary_provider
366 if prov is None:
367 return self._get_current_provider()
368
369 title = gmTools.coalesce (
370 prov['title'],
371 gmPerson.map_gender2salutation(prov['gender'])
372 )
373
374 tmp = u'%s %s. %s' % (
375 title,
376 prov['firstnames'][:1],
377 prov['lastnames']
378 )
379
380 return tmp
381 #--------------------------------------------------------
383 prov = gmStaff.gmCurrentProvider()
384
385 title = gmTools.coalesce (
386 prov['title'],
387 gmPerson.map_gender2salutation(prov['gender'])
388 )
389
390 tmp = u'%s %s. %s' % (
391 title,
392 prov['firstnames'][:1],
393 prov['lastnames']
394 )
395
396 return tmp
397 #--------------------------------------------------------
399 allg_state = self.pat.get_emr().allergy_state
400
401 if allg_state['last_confirmed'] is None:
402 date_confirmed = u''
403 else:
404 date_confirmed = u' (%s)' % allg_state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
405
406 tmp = u'%s%s' % (
407 allg_state.state_string,
408 date_confirmed
409 )
410 return tmp
411 #--------------------------------------------------------
412 # property definitions for static placeholders
413 #--------------------------------------------------------
414 placeholder_regex = property(lambda x: default_placeholder_regex, _setter_noop)
415
416 #--------------------------------------------------------
417
418 # placeholders
419 lastname = property(_get_lastname, _setter_noop)
420 firstname = property(_get_firstname, _setter_noop)
421 title = property(_get_title, _setter_noop)
422 date_of_birth = property(_get_dob, _setter_noop)
423
424 progress_notes = property(_get_progress_notes, _setter_noop)
425 soap = property(_get_progress_notes, _setter_noop)
426 soap_s = property(_get_soap_s, _setter_noop)
427 soap_o = property(_get_soap_o, _setter_noop)
428 soap_a = property(_get_soap_a, _setter_noop)
429 soap_p = property(_get_soap_p, _setter_noop)
430 soap_u = property(_get_soap_u, _setter_noop)
431 soap_admin = property(_get_soap_admin, _setter_noop)
432
433 allergy_state = property(_get_allergy_state, _setter_noop)
434
435 client_version = property(_get_client_version, _setter_noop)
436
437 current_provider = property(_get_current_provider, _setter_noop)
438 primary_praxis_provider = property(_get_primary_praxis_provider, _setter_noop)
439 #--------------------------------------------------------
440 # variant handlers
441 #--------------------------------------------------------
443
444 encounters = gmEMRStructWidgets.select_encounters(single_selection = False)
445 if not encounters:
446 return u''
447
448 template = data
449
450 lines = []
451 for enc in encounters:
452 try:
453 lines.append(template % enc)
454 except:
455 lines.append(u'error formatting encounter')
456 _log.exception('problem formatting encounter list')
457 _log.error('template: %s', template)
458 _log.error('encounter: %s', encounter)
459
460 return u'\n'.join(lines)
461 #--------------------------------------------------------
463 """Select encounters from list and format SOAP thereof.
464
465 data: soap_cats (' ' -> None -> admin) // date format
466 """
467 # defaults
468 cats = None
469 date_format = None
470
471 if data is not None:
472 data_parts = data.split('//')
473
474 # part[0]: categories
475 if len(data_parts[0]) > 0:
476 cats = []
477 if u' ' in data_parts[0]:
478 cats.append(None)
479 data_parts[0] = data_parts[0].replace(u' ', u'')
480 cats.extend(list(data_parts[0]))
481
482 # part[1]: date format
483 if len(data_parts) > 1:
484 if len(data_parts[1]) > 0:
485 date_format = data_parts[1]
486
487 encounters = gmEMRStructWidgets.select_encounters(single_selection = False)
488 if not encounters:
489 return u''
490
491 chunks = []
492 for enc in encounters:
493 chunks.append(enc.format_latex (
494 date_format = date_format,
495 soap_cats = cats,
496 soap_order = u'soap_rank, date'
497 ))
498
499 return u''.join(chunks)
500 #--------------------------------------------------------
502 # default: all categories, neutral template
503 cats = list(u'soapu')
504 cats.append(None)
505 template = u'%s'
506 interactive = True
507 line_length = 9999
508 target_format = None
509 time_range = None
510
511 if data is not None:
512 data_parts = data.split('//')
513
514 # part[0]: categories
515 cats = []
516 # ' ' -> None == admin
517 for c in list(data_parts[0]):
518 if c == u' ':
519 c = None
520 cats.append(c)
521 # '' -> SOAP + None
522 if cats == u'':
523 cats = list(u'soapu').append(None)
524
525 # part[1]: template
526 if len(data_parts) > 1:
527 template = data_parts[1]
528
529 # part[2]: line length
530 if len(data_parts) > 2:
531 try:
532 line_length = int(data_parts[2])
533 except:
534 line_length = 9999
535
536 # part[3]: weeks going back in time
537 if len(data_parts) > 3:
538 try:
539 time_range = 7 * int(data_parts[3])
540 except:
541 time_range = None
542
543 # part[4]: output format
544 if len(data_parts) > 4:
545 target_format = data_parts[4]
546
547 # FIXME: will need to be a generator later on
548 narr = self.pat.get_emr().get_as_journal(soap_cats = cats, time_range = time_range)
549
550 if len(narr) == 0:
551 return u''
552
553 if target_format == u'tex':
554 keys = narr[0].keys()
555 lines = []
556 line_dict = {}
557 for n in narr:
558 for key in keys:
559 if isinstance(n[key], basestring):
560 line_dict[key] = gmTools.tex_escape_string(text = n[key])
561 continue
562 line_dict[key] = n[key]
563 try:
564 lines.append((template % line_dict)[:line_length])
565 except KeyError:
566 return u'invalid key in template [%s], valid keys: %s]' % (template, str(keys))
567 else:
568 try:
569 lines = [ (template % n)[:line_length] for n in narr ]
570 except KeyError:
571 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys()))
572
573 return u'\n'.join(lines)
574 #--------------------------------------------------------
577 #--------------------------------------------------------
579
580 # default: all categories, neutral template
581 cats = list(u'soapu')
582 cats.append(None)
583 template = u'%s'
584
585 if data is not None:
586 data_parts = data.split('//')
587
588 # part[0]: categories
589 cats = []
590 # ' ' -> None == admin
591 for cat in list(data_parts[0]):
592 if cat == u' ':
593 cat = None
594 cats.append(cat)
595 # '' -> SOAP + None
596 if cats == u'':
597 cats = list(u'soapu')
598 cats.append(None)
599
600 # part[1]: template
601 if len(data_parts) > 1:
602 template = data_parts[1]
603
604 #narr = gmNarrativeWidgets.select_narrative_from_episodes_new(soap_cats = cats)
605 narr = gmNarrativeWidgets.select_narrative_from_episodes(soap_cats = cats)
606
607 if narr is None:
608 return u''
609
610 if len(narr) == 0:
611 return u''
612
613 try:
614 narr = [ template % n['narrative'] for n in narr ]
615 except KeyError:
616 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys()))
617
618 return u'\n'.join(narr)
619 #--------------------------------------------------------
621 if data is None:
622 return [_('template is missing')]
623
624 name = self.pat.get_active_name()
625
626 parts = {
627 'title': gmTools.coalesce(name['title'], u''),
628 'firstnames': name['firstnames'],
629 'lastnames': name['lastnames'],
630 'preferred': gmTools.coalesce (
631 initial = name['preferred'],
632 instead = u' ',
633 template_initial = u' "%s" '
634 )
635 }
636
637 return data % parts
638 #--------------------------------------------------------
641 #--------------------------------------------------------
642 # FIXME: extend to all supported genders
644 values = data.split('//', 2)
645
646 if len(values) == 2:
647 male_value, female_value = values
648 other_value = u'<unkown gender>'
649 elif len(values) == 3:
650 male_value, female_value, other_value = values
651 else:
652 return _('invalid gender mapping layout: [%s]') % data
653
654 if self.pat['gender'] == u'm':
655 return male_value
656
657 if self.pat['gender'] == u'f':
658 return female_value
659
660 return other_value
661 #--------------------------------------------------------
662 # address related placeholders
663 #--------------------------------------------------------
665
666 data_parts = data.split(u'//')
667
668 # address type
669 adr_type = data_parts[0].strip()
670 orig_type = adr_type
671 if adr_type != u'':
672 adrs = self.pat.get_addresses(address_type = adr_type)
673 if len(adrs) == 0:
674 _log.warning('no address for type [%s]', adr_type)
675 adr_type = u''
676 if adr_type == u'':
677 _log.debug('asking user for address type')
678 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat)
679 if adr is None:
680 if self.debug:
681 return _('no address type replacement selected')
682 return u''
683 adr_type = adr['address_type']
684 adr = self.pat.get_addresses(address_type = adr_type)[0]
685
686 # formatting template
687 template = _('%(street)s %(number)s, %(postcode)s %(urb)s, %(l10n_state)s, %(l10n_country)s')
688 if len(data_parts) > 1:
689 if data_parts[1].strip() != u'':
690 template = data_parts[1]
691
692 try:
693 return template % adr.fields_as_dict()
694 except StandardError:
695 _log.exception('error formatting address')
696 _log.error('template: %s', template)
697
698 return None
699 #--------------------------------------------------------
701 adr_type = data.strip()
702 orig_type = adr_type
703 if adr_type != u'':
704 adrs = self.pat.get_addresses(address_type = adr_type)
705 if len(adrs) == 0:
706 _log.warning('no street for address type [%s]', adr_type)
707 adr_type = u''
708 if adr_type == u'':
709 _log.debug('asking user for address type')
710 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat)
711 if adr is None:
712 if self.debug:
713 return _('no address type replacement selected')
714 return u''
715 adr_type = adr['address_type']
716 return self.pat.get_addresses(address_type = adr_type)[0]['street']
717 #--------------------------------------------------------
719 adr_type = data.strip()
720 orig_type = adr_type
721 if adr_type != u'':
722 adrs = self.pat.get_addresses(address_type = adr_type)
723 if len(adrs) == 0:
724 _log.warning('no number for address type [%s]', adr_type)
725 adr_type = u''
726 if adr_type == u'':
727 _log.debug('asking user for address type')
728 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat)
729 if adr is None:
730 if self.debug:
731 return _('no address type replacement selected')
732 return u''
733 adr_type = adr['address_type']
734 return self.pat.get_addresses(address_type = adr_type)[0]['number']
735 #--------------------------------------------------------
737 adr_type = data.strip()
738 orig_type = adr_type
739 if adr_type != u'':
740 adrs = self.pat.get_addresses(address_type = adr_type)
741 if len(adrs) == 0:
742 _log.warning('no location for address type [%s]', adr_type)
743 adr_type = u''
744 if adr_type == u'':
745 _log.debug('asking user for address type')
746 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat)
747 if adr is None:
748 if self.debug:
749 return _('no address type replacement selected')
750 return u''
751 adr_type = adr['address_type']
752 return self.pat.get_addresses(address_type = adr_type)[0]['urb']
753 #--------------------------------------------------------
755 adr_type = data.strip()
756 orig_type = adr_type
757 if adr_type != u'':
758 adrs = self.pat.get_addresses(address_type = adr_type)
759 if len(adrs) == 0:
760 _log.warning('no postcode for address type [%s]', adr_type)
761 adr_type = u''
762 if adr_type == u'':
763 _log.debug('asking user for address type')
764 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat)
765 if adr is None:
766 if self.debug:
767 return _('no address type replacement selected')
768 return u''
769 adr_type = adr['address_type']
770 return self.pat.get_addresses(address_type = adr_type)[0]['postcode']
771 #--------------------------------------------------------
773 adr_type = data.strip()
774 orig_type = adr_type
775 if adr_type != u'':
776 adrs = self.pat.get_addresses(address_type = adr_type)
777 if len(adrs) == 0:
778 _log.warning('no region for address type [%s]', adr_type)
779 adr_type = u''
780 if adr_type == u'':
781 _log.debug('asking user for address type')
782 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat)
783 if adr is None:
784 if self.debug:
785 return _('no address type replacement selected')
786 return u''
787 adr_type = adr['address_type']
788 return self.pat.get_addresses(address_type = adr_type)[0]['l10n_state']
789 #--------------------------------------------------------
791 adr_type = data.strip()
792 orig_type = adr_type
793 if adr_type != u'':
794 adrs = self.pat.get_addresses(address_type = adr_type)
795 if len(adrs) == 0:
796 _log.warning('no country for old men / address type [%s]', adr_type)
797 adr_type = u''
798 if adr_type == u'':
799 _log.debug('asking user for address type')
800 adr = gmPersonContactWidgets.select_address(missing = orig_type, person = self.pat)
801 if adr is None:
802 if self.debug:
803 return _('no address type replacement selected')
804 return u''
805 adr_type = adr['address_type']
806 return self.pat.get_addresses(address_type = adr_type)[0]['l10n_country']
807 #--------------------------------------------------------
809 comms = self.pat.get_comm_channels(comm_medium = data)
810 if len(comms) == 0:
811 if self.debug:
812 return _('no URL for comm channel [%s]') % data
813 return u''
814 return comms[0]['url']
815 #--------------------------------------------------------
817 data_parts = data.split(u'//')
818 if len(data_parts) < 2:
819 return u'current provider external ID: template is missing'
820
821 id_type = data_parts[0].strip()
822 if id_type == u'':
823 return u'current provider external ID: type is missing'
824
825 issuer = data_parts[1].strip()
826 if issuer == u'':
827 return u'current provider external ID: issuer is missing'
828
829 prov = gmStaff.gmCurrentProvider()
830 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer)
831
832 if len(ids) == 0:
833 if self.debug:
834 return _('no external ID [%s] by [%s]') % (id_type, issuer)
835 return u''
836
837 return ids[0]['value']
838 #--------------------------------------------------------
840 data_parts = data.split(u'//')
841 if len(data_parts) < 2:
842 return u'primary in-praxis provider external ID: template is missing'
843
844 id_type = data_parts[0].strip()
845 if id_type == u'':
846 return u'primary in-praxis provider external ID: type is missing'
847
848 issuer = data_parts[1].strip()
849 if issuer == u'':
850 return u'primary in-praxis provider external ID: issuer is missing'
851
852 prov = self.pat.primary_provider
853 if prov is None:
854 if self.debug:
855 return _('no primary in-praxis provider')
856 return u''
857
858 ids = prov.identity.get_external_ids(id_type = id_type, issuer = issuer)
859
860 if len(ids) == 0:
861 if self.debug:
862 return _('no external ID [%s] by [%s]') % (id_type, issuer)
863 return u''
864
865 return ids[0]['value']
866 #--------------------------------------------------------
868 data_parts = data.split(u'//')
869 if len(data_parts) < 2:
870 return u'patient external ID: template is missing'
871
872 id_type = data_parts[0].strip()
873 if id_type == u'':
874 return u'patient external ID: type is missing'
875
876 issuer = data_parts[1].strip()
877 if issuer == u'':
878 return u'patient external ID: issuer is missing'
879
880 ids = self.pat.get_external_ids(id_type = id_type, issuer = issuer)
881
882 if len(ids) == 0:
883 if self.debug:
884 return _('no external ID [%s] by [%s]') % (id_type, issuer)
885 return u''
886
887 return ids[0]['value']
888 #--------------------------------------------------------
890 if data is None:
891 return [_('template is missing')]
892
893 template, separator = data.split('//', 2)
894
895 emr = self.pat.get_emr()
896 return separator.join([ template % a for a in emr.get_allergies() ])
897 #--------------------------------------------------------
899
900 if data is None:
901 return [_('template is missing')]
902
903 emr = self.pat.get_emr()
904 return u'\n'.join([ data % a for a in emr.get_allergies() ])
905 #--------------------------------------------------------
907
908 if data is None:
909 return [_('template is missing')]
910
911 emr = self.pat.get_emr()
912 current_meds = emr.get_current_substance_intake (
913 include_inactive = False,
914 include_unapproved = False,
915 order_by = u'brand, substance'
916 )
917
918 return u'\n'.join([ data % m.fields_as_dict(date_format = '%Y %B %d') for m in current_meds ])
919 #--------------------------------------------------------
921
922 options = data.split('//')
923
924 if u'latex' in options:
925 return gmMedication.format_substance_intake (
926 emr = self.pat.get_emr(),
927 output_format = u'latex',
928 table_type = u'by-brand'
929 )
930
931 _log.error('no known current medications table formatting style in [%s]', data)
932 return _('unknown current medication table formatting style')
933 #--------------------------------------------------------
935
936 options = data.split('//')
937
938 if u'latex' in options:
939 return gmMedication.format_substance_intake_notes (
940 emr = self.pat.get_emr(),
941 output_format = u'latex',
942 table_type = u'by-brand'
943 )
944
945 _log.error('no known current medications notes formatting style in [%s]', data)
946 return _('unknown current medication notes formatting style')
947 #--------------------------------------------------------
949
950 options = data.split('//')
951
952 emr = self.pat.get_emr()
953
954 if u'latex' in options:
955 return gmPathLab.format_test_results (
956 results = emr.get_test_results_by_date(),
957 output_format = u'latex'
958 )
959
960 _log.error('no known test results table formatting style in [%s]', data)
961 return _('unknown test results table formatting style [%s]') % data
962 #--------------------------------------------------------
964
965 options = data.split('//')
966
967 emr = self.pat.get_emr()
968
969 if u'latex' in options:
970 return gmVaccination.format_latest_vaccinations(output_format = u'latex', emr = emr)
971
972 _log.error('no known vaccinations table formatting style in [%s]', data)
973 return _('unknown vaccinations table formatting style [%s]') % data
974 #--------------------------------------------------------
976 options = data.split('//')
977 template = options[0]
978 if len(options) > 1:
979 date_format = options[1]
980 else:
981 date_format = u'%Y %B %d'
982
983 emr = self.pat.get_emr()
984 vaccs = emr.get_vaccinations(order_by = u'date_given DESC, vaccine')
985
986 return u'\n'.join([ template % v.fields_as_dict(date_format = date_format) for v in vaccs ])
987 #--------------------------------------------------------
989
990 if data is None:
991 return [_('template is missing')]
992
993 probs = self.pat.get_emr().get_problems()
994
995 return u'\n'.join([ data % p for p in probs ])
996 #--------------------------------------------------------
999 #--------------------------------------------------------
1002 #--------------------------------------------------------
1004 # <data>:
1005 # format: tex (only, currently)
1006 # message: shown in input dialog, must not contain "//" or "::"
1007
1008 data_parts = data.split('//')
1009 format = data_parts[0]
1010 if len(data_parts) > 1:
1011 msg = data_parts[1]
1012 else:
1013 msg = _('generic text')
1014
1015 dlg = gmGuiHelpers.cMultilineTextEntryDlg (
1016 None,
1017 -1,
1018 title = _('Replacing <free_text> placeholder'),
1019 msg = _('Below you can enter free text.\n\n [%s]') % msg
1020 )
1021 dlg.enable_user_formatting = True
1022 decision = dlg.ShowModal()
1023
1024 if decision != wx.ID_SAVE:
1025 dlg.Destroy()
1026 if self.debug:
1027 return _('Text input cancelled by user.')
1028 return u''
1029
1030 text = dlg.value.strip()
1031 if dlg.is_user_formatted:
1032 dlg.Destroy()
1033 return text
1034
1035 dlg.Destroy()
1036
1037 if format == u'tex':
1038 return gmTools.tex_escape_string(text = text)
1039
1040 return text
1041 #--------------------------------------------------------
1043 try:
1044 bill = self.__cache['bill']
1045 except KeyError:
1046 from Gnumed.wxpython import gmBillingWidgets
1047 bill = gmBillingWidgets.manage_bills(patient = self.pat)
1048 if bill is None:
1049 if self.debug:
1050 return _('no bill selected')
1051 return u''
1052 self.__cache['bill'] = bill
1053
1054 return data % bill.fields_as_dict(date_format = '%Y %B %d')
1055 #--------------------------------------------------------
1057 try:
1058 bill = self.__cache['bill']
1059 except KeyError:
1060 from Gnumed.wxpython import gmBillingWidgets
1061 bill = gmBillingWidgets.manage_bills(patient = self.pat)
1062 if bill is None:
1063 if self.debug:
1064 return _('no bill selected')
1065 return u''
1066 self.__cache['bill'] = bill
1067
1068 return u'\n'.join([ data % i.fields_as_dict(date_format = '%Y %B %d') for i in bill.bill_items ])
1069 #--------------------------------------------------------
1070 # internal helpers
1071 #--------------------------------------------------------
1074 #=====================================================================
1076 """Functions a macro can legally use.
1077
1078 An instance of this class is passed to the GNUmed scripting
1079 listener. Hence, all actions a macro can legally take must
1080 be defined in this class. Thus we achieve some screening for
1081 security and also thread safety handling.
1082 """
1083 #-----------------------------------------------------------------
1085 if personality is None:
1086 raise gmExceptions.ConstructorError, 'must specify personality'
1087 self.__personality = personality
1088 self.__attached = 0
1089 self._get_source_personality = None
1090 self.__user_done = False
1091 self.__user_answer = 'no answer yet'
1092 self.__pat = gmPerson.gmCurrentPatient()
1093
1094 self.__auth_cookie = str(random.random())
1095 self.__pat_lock_cookie = str(random.random())
1096 self.__lock_after_load_cookie = str(random.random())
1097
1098 _log.info('slave mode personality is [%s]', personality)
1099 #-----------------------------------------------------------------
1100 # public API
1101 #-----------------------------------------------------------------
1103 if self.__attached:
1104 _log.error('attach with [%s] rejected, already serving a client', personality)
1105 return (0, _('attach rejected, already serving a client'))
1106 if personality != self.__personality:
1107 _log.error('rejecting attach to personality [%s], only servicing [%s]' % (personality, self.__personality))
1108 return (0, _('attach to personality [%s] rejected') % personality)
1109 self.__attached = 1
1110 self.__auth_cookie = str(random.random())
1111 return (1, self.__auth_cookie)
1112 #-----------------------------------------------------------------
1114 if not self.__attached:
1115 return 1
1116 if auth_cookie != self.__auth_cookie:
1117 _log.error('rejecting detach() with cookie [%s]' % auth_cookie)
1118 return 0
1119 self.__attached = 0
1120 return 1
1121 #-----------------------------------------------------------------
1123 if not self.__attached:
1124 return 1
1125 self.__user_done = False
1126 # FIXME: use self.__sync_cookie for syncing with user interaction
1127 wx.CallAfter(self._force_detach)
1128 return 1
1129 #-----------------------------------------------------------------
1131 ver = _cfg.get(option = u'client_version')
1132 return "GNUmed %s, %s $Revision: 1.51 $" % (ver, self.__class__.__name__)
1133 #-----------------------------------------------------------------
1135 """Shuts down this client instance."""
1136 if not self.__attached:
1137 return 0
1138 if auth_cookie != self.__auth_cookie:
1139 _log.error('non-authenticated shutdown_gnumed()')
1140 return 0
1141 wx.CallAfter(self._shutdown_gnumed, forced)
1142 return 1
1143 #-----------------------------------------------------------------
1145 """Raise ourselves to the top of the desktop."""
1146 if not self.__attached:
1147 return 0
1148 if auth_cookie != self.__auth_cookie:
1149 _log.error('non-authenticated raise_gnumed()')
1150 return 0
1151 return "cMacroPrimitives.raise_gnumed() not implemented"
1152 #-----------------------------------------------------------------
1154 if not self.__attached:
1155 return 0
1156 if auth_cookie != self.__auth_cookie:
1157 _log.error('non-authenticated get_loaded_plugins()')
1158 return 0
1159 gb = gmGuiBroker.GuiBroker()
1160 return gb['horstspace.notebook.gui'].keys()
1161 #-----------------------------------------------------------------
1163 """Raise a notebook plugin within GNUmed."""
1164 if not self.__attached:
1165 return 0
1166 if auth_cookie != self.__auth_cookie:
1167 _log.error('non-authenticated raise_notebook_plugin()')
1168 return 0
1169 # FIXME: use semaphore
1170 wx.CallAfter(gmPlugin.raise_notebook_plugin, a_plugin)
1171 return 1
1172 #-----------------------------------------------------------------
1174 """Load external patient, perhaps create it.
1175
1176 Callers must use get_user_answer() to get status information.
1177 It is unsafe to proceed without knowing the completion state as
1178 the controlled client may be waiting for user input from a
1179 patient selection list.
1180 """
1181 if not self.__attached:
1182 return (0, _('request rejected, you are not attach()ed'))
1183 if auth_cookie != self.__auth_cookie:
1184 _log.error('non-authenticated load_patient_from_external_source()')
1185 return (0, _('rejected load_patient_from_external_source(), not authenticated'))
1186 if self.__pat.locked:
1187 _log.error('patient is locked, cannot load from external source')
1188 return (0, _('current patient is locked'))
1189 self.__user_done = False
1190 wx.CallAfter(self._load_patient_from_external_source)
1191 self.__lock_after_load_cookie = str(random.random())
1192 return (1, self.__lock_after_load_cookie)
1193 #-----------------------------------------------------------------
1195 if not self.__attached:
1196 return (0, _('request rejected, you are not attach()ed'))
1197 if auth_cookie != self.__auth_cookie:
1198 _log.error('non-authenticated lock_load_patient()')
1199 return (0, _('rejected lock_load_patient(), not authenticated'))
1200 # FIXME: ask user what to do about wrong cookie
1201 if lock_after_load_cookie != self.__lock_after_load_cookie:
1202 _log.warning('patient lock-after-load request rejected due to wrong cookie [%s]' % lock_after_load_cookie)
1203 return (0, 'patient lock-after-load request rejected, wrong cookie provided')
1204 self.__pat.locked = True
1205 self.__pat_lock_cookie = str(random.random())
1206 return (1, self.__pat_lock_cookie)
1207 #-----------------------------------------------------------------
1209 if not self.__attached:
1210 return (0, _('request rejected, you are not attach()ed'))
1211 if auth_cookie != self.__auth_cookie:
1212 _log.error('non-authenticated lock_into_patient()')
1213 return (0, _('rejected lock_into_patient(), not authenticated'))
1214 if self.__pat.locked:
1215 _log.error('patient is already locked')
1216 return (0, _('already locked into a patient'))
1217 searcher = gmPersonSearch.cPatientSearcher_SQL()
1218 if type(search_params) == types.DictType:
1219 idents = searcher.get_identities(search_dict=search_params)
1220 raise StandardError("must use dto, not search_dict")
1221 else:
1222 idents = searcher.get_identities(search_term=search_params)
1223 if idents is None:
1224 return (0, _('error searching for patient with [%s]/%s') % (search_term, search_dict))
1225 if len(idents) == 0:
1226 return (0, _('no patient found for [%s]/%s') % (search_term, search_dict))
1227 # FIXME: let user select patient
1228 if len(idents) > 1:
1229 return (0, _('several matching patients found for [%s]/%s') % (search_term, search_dict))
1230 if not gmPatSearchWidgets.set_active_patient(patient = idents[0]):
1231 return (0, _('cannot activate patient [%s] (%s/%s)') % (str(idents[0]), search_term, search_dict))
1232 self.__pat.locked = True
1233 self.__pat_lock_cookie = str(random.random())
1234 return (1, self.__pat_lock_cookie)
1235 #-----------------------------------------------------------------
1237 if not self.__attached:
1238 return (0, _('request rejected, you are not attach()ed'))
1239 if auth_cookie != self.__auth_cookie:
1240 _log.error('non-authenticated unlock_patient()')
1241 return (0, _('rejected unlock_patient, not authenticated'))
1242 # we ain't locked anyways, so succeed
1243 if not self.__pat.locked:
1244 return (1, '')
1245 # FIXME: ask user what to do about wrong cookie
1246 if unlock_cookie != self.__pat_lock_cookie:
1247 _log.warning('patient unlock request rejected due to wrong cookie [%s]' % unlock_cookie)
1248 return (0, 'patient unlock request rejected, wrong cookie provided')
1249 self.__pat.locked = False
1250 return (1, '')
1251 #-----------------------------------------------------------------
1252 - def assume_staff_identity(self, auth_cookie = None, staff_name = "Dr.Jekyll", staff_creds = None):
1253 if not self.__attached:
1254 return 0
1255 if auth_cookie != self.__auth_cookie:
1256 _log.error('non-authenticated select_identity()')
1257 return 0
1258 return "cMacroPrimitives.assume_staff_identity() not implemented"
1259 #-----------------------------------------------------------------
1261 if not self.__user_done:
1262 return (0, 'still waiting')
1263 self.__user_done = False
1264 return (1, self.__user_answer)
1265 #-----------------------------------------------------------------
1266 # internal API
1267 #-----------------------------------------------------------------
1269 msg = _(
1270 'Someone tries to forcibly break the existing\n'
1271 'controlling connection. This may or may not\n'
1272 'have legitimate reasons.\n\n'
1273 'Do you want to allow breaking the connection ?'
1274 )
1275 can_break_conn = gmGuiHelpers.gm_show_question (
1276 aMessage = msg,
1277 aTitle = _('forced detach attempt')
1278 )
1279 if can_break_conn:
1280 self.__user_answer = 1
1281 else:
1282 self.__user_answer = 0
1283 self.__user_done = True
1284 if can_break_conn:
1285 self.__pat.locked = False
1286 self.__attached = 0
1287 return 1
1288 #-----------------------------------------------------------------
1290 top_win = wx.GetApp().GetTopWindow()
1291 if forced:
1292 top_win.Destroy()
1293 else:
1294 top_win.Close()
1295 #-----------------------------------------------------------------
1297 patient = gmPatSearchWidgets.get_person_from_external_sources(search_immediately = True, activate_immediately = True)
1298 if patient is not None:
1299 self.__user_answer = 1
1300 else:
1301 self.__user_answer = 0
1302 self.__user_done = True
1303 return 1
1304 #=====================================================================
1305 # main
1306 #=====================================================================
1307 if __name__ == '__main__':
1308
1309 if len(sys.argv) < 2:
1310 sys.exit()
1311
1312 if sys.argv[1] != 'test':
1313 sys.exit()
1314
1315 gmI18N.activate_locale()
1316 gmI18N.install_domain()
1317
1318 #--------------------------------------------------------
1320 handler = gmPlaceholderHandler()
1321 handler.debug = True
1322
1323 for placeholder in ['a', 'b']:
1324 print handler[placeholder]
1325
1326 pat = gmPersonSearch.ask_for_patient()
1327 if pat is None:
1328 return
1329
1330 gmPatSearchWidgets.set_active_patient(patient = pat)
1331
1332 print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d']
1333
1334 app = wx.PyWidgetTester(size = (200, 50))
1335 for placeholder in known_placeholders:
1336 print placeholder, "=", handler[placeholder]
1337
1338 ph = 'progress_notes::ap'
1339 print '%s: %s' % (ph, handler[ph])
1340 #--------------------------------------------------------
1342
1343 tests = [
1344 # should work:
1345 '$<lastname>$',
1346 '$<lastname::::3>$',
1347 '$<name::%(title)s %(firstnames)s%(preferred)s%(lastnames)s>$',
1348
1349 # should fail:
1350 'lastname',
1351 '$<lastname',
1352 '$<lastname::',
1353 '$<lastname::>$',
1354 '$<lastname::abc>$',
1355 '$<lastname::abc::>$',
1356 '$<lastname::abc::3>$',
1357 '$<lastname::abc::xyz>$',
1358 '$<lastname::::>$',
1359 '$<lastname::::xyz>$',
1360
1361 '$<date_of_birth::%Y-%m-%d>$',
1362 '$<date_of_birth::%Y-%m-%d::3>$',
1363 '$<date_of_birth::%Y-%m-%d::>$',
1364
1365 # should work:
1366 '$<adr_location::home::35>$',
1367 '$<gender_mapper::male//female//other::5>$',
1368 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\n::50>$',
1369 '$<allergy_list::%(descriptor)s, >$',
1370 '$<current_meds_table::latex//by-brand>$'
1371
1372 # 'firstname',
1373 # 'title',
1374 # 'date_of_birth',
1375 # 'progress_notes',
1376 # 'soap',
1377 # 'soap_s',
1378 # 'soap_o',
1379 # 'soap_a',
1380 # 'soap_p',
1381
1382 # 'soap',
1383 # 'progress_notes',
1384 # 'date_of_birth'
1385 ]
1386
1387 # tests = [
1388 # '$<latest_vaccs_table::latex>$'
1389 # ]
1390
1391 pat = gmPersonSearch.ask_for_patient()
1392 if pat is None:
1393 return
1394
1395 gmPatSearchWidgets.set_active_patient(patient = pat)
1396
1397 handler = gmPlaceholderHandler()
1398 handler.debug = True
1399
1400 for placeholder in tests:
1401 print placeholder, "=>", handler[placeholder]
1402 print "--------------"
1403 raw_input()
1404
1405 # print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d']
1406
1407 # app = wx.PyWidgetTester(size = (200, 50))
1408 # for placeholder in known_placeholders:
1409 # print placeholder, "=", handler[placeholder]
1410
1411 # ph = 'progress_notes::ap'
1412 # print '%s: %s' % (ph, handler[ph])
1413
1414 #--------------------------------------------------------
1416 from Gnumed.pycommon import gmScriptingListener
1417 import xmlrpclib
1418 listener = gmScriptingListener.cScriptingListener(macro_executor = cMacroPrimitives(personality='unit test'), port=9999)
1419
1420 s = xmlrpclib.ServerProxy('http://localhost:9999')
1421 print "should fail:", s.attach()
1422 print "should fail:", s.attach('wrong cookie')
1423 print "should work:", s.version()
1424 print "should fail:", s.raise_gnumed()
1425 print "should fail:", s.raise_notebook_plugin('test plugin')
1426 print "should fail:", s.lock_into_patient('kirk, james')
1427 print "should fail:", s.unlock_patient()
1428 status, conn_auth = s.attach('unit test')
1429 print "should work:", status, conn_auth
1430 print "should work:", s.version()
1431 print "should work:", s.raise_gnumed(conn_auth)
1432 status, pat_auth = s.lock_into_patient(conn_auth, 'kirk, james')
1433 print "should work:", status, pat_auth
1434 print "should fail:", s.unlock_patient(conn_auth, 'bogus patient unlock cookie')
1435 print "should work", s.unlock_patient(conn_auth, pat_auth)
1436 data = {'firstname': 'jame', 'lastnames': 'Kirk', 'gender': 'm'}
1437 status, pat_auth = s.lock_into_patient(conn_auth, data)
1438 print "should work:", status, pat_auth
1439 print "should work", s.unlock_patient(conn_auth, pat_auth)
1440 print s.detach('bogus detach cookie')
1441 print s.detach(conn_auth)
1442 del s
1443
1444 listener.shutdown()
1445 #--------------------------------------------------------
1447
1448 import re as regex
1449
1450 tests = [
1451 ' $<lastname>$ ',
1452 ' $<lastname::::3>$ ',
1453
1454 # should fail:
1455 '$<date_of_birth::%Y-%m-%d>$',
1456 '$<date_of_birth::%Y-%m-%d::3>$',
1457 '$<date_of_birth::%Y-%m-%d::>$',
1458
1459 '$<adr_location::home::35>$',
1460 '$<gender_mapper::male//female//other::5>$',
1461 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\\n::50>$',
1462 '$<allergy_list::%(descriptor)s, >$',
1463
1464 '\\noindent Patient: $<lastname>$, $<firstname>$',
1465 '$<allergies::%(descriptor)s & %(l10n_type)s & {\\footnotesize %(reaction)s} \tabularnewline \hline >$',
1466 '$<current_meds:: \item[%(substance)s] {\\footnotesize (%(brand)s)} %(preparation)s %(amount)s%(unit)s: %(schedule)s >$'
1467 ]
1468
1469 tests = [
1470
1471 'junk $<lastname::::3>$ junk',
1472 'junk $<lastname::abc::3>$ junk',
1473 'junk $<lastname::abc>$ junk',
1474 'junk $<lastname>$ junk',
1475
1476 'junk $<lastname>$ junk $<firstname>$ junk',
1477 'junk $<lastname::abc>$ junk $<fiststname::abc>$ junk',
1478 'junk $<lastname::abc::3>$ junk $<firstname::abc::3>$ junk',
1479 'junk $<lastname::::3>$ junk $<firstname::::3>$ junk'
1480
1481 ]
1482
1483 print "testing placeholder regex:", default_placeholder_regex
1484 print ""
1485
1486 for t in tests:
1487 print 'line: "%s"' % t
1488 print "placeholders:"
1489 for p in regex.findall(default_placeholder_regex, t, regex.IGNORECASE):
1490 print ' => "%s"' % p
1491 print " "
1492 #--------------------------------------------------------
1494
1495 phs = [
1496 #u'emr_journal::soapu //%(clin_when)s %(modified_by)s %(soap_cat)s %(narrative)s//110::',
1497 #u'free_text::tex//placeholder test::9999',
1498 #u'soap_for_encounters:://::9999',
1499 #u'soap_a',,
1500 #u'encounter_list::%(started)s: %(assessment_of_encounter)s::30',
1501 #u'patient_comm::homephone::1234',
1502 #u'$<patient_address::work::1234>$',
1503 #u'adr_region::home::1234',
1504 #u'adr_country::home::1234',
1505 #u'external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234',
1506 #u'primary_praxis_provider',
1507 #u'current_provider',
1508 #u'current_provider_external_id::Starfleet Serial Number//Star Fleet Central Staff Office::1234',
1509 #u'current_provider_external_id::LANR//LÄK::1234'
1510 #u'primary_praxis_provider_external_id::LANR//LÄK::1234'
1511 u'form_name_long::::1234',
1512 u'form_name_long::::5',
1513 u'form_name_long::::',
1514 #u'form_version::::5',
1515 #u'$<current_meds::\item %(brand)s %(preparation)s (%(substance)s) from %(started)s for %(duration)s as %(schedule)s until %(discontinued)s\\n::250>$',
1516 #u'$<vaccination_history::%(date_given)s: %(vaccine)s [%(batch_no)s] %(l10n_indications)s::250>$',
1517 #u'$<date_of_birth::%Y %B %d::20>$',
1518 ]
1519
1520 handler = gmPlaceholderHandler()
1521 handler.debug = True
1522
1523 gmStaff.set_current_provider_to_logged_on_user()
1524 pat = gmPersonSearch.ask_for_patient()
1525 if pat is None:
1526 return
1527
1528 gmPatSearchWidgets.set_active_patient(patient = pat)
1529
1530 app = wx.PyWidgetTester(size = (200, 50))
1531 handler.set_placeholder('form_name_long', 'ein Testformular')
1532 for ph in phs:
1533 print ph
1534 print "result:"
1535 print '%s' % handler[ph]
1536 handler.unset_placeholder('form_name_long')
1537 #--------------------------------------------------------
1538
1539 #test_placeholders()
1540 #test_new_variant_placeholders()
1541 #test_scripting()
1542 #test_placeholder_regex()
1543 test_placeholder()
1544
1545 #=====================================================================
1546
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri May 11 03:58:50 2012 | http://epydoc.sourceforge.net |