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