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