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