| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed macro primitives.
2
3 This module implements functions a macro can legally use.
4 """
5 #=====================================================================
6 __version__ = "$Revision: 1.51 $"
7 __author__ = "K.Hilbert <karsten.hilbert@gmx.net>"
8
9 import sys, time, random, types, logging
10
11
12 import wx
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17 from Gnumed.pycommon import gmI18N
18 if __name__ == '__main__':
19 gmI18N.activate_locale()
20 gmI18N.install_domain()
21 from Gnumed.pycommon import gmGuiBroker
22 from Gnumed.pycommon import gmTools
23 from Gnumed.pycommon import gmBorg
24 from Gnumed.pycommon import gmExceptions
25 from Gnumed.pycommon import gmCfg2
26 from Gnumed.pycommon import gmDateTime
27
28 from Gnumed.business import gmPerson, gmDemographicRecord, gmMedication, gmPathLab, gmPersonSearch
29 from Gnumed.business import gmVaccination, gmPersonSearch
30
31 from Gnumed.wxpython import gmGuiHelpers
32 from Gnumed.wxpython import gmNarrativeWidgets
33 from Gnumed.wxpython import gmPatSearchWidgets
34 from Gnumed.wxpython import gmPlugin
35 from Gnumed.wxpython import gmEMRStructWidgets
36
37
38 _log = logging.getLogger('gm.scripting')
39 _cfg = gmCfg2.gmCfgData()
40
41 #=====================================================================
42 known_placeholders = [
43 'lastname',
44 'firstname',
45 'title',
46 'date_of_birth',
47 'progress_notes',
48 'soap',
49 'soap_s',
50 'soap_o',
51 'soap_a',
52 'soap_p',
53 u'client_version',
54 u'current_provider',
55 u'allergy_state'
56 ]
57
58
59 # those must satisfy the pattern "$name::args::optional length$" when used
60 known_variant_placeholders = [
61 u'soap',
62 u'progress_notes', # "data" holds: categories//template
63 # categories: string with "soap ", " " == None == admin
64 # template: u'something %s something' (do not include // in template !)
65 u'emr_journal', # "data" format: <categories>//<template>//<line length>//<time range>//<target format>
66 # categories: string with any of "s", "o", "a", "p", " ";
67 # (" " == None == admin category)
68 # template: something %s something else
69 # (Do not include // in the template !)
70 # line length: the length of individual lines, not the total placeholder length
71 # time range: the number of weeks going back in time
72 # target format: "tex" or anything else, if "tex", data will be tex-escaped
73 u'date_of_birth',
74 u'adr_street', # "data" holds: type of address
75 u'adr_number',
76 u'adr_location',
77 u'adr_postcode',
78 u'gender_mapper', # "data" holds: value for male // value for female
79 u'current_meds', # "data" holds: line template
80 u'current_meds_table', # "data" holds: format, options
81 u'current_meds_notes', # "data" holds: format, options
82 u'lab_table', # "data" holds: format (currently "latex" only)
83 u'latest_vaccs_table', # "data" holds: format, options
84 u'today', # "data" holds: strftime format
85 u'tex_escape', # "data" holds: string to escape
86 u'allergies', # "data" holds: line template, one allergy per line
87 u'allergy_list', # "data" holds: template per allergy, allergies on one line
88 u'problems', # "data" holds: line template, one problem per line
89 u'name', # "data" holds: template for name parts arrangement
90 u'free_text', # show a dialog for entering some free text
91 u'soap_for_encounters' # "data" holds: soap cats // strftime date format
92 ]
93
94 default_placeholder_regex = r'\$<.+?>\$' # this one works (except that OOo cannot be non-greedy |-( )
95
96 #_regex_parts = [
97 # r'\$<\w+::.*(?::)\d+>\$',
98 # r'\$<\w+::.+(?!>\$)>\$',
99 # r'\$<\w+?>\$'
100 #]
101 #default_placeholder_regex = r'|'.join(_regex_parts)
102
103 default_placeholder_start = u'$<'
104 default_placeholder_end = u'>$'
105 #=====================================================================
107 """Replaces placeholders in forms, fields, etc.
108
109 - patient related placeholders operate on the currently active patient
110 - is passed to the forms handling code, for example
111
112 Note that this cannot be called from a non-gui thread unless
113 wrapped in wx.CallAfter.
114
115 There are currently three types of placeholders:
116
117 simple static placeholders
118 - those are listed in known_placeholders
119 - they are used as-is
120
121 extended static placeholders
122 - those are like the static ones but have "::::<NUMBER>" appended
123 where <NUMBER> is the maximum length
124
125 variant placeholders
126 - those are listed in known_variant_placeholders
127 - they are parsed into placeholder, data, and maximum length
128 - the length is optional
129 - data is passed to the handler
130 """
132
133 self.pat = gmPerson.gmCurrentPatient()
134 self.debug = False
135
136 self.invalid_placeholder_template = _('invalid placeholder [%s]')
137 #--------------------------------------------------------
138 # __getitem__ API
139 #--------------------------------------------------------
141 """Map self['placeholder'] to self.placeholder.
142
143 This is useful for replacing placeholders parsed out
144 of documents as strings.
145
146 Unknown/invalid placeholders still deliver a result but
147 it will be glaringly obvious if debugging is enabled.
148 """
149 _log.debug('replacing [%s]', placeholder)
150
151 original_placeholder = placeholder
152
153 if placeholder.startswith(default_placeholder_start):
154 placeholder = placeholder[len(default_placeholder_start):]
155 if placeholder.endswith(default_placeholder_end):
156 placeholder = placeholder[:-len(default_placeholder_end)]
157 else:
158 _log.debug('placeholder must either start with [%s] and end with [%s] or neither of both', default_placeholder_start, default_placeholder_end)
159 if self.debug:
160 return self.invalid_placeholder_template % original_placeholder
161 return None
162
163 # simple static placeholder ?
164 if placeholder in known_placeholders:
165 return getattr(self, placeholder)
166
167 # extended static placeholder ?
168 parts = placeholder.split('::::', 1)
169 if len(parts) == 2:
170 name, lng = parts
171 try:
172 return getattr(self, name)[:int(lng)]
173 except:
174 _log.exception('placeholder handling error: %s', original_placeholder)
175 if self.debug:
176 return self.invalid_placeholder_template % original_placeholder
177 return None
178
179 # variable placeholders
180 parts = placeholder.split('::')
181 if len(parts) == 2:
182 name, data = parts
183 lng = None
184 if len(parts) == 3:
185 name, data, lng = parts
186 try:
187 lng = int(lng)
188 except:
189 _log.exception('placeholder length definition error: %s, discarding length', original_placeholder)
190 lng = None
191 if len(parts) > 3:
192 _log.warning('invalid placeholder layout: %s', original_placeholder)
193 if self.debug:
194 return self.invalid_placeholder_template % original_placeholder
195 return None
196
197 handler = getattr(self, '_get_variant_%s' % name, None)
198 if handler is None:
199 _log.warning('no handler <_get_variant_%s> for placeholder %s', name, original_placeholder)
200 if self.debug:
201 return self.invalid_placeholder_template % original_placeholder
202 return None
203
204 try:
205 if lng is None:
206 return handler(data = data)
207 return handler(data = data)[:lng]
208 except:
209 _log.exception('placeholder handling error: %s', original_placeholder)
210 if self.debug:
211 return self.invalid_placeholder_template % original_placeholder
212 return None
213
214 _log.error('something went wrong, should never get here')
215 return None
216 #--------------------------------------------------------
217 # properties actually handling placeholders
218 #--------------------------------------------------------
219 # property helpers
220 #--------------------------------------------------------
224 #--------------------------------------------------------
226 return self.pat.get_active_name()['lastnames']
227 #--------------------------------------------------------
229 return self.pat.get_active_name()['firstnames']
230 #--------------------------------------------------------
233 #--------------------------------------------------------
235 return self._get_variant_date_of_birth(data='%x')
236 #--------------------------------------------------------
239 #--------------------------------------------------------
241 return self._get_variant_soap(data = u's')
242 #--------------------------------------------------------
244 return self._get_variant_soap(data = u'o')
245 #--------------------------------------------------------
247 return self._get_variant_soap(data = u'a')
248 #--------------------------------------------------------
250 return self._get_variant_soap(data = u'p')
251 #--------------------------------------------------------
254 #--------------------------------------------------------
256 return gmTools.coalesce (
257 _cfg.get(option = u'client_version'),
258 u'%s' % self.__class__.__name__
259 )
260 #--------------------------------------------------------
262 prov = gmPerson.gmCurrentProvider()
263
264 title = gmTools.coalesce (
265 prov['title'],
266 gmPerson.map_gender2salutation(prov['gender'])
267 )
268
269 tmp = u'%s %s. %s' % (
270 title,
271 prov['firstnames'][:1],
272 prov['lastnames']
273 )
274
275 return tmp
276 #--------------------------------------------------------
278 allg_state = self.pat.get_emr().allergy_state
279
280 if allg_state['last_confirmed'] is None:
281 date_confirmed = u''
282 else:
283 date_confirmed = u' (%s)' % allg_state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
284
285 tmp = u'%s%s' % (
286 allg_state.state_string,
287 date_confirmed
288 )
289 return tmp
290 #--------------------------------------------------------
291 # property definitions for static placeholders
292 #--------------------------------------------------------
293 placeholder_regex = property(lambda x: default_placeholder_regex, _setter_noop)
294
295 # placeholders
296 lastname = property(_get_lastname, _setter_noop)
297 firstname = property(_get_firstname, _setter_noop)
298 title = property(_get_title, _setter_noop)
299 date_of_birth = property(_get_dob, _setter_noop)
300
301 progress_notes = property(_get_progress_notes, _setter_noop)
302 soap = property(_get_progress_notes, _setter_noop)
303 soap_s = property(_get_soap_s, _setter_noop)
304 soap_o = property(_get_soap_o, _setter_noop)
305 soap_a = property(_get_soap_a, _setter_noop)
306 soap_p = property(_get_soap_p, _setter_noop)
307 soap_admin = property(_get_soap_admin, _setter_noop)
308
309 allergy_state = property(_get_allergy_state, _setter_noop)
310
311 client_version = property(_get_client_version, _setter_noop)
312
313 current_provider = property(_get_current_provider, _setter_noop)
314 #--------------------------------------------------------
315 # variant handlers
316 #--------------------------------------------------------
318 """Select encounters from list and format SOAP thereof.
319
320 data: soap_cats (' ' -> None -> admin) // date format
321 """
322 # defaults
323 cats = None
324 date_format = None
325
326 if data is not None:
327 data_parts = data.split('//')
328
329 # part[0]: categories
330 if len(data_parts[0]) > 0:
331 cats = []
332 if u' ' in data_parts[0]:
333 cats.append(None)
334 data_parts[0] = data_parts[0].replace(u' ', u'')
335 cats.extend(list(data_parts[0]))
336
337 # part[1]: date format
338 if len(data_parts) > 0:
339 if len(data_parts[1]) > 0:
340 date_format = data_parts[1]
341
342 encounters = gmEMRStructWidgets.select_encounters(single_selection = False)
343
344 chunks = []
345 for enc in encounters:
346 chunks.append(enc.format_latex (
347 date_format = date_format,
348 soap_cats = cats,
349 soap_order = u'soap_rank, date'
350 ))
351
352 return u''.join(chunks)
353 #--------------------------------------------------------
355 # default: all categories, neutral template
356 cats = list(u'soap')
357 cats.append(None)
358 template = u'%s'
359 interactive = True
360 line_length = 9999
361 target_format = None
362 time_range = None
363
364 if data is not None:
365 data_parts = data.split('//')
366
367 # part[0]: categories
368 cats = []
369 # ' ' -> None == admin
370 for c in list(data_parts[0]):
371 if c == u' ':
372 c = None
373 cats.append(c)
374 # '' -> SOAP + None
375 if cats == u'':
376 cats = list(u'soap').append(None)
377
378 # part[1]: template
379 if len(data_parts) > 0:
380 template = data_parts[1]
381
382 # part[2]: line length
383 if len(data_parts) > 1:
384 try:
385 line_length = int(data_parts[2])
386 except:
387 line_length = 9999
388
389 # part[3]: weeks going back in time
390 if len(data_parts) > 2:
391 try:
392 time_range = 7 * int(data_parts[3])
393 except:
394 time_range = None
395
396 # part[4]: output format
397 if len(data_parts) > 3:
398 target_format = data_parts[4]
399
400 # FIXME: will need to be a generator later on
401 narr = self.pat.get_emr().get_as_journal(soap_cats = cats, time_range = time_range)
402
403 if len(narr) == 0:
404 return u''
405
406 if target_format == u'tex':
407 keys = narr[0].keys()
408 lines = []
409 line_dict = {}
410 for n in narr:
411 for key in keys:
412 if isinstance(n[key], basestring):
413 line_dict[key] = gmTools.tex_escape_string(text = n[key])
414 continue
415 line_dict[key] = n[key]
416 try:
417 lines.append((template % line_dict)[:line_length])
418 except KeyError:
419 return u'invalid key in template [%s], valid keys: %s]' % (template, str(keys))
420 else:
421 try:
422 lines = [ (template % n)[:line_length] for n in narr ]
423 except KeyError:
424 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys()))
425
426 return u'\n'.join(lines)
427 #--------------------------------------------------------
430 #--------------------------------------------------------
432
433 # default: all categories, neutral template
434 cats = list(u'soap')
435 cats.append(None)
436 template = u'%s'
437
438 if data is not None:
439 data_parts = data.split('//')
440
441 # part[0]: categories
442 cats = []
443 # ' ' -> None == admin
444 for cat in list(data_parts[0]):
445 if cat == u' ':
446 cat = None
447 cats.append(cat)
448 # '' -> SOAP + None
449 if cats == u'':
450 cats = list(u'soap')
451 cats.append(None)
452
453 # part[1]: template
454 if len(data_parts) > 0:
455 template = data_parts[1]
456
457 #narr = gmNarrativeWidgets.select_narrative_from_episodes_new(soap_cats = cats)
458 narr = gmNarrativeWidgets.select_narrative_from_episodes(soap_cats = cats)
459
460 if narr is None:
461 return u''
462
463 if len(narr) == 0:
464 return u''
465
466 try:
467 narr = [ template % n['narrative'] for n in narr ]
468 except KeyError:
469 return u'invalid key in template [%s], valid keys: %s]' % (template, str(narr[0].keys()))
470
471 return u'\n'.join(narr)
472 #--------------------------------------------------------
474 if data is None:
475 return [_('template is missing')]
476
477 name = self.pat.get_active_name()
478
479 parts = {
480 'title': gmTools.coalesce(name['title'], u''),
481 'firstnames': name['firstnames'],
482 'lastnames': name['lastnames'],
483 'preferred': gmTools.coalesce (
484 initial = name['preferred'],
485 instead = u' ',
486 template_initial = u' "%s" '
487 )
488 }
489
490 return data % parts
491 #--------------------------------------------------------
494 #--------------------------------------------------------
495 # FIXME: extend to all supported genders
497 values = data.split('//', 2)
498
499 if len(values) == 2:
500 male_value, female_value = values
501 other_value = u'<unkown gender>'
502 elif len(values) == 3:
503 male_value, female_value, other_value = values
504 else:
505 return _('invalid gender mapping layout: [%s]') % data
506
507 if self.pat['gender'] == u'm':
508 return male_value
509
510 if self.pat['gender'] == u'f':
511 return female_value
512
513 return other_value
514 #--------------------------------------------------------
516 # if data == u'?':
517 # types = xxxxxxxxxxx
518 adrs = self.pat.get_addresses(address_type=data)
519 if len(adrs) == 0:
520 return _('no street for address type [%s]') % data
521 return adrs[0]['street']
522 #--------------------------------------------------------
524 adrs = self.pat.get_addresses(address_type=data)
525 if len(adrs) == 0:
526 return _('no number for address type [%s]') % data
527 return adrs[0]['number']
528 #--------------------------------------------------------
530 adrs = self.pat.get_addresses(address_type=data)
531 if len(adrs) == 0:
532 return _('no location for address type [%s]') % data
533 return adrs[0]['urb']
534 #--------------------------------------------------------
536 adrs = self.pat.get_addresses(address_type=data)
537 if len(adrs) == 0:
538 return _('no postcode for address type [%s]') % data
539 return adrs[0]['postcode']
540 #--------------------------------------------------------
542 if data is None:
543 return [_('template is missing')]
544
545 template, separator = data.split('//', 2)
546
547 emr = self.pat.get_emr()
548 return separator.join([ template % a for a in emr.get_allergies() ])
549 #--------------------------------------------------------
551
552 if data is None:
553 return [_('template is missing')]
554
555 emr = self.pat.get_emr()
556 return u'\n'.join([ data % a for a in emr.get_allergies() ])
557 #--------------------------------------------------------
559
560 if data is None:
561 return [_('template is missing')]
562
563 emr = self.pat.get_emr()
564 current_meds = emr.get_current_substance_intake (
565 include_inactive = False,
566 include_unapproved = False,
567 order_by = u'brand, substance'
568 )
569
570 # FIXME: we should be dealing with translating None to u'' here
571
572 return u'\n'.join([ data % m for m in current_meds ])
573 #--------------------------------------------------------
575
576 options = data.split('//')
577
578 if u'latex' in options:
579 return gmMedication.format_substance_intake (
580 emr = self.pat.get_emr(),
581 output_format = u'latex',
582 table_type = u'by-brand'
583 )
584
585 _log.error('no known current medications table formatting style in [%]', data)
586 return _('unknown current medication table formatting style')
587 #--------------------------------------------------------
589
590 options = data.split('//')
591
592 if u'latex' in options:
593 return gmMedication.format_substance_intake_notes (
594 emr = self.pat.get_emr(),
595 output_format = u'latex',
596 table_type = u'by-brand'
597 )
598
599 _log.error('no known current medications notes formatting style in [%]', data)
600 return _('unknown current medication notes formatting style')
601 #--------------------------------------------------------
603
604 options = data.split('//')
605
606 emr = self.pat.get_emr()
607
608 if u'latex' in options:
609 return gmPathLab.format_test_results (
610 results = emr.get_test_results_by_date(),
611 output_format = u'latex'
612 )
613
614 _log.error('no known test results table formatting style in [%s]', data)
615 return _('unknown test results table formatting style [%s]') % data
616 #--------------------------------------------------------
618
619 options = data.split('//')
620
621 emr = self.pat.get_emr()
622
623 if u'latex' in options:
624 return gmVaccination.format_latest_vaccinations(output_format = u'latex', emr = emr)
625
626 _log.error('no known vaccinations table formatting style in [%s]', data)
627 return _('unknown vaccinations table formatting style [%s]') % data
628 #--------------------------------------------------------
630
631 if data is None:
632 return [_('template is missing')]
633
634 probs = self.pat.get_emr().get_problems()
635
636 return u'\n'.join([ data % p for p in probs ])
637 #--------------------------------------------------------
640 #--------------------------------------------------------
643 #--------------------------------------------------------
645 # <data>:
646 # format: tex (only, currently)
647 # message: shown in input dialog, must not contain "//" or "::"
648
649 format, msg = data.split('//')
650
651 dlg = gmGuiHelpers.cMultilineTextEntryDlg (
652 None,
653 -1,
654 title = _('Replacing <free_text> placeholder'),
655 msg = _('Below you can enter free text.\n\n [%s]') % msg
656 )
657 dlg.enable_user_formatting = True
658 decision = dlg.ShowModal()
659
660 if decision != wx.ID_SAVE:
661 dlg.Destroy()
662 return _('Text input cancelled by user.')
663
664 text = dlg.value.strip()
665 if dlg.is_user_formatted:
666 dlg.Destroy()
667 return text
668
669 dlg.Destroy()
670
671 if format == u'tex':
672 return gmTools.tex_escape_string(text = text)
673
674 return text
675 #--------------------------------------------------------
676 # internal helpers
677 #--------------------------------------------------------
678
679 #=====================================================================
681 """Functions a macro can legally use.
682
683 An instance of this class is passed to the GNUmed scripting
684 listener. Hence, all actions a macro can legally take must
685 be defined in this class. Thus we achieve some screening for
686 security and also thread safety handling.
687 """
688 #-----------------------------------------------------------------
690 if personality is None:
691 raise gmExceptions.ConstructorError, 'must specify personality'
692 self.__personality = personality
693 self.__attached = 0
694 self._get_source_personality = None
695 self.__user_done = False
696 self.__user_answer = 'no answer yet'
697 self.__pat = gmPerson.gmCurrentPatient()
698
699 self.__auth_cookie = str(random.random())
700 self.__pat_lock_cookie = str(random.random())
701 self.__lock_after_load_cookie = str(random.random())
702
703 _log.info('slave mode personality is [%s]', personality)
704 #-----------------------------------------------------------------
705 # public API
706 #-----------------------------------------------------------------
708 if self.__attached:
709 _log.error('attach with [%s] rejected, already serving a client', personality)
710 return (0, _('attach rejected, already serving a client'))
711 if personality != self.__personality:
712 _log.error('rejecting attach to personality [%s], only servicing [%s]' % (personality, self.__personality))
713 return (0, _('attach to personality [%s] rejected') % personality)
714 self.__attached = 1
715 self.__auth_cookie = str(random.random())
716 return (1, self.__auth_cookie)
717 #-----------------------------------------------------------------
719 if not self.__attached:
720 return 1
721 if auth_cookie != self.__auth_cookie:
722 _log.error('rejecting detach() with cookie [%s]' % auth_cookie)
723 return 0
724 self.__attached = 0
725 return 1
726 #-----------------------------------------------------------------
728 if not self.__attached:
729 return 1
730 self.__user_done = False
731 # FIXME: use self.__sync_cookie for syncing with user interaction
732 wx.CallAfter(self._force_detach)
733 return 1
734 #-----------------------------------------------------------------
736 ver = _cfg.get(option = u'client_version')
737 return "GNUmed %s, %s $Revision: 1.51 $" % (ver, self.__class__.__name__)
738 #-----------------------------------------------------------------
740 """Shuts down this client instance."""
741 if not self.__attached:
742 return 0
743 if auth_cookie != self.__auth_cookie:
744 _log.error('non-authenticated shutdown_gnumed()')
745 return 0
746 wx.CallAfter(self._shutdown_gnumed, forced)
747 return 1
748 #-----------------------------------------------------------------
750 """Raise ourselves to the top of the desktop."""
751 if not self.__attached:
752 return 0
753 if auth_cookie != self.__auth_cookie:
754 _log.error('non-authenticated raise_gnumed()')
755 return 0
756 return "cMacroPrimitives.raise_gnumed() not implemented"
757 #-----------------------------------------------------------------
759 if not self.__attached:
760 return 0
761 if auth_cookie != self.__auth_cookie:
762 _log.error('non-authenticated get_loaded_plugins()')
763 return 0
764 gb = gmGuiBroker.GuiBroker()
765 return gb['horstspace.notebook.gui'].keys()
766 #-----------------------------------------------------------------
768 """Raise a notebook plugin within GNUmed."""
769 if not self.__attached:
770 return 0
771 if auth_cookie != self.__auth_cookie:
772 _log.error('non-authenticated raise_notebook_plugin()')
773 return 0
774 # FIXME: use semaphore
775 wx.CallAfter(gmPlugin.raise_notebook_plugin, a_plugin)
776 return 1
777 #-----------------------------------------------------------------
779 """Load external patient, perhaps create it.
780
781 Callers must use get_user_answer() to get status information.
782 It is unsafe to proceed without knowing the completion state as
783 the controlled client may be waiting for user input from a
784 patient selection list.
785 """
786 if not self.__attached:
787 return (0, _('request rejected, you are not attach()ed'))
788 if auth_cookie != self.__auth_cookie:
789 _log.error('non-authenticated load_patient_from_external_source()')
790 return (0, _('rejected load_patient_from_external_source(), not authenticated'))
791 if self.__pat.locked:
792 _log.error('patient is locked, cannot load from external source')
793 return (0, _('current patient is locked'))
794 self.__user_done = False
795 wx.CallAfter(self._load_patient_from_external_source)
796 self.__lock_after_load_cookie = str(random.random())
797 return (1, self.__lock_after_load_cookie)
798 #-----------------------------------------------------------------
800 if not self.__attached:
801 return (0, _('request rejected, you are not attach()ed'))
802 if auth_cookie != self.__auth_cookie:
803 _log.error('non-authenticated lock_load_patient()')
804 return (0, _('rejected lock_load_patient(), not authenticated'))
805 # FIXME: ask user what to do about wrong cookie
806 if lock_after_load_cookie != self.__lock_after_load_cookie:
807 _log.warning('patient lock-after-load request rejected due to wrong cookie [%s]' % lock_after_load_cookie)
808 return (0, 'patient lock-after-load request rejected, wrong cookie provided')
809 self.__pat.locked = True
810 self.__pat_lock_cookie = str(random.random())
811 return (1, self.__pat_lock_cookie)
812 #-----------------------------------------------------------------
814 if not self.__attached:
815 return (0, _('request rejected, you are not attach()ed'))
816 if auth_cookie != self.__auth_cookie:
817 _log.error('non-authenticated lock_into_patient()')
818 return (0, _('rejected lock_into_patient(), not authenticated'))
819 if self.__pat.locked:
820 _log.error('patient is already locked')
821 return (0, _('already locked into a patient'))
822 searcher = gmPersonSearch.cPatientSearcher_SQL()
823 if type(search_params) == types.DictType:
824 idents = searcher.get_identities(search_dict=search_params)
825 print "must use dto, not search_dict"
826 print xxxxxxxxxxxxxxxxx
827 else:
828 idents = searcher.get_identities(search_term=search_params)
829 if idents is None:
830 return (0, _('error searching for patient with [%s]/%s') % (search_term, search_dict))
831 if len(idents) == 0:
832 return (0, _('no patient found for [%s]/%s') % (search_term, search_dict))
833 # FIXME: let user select patient
834 if len(idents) > 1:
835 return (0, _('several matching patients found for [%s]/%s') % (search_term, search_dict))
836 if not gmPatSearchWidgets.set_active_patient(patient = idents[0]):
837 return (0, _('cannot activate patient [%s] (%s/%s)') % (str(idents[0]), search_term, search_dict))
838 self.__pat.locked = True
839 self.__pat_lock_cookie = str(random.random())
840 return (1, self.__pat_lock_cookie)
841 #-----------------------------------------------------------------
843 if not self.__attached:
844 return (0, _('request rejected, you are not attach()ed'))
845 if auth_cookie != self.__auth_cookie:
846 _log.error('non-authenticated unlock_patient()')
847 return (0, _('rejected unlock_patient, not authenticated'))
848 # we ain't locked anyways, so succeed
849 if not self.__pat.locked:
850 return (1, '')
851 # FIXME: ask user what to do about wrong cookie
852 if unlock_cookie != self.__pat_lock_cookie:
853 _log.warning('patient unlock request rejected due to wrong cookie [%s]' % unlock_cookie)
854 return (0, 'patient unlock request rejected, wrong cookie provided')
855 self.__pat.locked = False
856 return (1, '')
857 #-----------------------------------------------------------------
858 - def assume_staff_identity(self, auth_cookie = None, staff_name = "Dr.Jekyll", staff_creds = None):
859 if not self.__attached:
860 return 0
861 if auth_cookie != self.__auth_cookie:
862 _log.error('non-authenticated select_identity()')
863 return 0
864 return "cMacroPrimitives.assume_staff_identity() not implemented"
865 #-----------------------------------------------------------------
867 if not self.__user_done:
868 return (0, 'still waiting')
869 self.__user_done = False
870 return (1, self.__user_answer)
871 #-----------------------------------------------------------------
872 # internal API
873 #-----------------------------------------------------------------
875 msg = _(
876 'Someone tries to forcibly break the existing\n'
877 'controlling connection. This may or may not\n'
878 'have legitimate reasons.\n\n'
879 'Do you want to allow breaking the connection ?'
880 )
881 can_break_conn = gmGuiHelpers.gm_show_question (
882 aMessage = msg,
883 aTitle = _('forced detach attempt')
884 )
885 if can_break_conn:
886 self.__user_answer = 1
887 else:
888 self.__user_answer = 0
889 self.__user_done = True
890 if can_break_conn:
891 self.__pat.locked = False
892 self.__attached = 0
893 return 1
894 #-----------------------------------------------------------------
896 top_win = wx.GetApp().GetTopWindow()
897 if forced:
898 top_win.Destroy()
899 else:
900 top_win.Close()
901 #-----------------------------------------------------------------
903 patient = gmPatSearchWidgets.get_person_from_external_sources(search_immediately = True, activate_immediately = True)
904 if patient is not None:
905 self.__user_answer = 1
906 else:
907 self.__user_answer = 0
908 self.__user_done = True
909 return 1
910 #=====================================================================
911 # main
912 #=====================================================================
913 if __name__ == '__main__':
914
915 if len(sys.argv) < 2:
916 sys.exit()
917
918 if sys.argv[1] != 'test':
919 sys.exit()
920
921 gmI18N.activate_locale()
922 gmI18N.install_domain()
923
924 #--------------------------------------------------------
926 handler = gmPlaceholderHandler()
927 handler.debug = True
928
929 for placeholder in ['a', 'b']:
930 print handler[placeholder]
931
932 pat = gmPersonSearch.ask_for_patient()
933 if pat is None:
934 return
935
936 gmPatSearchWidgets.set_active_patient(patient = pat)
937
938 print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d']
939
940 app = wx.PyWidgetTester(size = (200, 50))
941 for placeholder in known_placeholders:
942 print placeholder, "=", handler[placeholder]
943
944 ph = 'progress_notes::ap'
945 print '%s: %s' % (ph, handler[ph])
946 #--------------------------------------------------------
948
949 tests = [
950 # should work:
951 '$<lastname>$',
952 '$<lastname::::3>$',
953 '$<name::%(title)s %(firstnames)s%(preferred)s%(lastnames)s>$',
954
955 # should fail:
956 'lastname',
957 '$<lastname',
958 '$<lastname::',
959 '$<lastname::>$',
960 '$<lastname::abc>$',
961 '$<lastname::abc::>$',
962 '$<lastname::abc::3>$',
963 '$<lastname::abc::xyz>$',
964 '$<lastname::::>$',
965 '$<lastname::::xyz>$',
966
967 '$<date_of_birth::%Y-%m-%d>$',
968 '$<date_of_birth::%Y-%m-%d::3>$',
969 '$<date_of_birth::%Y-%m-%d::>$',
970
971 # should work:
972 '$<adr_location::home::35>$',
973 '$<gender_mapper::male//female//other::5>$',
974 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\n::50>$',
975 '$<allergy_list::%(descriptor)s, >$',
976 '$<current_meds_table::latex//by-brand>$'
977
978 # 'firstname',
979 # 'title',
980 # 'date_of_birth',
981 # 'progress_notes',
982 # 'soap',
983 # 'soap_s',
984 # 'soap_o',
985 # 'soap_a',
986 # 'soap_p',
987
988 # 'soap',
989 # 'progress_notes',
990 # 'date_of_birth'
991 ]
992
993 tests = [
994 '$<latest_vaccs_table::latex>$'
995 ]
996
997 pat = gmPersonSearch.ask_for_patient()
998 if pat is None:
999 return
1000
1001 gmPatSearchWidgets.set_active_patient(patient = pat)
1002
1003 handler = gmPlaceholderHandler()
1004 handler.debug = True
1005
1006 for placeholder in tests:
1007 print placeholder, "=>", handler[placeholder]
1008 print "--------------"
1009 raw_input()
1010
1011 # print 'DOB (YYYY-MM-DD):', handler['date_of_birth::%Y-%m-%d']
1012
1013 # app = wx.PyWidgetTester(size = (200, 50))
1014 # for placeholder in known_placeholders:
1015 # print placeholder, "=", handler[placeholder]
1016
1017 # ph = 'progress_notes::ap'
1018 # print '%s: %s' % (ph, handler[ph])
1019
1020 #--------------------------------------------------------
1022 from Gnumed.pycommon import gmScriptingListener
1023 import xmlrpclib
1024 listener = gmScriptingListener.cScriptingListener(macro_executor = cMacroPrimitives(personality='unit test'), port=9999)
1025
1026 s = xmlrpclib.ServerProxy('http://localhost:9999')
1027 print "should fail:", s.attach()
1028 print "should fail:", s.attach('wrong cookie')
1029 print "should work:", s.version()
1030 print "should fail:", s.raise_gnumed()
1031 print "should fail:", s.raise_notebook_plugin('test plugin')
1032 print "should fail:", s.lock_into_patient('kirk, james')
1033 print "should fail:", s.unlock_patient()
1034 status, conn_auth = s.attach('unit test')
1035 print "should work:", status, conn_auth
1036 print "should work:", s.version()
1037 print "should work:", s.raise_gnumed(conn_auth)
1038 status, pat_auth = s.lock_into_patient(conn_auth, 'kirk, james')
1039 print "should work:", status, pat_auth
1040 print "should fail:", s.unlock_patient(conn_auth, 'bogus patient unlock cookie')
1041 print "should work", s.unlock_patient(conn_auth, pat_auth)
1042 data = {'firstname': 'jame', 'lastnames': 'Kirk', 'gender': 'm'}
1043 status, pat_auth = s.lock_into_patient(conn_auth, data)
1044 print "should work:", status, pat_auth
1045 print "should work", s.unlock_patient(conn_auth, pat_auth)
1046 print s.detach('bogus detach cookie')
1047 print s.detach(conn_auth)
1048 del s
1049
1050 listener.shutdown()
1051 #--------------------------------------------------------
1053
1054 import re as regex
1055
1056 tests = [
1057 ' $<lastname>$ ',
1058 ' $<lastname::::3>$ ',
1059
1060 # should fail:
1061 '$<date_of_birth::%Y-%m-%d>$',
1062 '$<date_of_birth::%Y-%m-%d::3>$',
1063 '$<date_of_birth::%Y-%m-%d::>$',
1064
1065 '$<adr_location::home::35>$',
1066 '$<gender_mapper::male//female//other::5>$',
1067 '$<current_meds::==> %(brand)s %(preparation)s (%(substance)s) <==\\n::50>$',
1068 '$<allergy_list::%(descriptor)s, >$',
1069
1070 '\\noindent Patient: $<lastname>$, $<firstname>$',
1071 '$<allergies::%(descriptor)s & %(l10n_type)s & {\\footnotesize %(reaction)s} \tabularnewline \hline >$',
1072 '$<current_meds:: \item[%(substance)s] {\\footnotesize (%(brand)s)} %(preparation)s %(amount)s%(unit)s: %(schedule)s >$'
1073 ]
1074
1075 tests = [
1076
1077 'junk $<lastname::::3>$ junk',
1078 'junk $<lastname::abc::3>$ junk',
1079 'junk $<lastname::abc>$ junk',
1080 'junk $<lastname>$ junk',
1081
1082 'junk $<lastname>$ junk $<firstname>$ junk',
1083 'junk $<lastname::abc>$ junk $<fiststname::abc>$ junk',
1084 'junk $<lastname::abc::3>$ junk $<firstname::abc::3>$ junk',
1085 'junk $<lastname::::3>$ junk $<firstname::::3>$ junk'
1086
1087 ]
1088
1089 print "testing placeholder regex:", default_placeholder_regex
1090 print ""
1091
1092 for t in tests:
1093 print 'line: "%s"' % t
1094 print "placeholders:"
1095 for p in regex.findall(default_placeholder_regex, t, regex.IGNORECASE):
1096 print ' => "%s"' % p
1097 print " "
1098 #--------------------------------------------------------
1100
1101 #ph = u'emr_journal::soap //%(date)s %(modified_by)s %(soap_cat)s %(narrative)s//30::'
1102 #ph = u'free_text::latex//placeholder test::9999'
1103 ph = u'soap_for_encounters:://::9999'
1104
1105 handler = gmPlaceholderHandler()
1106 handler.debug = True
1107
1108 pat = gmPersonSearch.ask_for_patient()
1109 if pat is None:
1110 return
1111
1112 gmPatSearchWidgets.set_active_patient(patient = pat)
1113
1114 app = wx.PyWidgetTester(size = (200, 50))
1115 print u'%s => %s' % (ph, handler[ph])
1116 #--------------------------------------------------------
1117
1118 #test_placeholders()
1119 #test_new_variant_placeholders()
1120 #test_scripting()
1121 #test_placeholder_regex()
1122 test_placeholder()
1123
1124 #=====================================================================
1125
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Wed May 4 03:58:24 2011 | http://epydoc.sourceforge.net |