1
2 """GNUmed quick person search widgets.
3
4 This widget allows to search for persons based on the
5 critera name, date of birth and person ID. It goes to
6 considerable lengths to understand the user's intent from
7 her input. For that to work well we need per-culture
8 query generators. However, there's always the fallback
9 generator.
10 """
11
12 __version__ = "$Revision: 1.132 $"
13 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
14 __license__ = 'GPL (for details see http://www.gnu.org/)'
15
16 import sys, os.path, glob, datetime as pyDT, re as regex, logging, webbrowser
17
18
19 import wx
20
21
22 if __name__ == '__main__':
23 sys.path.insert(0, '../../')
24 from Gnumed.pycommon import gmLog2
25 from Gnumed.pycommon import gmDispatcher, gmPG2, gmI18N, gmCfg, gmTools, gmDateTime, gmMatchProvider, gmCfg2
26 from Gnumed.business import gmPerson, gmKVK, gmSurgery
27 from Gnumed.wxpython import gmGuiHelpers, gmDemographicsWidgets, gmAuthWidgets, gmRegetMixin, gmPhraseWheel, gmEditArea
28 from Gnumed.wxGladeWidgets import wxgSelectPersonFromListDlg, wxgSelectPersonDTOFromListDlg, wxgMergePatientsDlg
29
30
31 _log = logging.getLogger('gm.person')
32 _log.info(__version__)
33
34 _cfg = gmCfg2.gmCfgData()
35
36 ID_PatPickList = wx.NewId()
37 ID_BTN_AddNew = wx.NewId()
38
39
43
153
155
170
172 for col in range(len(self.__cols)):
173 self._LCTRL_persons.InsertColumn(col, self.__cols[col])
174
176 self._LCTRL_persons.DeleteAllItems()
177
178 pos = len(persons) + 1
179 if pos == 1:
180 return False
181
182 for person in persons:
183 row_num = self._LCTRL_persons.InsertStringItem(pos, label = gmTools.coalesce(person['title'], ''))
184 self._LCTRL_persons.SetStringItem(index = row_num, col = 1, label = person['lastnames'])
185 self._LCTRL_persons.SetStringItem(index = row_num, col = 2, label = person['firstnames'])
186 self._LCTRL_persons.SetStringItem(index = row_num, col = 3, label = gmTools.coalesce(person['preferred'], ''))
187 self._LCTRL_persons.SetStringItem(index = row_num, col = 4, label = person.get_formatted_dob(format = '%x', encoding = gmI18N.get_encoding()))
188 self._LCTRL_persons.SetStringItem(index = row_num, col = 5, label = gmTools.coalesce(person['l10n_gender'], '?'))
189 label = u''
190 if person.is_patient:
191 enc = person.get_last_encounter()
192 if enc is not None:
193 label = u'%s (%s)' % (enc['started'].strftime('%x').decode(gmI18N.get_encoding()), enc['l10n_type'])
194 self._LCTRL_persons.SetStringItem(index = row_num, col = 6, label = label)
195 try: self._LCTRL_persons.SetStringItem(index = row_num, col = 7, label = person['match_type'])
196 except:
197 _log.exception('cannot set match_type field')
198 self._LCTRL_persons.SetStringItem(index = row_num, col = 7, label = u'??')
199
200 for col in range(len(self.__cols)):
201 self._LCTRL_persons.SetColumnWidth(col=col, width=wx.LIST_AUTOSIZE)
202
203 self._BTN_select.Enable(False)
204 self._LCTRL_persons.SetFocus()
205 self._LCTRL_persons.Select(0)
206
207 self._LCTRL_persons.set_data(data=persons)
208
210 return self._LCTRL_persons.get_item_data(self._LCTRL_persons.GetFirstSelected())
211
212
213
215 self._BTN_select.Enable(True)
216 return
217
219 self._BTN_select.Enable(True)
220 if self.IsModal():
221 self.EndModal(wx.ID_OK)
222 else:
223 self.Close()
224
226
238
240 for col in range(len(self.__cols)):
241 self._LCTRL_persons.InsertColumn(col, self.__cols[col])
242
244 self._LCTRL_persons.DeleteAllItems()
245
246 pos = len(dtos) + 1
247 if pos == 1:
248 return False
249
250 for rec in dtos:
251 row_num = self._LCTRL_persons.InsertStringItem(pos, label = rec['source'])
252 dto = rec['dto']
253 self._LCTRL_persons.SetStringItem(index = row_num, col = 1, label = dto.lastnames)
254 self._LCTRL_persons.SetStringItem(index = row_num, col = 2, label = dto.firstnames)
255 if dto.dob is None:
256 self._LCTRL_persons.SetStringItem(index = row_num, col = 3, label = u'')
257 else:
258 self._LCTRL_persons.SetStringItem(index = row_num, col = 3, label = dto.dob.strftime('%x').decode(gmI18N.get_encoding()))
259 self._LCTRL_persons.SetStringItem(index = row_num, col = 4, label = gmTools.coalesce(dto.gender, ''))
260
261 for col in range(len(self.__cols)):
262 self._LCTRL_persons.SetColumnWidth(col=col, width=wx.LIST_AUTOSIZE)
263
264 self._BTN_select.Enable(False)
265 self._LCTRL_persons.SetFocus()
266 self._LCTRL_persons.Select(0)
267
268 self._LCTRL_persons.set_data(data=dtos)
269
271 return self._LCTRL_persons.get_item_data(self._LCTRL_persons.GetFirstSelected())
272
273
274
276 self._BTN_select.Enable(True)
277 return
278
280 self._BTN_select.Enable(True)
281 if self.IsModal():
282 self.EndModal(wx.ID_OK)
283 else:
284 self.Close()
285
287
288 bdt_files = []
289
290
291
292 candidates = []
293 drives = 'cdefghijklmnopqrstuvwxyz'
294 for drive in drives:
295 candidate = drive + ':\Winacs\TEMP\BDT*.tmp'
296 candidates.extend(glob.glob(candidate))
297 for candidate in candidates:
298 path, filename = os.path.split(candidate)
299
300 bdt_files.append({'file': candidate, 'source': 'MCS/Isynet %s' % filename[-6:-4]})
301
302
303
304 src_order = [
305 ('explicit', 'return'),
306 ('workbase', 'append'),
307 ('local', 'append'),
308 ('user', 'append'),
309 ('system', 'append')
310 ]
311 xdt_profiles = _cfg.get (
312 group = 'workplace',
313 option = 'XDT profiles',
314 source_order = src_order
315 )
316 if xdt_profiles is None:
317 return []
318
319
320 src_order = [
321 ('explicit', 'return'),
322 ('workbase', 'return'),
323 ('local', 'return'),
324 ('user', 'return'),
325 ('system', 'return')
326 ]
327 for profile in xdt_profiles:
328 name = _cfg.get (
329 group = 'XDT profile %s' % profile,
330 option = 'filename',
331 source_order = src_order
332 )
333 if name is None:
334 _log.error('XDT profile [%s] does not define a <filename>' % profile)
335 continue
336 encoding = _cfg.get (
337 group = 'XDT profile %s' % profile,
338 option = 'encoding',
339 source_order = src_order
340 )
341 if encoding is None:
342 _log.warning('xDT source profile [%s] does not specify an <encoding> for BDT file [%s]' % (profile, name))
343 source = _cfg.get (
344 group = 'XDT profile %s' % profile,
345 option = 'source',
346 source_order = src_order
347 )
348 dob_format = _cfg.get (
349 group = 'XDT profile %s' % profile,
350 option = 'DOB format',
351 source_order = src_order
352 )
353 if dob_format is None:
354 _log.warning('XDT profile [%s] does not define a date of birth format in <DOB format>' % profile)
355 bdt_files.append({'file': name, 'source': source, 'encoding': encoding, 'dob_format': dob_format})
356
357 dtos = []
358 for bdt_file in bdt_files:
359 try:
360
361 dto = gmPerson.get_person_from_xdt (
362 filename = bdt_file['file'],
363 encoding = bdt_file['encoding'],
364 dob_format = bdt_file['dob_format']
365 )
366
367 except IOError:
368 gmGuiHelpers.gm_show_info (
369 _(
370 'Cannot access BDT file\n\n'
371 ' [%s]\n\n'
372 'to import patient.\n\n'
373 'Please check your configuration.'
374 ) % bdt_file,
375 _('Activating xDT patient')
376 )
377 _log.exception('cannot access xDT file [%s]' % bdt_file['file'])
378 continue
379 except:
380 gmGuiHelpers.gm_show_error (
381 _(
382 'Cannot load patient from BDT file\n\n'
383 ' [%s]'
384 ) % bdt_file,
385 _('Activating xDT patient')
386 )
387 _log.exception('cannot read patient from xDT file [%s]' % bdt_file['file'])
388 continue
389
390 dtos.append({'dto': dto, 'source': gmTools.coalesce(bdt_file['source'], dto.source)})
391
392 return dtos
393
395
396 pracsoft_files = []
397
398
399 candidates = []
400 drives = 'cdefghijklmnopqrstuvwxyz'
401 for drive in drives:
402 candidate = drive + ':\MDW2\PATIENTS.IN'
403 candidates.extend(glob.glob(candidate))
404 for candidate in candidates:
405 drive, filename = os.path.splitdrive(candidate)
406 pracsoft_files.append({'file': candidate, 'source': 'PracSoft (AU): drive %s' % drive})
407
408
409 src_order = [
410 ('explicit', 'append'),
411 ('workbase', 'append'),
412 ('local', 'append'),
413 ('user', 'append'),
414 ('system', 'append')
415 ]
416 fnames = _cfg.get (
417 group = 'AU PracSoft PATIENTS.IN',
418 option = 'filename',
419 source_order = src_order
420 )
421
422 src_order = [
423 ('explicit', 'return'),
424 ('user', 'return'),
425 ('system', 'return'),
426 ('local', 'return'),
427 ('workbase', 'return')
428 ]
429 source = _cfg.get (
430 group = 'AU PracSoft PATIENTS.IN',
431 option = 'source',
432 source_order = src_order
433 )
434
435 if source is not None:
436 for fname in fnames:
437 fname = os.path.abspath(os.path.expanduser(fname))
438 if os.access(fname, os.R_OK):
439 pracsoft_files.append({'file': os.path.expanduser(fname), 'source': source})
440 else:
441 _log.error('cannot read [%s] in AU PracSoft profile' % fname)
442
443
444 dtos = []
445 for pracsoft_file in pracsoft_files:
446 try:
447 tmp = gmPerson.get_persons_from_pracsoft_file(filename = pracsoft_file['file'])
448 except:
449 _log.exception('cannot parse PracSoft file [%s]' % pracsoft_file['file'])
450 continue
451 for dto in tmp:
452 dtos.append({'dto': dto, 'source': pracsoft_file['source']})
453
454 return dtos
455
470
472 """Load patient from external source.
473
474 - scan external sources for candidates
475 - let user select source
476 - if > 1 available: always
477 - if only 1 available: depending on search_immediately
478 - search for patients matching info from external source
479 - if more than one match:
480 - let user select patient
481 - if no match:
482 - create patient
483 - activate patient
484 """
485
486 dtos = []
487 dtos.extend(load_persons_from_xdt())
488 dtos.extend(load_persons_from_pracsoft_au())
489 dtos.extend(load_persons_from_kvks())
490
491
492 if len(dtos) == 0:
493 gmDispatcher.send(signal='statustext', msg=_('No patients found in external sources.'))
494 return None
495
496
497 if (len(dtos) == 1) and (dtos[0]['dto'].dob is not None):
498 dto = dtos[0]['dto']
499
500 curr_pat = gmPerson.gmCurrentPatient()
501 if curr_pat.connected:
502 key_dto = dto.firstnames + dto.lastnames + dto.dob.strftime('%Y-%m-%d') + dto.gender
503 names = curr_pat.get_active_name()
504 key_pat = names['firstnames'] + names['lastnames'] + curr_pat.get_formatted_dob(format = '%Y-%m-%d') + curr_pat['gender']
505 _log.debug('current patient: %s' % key_pat)
506 _log.debug('dto patient : %s' % key_dto)
507 if key_dto == key_pat:
508 gmDispatcher.send(signal='statustext', msg=_('The only external patient is already active in GNUmed.'), beep=False)
509 return None
510
511
512 if (len(dtos) == 1) and search_immediately:
513 dto = dtos[0]['dto']
514
515
516 else:
517 if parent is None:
518 parent = wx.GetApp().GetTopWindow()
519 dlg = cSelectPersonDTOFromListDlg(parent=parent, id=-1)
520 dlg.set_dtos(dtos=dtos)
521 result = dlg.ShowModal()
522 if result == wx.ID_CANCEL:
523 return None
524 dto = dlg.get_selected_dto()['dto']
525 dlg.Destroy()
526
527
528 idents = dto.get_candidate_identities(can_create=True)
529 if idents is None:
530 gmGuiHelpers.gm_show_info (_(
531 'Cannot create new patient:\n\n'
532 ' [%s %s (%s), %s]'
533 ) % (dto.firstnames, dto.lastnames, dto.gender, dto.dob.strftime('%x').decode(gmI18N.get_encoding())),
534 _('Activating external patient')
535 )
536 return None
537
538 if len(idents) == 1:
539 ident = idents[0]
540
541 if len(idents) > 1:
542 if parent is None:
543 parent = wx.GetApp().GetTopWindow()
544 dlg = cSelectPersonFromListDlg(parent=parent, id=-1)
545 dlg.set_persons(persons=idents)
546 result = dlg.ShowModal()
547 if result == wx.ID_CANCEL:
548 return None
549 ident = dlg.get_selected_person()
550 dlg.Destroy()
551
552 if activate_immediately:
553 if not set_active_patient(patient = ident):
554 gmGuiHelpers.gm_show_info (
555 _(
556 'Cannot activate patient:\n\n'
557 '%s %s (%s)\n'
558 '%s'
559 ) % (dto.firstnames, dto.lastnames, dto.gender, dto.dob.strftime('%x').decode(gmI18N.get_encoding())),
560 _('Activating external patient')
561 )
562 return None
563
564 dto.import_extra_data(identity = ident)
565 dto.delete_from_source()
566
567 return ident
568
570 """Widget for smart search for persons."""
571
573
574 try:
575 kwargs['style'] = kwargs['style'] | wx.TE_PROCESS_ENTER
576 except KeyError:
577 kwargs['style'] = wx.TE_PROCESS_ENTER
578
579
580
581 wx.TextCtrl.__init__(self, *args, **kwargs)
582
583 self.person = None
584
585 self._tt_search_hints = _(
586 'To search for a person type any of: \n'
587 '\n'
588 ' - fragment of last or first name\n'
589 " - date of birth (can start with '$' or '*')\n"
590 " - GNUmed ID of person (can start with '#')\n"
591 ' - exterenal ID of person\n'
592 '\n'
593 'and hit <ENTER>.\n'
594 '\n'
595 'Shortcuts:\n'
596 ' <F2>\n'
597 ' - scan external sources for persons\n'
598 ' <CURSOR-UP>\n'
599 ' - recall most recently used search term\n'
600 ' <CURSOR-DOWN>\n'
601 ' - list 10 most recently found persons\n'
602 )
603 self.SetToolTipString(self._tt_search_hints)
604
605
606 self.__person_searcher = gmPerson.cPatientSearcher_SQL()
607
608 self._prev_search_term = None
609 self.__prev_idents = []
610 self._lclick_count = 0
611
612 self.__register_events()
613
614
615
617 self.__person = person
618 wx.CallAfter(self._display_name)
619
622
623 person = property(_get_person, _set_person)
624
625
626
634
636
637 if not isinstance(ident, gmPerson.cIdentity):
638 return False
639
640
641 for known_ident in self.__prev_idents:
642 if known_ident['pk_identity'] == ident['pk_identity']:
643 return True
644
645 self.__prev_idents.append(ident)
646
647
648 if len(self.__prev_idents) > 10:
649 self.__prev_idents.pop(0)
650
651 return True
652
653
654
656 wx.EVT_CHAR(self, self.__on_char)
657 wx.EVT_SET_FOCUS(self, self._on_get_focus)
658 wx.EVT_KILL_FOCUS (self, self._on_loose_focus)
659 wx.EVT_TEXT_ENTER (self, self.GetId(), self.__on_enter)
660
662 """upon tabbing in
663
664 - select all text in the field so that the next
665 character typed will delete it
666 """
667 wx.CallAfter(self.SetSelection, -1, -1)
668 evt.Skip()
669
671
672
673
674
675
676
677
678
679
680 wx.CallAfter(self.SetSelection, 0, 0)
681
682 self._display_name()
683 self._remember_ident(self.person)
684
685 evt.Skip()
686
689
691 """True: patient was selected.
692 False: no patient was selected.
693 """
694 keycode = evt.GetKeyCode()
695
696
697 if keycode == wx.WXK_DOWN:
698 evt.Skip()
699 if len(self.__prev_idents) == 0:
700 return False
701
702 dlg = cSelectPersonFromListDlg(parent = wx.GetTopLevelParent(self), id = -1)
703 dlg.set_persons(persons = self.__prev_idents)
704 result = dlg.ShowModal()
705 if result == wx.ID_OK:
706 wx.BeginBusyCursor()
707 self.person = dlg.get_selected_person()
708 dlg.Destroy()
709 wx.EndBusyCursor()
710 return True
711
712 dlg.Destroy()
713 return False
714
715
716 if keycode == wx.WXK_UP:
717 evt.Skip()
718
719 if self._prev_search_term is not None:
720 self.SetValue(self._prev_search_term)
721 return False
722
723
724 if keycode == wx.WXK_F2:
725 evt.Skip()
726 dbcfg = gmCfg.cCfgSQL()
727 search_immediately = bool(dbcfg.get2 (
728 option = 'patient_search.external_sources.immediately_search_if_single_source',
729 workplace = gmSurgery.gmCurrentPractice().active_workplace,
730 bias = 'user',
731 default = 0
732 ))
733 p = get_person_from_external_sources (
734 parent = wx.GetTopLevelParent(self),
735 search_immediately = search_immediately
736 )
737 if p is not None:
738 self.person = p
739 return True
740 return False
741
742
743
744
745 evt.Skip()
746
748 """This is called from the ENTER handler."""
749
750
751 curr_search_term = self.GetValue().strip()
752 if curr_search_term == '':
753 return None
754
755
756 if self.person is not None:
757 if curr_search_term == self.person['description']:
758 return None
759
760
761 if self.IsModified():
762 self._prev_search_term = curr_search_term
763
764 self._on_enter(search_term = curr_search_term)
765
767 """This can be overridden in child classes."""
768
769 wx.BeginBusyCursor()
770
771
772 idents = self.__person_searcher.get_identities(search_term)
773
774 if idents is None:
775 wx.EndBusyCursor()
776 gmGuiHelpers.gm_show_info (
777 _('Error searching for matching persons.\n\n'
778 'Search term: "%s"'
779 ) % search_term,
780 _('selecting person')
781 )
782 return None
783
784 _log.info("%s matching person(s) found", len(idents))
785
786 if len(idents) == 0:
787 wx.EndBusyCursor()
788
789 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
790 wx.GetTopLevelParent(self),
791 -1,
792 caption = _('Selecting patient'),
793 question = _(
794 'Cannot find any matching patients for the search term\n\n'
795 ' "%s"\n\n'
796 'You may want to try a shorter search term.\n'
797 ) % search_term,
798 button_defs = [
799 {'label': _('Go back'), 'tooltip': _('Go back and search again.'), 'default': True},
800 {'label': _('Create new'), 'tooltip': _('Create new patient.')}
801 ]
802 )
803 if dlg.ShowModal() != wx.ID_NO:
804 return
805
806 success = gmDemographicsWidgets.create_new_person(activate = True)
807 if success:
808 self.person = gmPerson.gmCurrentPatient()
809 else:
810 self.person = None
811 return None
812
813
814 if len(idents) == 1:
815 self.person = idents[0]
816 wx.EndBusyCursor()
817 return None
818
819
820 dlg = cSelectPersonFromListDlg(parent=wx.GetTopLevelParent(self), id=-1)
821 dlg.set_persons(persons=idents)
822 wx.EndBusyCursor()
823 result = dlg.ShowModal()
824 if result == wx.ID_CANCEL:
825 dlg.Destroy()
826 return None
827
828 wx.BeginBusyCursor()
829 self.person = dlg.get_selected_person()
830 dlg.Destroy()
831 wx.EndBusyCursor()
832
833 return None
834
836
837
838 try:
839 patient['dob']
840 check_dob = True
841 except TypeError:
842 check_dob = False
843
844 if check_dob:
845 if patient['dob'] is None:
846 gmGuiHelpers.gm_show_warning (
847 aTitle = _('Checking date of birth'),
848 aMessage = _(
849 '\n'
850 ' %s\n'
851 '\n'
852 'The date of birth for this patient is not known !\n'
853 '\n'
854 'You can proceed to work on the patient but\n'
855 'GNUmed will be unable to assist you with\n'
856 'age-related decisions.\n'
857 ) % patient['description_gender']
858 )
859
860 success = gmPerson.set_active_patient(patient = patient, forced_reload = forced_reload)
861
862 if success:
863 if patient['dob'] is not None:
864 dbcfg = gmCfg.cCfgSQL()
865 dob_distance = dbcfg.get2 (
866 option = u'patient_search.dob_warn_interval',
867 workplace = gmSurgery.gmCurrentPractice().active_workplace,
868 bias = u'user',
869 default = u'1 week'
870 )
871
872 if patient.dob_in_range(dob_distance, dob_distance):
873 now = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone)
874 enc = gmI18N.get_encoding()
875 gmDispatcher.send(signal = 'statustext', msg = _(
876 '%(pat)s turns %(age)s on %(month)s %(day)s ! (today is %(month_now)s %(day_now)s)') % {
877 'pat': patient.get_description_gender(),
878 'age': patient.get_medical_age().strip('y'),
879 'month': patient.get_formatted_dob(format = '%B', encoding = enc),
880 'day': patient.get_formatted_dob(format = '%d', encoding = enc),
881 'month_now': now.strftime('%B').decode(enc),
882 'day_now': now.strftime('%d')
883 }
884 )
885
886 return success
887
889
934
935
936
938 name = _('<type here to search patient>')
939
940 curr_pat = gmPerson.gmCurrentPatient()
941 if curr_pat.connected:
942 name = curr_pat['description']
943 if curr_pat.locked:
944 name = _('%(name)s (locked)') % {'name': name}
945
946 self.SetValue(name)
947
948 if self.person is None:
949 self.SetToolTipString(self._tt_search_hints)
950 return
951
952 tt = u'%s%s-----------------------------------\n%s' % (
953 gmTools.coalesce(self.person['emergency_contact'], u'', _('In case of emergency contact:') + u'\n %s\n'),
954 gmTools.coalesce(self.person['comment'], u'', u'\n%s\n'),
955 self._tt_search_hints
956 )
957 self.SetToolTipString(tt)
958
960 if not set_active_patient(patient=pat, forced_reload = self.__always_reload_after_search):
961 _log.error('cannot change active patient')
962 return None
963
964 self._remember_ident(pat)
965
966 return True
967
968
969
971
972 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
973 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_name_identity_change)
974 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_name_identity_change)
975
976 gmDispatcher.connect(signal = 'patient_locked', receiver = self._on_post_patient_selection)
977 gmDispatcher.connect(signal = 'patient_unlocked', receiver = self._on_post_patient_selection)
978
980 wx.CallAfter(self._display_name)
981
987
989
990 if self.__always_dismiss_on_search:
991 _log.warning("dismissing patient before patient search")
992 self._set_person_as_active_patient(-1)
993
994 super(self.__class__, self)._on_enter(search_term=search_term)
995
996 if self.person is None:
997 return
998
999 self._set_person_as_active_patient(self.person)
1000
1002
1003 success = super(self.__class__, self)._on_char(evt)
1004 if success:
1005 self._set_person_as_active_patient(self.person)
1006
1007
1008
1010
1019
1020
1022 self.matcher.set_items([ {'data': i, 'label': i, 'weight': 1} for i in items ])
1023
1024
1025 from Gnumed.wxGladeWidgets import wxgWaitingListEntryEditAreaPnl
1026
1027 -class cWaitingListEntryEditAreaPnl(wxgWaitingListEntryEditAreaPnl.wxgWaitingListEntryEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1028
1029 - def __init__ (self, *args, **kwargs):
1030
1031 try:
1032 self.patient = kwargs['patient']
1033 del kwargs['patient']
1034 except KeyError:
1035 self.patient = None
1036
1037 try:
1038 data = kwargs['entry']
1039 del kwargs['entry']
1040 except KeyError:
1041 data = None
1042
1043 wxgWaitingListEntryEditAreaPnl.wxgWaitingListEntryEditAreaPnl.__init__(self, *args, **kwargs)
1044 gmEditArea.cGenericEditAreaMixin.__init__(self)
1045
1046 if data is None:
1047 self.mode = 'new'
1048 else:
1049 self.data = data
1050 self.mode = 'edit'
1051
1052 praxis = gmSurgery.gmCurrentPractice()
1053 pats = praxis.waiting_list_patients
1054 zones = {}
1055 zones.update([ [p['waiting_zone'], None] for p in pats if p['waiting_zone'] is not None ])
1056 self._PRW_zone.update_matcher(items = zones.keys())
1057
1058
1059
1060 - def _refresh_as_new(self):
1061 if self.patient is None:
1062 self._PRW_patient.person = None
1063 self._PRW_patient.Enable(True)
1064 self._PRW_patient.SetFocus()
1065 else:
1066 self._PRW_patient.person = self.patient
1067 self._PRW_patient.Enable(False)
1068 self._PRW_comment.SetFocus()
1069 self._PRW_patient._display_name()
1070
1071 self._PRW_comment.SetValue(u'')
1072 self._PRW_zone.SetValue(u'')
1073 self._SPCTRL_urgency.SetValue(0)
1074
1076 self._PRW_patient.person = gmPerson.cIdentity(aPK_obj = self.data['pk_identity'])
1077 self._PRW_patient.Enable(False)
1078 self._PRW_patient._display_name()
1079
1080 self._PRW_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
1081 self._PRW_zone.SetValue(gmTools.coalesce(self.data['waiting_zone'], u''))
1082 self._SPCTRL_urgency.SetValue(self.data['urgency'])
1083
1084 self._PRW_comment.SetFocus()
1085
1086 - def _valid_for_save(self):
1087 validity = True
1088
1089 self.display_tctrl_as_valid(tctrl = self._PRW_patient, valid = (self._PRW_patient.person is not None))
1090 validity = (self._PRW_patient.person is not None)
1091
1092 if validity is False:
1093 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add to waiting list. Missing essential input.'))
1094
1095 return validity
1096
1097 - def _save_as_new(self):
1098
1099 self._PRW_patient.person.put_on_waiting_list (
1100 urgency = self._SPCTRL_urgency.GetValue(),
1101 comment = gmTools.none_if(self._PRW_comment.GetValue().strip(), u''),
1102 zone = gmTools.none_if(self._PRW_zone.GetValue().strip(), u'')
1103 )
1104
1105 self.data = {'pk_identity': None, 'comment': None, 'waiting_zone': None, 'urgency': 0}
1106 return True
1107
1108 - def _save_as_update(self):
1109 gmSurgery.gmCurrentPractice().update_in_waiting_list (
1110 pk = self.data['pk_waiting_list'],
1111 urgency = self._SPCTRL_urgency.GetValue(),
1112 comment = self._PRW_comment.GetValue().strip(),
1113 zone = self._PRW_zone.GetValue().strip()
1114 )
1115 return True
1116
1117 from Gnumed.wxGladeWidgets import wxgWaitingListPnl
1118
1119 -class cWaitingListPnl(wxgWaitingListPnl.wxgWaitingListPnl, gmRegetMixin.cRegetOnPaintMixin):
1120
1130
1131
1132
1134 self._LCTRL_patients.set_columns ([
1135 _('Zone'),
1136 _('Urgency'),
1137
1138 _('Waiting time'),
1139 _('Patient'),
1140 _('Born'),
1141 _('Comment')
1142 ])
1143 self._LCTRL_patients.set_column_widths(widths = [wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE])
1144 self._PRW_zone.add_callback_on_selection(callback = self._on_zone_selected)
1145 self._PRW_zone.add_callback_on_lose_focus(callback = self._on_zone_selected)
1146
1148 gmDispatcher.connect(signal = u'waiting_list_generic_mod_db', receiver = self._on_waiting_list_modified)
1149
1151
1152 praxis = gmSurgery.gmCurrentPractice()
1153 pats = praxis.waiting_list_patients
1154
1155
1156 zones = {}
1157 zones.update([ [p['waiting_zone'], None] for p in pats if p['waiting_zone'] is not None ])
1158 self._PRW_zone.update_matcher(items = zones.keys())
1159 del zones
1160
1161
1162 self.__current_zone = self._PRW_zone.GetValue().strip()
1163 if self.__current_zone == u'':
1164 pats = [ p for p in pats ]
1165 else:
1166 pats = [ p for p in pats if p['waiting_zone'] == self.__current_zone ]
1167
1168 self._LCTRL_patients.set_string_items (
1169 [ [
1170 gmTools.coalesce(p['waiting_zone'], u''),
1171 p['urgency'],
1172 p['waiting_time_formatted'].replace(u'00 ', u'', 1).replace('00:', u'').lstrip('0'),
1173 u'%s, %s (%s)' % (p['lastnames'], p['firstnames'], p['l10n_gender']),
1174 p['dob'],
1175 gmTools.coalesce(p['comment'], u'')
1176 ] for p in pats
1177 ]
1178 )
1179 self._LCTRL_patients.set_column_widths()
1180 self._LCTRL_patients.set_data(pats)
1181 self._LCTRL_patients.Refresh()
1182 self._LCTRL_patients.SetToolTipString ( _(
1183 '%s patients are waiting.\n'
1184 '\n'
1185 'Doubleclick to activate (entry will stay in list).'
1186 ) % len(pats))
1187
1188 self._LBL_no_of_patients.SetLabel(_('(%s patients)') % len(pats))
1189
1190 if len(pats) == 0:
1191 self._BTN_activate.Enable(False)
1192 self._BTN_activateplus.Enable(False)
1193 self._BTN_remove.Enable(False)
1194 self._BTN_edit.Enable(False)
1195 self._BTN_up.Enable(False)
1196 self._BTN_down.Enable(False)
1197 else:
1198 self._BTN_activate.Enable(True)
1199 self._BTN_activateplus.Enable(True)
1200 self._BTN_remove.Enable(True)
1201 self._BTN_edit.Enable(True)
1202 if len(pats) > 1:
1203 self._BTN_up.Enable(True)
1204 self._BTN_down.Enable(True)
1205
1206
1207
1209 if self.__current_zone == self._PRW_zone.GetValue().strip():
1210 return True
1211 wx.CallAfter(self.__refresh_waiting_list)
1212 return True
1213
1215 wx.CallAfter(self._schedule_data_reget)
1216
1223
1230
1238
1250
1259
1265
1271
1277
1278
1279
1280
1281
1283 self.__refresh_waiting_list()
1284 return True
1285
1286
1287
1288 if __name__ == "__main__":
1289
1290 if len(sys.argv) > 1:
1291 if sys.argv[1] == 'test':
1292 gmI18N.activate_locale()
1293 gmI18N.install_domain()
1294
1295 app = wx.PyWidgetTester(size = (200, 40))
1296
1297
1298
1299 app.SetWidget(cWaitingListPnl, -1)
1300 app.MainLoop()
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405