1 """Widgets dealing with patient demographics."""
2
3 __author__ = "R.Terry, SJ Tan, I Haywood, Carlos Moro <cfmoro1976@yahoo.es>"
4 __license__ = 'GPL v2 or later (details at http://www.gnu.org)'
5
6
7 import sys
8 import sys
9 import codecs
10 import re as regex
11 import logging
12 import os
13 import datetime as pydt
14
15
16 import wx
17 import wx.wizard
18 import wx.lib.imagebrowser as wx_imagebrowser
19 import wx.lib.statbmp as wx_genstatbmp
20
21
22
23 if __name__ == '__main__':
24 sys.path.insert(0, '../../')
25 from Gnumed.pycommon import gmDispatcher
26 from Gnumed.pycommon import gmI18N
27 from Gnumed.pycommon import gmMatchProvider
28 from Gnumed.pycommon import gmPG2
29 from Gnumed.pycommon import gmTools
30 from Gnumed.pycommon import gmCfg
31 from Gnumed.pycommon import gmDateTime
32 from Gnumed.pycommon import gmShellAPI
33 from Gnumed.pycommon import gmNetworkTools
34
35 from Gnumed.business import gmDemographicRecord
36 from Gnumed.business import gmPersonSearch
37 from Gnumed.business import gmSurgery
38 from Gnumed.business import gmPerson
39 from Gnumed.business import gmStaff
40
41 from Gnumed.wxpython import gmPhraseWheel
42 from Gnumed.wxpython import gmRegetMixin
43 from Gnumed.wxpython import gmAuthWidgets
44 from Gnumed.wxpython import gmPersonContactWidgets
45 from Gnumed.wxpython import gmEditArea
46 from Gnumed.wxpython import gmListWidgets
47 from Gnumed.wxpython import gmDateTimeInput
48 from Gnumed.wxpython import gmDataMiningWidgets
49 from Gnumed.wxpython import gmGuiHelpers
50
51
52
53 _log = logging.getLogger('gm.ui')
54
55
56 try:
57 _('dummy-no-need-to-translate-but-make-epydoc-happy')
58 except NameError:
59 _ = lambda x:x
60
61
62
63
65 if tag_image is not None:
66 if tag_image['is_in_use']:
67 gmGuiHelpers.gm_show_info (
68 aTitle = _('Editing tag'),
69 aMessage = _(
70 'Cannot edit the image tag\n'
71 '\n'
72 ' "%s"\n'
73 '\n'
74 'because it is currently in use.\n'
75 ) % tag_image['l10n_description']
76 )
77 return False
78
79 ea = cTagImageEAPnl(parent = parent, id = -1)
80 ea.data = tag_image
81 ea.mode = gmTools.coalesce(tag_image, 'new', 'edit')
82 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
83 dlg.SetTitle(gmTools.coalesce(tag_image, _('Adding new tag'), _('Editing tag')))
84 if dlg.ShowModal() == wx.ID_OK:
85 dlg.Destroy()
86 return True
87 dlg.Destroy()
88 return False
89
101
102 def edit(tag_image=None):
103 return edit_tag_image(parent = parent, tag_image = tag_image, single_entry = (tag_image is not None))
104
105 def delete(tag):
106 if tag['is_in_use']:
107 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this tag. It is in use.'), beep = True)
108 return False
109
110 return gmDemographicRecord.delete_tag_image(tag_image = tag['pk_tag_image'])
111
112 def refresh(lctrl):
113 tags = gmDemographicRecord.get_tag_images(order_by = u'l10n_description')
114 items = [ [
115 t['l10n_description'],
116 gmTools.bool2subst(t['is_in_use'], u'X', u''),
117 u'%s' % t['size'],
118 t['pk_tag_image']
119 ] for t in tags ]
120 lctrl.set_string_items(items)
121 lctrl.set_column_widths(widths = [wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE])
122 lctrl.set_data(tags)
123
124 msg = _('\nTags with images registered with GNUmed.\n')
125
126 tag = gmListWidgets.get_choices_from_list (
127 parent = parent,
128 msg = msg,
129 caption = _('Showing tags with images.'),
130 columns = [_('Tag name'), _('In use'), _('Image size'), u'#'],
131 single_selection = True,
132 new_callback = edit,
133 edit_callback = edit,
134 delete_callback = delete,
135 refresh_callback = refresh,
136 left_extra_button = (_('WWW'), _('Go to www.openclipart.org for images.'), go_to_openclipart_org)
137 )
138
139 return tag
140
141 from Gnumed.wxGladeWidgets import wxgTagImageEAPnl
142
143 -class cTagImageEAPnl(wxgTagImageEAPnl.wxgTagImageEAPnl, gmEditArea.cGenericEditAreaMixin):
144
162
163
164
166
167 valid = True
168
169 if self.mode == u'new':
170 if self.__selected_image_file is None:
171 valid = False
172 gmDispatcher.send(signal = 'statustext', msg = _('Must pick an image file for a new tag.'), beep = True)
173 self._BTN_pick_image.SetFocus()
174
175 if self.__selected_image_file is not None:
176 try:
177 open(self.__selected_image_file).close()
178 except StandardError:
179 valid = False
180 self.__selected_image_file = None
181 gmDispatcher.send(signal = 'statustext', msg = _('Cannot open the image file [%s].') % self.__selected_image_file, beep = True)
182 self._BTN_pick_image.SetFocus()
183
184 if self._TCTRL_description.GetValue().strip() == u'':
185 valid = False
186 self.display_tctrl_as_valid(self._TCTRL_description, False)
187 self._TCTRL_description.SetFocus()
188 else:
189 self.display_tctrl_as_valid(self._TCTRL_description, True)
190
191 return (valid is True)
192
211
231
233 self._TCTRL_description.SetValue(u'')
234 self._TCTRL_filename.SetValue(u'')
235 self._BMP_image.SetBitmap(bitmap = wx.EmptyBitmap(100, 100))
236
237 self.__selected_image_file = None
238
239 self._TCTRL_description.SetFocus()
240
242 self._refresh_as_new()
243
256
257
258
270
271
286
287 def delete(tag):
288 do_delete = gmGuiHelpers.gm_show_question (
289 title = _('Deleting patient tag'),
290 question = _('Do you really want to delete this patient tag ?')
291 )
292 if not do_delete:
293 return False
294 patient.remove_tag(tag = tag['pk_identity_tag'])
295 return True
296
297 def manage_available_tags(tag):
298 manage_tag_images(parent = parent)
299 return False
300
301 msg = _('Tags of patient: %s\n') % patient['description_gender']
302
303 return gmListWidgets.get_choices_from_list (
304 parent = parent,
305 msg = msg,
306 caption = _('Showing patient tags'),
307 columns = [_('Tag'), _('Comment')],
308 single_selection = False,
309 delete_callback = delete,
310 refresh_callback = refresh,
311 left_extra_button = (_('Manage'), _('Manage available tags.'), manage_available_tags)
312 )
313
314 from Gnumed.wxGladeWidgets import wxgVisualSoapPresenterPnl
315
317
319 wxgVisualSoapPresenterPnl.wxgVisualSoapPresenterPnl.__init__(self, *args, **kwargs)
320 self._SZR_bitmaps = self.GetSizer()
321 self.__bitmaps = []
322
323 self.__context_popup = wx.Menu()
324
325 item = self.__context_popup.Append(-1, _('&Edit comment'))
326 self.Bind(wx.EVT_MENU, self.__edit_tag, item)
327
328 item = self.__context_popup.Append(-1, _('&Remove tag'))
329 self.Bind(wx.EVT_MENU, self.__remove_tag, item)
330
331
332
334
335 self.clear()
336
337 for tag in patient.get_tags(order_by = u'l10n_description'):
338 fname = tag.export_image2file()
339 if fname is None:
340 _log.warning('cannot export image data of tag [%s]', tag['l10n_description'])
341 continue
342 img = gmGuiHelpers.file2scaled_image(filename = fname, height = 20)
343 bmp = wx_genstatbmp.GenStaticBitmap(self, -1, img, style = wx.NO_BORDER)
344 bmp.SetToolTipString(u'%s%s' % (
345 tag['l10n_description'],
346 gmTools.coalesce(tag['comment'], u'', u'\n\n%s')
347 ))
348 bmp.tag = tag
349 bmp.Bind(wx.EVT_RIGHT_UP, self._on_bitmap_rightclicked)
350
351 self._SZR_bitmaps.Add(bmp, 0, wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, 1)
352 self.__bitmaps.append(bmp)
353
354 self.GetParent().Layout()
355
357 while len(self._SZR_bitmaps.GetChildren()) > 0:
358 self._SZR_bitmaps.Detach(0)
359
360
361 for bmp in self.__bitmaps:
362 bmp.Destroy()
363 self.__bitmaps = []
364
365
366
374
376 if self.__current_tag is None:
377 return
378
379 msg = _('Edit the comment on tag [%s]') % self.__current_tag['l10n_description']
380 comment = wx.GetTextFromUser (
381 message = msg,
382 caption = _('Editing tag comment'),
383 default_value = gmTools.coalesce(self.__current_tag['comment'], u''),
384 parent = self
385 )
386
387 if comment == u'':
388 return
389
390 if comment.strip() == self.__current_tag['comment']:
391 return
392
393 if comment == u' ':
394 self.__current_tag['comment'] = None
395 else:
396 self.__current_tag['comment'] = comment.strip()
397
398 self.__current_tag.save()
399
400
401
403 self.__current_tag = evt.GetEventObject().tag
404 self.PopupMenu(self.__context_popup, pos = wx.DefaultPosition)
405 self.__current_tag = None
406
407
409
411
412 kwargs['message'] = _("Today's KOrganizer appointments ...")
413 kwargs['button_defs'] = [
414 {'label': _('Reload'), 'tooltip': _('Reload appointments from KOrganizer')},
415 {'label': u''},
416 {'label': u''},
417 {'label': u''},
418 {'label': u'KOrganizer', 'tooltip': _('Launch KOrganizer')}
419 ]
420 gmDataMiningWidgets.cPatientListingPnl.__init__(self, *args, **kwargs)
421
422 self.fname = os.path.expanduser(os.path.join('~', '.gnumed', 'tmp', 'korganizer2gnumed.csv'))
423 self.reload_cmd = 'konsolekalendar --view --export-type csv --export-file %s' % self.fname
424
425
429
439
441 try: os.remove(self.fname)
442 except OSError: pass
443 gmShellAPI.run_command_in_shell(command=self.reload_cmd, blocking=True)
444 try:
445 csv_file = codecs.open(self.fname , mode = 'rU', encoding = 'utf8', errors = 'replace')
446 except IOError:
447 gmDispatcher.send(signal = u'statustext', msg = _('Cannot access KOrganizer transfer file [%s]') % self.fname, beep = True)
448 return
449
450 csv_lines = gmTools.unicode_csv_reader (
451 csv_file,
452 delimiter = ','
453 )
454
455 self._LCTRL_items.set_columns ([
456 _('Place'),
457 _('Start'),
458 u'',
459 u'',
460 _('Patient'),
461 _('Comment')
462 ])
463 items = []
464 data = []
465 for line in csv_lines:
466 items.append([line[5], line[0], line[1], line[3], line[4], line[6]])
467 data.append([line[4], line[7]])
468
469 self._LCTRL_items.set_string_items(items = items)
470 self._LCTRL_items.set_column_widths()
471 self._LCTRL_items.set_data(data = data)
472 self._LCTRL_items.patient_key = 0
473
474
475
478
479
480
482
483 pat = gmPerson.gmCurrentPatient()
484 curr_jobs = pat.get_occupations()
485 if len(curr_jobs) > 0:
486 old_job = curr_jobs[0]['l10n_occupation']
487 update = curr_jobs[0]['modified_when'].strftime('%m/%Y')
488 else:
489 old_job = u''
490 update = u''
491
492 msg = _(
493 'Please enter the primary occupation of the patient.\n'
494 '\n'
495 'Currently recorded:\n'
496 '\n'
497 ' %s (last updated %s)'
498 ) % (old_job, update)
499
500 new_job = wx.GetTextFromUser (
501 message = msg,
502 caption = _('Editing primary occupation'),
503 default_value = old_job,
504 parent = None
505 )
506 if new_job.strip() == u'':
507 return
508
509 for job in curr_jobs:
510
511 if job['l10n_occupation'] != new_job:
512 pat.unlink_occupation(occupation = job['l10n_occupation'])
513
514 pat.link_occupation(occupation = new_job)
515
516
531
532
533
534
536
537 go_ahead = gmGuiHelpers.gm_show_question (
538 _('Are you sure you really, positively want\n'
539 'to disable the following person ?\n'
540 '\n'
541 ' %s %s %s\n'
542 ' born %s\n'
543 '\n'
544 '%s\n'
545 ) % (
546 identity['firstnames'],
547 identity['lastnames'],
548 identity['gender'],
549 identity.get_formatted_dob(),
550 gmTools.bool2subst (
551 identity.is_patient,
552 _('This patient DID receive care.'),
553 _('This person did NOT receive care.')
554 )
555 ),
556 _('Disabling person')
557 )
558 if not go_ahead:
559 return True
560
561
562 conn = gmAuthWidgets.get_dbowner_connection (
563 procedure = _('Disabling patient')
564 )
565
566 if conn is False:
567 return True
568
569 if conn is None:
570 return False
571
572
573 gmPG2.run_rw_queries(queries = [{'cmd': u"update dem.identity set deleted=True where pk=%s", 'args': [identity['pk_identity']]}])
574
575 return True
576
577
578
579
594
596
598 query = u"""
599 (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
600 union
601 (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
602 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
603 mp.setThresholds(3, 5, 9)
604 gmPhraseWheel.cPhraseWheel.__init__ (
605 self,
606 *args,
607 **kwargs
608 )
609 self.SetToolTipString(_("Type or select a first name (forename/Christian name/given name)."))
610 self.capitalisation_mode = gmTools.CAPS_NAMES
611 self.matcher = mp
612
614
616 query = u"""
617 (SELECT distinct preferred, preferred from dem.names where preferred %(fragment_condition)s order by preferred limit 20)
618 union
619 (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
620 union
621 (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
622 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
623 mp.setThresholds(3, 5, 9)
624 gmPhraseWheel.cPhraseWheel.__init__ (
625 self,
626 *args,
627 **kwargs
628 )
629 self.SetToolTipString(_("Type or select an alias (nick name, preferred name, call name, warrior name, artist name)."))
630
631
632 self.matcher = mp
633
635
637 query = u"SELECT distinct title, title from dem.identity where title %(fragment_condition)s"
638 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
639 mp.setThresholds(1, 3, 9)
640 gmPhraseWheel.cPhraseWheel.__init__ (
641 self,
642 *args,
643 **kwargs
644 )
645 self.SetToolTipString(_("Type or select a title. Note that the title applies to the person, not to a particular name !"))
646 self.matcher = mp
647
649 """Let user select a gender."""
650
651 _gender_map = None
652
654
655 if cGenderSelectionPhraseWheel._gender_map is None:
656 cmd = u"""
657 SELECT tag, l10n_label, sort_weight
658 from dem.v_gender_labels
659 order by sort_weight desc"""
660 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
661 cGenderSelectionPhraseWheel._gender_map = {}
662 for gender in rows:
663 cGenderSelectionPhraseWheel._gender_map[gender[idx['tag']]] = {
664 'data': gender[idx['tag']],
665 'field_label': gender[idx['l10n_label']],
666 'list_label': gender[idx['l10n_label']],
667 'weight': gender[idx['sort_weight']]
668 }
669
670 mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = cGenderSelectionPhraseWheel._gender_map.values())
671 mp.setThresholds(1, 1, 3)
672
673 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
674 self.selection_only = True
675 self.matcher = mp
676 self.picklist_delay = 50
677
679
681 query = u"""
682 SELECT DISTINCT ON (list_label)
683 pk AS data,
684 name AS field_label,
685 name || coalesce(' (' || issuer || ')', '') as list_label
686 FROM dem.enum_ext_id_types
687 WHERE name %(fragment_condition)s
688 ORDER BY list_label
689 LIMIT 25
690 """
691 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
692 mp.setThresholds(1, 3, 5)
693 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
694 self.SetToolTipString(_("Enter or select a type for the external ID."))
695 self.matcher = mp
696
701
716
717
718
719 from Gnumed.wxGladeWidgets import wxgExternalIDEditAreaPnl
720
721 -class cExternalIDEditAreaPnl(wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
722 """An edit area for editing/creating external IDs.
723
724 Does NOT act on/listen to the current patient.
725 """
745
748
749
750
771
789
805
811
813 self._refresh_as_new()
814 self._PRW_issuer.SetText(self.data['issuer'])
815
821
822
823
825 """Set the issuer according to the selected type.
826
827 Matches are fetched from existing records in backend.
828 """
829 pk_curr_type = self._PRW_type.GetData()
830 if pk_curr_type is None:
831 return True
832 rows, idx = gmPG2.run_ro_queries(queries = [{
833 'cmd': u"SELECT issuer from dem.enum_ext_id_types where pk = %s",
834 'args': [pk_curr_type]
835 }])
836 if len(rows) == 0:
837 return True
838 wx.CallAfter(self._PRW_issuer.SetText, rows[0][0])
839 return True
840
841
842
843
845 allow_empty_dob = gmGuiHelpers.gm_show_question (
846 _(
847 'Are you sure you want to leave this person\n'
848 'without a valid date of birth ?\n'
849 '\n'
850 'This can be useful for temporary staff members\n'
851 'but will provoke nag screens if this person\n'
852 'becomes a patient.\n'
853 ),
854 _('Validating date of birth')
855 )
856 return allow_empty_dob
857
859
860
861 if dob_prw.is_valid_timestamp(allow_empty = False):
862 dob = dob_prw.date
863
864 if (dob.year > 1899) and (dob < gmDateTime.pydt_now_here()):
865 return True
866
867 if dob.year < 1900:
868 msg = _(
869 'DOB: %s\n'
870 '\n'
871 'While this is a valid point in time Python does\n'
872 'not know how to deal with it.\n'
873 '\n'
874 'We suggest using January 1st 1901 instead and adding\n'
875 'the true date of birth to the patient comment.\n'
876 '\n'
877 'Sorry for the inconvenience %s'
878 ) % (dob, gmTools.u_frowning_face)
879 else:
880 msg = _(
881 'DOB: %s\n'
882 '\n'
883 'Date of birth in the future !'
884 ) % dob
885 gmGuiHelpers.gm_show_error (
886 msg,
887 _('Validating date of birth')
888 )
889 dob_prw.display_as_valid(False)
890 dob_prw.SetFocus()
891 return False
892
893
894 if dob_prw.GetValue().strip() != u'':
895 dob_prw.display_as_valid(False)
896 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of birth.'))
897 dob_prw.SetFocus()
898 return False
899
900
901 dob_prw.display_as_valid(False)
902 return True
903
904
906
907 val = ctrl.GetValue().strip()
908
909 if val == u'':
910 return True
911
912 converted, hours = gmTools.input2int(val[:2], 0, 23)
913 if not converted:
914 return False
915
916 converted, minutes = gmTools.input2int(val[3:5], 0, 59)
917 if not converted:
918 return False
919
920 return True
921
922
923 from Gnumed.wxGladeWidgets import wxgIdentityEAPnl
924
925 -class cIdentityEAPnl(wxgIdentityEAPnl.wxgIdentityEAPnl, gmEditArea.cGenericEditAreaMixin):
926 """An edit area for editing/creating title/gender/dob/dod etc."""
927
943
944
945
946
947
948
949
950
952
953 has_error = False
954
955 if self._PRW_gender.GetData() is None:
956 self._PRW_gender.SetFocus()
957 has_error = True
958
959 if self.data is not None:
960 if not _validate_dob_field(self._PRW_dob):
961 has_error = True
962
963
964 if _validate_tob_field(self._TCTRL_tob):
965 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True)
966 else:
967 has_error = True
968 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False)
969
970 if not self._PRW_dod.is_valid_timestamp(allow_empty = True):
971 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.'))
972 self._PRW_dod.SetFocus()
973 has_error = True
974
975 return (has_error is False)
976
980
982
983 if self._PRW_dob.GetValue().strip() == u'':
984 if not _empty_dob_allowed():
985 return False
986 self.data['dob'] = None
987 else:
988 self.data['dob'] = self._PRW_dob.GetData()
989 self.data['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
990 val = self._TCTRL_tob.GetValue().strip()
991 if val == u'':
992 self.data['tob'] = None
993 else:
994 self.data['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
995 self.data['gender'] = self._PRW_gender.GetData()
996 self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'')
997 self.data['deceased'] = self._PRW_dod.GetData()
998 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
999
1000 self.data.save()
1001 return True
1002
1005
1039
1042
1043 from Gnumed.wxGladeWidgets import wxgPersonNameEAPnl
1044
1045 -class cPersonNameEAPnl(wxgPersonNameEAPnl.wxgPersonNameEAPnl, gmEditArea.cGenericEditAreaMixin):
1046 """An edit area for editing/creating names of people.
1047
1048 Does NOT act on/listen to the current patient.
1049 """
1070
1071
1072
1073
1074
1075
1076
1077
1079 validity = True
1080
1081 if self._PRW_lastname.GetValue().strip() == u'':
1082 validity = False
1083 self._PRW_lastname.display_as_valid(False)
1084 self._PRW_lastname.SetFocus()
1085 else:
1086 self._PRW_lastname.display_as_valid(True)
1087
1088 if self._PRW_firstname.GetValue().strip() == u'':
1089 validity = False
1090 self._PRW_firstname.display_as_valid(False)
1091 self._PRW_firstname.SetFocus()
1092 else:
1093 self._PRW_firstname.display_as_valid(True)
1094
1095 return validity
1096
1098
1099 first = self._PRW_firstname.GetValue().strip()
1100 last = self._PRW_lastname.GetValue().strip()
1101 active = self._CHBOX_active.GetValue()
1102
1103 data = self.__identity.add_name(first, last, active)
1104
1105 old_nick = self.__identity['active_name']['preferred']
1106 new_nick = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
1107 if active:
1108 data['preferred'] = gmTools.coalesce(new_nick, old_nick)
1109 else:
1110 data['preferred'] = new_nick
1111 data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1112 data.save()
1113
1114 self.data = data
1115 return True
1116
1118 """The knack here is that we can only update a few fields.
1119
1120 Otherwise we need to clone the name and update that.
1121 """
1122 first = self._PRW_firstname.GetValue().strip()
1123 last = self._PRW_lastname.GetValue().strip()
1124 active = self._CHBOX_active.GetValue()
1125
1126 current_name = self.data['firstnames'].strip() + self.data['lastnames'].strip()
1127 new_name = first + last
1128
1129
1130 if new_name == current_name:
1131 self.data['active_name'] = self._CHBOX_active.GetValue()
1132 self.data['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
1133 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1134 self.data.save()
1135
1136 else:
1137 name = self.__identity.add_name(first, last, active)
1138 name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
1139 name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1140 name.save()
1141 self.data = name
1142
1143 return True
1144
1153
1160
1169
1170
1171
1173 """A list for managing a person's names.
1174
1175 Does NOT act on/listen to the current patient.
1176 """
1194
1195
1196
1197 - def refresh(self, *args, **kwargs):
1214
1215
1216
1218 self._LCTRL_items.set_columns(columns = [
1219 _('Active'),
1220 _('Lastname'),
1221 _('Firstname(s)'),
1222 _('Preferred Name'),
1223 _('Comment')
1224 ])
1225
1236
1246
1248
1249 if len(self.__identity.get_names()) == 1:
1250 gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the only name of a person.'), beep = True)
1251 return False
1252
1253 if name['active_name']:
1254 gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the active name of a person.'), beep = True)
1255 return False
1256
1257 go_ahead = gmGuiHelpers.gm_show_question (
1258 _( 'It is often advisable to keep old names around and\n'
1259 'just create a new "currently active" name.\n'
1260 '\n'
1261 'This allows finding the patient by both the old\n'
1262 'and the new name (think before/after marriage).\n'
1263 '\n'
1264 'Do you still want to really delete\n'
1265 "this name from the patient ?"
1266 ),
1267 _('Deleting name')
1268 )
1269 if not go_ahead:
1270 return False
1271
1272 self.__identity.delete_name(name = name)
1273 return True
1274
1275
1276
1278 return self.__identity
1279
1283
1284 identity = property(_get_identity, _set_identity)
1285
1287 """A list for managing a person's external IDs.
1288
1289 Does NOT act on/listen to the current patient.
1290 """
1308
1309
1310
1311 - def refresh(self, *args, **kwargs):
1328
1329
1330
1332 self._LCTRL_items.set_columns(columns = [
1333 _('ID type'),
1334 _('Value'),
1335 _('Issuer'),
1336 _('Comment')
1337 ])
1338
1349
1360
1362 go_ahead = gmGuiHelpers.gm_show_question (
1363 _( 'Do you really want to delete this\n'
1364 'external ID from the patient ?'),
1365 _('Deleting external ID')
1366 )
1367 if not go_ahead:
1368 return False
1369 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id'])
1370 return True
1371
1372
1373
1375 return self.__identity
1376
1380
1381 identity = property(_get_identity, _set_identity)
1382
1383
1384
1385 from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl
1386
1388 """A panel for editing identity data for a person.
1389
1390 - provides access to:
1391 - identity EA
1392 - name list manager
1393 - external IDs list manager
1394
1395 Does NOT act on/listen to the current patient.
1396 """
1403
1404
1405
1407 self._PNL_names.identity = self.__identity
1408 self._PNL_ids.identity = self.__identity
1409
1410 self._PNL_identity.mode = 'new'
1411 self._PNL_identity.data = self.__identity
1412 if self.__identity is not None:
1413 self._PNL_identity.mode = 'edit'
1414 self._PNL_identity._refresh_from_existing()
1415
1416
1417
1419 return self.__identity
1420
1424
1425 identity = property(_get_identity, _set_identity)
1426
1427
1428
1432
1433
1436
1437
1438 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl
1439
1448
1449
1450
1452
1453 tt = _('Link another person in this database as the emergency contact:\n\nEnter person name part or identifier and hit <enter>.')
1454
1455 if self.__identity is None:
1456 self._TCTRL_er_contact.SetValue(u'')
1457 self._TCTRL_person.person = None
1458 self._TCTRL_person.SetToolTipString(tt)
1459
1460 self._PRW_provider.SetText(value = u'', data = None)
1461 return
1462
1463 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], u''))
1464 if self.__identity['pk_emergency_contact'] is not None:
1465 ident = gmPerson.cIdentity(aPK_obj = self.__identity['pk_emergency_contact'])
1466 self._TCTRL_person.person = ident
1467 tt = u'%s\n\n%s\n\n%s' % (
1468 tt,
1469 ident['description_gender'],
1470 u'\n'.join([
1471 u'%s: %s%s' % (
1472 c['l10n_comm_type'],
1473 c['url'],
1474 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), u'', u'')
1475 )
1476 for c in ident.get_comm_channels()
1477 ])
1478 )
1479 else:
1480 self._TCTRL_person.person = None
1481
1482 self._TCTRL_person.SetToolTipString(tt)
1483
1484 if self.__identity['pk_primary_provider'] is None:
1485 self._PRW_provider.SetText(value = u'', data = None)
1486 else:
1487 self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
1488
1489
1490
1492 return self.__identity
1493
1497
1498 identity = property(_get_identity, _set_identity)
1499
1500
1501
1516
1519
1530
1538
1539
1540
1542
1543 dbcfg = gmCfg.cCfgSQL()
1544
1545 def_region = dbcfg.get2 (
1546 option = u'person.create.default_region',
1547 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1548 bias = u'user'
1549 )
1550 def_country = None
1551
1552 if def_region is None:
1553 def_country = dbcfg.get2 (
1554 option = u'person.create.default_country',
1555 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1556 bias = u'user'
1557 )
1558 else:
1559 countries = gmDemographicRecord.get_country_for_region(region = def_region)
1560 if len(countries) == 1:
1561 def_country = countries[0]['code_country']
1562
1563 if parent is None:
1564 parent = wx.GetApp().GetTopWindow()
1565
1566 ea = cNewPatientEAPnl(parent = parent, id = -1, country = def_country, region = def_region)
1567 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
1568 dlg.SetTitle(_('Adding new person'))
1569 ea._PRW_lastname.SetFocus()
1570 result = dlg.ShowModal()
1571 pat = ea.data
1572 dlg.Destroy()
1573
1574 if result != wx.ID_OK:
1575 return False
1576
1577 _log.debug('created new person [%s]', pat.ID)
1578
1579 if activate:
1580 from Gnumed.wxpython import gmPatSearchWidgets
1581 gmPatSearchWidgets.set_active_patient(patient = pat)
1582
1583 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
1584
1585 return True
1586
1587 from Gnumed.wxGladeWidgets import wxgNewPatientEAPnl
1588
1589 -class cNewPatientEAPnl(wxgNewPatientEAPnl.wxgNewPatientEAPnl, gmEditArea.cGenericEditAreaMixin):
1590
1592
1593 try:
1594 self.default_region = kwargs['region']
1595 del kwargs['region']
1596 except KeyError:
1597 self.default_region = None
1598
1599 try:
1600 self.default_country = kwargs['country']
1601 del kwargs['country']
1602 except KeyError:
1603 self.default_country = None
1604
1605 wxgNewPatientEAPnl.wxgNewPatientEAPnl.__init__(self, *args, **kwargs)
1606 gmEditArea.cGenericEditAreaMixin.__init__(self)
1607
1608 self.mode = 'new'
1609 self.data = None
1610 self._address = None
1611
1612 self.__init_ui()
1613 self.__register_interests()
1614
1615
1616
1618 self._PRW_lastname.final_regex = '.+'
1619 self._PRW_firstnames.final_regex = '.+'
1620 self._PRW_address_searcher.selection_only = False
1621
1622
1623
1624
1625 if self.default_country is not None:
1626 match = self._PRW_country._data2match(data = self.default_country)
1627 if match is not None:
1628 self._PRW_country.SetText(value = match['field_label'], data = match['data'])
1629
1630 if self.default_region is not None:
1631 self._PRW_region.SetText(value = self.default_region)
1632
1633 self._PRW_type.SetText(value = u'home')
1634
1635
1636 self._PRW_primary_provider.SetData(data = gmStaff.gmCurrentProvider()['pk_staff'])
1637
1638 self._PRW_lastname.SetFocus()
1639
1641
1642 adr = self._PRW_address_searcher.address
1643 if adr is None:
1644 return True
1645
1646 if ctrl.GetValue().strip() != adr[field]:
1647 wx.CallAfter(self._PRW_address_searcher.SetText, value = u'', data = None)
1648 return True
1649
1650 return False
1651
1653 adr = self._PRW_address_searcher.address
1654 if adr is None:
1655 return True
1656
1657 self._PRW_zip.SetText(value = adr['postcode'], data = adr['postcode'])
1658
1659 self._PRW_street.SetText(value = adr['street'], data = adr['street'])
1660 self._PRW_street.set_context(context = u'zip', val = adr['postcode'])
1661
1662 self._PRW_urb.SetText(value = adr['urb'], data = adr['urb'])
1663 self._PRW_urb.set_context(context = u'zip', val = adr['postcode'])
1664
1665 self._PRW_region.SetText(value = adr['l10n_state'], data = adr['code_state'])
1666 self._PRW_region.set_context(context = u'zip', val = adr['postcode'])
1667
1668 self._PRW_country.SetText(value = adr['l10n_country'], data = adr['code_country'])
1669 self._PRW_country.set_context(context = u'zip', val = adr['postcode'])
1670
1672 error = False
1673
1674
1675 if self._PRW_lastname.GetValue().strip() == u'':
1676 error = True
1677 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.'))
1678 self._PRW_lastname.display_as_valid(False)
1679 else:
1680 self._PRW_lastname.display_as_valid(True)
1681
1682 if self._PRW_firstnames.GetValue().strip() == '':
1683 error = True
1684 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.'))
1685 self._PRW_firstnames.display_as_valid(False)
1686 else:
1687 self._PRW_firstnames.display_as_valid(True)
1688
1689
1690 if self._PRW_gender.GetData() is None:
1691 error = True
1692 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.'))
1693 self._PRW_gender.display_as_valid(False)
1694 else:
1695 self._PRW_gender.display_as_valid(True)
1696
1697
1698 if not _validate_dob_field(self._PRW_dob):
1699 error = True
1700
1701
1702 if _validate_tob_field(self._TCTRL_tob):
1703 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True)
1704 else:
1705 error = True
1706 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False)
1707
1708 return (not error)
1709
1711
1712
1713 if self._PRW_address_searcher.GetData() is not None:
1714 wx.CallAfter(self.__set_fields_from_address_searcher)
1715 return True
1716
1717
1718 fields_to_fill = (
1719 self._TCTRL_number,
1720 self._PRW_zip,
1721 self._PRW_street,
1722 self._PRW_urb,
1723 self._PRW_type
1724 )
1725 no_of_filled_fields = 0
1726
1727 for field in fields_to_fill:
1728 if field.GetValue().strip() != u'':
1729 no_of_filled_fields += 1
1730 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
1731 field.Refresh()
1732
1733
1734 if no_of_filled_fields == 0:
1735 if empty_address_is_valid:
1736 return True
1737 else:
1738 return None
1739
1740
1741 if no_of_filled_fields != len(fields_to_fill):
1742 for field in fields_to_fill:
1743 if field.GetValue().strip() == u'':
1744 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
1745 field.SetFocus()
1746 field.Refresh()
1747 msg = _('To properly create an address, all the related fields must be filled in.')
1748 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
1749 return False
1750
1751
1752
1753
1754 strict_fields = (
1755 self._PRW_type,
1756 self._PRW_region,
1757 self._PRW_country
1758 )
1759 error = False
1760 for field in strict_fields:
1761 if field.GetData() is None:
1762 error = True
1763 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
1764 field.SetFocus()
1765 else:
1766 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
1767 field.Refresh()
1768
1769 if error:
1770 msg = _('This field must contain an item selected from the dropdown list.')
1771 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
1772 return False
1773
1774 return True
1775
1793
1794
1795
1797 """Set the gender according to entered firstname.
1798
1799 Matches are fetched from existing records in backend.
1800 """
1801
1802
1803 if self._PRW_gender.GetData() is not None:
1804 return True
1805
1806 firstname = self._PRW_firstnames.GetValue().strip()
1807 if firstname == u'':
1808 return True
1809
1810 gender = gmPerson.map_firstnames2gender(firstnames = firstname)
1811 if gender is None:
1812 return True
1813
1814 wx.CallAfter(self._PRW_gender.SetData, gender)
1815 return True
1816
1818 self.__perhaps_invalidate_address_searcher(self._PRW_zip, 'postcode')
1819
1820 zip_code = gmTools.none_if(self._PRW_zip.GetValue().strip(), u'')
1821 self._PRW_street.set_context(context = u'zip', val = zip_code)
1822 self._PRW_urb.set_context(context = u'zip', val = zip_code)
1823 self._PRW_region.set_context(context = u'zip', val = zip_code)
1824 self._PRW_country.set_context(context = u'zip', val = zip_code)
1825
1826 return True
1827
1829 self.__perhaps_invalidate_address_searcher(self._PRW_country, 'l10n_country')
1830
1831 country = gmTools.none_if(self._PRW_country.GetValue().strip(), u'')
1832 self._PRW_region.set_context(context = u'country', val = country)
1833
1834 return True
1835
1837 if self._TCTRL_number.GetValue().strip() == u'':
1838 adr = self._PRW_address_searcher.address
1839 if adr is None:
1840 return True
1841 self._TCTRL_number.SetValue(adr['number'])
1842 return True
1843
1844 self.__perhaps_invalidate_address_searcher(self._TCTRL_number, 'number')
1845 return True
1846
1848 if self._TCTRL_unit.GetValue().strip() == u'':
1849 adr = self._PRW_address_searcher.address
1850 if adr is None:
1851 return True
1852 self._TCTRL_unit.SetValue(gmTools.coalesce(adr['subunit'], u''))
1853 return True
1854
1855 self.__perhaps_invalidate_address_searcher(self._TCTRL_unit, 'subunit')
1856 return True
1857
1859 mapping = [
1860 (self._PRW_street, 'street'),
1861 (self._PRW_urb, 'urb'),
1862 (self._PRW_region, 'l10n_state')
1863 ]
1864
1865 for ctrl, field in mapping:
1866 if self.__perhaps_invalidate_address_searcher(ctrl, field):
1867 return True
1868
1869 return True
1870
1872 if self._PRW_address_searcher.address is None:
1873 return True
1874
1875 wx.CallAfter(self.__set_fields_from_address_searcher)
1876 return True
1877
1878
1879
1881 if self._PRW_primary_provider.GetValue().strip() == u'':
1882 self._PRW_primary_provider.display_as_valid(True)
1883 else:
1884 if self._PRW_primary_provider.GetData() is None:
1885 self._PRW_primary_provider.display_as_valid(False)
1886 else:
1887 self._PRW_primary_provider.display_as_valid(True)
1888 return (self.__identity_valid_for_save() and self.__address_valid_for_save(empty_address_is_valid = True))
1889
1891
1892 if self._PRW_dob.GetValue().strip() == u'':
1893 if not _empty_dob_allowed():
1894 self._PRW_dob.display_as_valid(False)
1895 self._PRW_dob.SetFocus()
1896 return False
1897
1898
1899 new_identity = gmPerson.create_identity (
1900 gender = self._PRW_gender.GetData(),
1901 dob = self._PRW_dob.GetData(),
1902 lastnames = self._PRW_lastname.GetValue().strip(),
1903 firstnames = self._PRW_firstnames.GetValue().strip()
1904 )
1905 _log.debug('identity created: %s' % new_identity)
1906
1907 new_identity['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
1908 val = self._TCTRL_tob.GetValue().strip()
1909 if val != u'':
1910 new_identity['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
1911 new_identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip())
1912 new_identity.set_nickname(nickname = gmTools.none_if(self._PRW_nickname.GetValue().strip(), u''))
1913
1914 prov = self._PRW_primary_provider.GetData()
1915 if prov is not None:
1916 new_identity['pk_primary_provider'] = prov
1917 new_identity['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1918 new_identity.save()
1919
1920
1921
1922 is_valid = self.__address_valid_for_save(empty_address_is_valid = False)
1923 if is_valid is True:
1924
1925
1926 try:
1927 new_identity.link_address (
1928 number = self._TCTRL_number.GetValue().strip(),
1929 street = self._PRW_street.GetValue().strip(),
1930 postcode = self._PRW_zip.GetValue().strip(),
1931 urb = self._PRW_urb.GetValue().strip(),
1932 state = self._PRW_region.GetData(),
1933 country = self._PRW_country.GetData(),
1934 subunit = gmTools.none_if(self._TCTRL_unit.GetValue().strip(), u''),
1935 id_type = self._PRW_type.GetData()
1936 )
1937 except gmPG2.dbapi.InternalError:
1938 _log.debug('number: >>%s<<', self._TCTRL_number.GetValue().strip())
1939 _log.debug('(sub)unit: >>%s<<', self._TCTRL_unit.GetValue().strip())
1940 _log.debug('street: >>%s<<', self._PRW_street.GetValue().strip())
1941 _log.debug('postcode: >>%s<<', self._PRW_zip.GetValue().strip())
1942 _log.debug('urb: >>%s<<', self._PRW_urb.GetValue().strip())
1943 _log.debug('state: >>%s<<', self._PRW_region.GetData().strip())
1944 _log.debug('country: >>%s<<', self._PRW_country.GetData().strip())
1945 _log.exception('cannot link address')
1946 gmGuiHelpers.gm_show_error (
1947 aTitle = _('Saving address'),
1948 aMessage = _(
1949 'Cannot save this address.\n'
1950 '\n'
1951 'You will have to add it via the Demographics plugin.\n'
1952 )
1953 )
1954 elif is_valid is False:
1955 gmGuiHelpers.gm_show_error (
1956 aTitle = _('Saving address'),
1957 aMessage = _(
1958 'Address not saved.\n'
1959 '\n'
1960 'You will have to add it via the Demographics plugin.\n'
1961 )
1962 )
1963
1964
1965
1966 channel_name = self._PRW_channel_type.GetValue().strip()
1967 pk_channel_type = self._PRW_channel_type.GetData()
1968 if pk_channel_type is None:
1969 if channel_name == u'':
1970 channel_name = u'homephone'
1971 new_identity.link_comm_channel (
1972 comm_medium = channel_name,
1973 pk_channel_type = pk_channel_type,
1974 url = gmTools.none_if(self._TCTRL_phone.GetValue().strip(), u''),
1975 is_confidential = False
1976 )
1977
1978
1979 pk_type = self._PRW_external_id_type.GetData()
1980 id_value = self._TCTRL_external_id_value.GetValue().strip()
1981 if (pk_type is not None) and (id_value != u''):
1982 new_identity.add_external_id(value = id_value, pk_type = pk_type)
1983
1984
1985 new_identity.link_occupation (
1986 occupation = gmTools.none_if(self._PRW_occupation.GetValue().strip(), u'')
1987 )
1988
1989 self.data = new_identity
1990 return True
1991
1993 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
1994
1998
2001
2003 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
2004
2005
2006
2007
2009 """Notebook displaying demographics editing pages:
2010
2011 - Identity (as per Jim/Rogerio 12/2011)
2012 - Contacts (addresses, phone numbers, etc)
2013 - Social network (significant others, GP, etc)
2014
2015 Does NOT act on/listen to the current patient.
2016 """
2017
2019
2020 wx.Notebook.__init__ (
2021 self,
2022 parent = parent,
2023 id = id,
2024 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
2025 name = self.__class__.__name__
2026 )
2027
2028 self.__identity = None
2029 self.__do_layout()
2030 self.SetSelection(0)
2031
2032
2033
2035 """Populate fields in pages with data from model."""
2036 for page_idx in range(self.GetPageCount()):
2037 page = self.GetPage(page_idx)
2038 page.identity = self.__identity
2039
2040 return True
2041
2042
2043
2045 """Build patient edition notebook pages."""
2046
2047
2048 new_page = cPersonIdentityManagerPnl(self, -1)
2049 new_page.identity = self.__identity
2050 self.AddPage (
2051 page = new_page,
2052 text = _('Identity'),
2053 select = False
2054 )
2055
2056
2057 new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1)
2058 new_page.identity = self.__identity
2059 self.AddPage (
2060 page = new_page,
2061 text = _('Contacts'),
2062 select = True
2063 )
2064
2065
2066 new_page = cPersonSocialNetworkManagerPnl(self, -1)
2067 new_page.identity = self.__identity
2068 self.AddPage (
2069 page = new_page,
2070 text = _('Social network'),
2071 select = False
2072 )
2073
2074
2075
2077 return self.__identity
2078
2081
2082 identity = property(_get_identity, _set_identity)
2083
2084
2085
2086
2087
2088
2090 """Page containing patient occupations edition fields.
2091 """
2092 - def __init__(self, parent, id, ident=None):
2093 """
2094 Creates a new instance of BasicPatDetailsPage
2095 @param parent - The parent widget
2096 @type parent - A wx.Window instance
2097 @param id - The widget id
2098 @type id - An integer
2099 """
2100 wx.Panel.__init__(self, parent, id)
2101 self.__ident = ident
2102 self.__do_layout()
2103
2105 PNL_form = wx.Panel(self, -1)
2106
2107 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
2108 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1)
2109 self.PRW_occupation.SetToolTipString(_("primary occupation of the patient"))
2110
2111 STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated'))
2112 self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY)
2113
2114
2115 SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4)
2116 SZR_input.AddGrowableCol(1)
2117 SZR_input.Add(STT_occupation, 0, wx.SHAPED)
2118 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
2119 SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED)
2120 SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND)
2121 PNL_form.SetSizerAndFit(SZR_input)
2122
2123
2124 SZR_main = wx.BoxSizer(wx.VERTICAL)
2125 SZR_main.Add(PNL_form, 1, wx.EXPAND)
2126 self.SetSizer(SZR_main)
2127
2130
2131 - def refresh(self, identity=None):
2132 if identity is not None:
2133 self.__ident = identity
2134 jobs = self.__ident.get_occupations()
2135 if len(jobs) > 0:
2136 self.PRW_occupation.SetText(jobs[0]['l10n_occupation'])
2137 self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y'))
2138 return True
2139
2141 if self.PRW_occupation.IsModified():
2142 new_job = self.PRW_occupation.GetValue().strip()
2143 jobs = self.__ident.get_occupations()
2144 for job in jobs:
2145 if job['l10n_occupation'] == new_job:
2146 continue
2147 self.__ident.unlink_occupation(occupation = job['l10n_occupation'])
2148 self.__ident.link_occupation(occupation = new_job)
2149 return True
2150
2152 """Patient demographics plugin for main notebook.
2153
2154 Hosts another notebook with pages for Identity, Contacts, etc.
2155
2156 Acts on/listens to the currently active patient.
2157 """
2158
2164
2165
2166
2167
2168
2169
2171 """Arrange widgets."""
2172 self.__patient_notebook = cPersonDemographicsEditorNb(self, -1)
2173
2174 szr_main = wx.BoxSizer(wx.VERTICAL)
2175 szr_main.Add(self.__patient_notebook, 1, wx.EXPAND)
2176 self.SetSizerAndFit(szr_main)
2177
2178
2179
2181 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2182 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
2183
2185 self._schedule_data_reget()
2186
2188 self._schedule_data_reget()
2189
2190
2200
2201
2202 if __name__ == "__main__":
2203
2204
2206 app = wx.PyWidgetTester(size = (600, 400))
2207 app.SetWidget(cKOrganizerSchedulePnl)
2208 app.MainLoop()
2209
2211 app = wx.PyWidgetTester(size = (600, 400))
2212 widget = cPersonNamesManagerPnl(app.frame, -1)
2213 widget.identity = activate_patient()
2214 app.frame.Show(True)
2215 app.MainLoop()
2216
2218 app = wx.PyWidgetTester(size = (600, 400))
2219 widget = cPersonIDsManagerPnl(app.frame, -1)
2220 widget.identity = activate_patient()
2221 app.frame.Show(True)
2222 app.MainLoop()
2223
2225 app = wx.PyWidgetTester(size = (600, 400))
2226 widget = cPersonIdentityManagerPnl(app.frame, -1)
2227 widget.identity = activate_patient()
2228 app.frame.Show(True)
2229 app.MainLoop()
2230
2235
2237 app = wx.PyWidgetTester(size = (600, 400))
2238 widget = cPersonDemographicsEditorNb(app.frame, -1)
2239 widget.identity = activate_patient()
2240 widget.refresh()
2241 app.frame.Show(True)
2242 app.MainLoop()
2243
2252
2253 if len(sys.argv) > 1 and sys.argv[1] == 'test':
2254
2255 gmI18N.activate_locale()
2256 gmI18N.install_domain(domain='gnumed')
2257 gmPG2.get_connection()
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269 test_person_ids_pnl()
2270
2271
2272
2273
2274
2275
2276