| Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed macro primitives.
2
3 This module implements functions a macro can legally use.
4 """
5 #=====================================================================
6 # $Source: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmMacro.py,v $
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, gmGuiBroker, gmExceptions, gmBorg, gmTools
19 from Gnumed.pycommon import gmCfg2, gmDateTime
20 from Gnumed.business import gmPerson, gmDemographicRecord
21 from Gnumed.wxpython import gmGuiHelpers, gmPlugin, gmPatSearchWidgets, gmNarrativeWidgets
22
23
24 _log = logging.getLogger('gm.scripting')
25 _cfg = gmCfg2.gmCfgData()
26
27 #=====================================================================
28 known_placeholders = [
29 'lastname',
30 'firstname',
31 'title',
32 'date_of_birth',
33 'progress_notes',
34 'soap',
35 'soap_s',
36 'soap_o',
37 'soap_a',
38 'soap_p',
39 u'client_version',
40 u'current_provider',
41 u'allergy_state'
42 ]
43
44
45 # those must satisfy the pattern "$name::args::optional length$" when used
46 known_variant_placeholders = [
47 u'soap',
48 u'progress_notes',
49 u'date_of_birth',
50 u'adr_street', # "data" holds: type of address
51 u'adr_number',
52 u'adr_location',
53 u'adr_postcode',
54 u'gender_mapper', # "data" holds: value for male // value for female
55 u'current_meds', # "data" holds: line template
56 u'today', # "data" holds: strftime format
57 u'tex_escape', # "data" holds: string to escape
58 u'allergies', # "data" holds: line template, one allergy per line
59 u'allergy_list', # "data" holds: template per allergy, allergies on one line
60 u'problems', # "data" holds: line template, one problem per line
61 u'name' # "data" holds: template for name parts arrangement
62 ]
63
64 default_placeholder_regex = r'\$<.+?>\$' # this one works (except that OOo cannot be non-greedy |-( )
65
66 #_regex_parts = [
67 # r'\$<\w+::.*(?::)\d+>\$',
68 # r'\$<\w+::.+(?!>\$)>\$',
69 # r'\$<\w+?>\$'
70 #]
71 #default_placeholder_regex = r'|'.join(_regex_parts)
72
73 default_placeholder_start = u'$<'
74 default_placeholder_end = u'>$'
75 #=====================================================================
77 """Replaces placeholders in forms, fields, etc.
78
79 - patient related placeholders operate on the currently active patient
80 - is passed to the forms handling code, for example
81
82 Note that this cannot be called from a non-gui thread unless
83 wrapped in wx.CallAfter.
84
85 There are currently three types of placeholders:
86
87 simple static placeholders
88 - those are listed in known_placeholders
89 - they are used as-is
90
91 extended static placeholders
92 - those are like the static ones but have "::::<NUMBER>" appended
93 where <NUMBER> is the maximum length
94
95 variant placeholders
96 - those are listed in known_variant_placeholders
97 - they are parsed into placeholder, data, and maximum length
98 - the length is optional
99 - data is passed to the handler
100 """
102
103 self.pat = gmPerson.gmCurrentPatient()
104 self.debug = False
105
106 self.invalid_placeholder_template = _('invalid placeholder [%s]')
107 #--------------------------------------------------------
108 # __getitem__ API
109 #--------------------------------------------------------
111 """Map self['placeholder'] to self.placeholder.
112
113 This is useful for replacing placeholders parsed out
114 of documents as strings.
115
116 Unknown/invalid placeholders still deliver a result but
117 it will be glaringly obvious if debugging is enabled.
118 """
119 _log.debug('replacing [%s]', placeholder)
120
121 original_placeholder = placeholder
122
123 if placeholder.startswith(default_placeholder_start):
124 placeholder = placeholder[len(default_placeholder_start):]
125 if placeholder.endswith(default_placeholder_end):
126 placeholder = placeholder[:-len(default_placeholder_end)]
127 else:
128 _log.debug('placeholder must either start with [%s] and end with [%s] or neither of both', default_placeholder_start, default_placeholder_end)
129 if self.debug:
130 return self.invalid_placeholder_template % original_placeholder
131 return None
132
133 # simple static placeholder ?
134 if placeholder in known_placeholders:
135 return getattr(self, placeholder)
136
137 # extended static placeholder ?
138 parts = placeholder.split('::::', 1)
139 if len(parts) == 2:
140 name, lng = parts
141 try:
142 return getattr(self, name)[:int(lng)]
143 except:
144 _log.exception('placeholder handling error: %s', original_placeholder)
145 if self.debug:
146 return self.invalid_placeholder_template % original_placeholder
147 return None
148
149 # variable placeholders
150 parts = placeholder.split('::', 2)
151 if len(parts) == 2:
152 name, data = parts
153 lng = None
154 elif len(parts) == 3:
155 name, data, lng = parts
156 try:
157 lng = int(lng)
158 except:
159 _log.exception('placeholder length definition error: %s, discarding length', original_placeholder)
160 lng = None
161 else:
162 _log.warning('invalid placeholder layout: %s', original_placeholder)
163 if self.debug:
164 return self.invalid_placeholder_template % original_placeholder
165 return None
166
167 handler = getattr(self, '_get_variant_%s' % name, None)
168 if handler is None:
169 _log.warning('no handler <_get_variant_%s> for placeholder %s', name, original_placeholder)
170 if self.debug:
171 return self.invalid_placeholder_template % original_placeholder
172 return None
173
174 try:
175 if lng is None:
176 return handler(data = data)
177 return handler(data = data)[:lng]
178 except:
179 _log.exception('placeholder handling error: %s', original_placeholder)
180 if self.debug:
181 return self.invalid_placeholder_template % original_placeholder
182 return None
183
184 _log.error('something went wrong, should never get here')
185 return None
186 #--------------------------------------------------------
187 # properties actually handling placeholders
188 #--------------------------------------------------------
189 # property helpers
190 #--------------------------------------------------------
194 #--------------------------------------------------------
196 return self.pat.get_active_name()['lastnames']
197 #--------------------------------------------------------
199 return self.pat.get_active_name()['firstnames']
200 #--------------------------------------------------------
203 #--------------------------------------------------------
205 return self._get_variant_date_of_birth(data='%x')
206 #--------------------------------------------------------
209 #--------------------------------------------------------
211 return self._get_variant_soap(data = u's')
212 #--------------------------------------------------------
214 return self._get_variant_soap(data = u'o')
215 #--------------------------------------------------------
217 return self._get_variant_soap(data = u'a')
218 #--------------------------------------------------------
220 return self._get_variant_soap(data = u'p')
221 #--------------------------------------------------------
224 #--------------------------------------------------------
226 return gmTools.coalesce (
227 _cfg.get(option = u'client_version'),
228 u'%s' % self.__class__.__name__
229 )
230 #--------------------------------------------------------
232 prov = gmPerson.gmCurrentProvider()
233
234 title = gmTools.coalesce (
235 prov['title'],
236 gmPerson.map_gender2salutation(prov['gender'])
237 )
238
239 tmp = u'%s %s. %s' % (
240 title,
241 prov['firstnames'][:1],
242 prov['lastnames']
243 )
244
245 return tmp
246 #--------------------------------------------------------
248 allg_state = self.pat.get_emr().allergy_state
249 tmp = u'%s (%s)' % (
250 allg_state.state_string,
251 allg_state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
252 )
253 return tmp
254 #--------------------------------------------------------
255 # property definitions for static placeholders
256 #--------------------------------------------------------
257 placeholder_regex = property(lambda x: default_placeholder_regex, _setter_noop)
258
259 # placeholders
260 lastname = property(_get_lastname, _setter_noop)
261 firstname = property(_get_firstname, _setter_noop)
262 title = property(_get_title, _setter_noop)
263 date_of_birth = property(_get_dob, _setter_noop)
264
265 progress_notes = property(_get_progress_notes, _setter_noop)
266 soap = property(_get_progress_notes, _setter_noop)
267 soap_s = property(_get_soap_s, _setter_noop)
268 soap_o = property(_get_soap_o, _setter_noop)
269 soap_a = property(_get_soap_a, _setter_noop)
270 soap_p = property(_get_soap_p, _setter_noop)
271 soap_admin = property(_get_soap_admin, _setter_noop)
272
273 allergy_state = property(_get_allergy_state, _setter_noop)
274
275 client_version = property(_get_client_version, _setter_noop)
276
277 current_provider = property(_get_current_provider, _setter_noop)
278 #--------------------------------------------------------
279 # variant handlers
280 #--------------------------------------------------------
283 #--------------------------------------------------------
285 if data is None:
286 cats = list(data)
287 template = u'%s'
288 else:
289 parts = data.split('//', 2)
290 if len(parts) == 1:
291 cats = list(parts)
292 template = u'%s'
293 else:
294 cats = list(parts[0])
295 template = parts[1]
296
297 narr = gmNarrativeWidgets.select_narrative_from_episodes(soap_cats = cats)
298
299 if len(narr) == 0:
300 return u''
301
302 narr = [ template % n['narrative'] for n in narr ]
303
304 return u'\n'.join(narr)
305 #--------------------------------------------------------
307 if data is None:
308 return [_('template is missing')]
309
310 name = self.pat.get_active_name()
311
312 parts = {
313 'title': gmTools.coalesce(name['title'], u''),
314 'firstnames': name['firstnames'],
315 'lastnames': name['lastnames'],
316 'preferred': gmTools.coalesce (
317 initial = name['preferred'],
318 instead = u' ',
319 template_initial = u' "%s" '
320 )
321 }
322
323 return data % parts
324 #--------------------------------------------------------
327 #--------------------------------------------------------
328 # FIXME: extend to all supported genders
330 values = data.split('//', 2)
331
332 if len(values) == 2:
333 male_value, female_value = values
334 other_value = u'<unkown gender>'
335 elif len(values) == 3:
336 male_value, female_value, other_value = values
337 else:
338 return _('invalid gender mapping layout: [%s]') % data
339
340 if self.pat['gender'] == u'm':
341 return male_value
342
343 if self.pat['gender'] == u'f':
344 return female_value
345
346 return other_value
347 #--------------------------------------------------------
349 # if data == u'?':
350 # types = xxxxxxxxxxx
351 adrs = self.pat.get_addresses(address_type=data)
352 if len(adrs) == 0:
353 return _('no street for address type [%s]') % data
354 return adrs[0]['street']
355 #--------------------------------------------------------
357 adrs = self.pat.get_addresses(address_type=data)
358 if len(adrs) == 0:
359 return _('no number for address type [%s]') % data
360 return adrs[0]['number']
361 #--------------------------------------------------------
363 adrs = self.pat.get_addresses(address_type=data)
364 if len(adrs) == 0:
365 return _('no location for address type [%s]') % data
366 return adrs[0]['urb']
367 #--------------------------------------------------------
369 adrs = self.pat.get_addresses(address_type=data)
370 if len(adrs) == 0:
371 return _('no postcode for address type [%s]') % data
372 return adrs[0]['postcode']
373 #--------------------------------------------------------
375 if data is None:
376 return [_('template is missing')]
377
378 template, separator = data.split('//', 2)
379
380 emr = self.pat.get_emr()
381 return separator.join([ template % a for a in emr.get_allergies() ])
382 #--------------------------------------------------------
384
385 if data is None:
386 return [_('template is missing')]
387
388 emr = self.pat.get_emr()
389 return u'\n'.join([ data % a for a in emr.get_allergies() ])
390 #--------------------------------------------------------
392
393 if data is None:
394 return [_('template is missing')]
395
396 emr = self.pat.get_emr()
397 current_meds = emr.get_current_substance_intake (
398 include_inactive = False,
399 include_unapproved = False,
400 order_by = u'brand, substance'
401 )
402
403 return u'\n'.join([ data % m for m in current_meds ])
404 #--------------------------------------------------------
406
407 if data is None:
408 return [_('template is missing')]
409
410 probs = self.pat.get_emr().get_problems()
411
412 return u'\n'.join([ data % p for p in probs ])
413 #--------------------------------------------------------
416 #--------------------------------------------------------
419 #--------------------------------------------------------
420 # internal helpers
421 #--------------------------------------------------------
422
423 #=====================================================================
425 """Functions a macro can legally use.
426
427 An instance of this class is passed to the GNUmed scripting
428 listener. Hence, all actions a macro can legally take must
429 be defined in this class. Thus we achieve some screening for
430 security and also thread safety handling.
431 """
432 #-----------------------------------------------------------------
434 if personality is None:
435 raise gmExceptions.ConstructorError, 'must specify personality'
436 self.__personality = personality
437 self.__attached = 0
438 self._get_source_personality = None
439 self.__user_done = False
440 self.__user_answer = 'no answer yet'
441 self.__pat = gmPerson.gmCurrentPatient()
442
443 self.__auth_cookie = str(random.random())
444 self.__pat_lock_cookie = str(random.random())
445 self.__lock_after_load_cookie = str(random.random())
446
447 _log.info('slave mode personality is [%s]', personality)
448 #-----------------------------------------------------------------
449 # public API
450 #-----------------------------------------------------------------
452 if self.__attached:
453 _log.error('attach with [%s] rejected, already serving a client', personality)
454 return (0, _('attach rejected, already serving a client'))
455 if personality != self.__personality:
456 _log.error('rejecting attach to personality [%s], only servicing [%s]' % (personality, self.__personality))
457 return (0, _('attach to personality [%s] rejected') % personality)
458 self.__attached = 1
459 self.__auth_cookie = str(random.random())
460 return (1, self.__auth_cookie)
461 #-----------------------------------------------------------------
463 if not self.__attached:
464 return 1
465 if auth_cookie != self.__auth_cookie:
466 _log.error('rejecting detach() with cookie [%s]' % auth_cookie)
467 return 0
468 self.__attached = 0
469 return 1
470 #-----------------------------------------------------------------
472 if not self.__attached:
473 return 1
474 self.__user_done = False
475 # FIXME: use self.__sync_cookie for syncing with user interaction
476 wx.CallAfter(self._force_detach)
477 return 1
478 #-----------------------------------------------------------------
480 ver = _cfg.get(option = u'client_version')
481 return "GNUmed %s, %s $Revision: 1.51 $" % (ver, self.__class__.__name__)
482 #-----------------------------------------------------------------
484 """Shuts down this client instance."""
485 if not self.__attached:
486 return 0
487 if auth_cookie != self.__auth_cookie:
488 _log.error('non-authenticated shutdown_gnumed()')
489 return 0
490 wx.CallAfter(self._shutdown_gnumed, forced)
491 return 1
492 #-----------------------------------------------------------------
494 """Raise ourselves to the top of the desktop."""
495 if not self.__attached:
496 return 0
497 if auth_cookie != self.__auth_cookie:
498 _log.error('non-authenticated raise_gnumed()')
499 return 0
500 return "cMacroPrimitives.raise_gnumed() not implemented"
501 #-----------------------------------------------------------------
503 if not self.__attached:
504 return 0
505 if auth_cookie != self.__auth_cookie:
506 _log.error('non-authenticated get_loaded_plugins()')
507 return 0
508 gb = gmGuiBroker.GuiBroker()
509 return gb['horstspace.notebook.gui'].keys()
510 #-----------------------------------------------------------------
512 """Raise a notebook plugin within GNUmed."""
513 if not self.__attached:
514 return 0
515 if auth_cookie != self.__auth_cookie:
516 _log.error('non-authenticated raise_notebook_plugin()')
517 return 0
518 # FIXME: use semaphore
519 wx.CallAfter(gmPlugin.raise_notebook_plugin, a_plugin)
520 return 1
521 #-----------------------------------------------------------------
523 """Load external patient, perhaps create it.
524
525 Callers must use get_user_answer() to get status information.
526 It is unsafe to proceed without knowing the completion state as
527 the controlled client may be waiting for user input from a
528 patient selection list.
529 """
530 if not self.__attached:
531 return (0, _('request rejected, you are not attach()ed'))
532 if auth_cookie != self.__auth_cookie:
533 _log.error('non-authenticated load_patient_from_external_source()')
534 return (0, _('rejected load_patient_from_external_source(), not authenticated'))
535 if self.__pat.locked:
536 _log.error('patient is locked, cannot load from external source')
537 return (0, _('current patient is locked'))
538 self.__user_done = False
539 wx.CallAfter(self._load_patient_from_external_source)
540 self.__lock_after_load_cookie = str(random.random())
541 return (1, self.__lock_after_load_cookie)
542 #-----------------------------------------------------------------
544 if not self.__attached:
545 return (0, _('request rejected, you are not attach()ed'))
546 if auth_cookie != self.__auth_cookie:
547 _log.error('non-authenticated lock_load_patient()')
548 return (0, _('rejected lock_load_patient(), not authenticated'))
549 # FIXME: ask user what to do about wrong cookie
550 if lock_after_load_cookie != self.__lock_after_load_cookie:
551 _log.warning('patient lock-after-load request rejected due to wrong cookie [%s]' % lock_after_load_cookie)
552 return (0, 'patient lock-after-load request rejected, wrong cookie provided')
553 self.__pat.locked = True
554 self.__pat_lock_cookie = str(random.random())
555 return (1, self.__pat_lock_cookie)
556 #-----------------------------------------------------------------
558 if not self.__attached:
559 return (0, _('request rejected, you are not attach()ed'))
560 if auth_cookie != self.__auth_cookie:
561 _log.error('non-authenticated lock_into_patient()')
562 return (0, _('rejected lock_into_patient(), not authenticated'))
563 if self.__pat.locked:
564 _log.error('patient is already locked')
565 return (0, _('already locked into a patient'))
566 searcher = gmPerson.cPatientSearcher_SQL()
567 if type(search_params) == types.DictType:
568 idents = searcher.get_identities(search_dict=search_params)
569 print "must use dto, not search_dict"
570 print xxxxxxxxxxxxxxxxx
571 else:
572 idents = searcher.get_identities(search_term=search_params)
573 if idents is None:
574 return (0, _('error searching for patient with [%s]/%s') % (search_term, search_dict))
575 if len(idents) == 0:
576 return (0, _('no patient found for [%s]/%s') % (search_term, search_dict))
577 # FIXME: let user select patient
578 if len(idents) > 1:
579 return (0, _('several matching patients found for [%s]/%s') % (search_term, search_dict))
580 if not gmPatSearchWidgets.set_active_patient(patient = idents[0]):
581 return (0, _('cannot activate patient [%s] (%s/%s)') % (str(idents[0]), search_term, search_dict))
582 self.__pat.locked = True
583 self.__pat_lock_cookie = str(random.random())
584 return (1, self.__pat_lock_cookie)
585 #-----------------------------------------------------------------
587 if not self.__attached:
588 return (0, _('request rejected, you are not attach()ed'))
589 if auth_cookie != self.__auth_cookie:
590 _log.error('non-authenticated unlock_patient()')
591 return (0, _('rejected unlock_patient, not authenticated'))
592 # we ain't locked anyways, so succeed
593 if not self.__pat.locked:
594 return (1, '')
595 # FIXME: ask user what to do about wrong cookie
596 if unlock_cookie != self.__pat_lock_cookie:
597 _log.warning('patient unlock request rejected due to wrong cookie [%s]' % unlock_cookie)
598 return (0, 'patient unlock request rejected, wrong cookie provided')
599 self.__pat.locked = False
600 return (1, '')
601 #-----------------------------------------------------------------
602 - def assume_staff_identity(self, auth_cookie = None, staff_name = "Dr.Jekyll", staff_creds = None):
603 if not self.__attached:
604 return 0
605 if auth_cookie != self.__auth_cookie:
606 _log.error('non-authenticated select_identity()')
607 return 0
608 return "cMacroPrimitives.assume_staff_identity() not implemented"
609 #-----------------------------------------------------------------
611 if not self.__user_done:
612 return (0, 'still waiting')
613 self.__user_done = False
614 return (1, self.__user_answer)
615 #-----------------------------------------------------------------
616 # internal API
617 #-----------------------------------------------------------------
619 msg = _(
620 'Someone tries to forcibly break the existing\n'
621 'controlling connection. This may or may not\n'
622 'have legitimate reasons.\n\n'
623 'Do you want to allow breaking the connection ?'
624 )
625 can_break_conn = gmGuiHelpers.gm_show_question (
626 aMessage = msg,
627 aTitle = _('forced detach attempt')
628 )
629 if can_break_conn:
630 self.__user_answer = 1
631 else:
632 self.__user_answer = 0
633 self.__user_done = True
634 if can_break_conn:
635 self.__pat.locked = False
636 self.__attached = 0
637 return 1
638 #-----------------------------------------------------------------
640 top_win = wx.GetApp().GetTopWindow()
641 if forced:
642 top_win.Destroy()
643 else:
644 top_win.Close()
645 #-----------------------------------------------------------------
647 patient = gmPatSearchWidgets.get_person_from_external_sources(search_immediately = True, activate_immediately = True)
648 if patient is not None:
649 self.__user_answer = 1
650 else:
651 self.__user_answer = 0
652 self.__user_done = True
653 return 1
654 #=====================================================================
655 # main
656 #=====================================================================
657 if __name__ == '__main__':
658
659 gmI18N.activate_locale()
660 gmI18N.install_domain()
661
662 #--------------------------------------------------------
664 handler = gmPlaceholderHandler()
665 handler.debug = True
666
667 for placeholder in ['a', 'b']:
668 print handler[placeholder]
669
670 pat = gmPerson.ask_for_patient()
671 if pat is None:
672 return
673
674 gmPatSearchWidgets.set_active_patient(patient = pat)
675
676 print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d']
677
678 app = wx.PyWidgetTester(size = (200, 50))
679 for placeholder in known_placeholders:
680 print placeholder, "=", handler[placeholder]
681
682 ph = 'progress_notes::ap'
683 print '%s: %s' % (ph, handler[ph])
684 #--------------------------------------------------------
686
687 tests = [
688 # should work:
689 '$<lastname>$',
690 '$<lastname::::3>$',
691 '$<name::%(title)s %(firstnames)s%(preferred)s%(lastnames)s>$',
692
693 # should fail:
694 'lastname',
695 '$<lastname',
696 '$<lastname::',
697 '$<lastname::>$',
698 '$<lastname::abc>$',
699 '$<lastname::abc::>$',
700 '$<lastname::abc::3>$',
701 '$<lastname::abc::xyz>$',
702 '$<lastname::::>$',
703 '$<lastname::::xyz>$',
704
705 '$<date_of_birth::%Y-%m-%d>$',
706 '$<date_of_birth::%Y-%m-%d::3>$',
707 '$<date_of_birth::%Y-%m-%d::>$',
708
709 # should work:
710 '$<adr_location::home::35>$',
711 '$<gender_mapper::male//female//other::5>$',
712 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\n::50>$',
713 '$<allergy_list::%(descriptor)s, >$'
714
715 # 'firstname',
716 # 'title',
717 # 'date_of_birth',
718 # 'progress_notes',
719 # 'soap',
720 # 'soap_s',
721 # 'soap_o',
722 # 'soap_a',
723 # 'soap_p',
724
725 # 'soap',
726 # 'progress_notes',
727 # 'date_of_birth'
728 ]
729
730 pat = gmPerson.ask_for_patient()
731 if pat is None:
732 return
733
734 gmPatSearchWidgets.set_active_patient(patient = pat)
735
736 handler = gmPlaceholderHandler()
737 handler.debug = True
738
739 for placeholder in tests:
740 print placeholder, "=>", handler[placeholder]
741 print "--------------"
742 raw_input()
743
744 # print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d']
745
746 # app = wx.PyWidgetTester(size = (200, 50))
747 # for placeholder in known_placeholders:
748 # print placeholder, "=", handler[placeholder]
749
750 # ph = 'progress_notes::ap'
751 # print '%s: %s' % (ph, handler[ph])
752
753 #--------------------------------------------------------
755 from Gnumed.pycommon import gmScriptingListener
756 import xmlrpclib
757 listener = gmScriptingListener.cScriptingListener(macro_executor = cMacroPrimitives(personality='unit test'), port=9999)
758
759 s = xmlrpclib.ServerProxy('http://localhost:9999')
760 print "should fail:", s.attach()
761 print "should fail:", s.attach('wrong cookie')
762 print "should work:", s.version()
763 print "should fail:", s.raise_gnumed()
764 print "should fail:", s.raise_notebook_plugin('test plugin')
765 print "should fail:", s.lock_into_patient('kirk, james')
766 print "should fail:", s.unlock_patient()
767 status, conn_auth = s.attach('unit test')
768 print "should work:", status, conn_auth
769 print "should work:", s.version()
770 print "should work:", s.raise_gnumed(conn_auth)
771 status, pat_auth = s.lock_into_patient(conn_auth, 'kirk, james')
772 print "should work:", status, pat_auth
773 print "should fail:", s.unlock_patient(conn_auth, 'bogus patient unlock cookie')
774 print "should work", s.unlock_patient(conn_auth, pat_auth)
775 data = {'firstname': 'jame', 'lastnames': 'Kirk', 'gender': 'm'}
776 status, pat_auth = s.lock_into_patient(conn_auth, data)
777 print "should work:", status, pat_auth
778 print "should work", s.unlock_patient(conn_auth, pat_auth)
779 print s.detach('bogus detach cookie')
780 print s.detach(conn_auth)
781 del s
782
783 listener.shutdown()
784 #--------------------------------------------------------
786
787 import re as regex
788
789 tests = [
790 ' $<lastname>$ ',
791 ' $<lastname::::3>$ ',
792
793 # should fail:
794 '$<date_of_birth::%Y-%m-%d>$',
795 '$<date_of_birth::%Y-%m-%d::3>$',
796 '$<date_of_birth::%Y-%m-%d::>$',
797
798 '$<adr_location::home::35>$',
799 '$<gender_mapper::male//female//other::5>$',
800 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\\n::50>$',
801 '$<allergy_list::%(descriptor)s, >$',
802
803 '\\noindent Patient: $<lastname>$, $<firstname>$',
804 '$<allergies::%(descriptor)s & %(l10n_type)s & {\\footnotesize %(reaction)s} \tabularnewline \hline >$',
805 '$<current_meds:: \item[%(substance)s] {\\footnotesize (%(brand)s)} %(preparation)s %(strength)s: %(schedule)s >$'
806 ]
807
808 tests = [
809
810 'junk $<lastname::::3>$ junk',
811 'junk $<lastname::abc::3>$ junk',
812 'junk $<lastname::abc>$ junk',
813 'junk $<lastname>$ junk',
814
815 'junk $<lastname>$ junk $<firstname>$ junk',
816 'junk $<lastname::abc>$ junk $<fiststname::abc>$ junk',
817 'junk $<lastname::abc::3>$ junk $<firstname::abc::3>$ junk',
818 'junk $<lastname::::3>$ junk $<firstname::::3>$ junk'
819
820 ]
821
822 print "testing placeholder regex:", default_placeholder_regex
823 print ""
824
825 for t in tests:
826 print 'line: "%s"' % t
827 print "placeholders:"
828 for p in regex.findall(default_placeholder_regex, t, regex.IGNORECASE):
829 print ' => "%s"' % p
830 print " "
831 #--------------------------------------------------------
832
833 if len(sys.argv) > 1 and sys.argv[1] == 'test':
834 #test_placeholders()
835 test_new_variant_placeholders()
836 #test_scripting()
837 #test_placeholder_regex()
838
839 #=====================================================================
840 # $Log: gmMacro.py,v $
841 # Revision 1.51 2010/01/31 18:18:28 ncq
842 # - add name variant placeholder
843 # - try to work around OOo being always greedy
844 #
845 # Revision 1.50 2010/01/21 08:44:46 ncq
846 # - implement new placeholders, improve others
847 #
848 # Revision 1.49 2010/01/15 12:43:46 ncq
849 # - tex-escape placeholder
850 # - return safe substitute if real client version unavailable
851 #
852 # Revision 1.48 2009/12/22 12:01:58 ncq
853 # - escape dollar signs as they frequently mean something
854 #
855 # Revision 1.47 2009/12/21 20:28:02 ncq
856 # - allergies placeholder
857 #
858 # Revision 1.46 2009/12/21 15:11:30 ncq
859 # - client_version, current_provider, today, current_meds
860 # - placeholder regex must be non-greedy to support several per line
861 # - improved logging
862 # - don't throw exceptions on placeholder substitution, rather return hint
863 #
864 # Revision 1.45 2009/09/29 13:18:28 ncq
865 # - implement address placeholders
866 # - implement gender mapper placeholder
867 #
868 # Revision 1.44 2009/06/04 16:30:30 ncq
869 # - use set active patient from pat search widgets
870 #
871 # Revision 1.43 2009/03/10 14:23:32 ncq
872 # - support new style placeholders and test
873 #
874 # Revision 1.42 2009/01/15 11:40:20 ncq
875 # - better logging
876 #
877 # Revision 1.41 2008/03/20 15:30:37 ncq
878 # - fix misplaced %
879 #
880 # Revision 1.40 2008/03/09 20:16:32 ncq
881 # *** empty log message ***
882 #
883 # Revision 1.39 2008/03/05 22:30:14 ncq
884 # - new style logging
885 #
886 # Revision 1.38 2007/12/03 20:45:16 ncq
887 # - add variant placeholder handling ! :-)
888 #
889 # Revision 1.37 2007/11/05 12:10:21 ncq
890 # - support admin soap type
891 #
892 # Revision 1.36 2007/10/19 12:52:00 ncq
893 # - immediately search for patient matches
894 #
895 # Revision 1.35 2007/09/16 22:40:46 ncq
896 # - fix soap_* placeholder handling
897 #
898 # Revision 1.34 2007/09/09 19:17:44 ncq
899 # - add a bunch of placeholders regarding SOAP notes
900 #
901 # Revision 1.33 2007/08/29 22:09:32 ncq
902 # - narrative widgets factored out
903 #
904 # Revision 1.32 2007/08/13 21:59:54 ncq
905 # - add placeholder handler
906 # - add progress_notes placeholder
907 # - improved test suite
908 #
909 # Revision 1.31 2007/07/17 21:44:24 ncq
910 # - use patient.locked properly
911 #
912 # Revision 1.30 2007/07/11 21:09:54 ncq
913 # - use curr_pat.locked
914 #
915 # Revision 1.29 2007/07/03 16:00:56 ncq
916 # - remove unneeded import
917 #
918 # Revision 1.28 2007/01/21 12:21:38 ncq
919 # - comment on search_dict -> dto
920 #
921 # Revision 1.27 2006/12/25 22:54:44 ncq
922 # - comment fix
923 #
924 # Revision 1.26 2006/07/22 12:15:08 ncq
925 # - add missing import
926 #
927 # Revision 1.25 2006/07/22 10:04:51 ncq
928 # - cleanup
929 # - pre-init all attributes so connectors won't kill the GNUmed slave
930 # with stupid AttributeExceptions
931 # - add lock_loaded_patient()
932 #
933 # Revision 1.24 2006/07/21 14:47:19 ncq
934 # - cleanup
935 # - add (_)load_patient_from_external_source()
936 # - improve testing
937 #
938 # Revision 1.23 2006/05/04 09:49:20 ncq
939 # - get_clinical_record() -> get_emr()
940 # - adjust to changes in set_active_patient()
941 # - need explicit set_active_patient() after ask_for_patient() if wanted
942 #
943 # Revision 1.22 2005/11/28 23:07:34 ncq
944 # - add shutdown_gnumed()
945 #
946 # Revision 1.21 2005/11/27 22:08:38 ncq
947 # - patient searcher has somewhat changed so adapt
948 #
949 # Revision 1.20 2005/11/27 20:38:10 ncq
950 # - properly import wx
951 #
952 # Revision 1.19 2005/09/28 21:27:30 ncq
953 # - a lot of wx2.6-ification
954 #
955 # Revision 1.18 2005/09/28 15:57:48 ncq
956 # - a whole bunch of wx.Foo -> wx.Foo
957 #
958 # Revision 1.17 2005/09/27 20:44:59 ncq
959 # - wx.wx* -> wx.*
960 #
961 # Revision 1.16 2005/01/31 10:37:26 ncq
962 # - gmPatient.py -> gmPerson.py
963 #
964 # Revision 1.15 2004/09/13 09:38:29 ncq
965 # - allow to wait for user interaction in controlled GnuMed instance
966 # despite having to use wxCallAfter by waiting on a semaphore
967 #
968 # Revision 1.14 2004/07/24 17:13:25 ncq
969 # - main.plugins.gui now horstspace.notebook.gui
970 #
971 # Revision 1.13 2004/06/25 13:28:00 ncq
972 # - logically separate notebook and clinical window plugins completely
973 #
974 # Revision 1.12 2004/06/01 07:59:55 ncq
975 # - comments improved
976 #
977 # Revision 1.11 2004/03/20 19:48:07 ncq
978 # - adapt to flat id list from get_patient_ids
979 #
980 # Revision 1.10 2004/03/20 17:54:18 ncq
981 # - lock_into_patient now supports dicts and strings
982 # - fix unit test
983 #
984 # Revision 1.9 2004/03/12 13:22:38 ncq
985 # - comment on semaphore for GUI actions
986 #
987 # Revision 1.8 2004/03/05 11:22:35 ncq
988 # - import from Gnumed.<pkg>
989 #
990 # Revision 1.7 2004/02/25 09:46:22 ncq
991 # - import from pycommon now, not python-common
992 #
993 # Revision 1.6 2004/02/17 10:45:30 ncq
994 # - return authentication cookie from attach()
995 # - use that cookie in all RPCs
996 # - add assume_staff_identity()
997 #
998 # Revision 1.5 2004/02/12 23:57:22 ncq
999 # - now also use random cookie for attach/detach
1000 # - add force_detach() with user feedback
1001 # - add get_loaded_plugins()
1002 # - implement raise_plugin()
1003 #
1004 # Revision 1.4 2004/02/05 23:52:05 ncq
1005 # - remove spurious return 0
1006 #
1007 # Revision 1.3 2004/02/05 20:46:18 ncq
1008 # - require attach() cookie for detach(), too
1009 #
1010 # Revision 1.2 2004/02/05 20:40:34 ncq
1011 # - added attach()
1012 # - only allow attach()ed clients to call methods
1013 # - introduce patient locking/unlocking cookie
1014 # - enhance unit test
1015 #
1016 # Revision 1.1 2004/02/05 18:10:44 ncq
1017 # - actually minimally functional macro executor with test code
1018 #
1019
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Feb 9 04:01:36 2010 | http://epydoc.sourceforge.net |