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