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(gmTools.gmPaths().tmp_dir, '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
537
538
540
541 go_ahead = gmGuiHelpers.gm_show_question (
542 _('Are you sure you really, positively want\n'
543 'to disable the following person ?\n'
544 '\n'
545 ' %s %s %s\n'
546 ' born %s\n'
547 '\n'
548 '%s\n'
549 ) % (
550 identity['firstnames'],
551 identity['lastnames'],
552 identity['gender'],
553 identity.get_formatted_dob(),
554 gmTools.bool2subst (
555 identity.is_patient,
556 _('This patient DID receive care.'),
557 _('This person did NOT receive care.')
558 )
559 ),
560 _('Disabling person')
561 )
562 if not go_ahead:
563 return True
564
565
566 conn = gmAuthWidgets.get_dbowner_connection (
567 procedure = _('Disabling patient')
568 )
569
570 if conn is False:
571 return True
572
573 if conn is None:
574 return False
575
576
577 gmPG2.run_rw_queries(queries = [{'cmd': u"update dem.identity set deleted=True where pk=%s", 'args': [identity['pk_identity']]}])
578
579 return True
580
581
582
583
598
600
602 query = u"""
603 (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
604 union
605 (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
606 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
607 mp.setThresholds(3, 5, 9)
608 gmPhraseWheel.cPhraseWheel.__init__ (
609 self,
610 *args,
611 **kwargs
612 )
613 self.SetToolTipString(_("Type or select a first name (forename/Christian name/given name)."))
614 self.capitalisation_mode = gmTools.CAPS_NAMES
615 self.matcher = mp
616
618
620 query = u"""
621 (SELECT distinct preferred, preferred from dem.names where preferred %(fragment_condition)s order by preferred limit 20)
622 union
623 (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
624 union
625 (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
626 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
627 mp.setThresholds(3, 5, 9)
628 gmPhraseWheel.cPhraseWheel.__init__ (
629 self,
630 *args,
631 **kwargs
632 )
633 self.SetToolTipString(_("Type or select an alias (nick name, preferred name, call name, warrior name, artist name)."))
634
635
636 self.matcher = mp
637
639
641 query = u"SELECT distinct title, title from dem.identity where title %(fragment_condition)s"
642 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
643 mp.setThresholds(1, 3, 9)
644 gmPhraseWheel.cPhraseWheel.__init__ (
645 self,
646 *args,
647 **kwargs
648 )
649 self.SetToolTipString(_("Type or select a title. Note that the title applies to the person, not to a particular name !"))
650 self.matcher = mp
651
653 """Let user select a gender."""
654
655 _gender_map = None
656
658
659 if cGenderSelectionPhraseWheel._gender_map is None:
660 cmd = u"""
661 SELECT tag, l10n_label, sort_weight
662 from dem.v_gender_labels
663 order by sort_weight desc"""
664 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
665 cGenderSelectionPhraseWheel._gender_map = {}
666 for gender in rows:
667 cGenderSelectionPhraseWheel._gender_map[gender[idx['tag']]] = {
668 'data': gender[idx['tag']],
669 'field_label': gender[idx['l10n_label']],
670 'list_label': gender[idx['l10n_label']],
671 'weight': gender[idx['sort_weight']]
672 }
673
674 mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = cGenderSelectionPhraseWheel._gender_map.values())
675 mp.setThresholds(1, 1, 3)
676
677 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
678 self.selection_only = True
679 self.matcher = mp
680 self.picklist_delay = 50
681
683
685 query = u"""
686 SELECT DISTINCT ON (list_label)
687 pk AS data,
688 name AS field_label,
689 name || coalesce(' (' || issuer || ')', '') as list_label
690 FROM dem.enum_ext_id_types
691 WHERE name %(fragment_condition)s
692 ORDER BY list_label
693 LIMIT 25
694 """
695 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
696 mp.setThresholds(1, 3, 5)
697 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
698 self.SetToolTipString(_("Enter or select a type for the external ID."))
699 self.matcher = mp
700
705
720
721
722
723 from Gnumed.wxGladeWidgets import wxgExternalIDEditAreaPnl
724
725 -class cExternalIDEditAreaPnl(wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
726 """An edit area for editing/creating external IDs.
727
728 Does NOT act on/listen to the current patient.
729 """
749
752
753
754
775
793
809
815
817 self._refresh_as_new()
818 self._PRW_issuer.SetText(self.data['issuer'])
819
825
826
827
829 """Set the issuer according to the selected type.
830
831 Matches are fetched from existing records in backend.
832 """
833 pk_curr_type = self._PRW_type.GetData()
834 if pk_curr_type is None:
835 return True
836 rows, idx = gmPG2.run_ro_queries(queries = [{
837 'cmd': u"SELECT issuer from dem.enum_ext_id_types where pk = %s",
838 'args': [pk_curr_type]
839 }])
840 if len(rows) == 0:
841 return True
842 wx.CallAfter(self._PRW_issuer.SetText, rows[0][0])
843 return True
844
845
846
847
849 allow_empty_dob = gmGuiHelpers.gm_show_question (
850 _(
851 'Are you sure you want to leave this person\n'
852 'without a valid date of birth ?\n'
853 '\n'
854 'This can be useful for temporary staff members\n'
855 'but will provoke nag screens if this person\n'
856 'becomes a patient.\n'
857 ),
858 _('Validating date of birth')
859 )
860 return allow_empty_dob
861
863
864
865 if dob_prw.is_valid_timestamp(allow_empty = False):
866 dob = dob_prw.date
867
868 if (dob.year > 1899) and (dob < gmDateTime.pydt_now_here()):
869 return True
870
871 if dob.year < 1900:
872 msg = _(
873 'DOB: %s\n'
874 '\n'
875 'While this is a valid point in time Python does\n'
876 'not know how to deal with it.\n'
877 '\n'
878 'We suggest using January 1st 1901 instead and adding\n'
879 'the true date of birth to the patient comment.\n'
880 '\n'
881 'Sorry for the inconvenience %s'
882 ) % (dob, gmTools.u_frowning_face)
883 else:
884 msg = _(
885 'DOB: %s\n'
886 '\n'
887 'Date of birth in the future !'
888 ) % dob
889 gmGuiHelpers.gm_show_error (
890 msg,
891 _('Validating date of birth')
892 )
893 dob_prw.display_as_valid(False)
894 dob_prw.SetFocus()
895 return False
896
897
898 if dob_prw.GetValue().strip() != u'':
899 dob_prw.display_as_valid(False)
900 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of birth.'))
901 dob_prw.SetFocus()
902 return False
903
904
905 dob_prw.display_as_valid(False)
906 return True
907
908
910
911 val = ctrl.GetValue().strip()
912
913 if val == u'':
914 return True
915
916 converted, hours = gmTools.input2int(val[:2], 0, 23)
917 if not converted:
918 return False
919
920 converted, minutes = gmTools.input2int(val[3:5], 0, 59)
921 if not converted:
922 return False
923
924 return True
925
926
927 from Gnumed.wxGladeWidgets import wxgIdentityEAPnl
928
929 -class cIdentityEAPnl(wxgIdentityEAPnl.wxgIdentityEAPnl, gmEditArea.cGenericEditAreaMixin):
930 """An edit area for editing/creating title/gender/dob/dod etc."""
931
947
948
949
950
951
952
953
954
956
957 has_error = False
958
959 if self._PRW_gender.GetData() is None:
960 self._PRW_gender.SetFocus()
961 has_error = True
962
963 if self.data is not None:
964 if not _validate_dob_field(self._PRW_dob):
965 has_error = True
966
967
968 if _validate_tob_field(self._TCTRL_tob):
969 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True)
970 else:
971 has_error = True
972 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False)
973
974 if not self._PRW_dod.is_valid_timestamp(allow_empty = True):
975 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.'))
976 self._PRW_dod.SetFocus()
977 has_error = True
978
979 return (has_error is False)
980
984
986
987 if self._PRW_dob.GetValue().strip() == u'':
988 if not _empty_dob_allowed():
989 return False
990 self.data['dob'] = None
991 else:
992 self.data['dob'] = self._PRW_dob.GetData()
993 self.data['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
994 val = self._TCTRL_tob.GetValue().strip()
995 if val == u'':
996 self.data['tob'] = None
997 else:
998 self.data['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
999 self.data['gender'] = self._PRW_gender.GetData()
1000 self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'')
1001 self.data['deceased'] = self._PRW_dod.GetData()
1002 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1003
1004 self.data.save()
1005 return True
1006
1009
1043
1046
1047 from Gnumed.wxGladeWidgets import wxgPersonNameEAPnl
1048
1049 -class cPersonNameEAPnl(wxgPersonNameEAPnl.wxgPersonNameEAPnl, gmEditArea.cGenericEditAreaMixin):
1050 """An edit area for editing/creating names of people.
1051
1052 Does NOT act on/listen to the current patient.
1053 """
1074
1075
1076
1077
1078
1079
1080
1081
1083 validity = True
1084
1085 if self._PRW_lastname.GetValue().strip() == u'':
1086 validity = False
1087 self._PRW_lastname.display_as_valid(False)
1088 self._PRW_lastname.SetFocus()
1089 else:
1090 self._PRW_lastname.display_as_valid(True)
1091
1092 if self._PRW_firstname.GetValue().strip() == u'':
1093 validity = False
1094 self._PRW_firstname.display_as_valid(False)
1095 self._PRW_firstname.SetFocus()
1096 else:
1097 self._PRW_firstname.display_as_valid(True)
1098
1099 return validity
1100
1102
1103 first = self._PRW_firstname.GetValue().strip()
1104 last = self._PRW_lastname.GetValue().strip()
1105 active = self._CHBOX_active.GetValue()
1106
1107 try:
1108 data = self.__identity.add_name(first, last, active)
1109 except gmPG2.dbapi.IntegrityError as exc:
1110 _log.exception('cannot save new name')
1111 gmGuiHelpers.gm_show_error (
1112 aTitle = _('Adding name'),
1113 aMessage = _(
1114 'Cannot add this name to the patient !\n'
1115 '\n'
1116 ' %s'
1117 ) % str(exc)
1118 )
1119 return False
1120
1121 old_nick = self.__identity['active_name']['preferred']
1122 new_nick = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
1123 if active:
1124 data['preferred'] = gmTools.coalesce(new_nick, old_nick)
1125 else:
1126 data['preferred'] = new_nick
1127 data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1128 data.save()
1129
1130 self.data = data
1131 return True
1132
1134 """The knack here is that we can only update a few fields.
1135
1136 Otherwise we need to clone the name and update that.
1137 """
1138 first = self._PRW_firstname.GetValue().strip()
1139 last = self._PRW_lastname.GetValue().strip()
1140 active = self._CHBOX_active.GetValue()
1141
1142 current_name = self.data['firstnames'].strip() + self.data['lastnames'].strip()
1143 new_name = first + last
1144
1145
1146 if new_name == current_name:
1147 self.data['active_name'] = self._CHBOX_active.GetValue()
1148 self.data['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
1149 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1150 self.data.save()
1151
1152 else:
1153 try:
1154 name = self.__identity.add_name(first, last, active)
1155 except gmPG2.dbapi.IntegrityError as exc:
1156 _log.exception('cannot clone name when editing existing name')
1157 gmGuiHelpers.gm_show_error (
1158 aTitle = _('Editing name'),
1159 aMessage = _(
1160 'Cannot clone a copy of this name !\n'
1161 '\n'
1162 ' %s'
1163 ) % str(exc)
1164 )
1165 return False
1166 name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
1167 name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1168 name.save()
1169 self.data = name
1170
1171 return True
1172
1181
1188
1197
1198
1199
1201 """A list for managing a person's names.
1202
1203 Does NOT act on/listen to the current patient.
1204 """
1222
1223
1224
1225 - def refresh(self, *args, **kwargs):
1242
1243
1244
1246 self._LCTRL_items.set_columns(columns = [
1247 _('Active'),
1248 _('Lastname'),
1249 _('Firstname(s)'),
1250 _('Preferred Name'),
1251 _('Comment')
1252 ])
1253
1264
1274
1276
1277 if len(self.__identity.get_names()) == 1:
1278 gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the only name of a person.'), beep = True)
1279 return False
1280
1281 if name['active_name']:
1282 gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the active name of a person.'), beep = True)
1283 return False
1284
1285 go_ahead = gmGuiHelpers.gm_show_question (
1286 _( 'It is often advisable to keep old names around and\n'
1287 'just create a new "currently active" name.\n'
1288 '\n'
1289 'This allows finding the patient by both the old\n'
1290 'and the new name (think before/after marriage).\n'
1291 '\n'
1292 'Do you still want to really delete\n'
1293 "this name from the patient ?"
1294 ),
1295 _('Deleting name')
1296 )
1297 if not go_ahead:
1298 return False
1299
1300 self.__identity.delete_name(name = name)
1301 return True
1302
1303
1304
1306 return self.__identity
1307
1311
1312 identity = property(_get_identity, _set_identity)
1313
1315 """A list for managing a person's external IDs.
1316
1317 Does NOT act on/listen to the current patient.
1318 """
1336
1337
1338
1339 - def refresh(self, *args, **kwargs):
1356
1357
1358
1360 self._LCTRL_items.set_columns(columns = [
1361 _('ID type'),
1362 _('Value'),
1363 _('Issuer'),
1364 _('Comment')
1365 ])
1366
1377
1388
1390 go_ahead = gmGuiHelpers.gm_show_question (
1391 _( 'Do you really want to delete this\n'
1392 'external ID from the patient ?'),
1393 _('Deleting external ID')
1394 )
1395 if not go_ahead:
1396 return False
1397 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id'])
1398 return True
1399
1400
1401
1403 return self.__identity
1404
1408
1409 identity = property(_get_identity, _set_identity)
1410
1411
1412
1413 from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl
1414
1416 """A panel for editing identity data for a person.
1417
1418 - provides access to:
1419 - identity EA
1420 - name list manager
1421 - external IDs list manager
1422
1423 Does NOT act on/listen to the current patient.
1424 """
1431
1432
1433
1435 self._PNL_names.identity = self.__identity
1436 self._PNL_ids.identity = self.__identity
1437
1438 self._PNL_identity.mode = 'new'
1439 self._PNL_identity.data = self.__identity
1440 if self.__identity is not None:
1441 self._PNL_identity.mode = 'edit'
1442 self._PNL_identity._refresh_from_existing()
1443
1444
1445
1447 return self.__identity
1448
1452
1453 identity = property(_get_identity, _set_identity)
1454
1455
1456
1460
1461
1464
1465
1466 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl
1467
1476
1477
1478
1480
1481 tt = _('Link another person in this database as the emergency contact:\n\nEnter person name part or identifier and hit <enter>.')
1482
1483 if self.__identity is None:
1484 self._TCTRL_er_contact.SetValue(u'')
1485 self._TCTRL_person.person = None
1486 self._TCTRL_person.SetToolTipString(tt)
1487
1488 self._PRW_provider.SetText(value = u'', data = None)
1489 return
1490
1491 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], u''))
1492 if self.__identity['pk_emergency_contact'] is not None:
1493 ident = gmPerson.cIdentity(aPK_obj = self.__identity['pk_emergency_contact'])
1494 self._TCTRL_person.person = ident
1495 tt = u'%s\n\n%s\n\n%s' % (
1496 tt,
1497 ident['description_gender'],
1498 u'\n'.join([
1499 u'%s: %s%s' % (
1500 c['l10n_comm_type'],
1501 c['url'],
1502 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), u'', u'')
1503 )
1504 for c in ident.get_comm_channels()
1505 ])
1506 )
1507 else:
1508 self._TCTRL_person.person = None
1509
1510 self._TCTRL_person.SetToolTipString(tt)
1511
1512 if self.__identity['pk_primary_provider'] is None:
1513 self._PRW_provider.SetText(value = u'', data = None)
1514 else:
1515 self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
1516
1517
1518
1520 return self.__identity
1521
1525
1526 identity = property(_get_identity, _set_identity)
1527
1528
1529
1544
1547
1558
1566
1567
1568
1570
1571 dbcfg = gmCfg.cCfgSQL()
1572
1573 def_region = dbcfg.get2 (
1574 option = u'person.create.default_region',
1575 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1576 bias = u'user'
1577 )
1578 def_country = None
1579
1580 if def_region is None:
1581 def_country = dbcfg.get2 (
1582 option = u'person.create.default_country',
1583 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1584 bias = u'user'
1585 )
1586 else:
1587 countries = gmDemographicRecord.get_country_for_region(region = def_region)
1588 if len(countries) == 1:
1589 def_country = countries[0]['code_country']
1590
1591 if parent is None:
1592 parent = wx.GetApp().GetTopWindow()
1593
1594 ea = cNewPatientEAPnl(parent = parent, id = -1, country = def_country, region = def_region)
1595 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
1596 dlg.SetTitle(_('Adding new person'))
1597 ea._PRW_lastname.SetFocus()
1598 result = dlg.ShowModal()
1599 pat = ea.data
1600 dlg.Destroy()
1601
1602 if result != wx.ID_OK:
1603 return False
1604
1605 _log.debug('created new person [%s]', pat.ID)
1606
1607 if activate:
1608 from Gnumed.wxpython import gmPatSearchWidgets
1609 gmPatSearchWidgets.set_active_patient(patient = pat)
1610
1611 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
1612
1613 return True
1614
1615 from Gnumed.wxGladeWidgets import wxgNewPatientEAPnl
1616
1617 -class cNewPatientEAPnl(wxgNewPatientEAPnl.wxgNewPatientEAPnl, gmEditArea.cGenericEditAreaMixin):
1618
1620
1621 try:
1622 self.default_region = kwargs['region']
1623 del kwargs['region']
1624 except KeyError:
1625 self.default_region = None
1626
1627 try:
1628 self.default_country = kwargs['country']
1629 del kwargs['country']
1630 except KeyError:
1631 self.default_country = None
1632
1633 wxgNewPatientEAPnl.wxgNewPatientEAPnl.__init__(self, *args, **kwargs)
1634 gmEditArea.cGenericEditAreaMixin.__init__(self)
1635
1636 self.mode = 'new'
1637 self.data = None
1638 self._address = None
1639
1640 self.__init_ui()
1641 self.__register_interests()
1642
1643
1644
1646 self._PRW_lastname.final_regex = '.+'
1647 self._PRW_firstnames.final_regex = '.+'
1648 self._PRW_address_searcher.selection_only = False
1649
1650
1651
1652
1653 if self.default_country is not None:
1654 match = self._PRW_country._data2match(data = self.default_country)
1655 if match is not None:
1656 self._PRW_country.SetText(value = match['field_label'], data = match['data'])
1657
1658 if self.default_region is not None:
1659 self._PRW_region.SetText(value = self.default_region)
1660
1661 self._PRW_type.SetText(value = u'home')
1662
1663
1664 self._PRW_primary_provider.SetData(data = gmStaff.gmCurrentProvider()['pk_staff'])
1665
1666 self._PRW_lastname.SetFocus()
1667
1669
1670 adr = self._PRW_address_searcher.address
1671 if adr is None:
1672 return True
1673
1674 if ctrl.GetValue().strip() != adr[field]:
1675 wx.CallAfter(self._PRW_address_searcher.SetText, value = u'', data = None)
1676 return True
1677
1678 return False
1679
1681 adr = self._PRW_address_searcher.address
1682 if adr is None:
1683 return True
1684
1685 self._PRW_zip.SetText(value = adr['postcode'], data = adr['postcode'])
1686
1687 self._PRW_street.SetText(value = adr['street'], data = adr['street'])
1688 self._PRW_street.set_context(context = u'zip', val = adr['postcode'])
1689
1690 self._PRW_urb.SetText(value = adr['urb'], data = adr['urb'])
1691 self._PRW_urb.set_context(context = u'zip', val = adr['postcode'])
1692
1693 self._PRW_region.SetText(value = adr['l10n_state'], data = adr['code_state'])
1694 self._PRW_region.set_context(context = u'zip', val = adr['postcode'])
1695
1696 self._PRW_country.SetText(value = adr['l10n_country'], data = adr['code_country'])
1697 self._PRW_country.set_context(context = u'zip', val = adr['postcode'])
1698
1700 error = False
1701
1702
1703 if self._PRW_lastname.GetValue().strip() == u'':
1704 error = True
1705 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.'))
1706 self._PRW_lastname.display_as_valid(False)
1707 else:
1708 self._PRW_lastname.display_as_valid(True)
1709
1710 if self._PRW_firstnames.GetValue().strip() == '':
1711 error = True
1712 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.'))
1713 self._PRW_firstnames.display_as_valid(False)
1714 else:
1715 self._PRW_firstnames.display_as_valid(True)
1716
1717
1718 if self._PRW_gender.GetData() is None:
1719 error = True
1720 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.'))
1721 self._PRW_gender.display_as_valid(False)
1722 else:
1723 self._PRW_gender.display_as_valid(True)
1724
1725
1726 if not _validate_dob_field(self._PRW_dob):
1727 error = True
1728
1729
1730 if _validate_tob_field(self._TCTRL_tob):
1731 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True)
1732 else:
1733 error = True
1734 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False)
1735
1736 return (not error)
1737
1739
1740
1741 if self._PRW_address_searcher.GetData() is not None:
1742 wx.CallAfter(self.__set_fields_from_address_searcher)
1743 return True
1744
1745
1746 fields_to_fill = (
1747 self._TCTRL_number,
1748 self._PRW_zip,
1749 self._PRW_street,
1750 self._PRW_urb,
1751 self._PRW_type
1752 )
1753 no_of_filled_fields = 0
1754
1755 for field in fields_to_fill:
1756 if field.GetValue().strip() != u'':
1757 no_of_filled_fields += 1
1758 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
1759 field.Refresh()
1760
1761
1762 if no_of_filled_fields == 0:
1763 if empty_address_is_valid:
1764 return True
1765 else:
1766 return None
1767
1768
1769 if no_of_filled_fields != len(fields_to_fill):
1770 for field in fields_to_fill:
1771 if field.GetValue().strip() == u'':
1772 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
1773 field.SetFocus()
1774 field.Refresh()
1775 msg = _('To properly create an address, all the related fields must be filled in.')
1776 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
1777 return False
1778
1779
1780
1781
1782 strict_fields = (
1783 self._PRW_type,
1784 self._PRW_region,
1785 self._PRW_country
1786 )
1787 error = False
1788 for field in strict_fields:
1789 if field.GetData() is None:
1790 error = True
1791 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
1792 field.SetFocus()
1793 else:
1794 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
1795 field.Refresh()
1796
1797 if error:
1798 msg = _('This field must contain an item selected from the dropdown list.')
1799 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
1800 return False
1801
1802 return True
1803
1821
1822
1823
1825 """Set the gender according to entered firstname.
1826
1827 Matches are fetched from existing records in backend.
1828 """
1829
1830
1831 if self._PRW_gender.GetData() is not None:
1832 return True
1833
1834 firstname = self._PRW_firstnames.GetValue().strip()
1835 if firstname == u'':
1836 return True
1837
1838 gender = gmPerson.map_firstnames2gender(firstnames = firstname)
1839 if gender is None:
1840 return True
1841
1842 wx.CallAfter(self._PRW_gender.SetData, gender)
1843 return True
1844
1846 self.__perhaps_invalidate_address_searcher(self._PRW_zip, 'postcode')
1847
1848 zip_code = gmTools.none_if(self._PRW_zip.GetValue().strip(), u'')
1849 self._PRW_street.set_context(context = u'zip', val = zip_code)
1850 self._PRW_urb.set_context(context = u'zip', val = zip_code)
1851 self._PRW_region.set_context(context = u'zip', val = zip_code)
1852 self._PRW_country.set_context(context = u'zip', val = zip_code)
1853
1854 return True
1855
1857 self.__perhaps_invalidate_address_searcher(self._PRW_country, 'l10n_country')
1858
1859 country = gmTools.none_if(self._PRW_country.GetValue().strip(), u'')
1860 self._PRW_region.set_context(context = u'country', val = country)
1861
1862 return True
1863
1865 if self._TCTRL_number.GetValue().strip() == u'':
1866 adr = self._PRW_address_searcher.address
1867 if adr is None:
1868 return True
1869 self._TCTRL_number.SetValue(adr['number'])
1870 return True
1871
1872 self.__perhaps_invalidate_address_searcher(self._TCTRL_number, 'number')
1873 return True
1874
1876 if self._TCTRL_unit.GetValue().strip() == u'':
1877 adr = self._PRW_address_searcher.address
1878 if adr is None:
1879 return True
1880 self._TCTRL_unit.SetValue(gmTools.coalesce(adr['subunit'], u''))
1881 return True
1882
1883 self.__perhaps_invalidate_address_searcher(self._TCTRL_unit, 'subunit')
1884 return True
1885
1887 mapping = [
1888 (self._PRW_street, 'street'),
1889 (self._PRW_urb, 'urb'),
1890 (self._PRW_region, 'l10n_state')
1891 ]
1892
1893 for ctrl, field in mapping:
1894 if self.__perhaps_invalidate_address_searcher(ctrl, field):
1895 return True
1896
1897 return True
1898
1900 if self._PRW_address_searcher.address is None:
1901 return True
1902
1903 wx.CallAfter(self.__set_fields_from_address_searcher)
1904 return True
1905
1906
1907
1909 if self._PRW_primary_provider.GetValue().strip() == u'':
1910 self._PRW_primary_provider.display_as_valid(True)
1911 else:
1912 if self._PRW_primary_provider.GetData() is None:
1913 self._PRW_primary_provider.display_as_valid(False)
1914 else:
1915 self._PRW_primary_provider.display_as_valid(True)
1916 return (self.__identity_valid_for_save() and self.__address_valid_for_save(empty_address_is_valid = True))
1917
1919
1920 if self._PRW_dob.GetValue().strip() == u'':
1921 if not _empty_dob_allowed():
1922 self._PRW_dob.display_as_valid(False)
1923 self._PRW_dob.SetFocus()
1924 return False
1925
1926
1927 new_identity = gmPerson.create_identity (
1928 gender = self._PRW_gender.GetData(),
1929 dob = self._PRW_dob.GetData(),
1930 lastnames = self._PRW_lastname.GetValue().strip(),
1931 firstnames = self._PRW_firstnames.GetValue().strip()
1932 )
1933 _log.debug('identity created: %s' % new_identity)
1934
1935 new_identity['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
1936 val = self._TCTRL_tob.GetValue().strip()
1937 if val != u'':
1938 new_identity['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
1939 new_identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip())
1940 new_identity.set_nickname(nickname = gmTools.none_if(self._PRW_nickname.GetValue().strip(), u''))
1941
1942 prov = self._PRW_primary_provider.GetData()
1943 if prov is not None:
1944 new_identity['pk_primary_provider'] = prov
1945 new_identity['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1946 new_identity.save()
1947
1948
1949
1950 is_valid = self.__address_valid_for_save(empty_address_is_valid = False)
1951 if is_valid is True:
1952
1953
1954 try:
1955 new_identity.link_address (
1956 number = self._TCTRL_number.GetValue().strip(),
1957 street = self._PRW_street.GetValue().strip(),
1958 postcode = self._PRW_zip.GetValue().strip(),
1959 urb = self._PRW_urb.GetValue().strip(),
1960 state = self._PRW_region.GetData(),
1961 country = self._PRW_country.GetData(),
1962 subunit = gmTools.none_if(self._TCTRL_unit.GetValue().strip(), u''),
1963 id_type = self._PRW_type.GetData()
1964 )
1965 except gmPG2.dbapi.InternalError:
1966 _log.debug('number: >>%s<<', self._TCTRL_number.GetValue().strip())
1967 _log.debug('(sub)unit: >>%s<<', self._TCTRL_unit.GetValue().strip())
1968 _log.debug('street: >>%s<<', self._PRW_street.GetValue().strip())
1969 _log.debug('postcode: >>%s<<', self._PRW_zip.GetValue().strip())
1970 _log.debug('urb: >>%s<<', self._PRW_urb.GetValue().strip())
1971 _log.debug('state: >>%s<<', self._PRW_region.GetData().strip())
1972 _log.debug('country: >>%s<<', self._PRW_country.GetData().strip())
1973 _log.exception('cannot link address')
1974 gmGuiHelpers.gm_show_error (
1975 aTitle = _('Saving address'),
1976 aMessage = _(
1977 'Cannot save this address.\n'
1978 '\n'
1979 'You will have to add it via the Demographics plugin.\n'
1980 )
1981 )
1982 elif is_valid is False:
1983 gmGuiHelpers.gm_show_error (
1984 aTitle = _('Saving address'),
1985 aMessage = _(
1986 'Address not saved.\n'
1987 '\n'
1988 'You will have to add it via the Demographics plugin.\n'
1989 )
1990 )
1991
1992
1993
1994 channel_name = self._PRW_channel_type.GetValue().strip()
1995 pk_channel_type = self._PRW_channel_type.GetData()
1996 if pk_channel_type is None:
1997 if channel_name == u'':
1998 channel_name = u'homephone'
1999 new_identity.link_comm_channel (
2000 comm_medium = channel_name,
2001 pk_channel_type = pk_channel_type,
2002 url = gmTools.none_if(self._TCTRL_phone.GetValue().strip(), u''),
2003 is_confidential = False
2004 )
2005
2006
2007 pk_type = self._PRW_external_id_type.GetData()
2008 id_value = self._TCTRL_external_id_value.GetValue().strip()
2009 if (pk_type is not None) and (id_value != u''):
2010 new_identity.add_external_id(value = id_value, pk_type = pk_type)
2011
2012
2013 new_identity.link_occupation (
2014 occupation = gmTools.none_if(self._PRW_occupation.GetValue().strip(), u'')
2015 )
2016
2017 self.data = new_identity
2018 return True
2019
2021 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
2022
2026
2029
2031 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
2032
2033
2034
2035
2037 """Notebook displaying demographics editing pages:
2038
2039 - Identity (as per Jim/Rogerio 12/2011)
2040 - Contacts (addresses, phone numbers, etc)
2041 - Social network (significant others, GP, etc)
2042
2043 Does NOT act on/listen to the current patient.
2044 """
2045
2047
2048 wx.Notebook.__init__ (
2049 self,
2050 parent = parent,
2051 id = id,
2052 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
2053 name = self.__class__.__name__
2054 )
2055
2056 self.__identity = None
2057 self.__do_layout()
2058 self.SetSelection(0)
2059
2060
2061
2063 """Populate fields in pages with data from model."""
2064 for page_idx in range(self.GetPageCount()):
2065 page = self.GetPage(page_idx)
2066 page.identity = self.__identity
2067
2068 return True
2069
2070
2071
2073 """Build patient edition notebook pages."""
2074
2075
2076 new_page = cPersonIdentityManagerPnl(self, -1)
2077 new_page.identity = self.__identity
2078 self.AddPage (
2079 page = new_page,
2080 text = _('Identity'),
2081 select = False
2082 )
2083
2084
2085 new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1)
2086 new_page.identity = self.__identity
2087 self.AddPage (
2088 page = new_page,
2089 text = _('Contacts'),
2090 select = True
2091 )
2092
2093
2094 new_page = cPersonSocialNetworkManagerPnl(self, -1)
2095 new_page.identity = self.__identity
2096 self.AddPage (
2097 page = new_page,
2098 text = _('Social network'),
2099 select = False
2100 )
2101
2102
2103
2105 return self.__identity
2106
2109
2110 identity = property(_get_identity, _set_identity)
2111
2112
2113
2114
2115
2116
2118 """Page containing patient occupations edition fields.
2119 """
2120 - def __init__(self, parent, id, ident=None):
2121 """
2122 Creates a new instance of BasicPatDetailsPage
2123 @param parent - The parent widget
2124 @type parent - A wx.Window instance
2125 @param id - The widget id
2126 @type id - An integer
2127 """
2128 wx.Panel.__init__(self, parent, id)
2129 self.__ident = ident
2130 self.__do_layout()
2131
2133 PNL_form = wx.Panel(self, -1)
2134
2135 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
2136 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1)
2137 self.PRW_occupation.SetToolTipString(_("primary occupation of the patient"))
2138
2139 STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated'))
2140 self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY)
2141
2142
2143 SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4)
2144 SZR_input.AddGrowableCol(1)
2145 SZR_input.Add(STT_occupation, 0, wx.SHAPED)
2146 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
2147 SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED)
2148 SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND)
2149 PNL_form.SetSizerAndFit(SZR_input)
2150
2151
2152 SZR_main = wx.BoxSizer(wx.VERTICAL)
2153 SZR_main.Add(PNL_form, 1, wx.EXPAND)
2154 self.SetSizer(SZR_main)
2155
2158
2159 - def refresh(self, identity=None):
2160 if identity is not None:
2161 self.__ident = identity
2162 jobs = self.__ident.get_occupations()
2163 if len(jobs) > 0:
2164 self.PRW_occupation.SetText(jobs[0]['l10n_occupation'])
2165 self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y'))
2166 return True
2167
2169 if self.PRW_occupation.IsModified():
2170 new_job = self.PRW_occupation.GetValue().strip()
2171 jobs = self.__ident.get_occupations()
2172 for job in jobs:
2173 if job['l10n_occupation'] == new_job:
2174 continue
2175 self.__ident.unlink_occupation(occupation = job['l10n_occupation'])
2176 self.__ident.link_occupation(occupation = new_job)
2177 return True
2178
2180 """Patient demographics plugin for main notebook.
2181
2182 Hosts another notebook with pages for Identity, Contacts, etc.
2183
2184 Acts on/listens to the currently active patient.
2185 """
2186
2192
2193
2194
2195
2196
2197
2199 """Arrange widgets."""
2200 self.__patient_notebook = cPersonDemographicsEditorNb(self, -1)
2201
2202 szr_main = wx.BoxSizer(wx.VERTICAL)
2203 szr_main.Add(self.__patient_notebook, 1, wx.EXPAND)
2204 self.SetSizerAndFit(szr_main)
2205
2206
2207
2209 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2210 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
2211
2213 self._schedule_data_reget()
2214
2216 self._schedule_data_reget()
2217
2218
2228
2229
2230 if __name__ == "__main__":
2231
2232
2234 app = wx.PyWidgetTester(size = (600, 400))
2235 app.SetWidget(cKOrganizerSchedulePnl)
2236 app.MainLoop()
2237
2239 app = wx.PyWidgetTester(size = (600, 400))
2240 widget = cPersonNamesManagerPnl(app.frame, -1)
2241 widget.identity = activate_patient()
2242 app.frame.Show(True)
2243 app.MainLoop()
2244
2246 app = wx.PyWidgetTester(size = (600, 400))
2247 widget = cPersonIDsManagerPnl(app.frame, -1)
2248 widget.identity = activate_patient()
2249 app.frame.Show(True)
2250 app.MainLoop()
2251
2253 app = wx.PyWidgetTester(size = (600, 400))
2254 widget = cPersonIdentityManagerPnl(app.frame, -1)
2255 widget.identity = activate_patient()
2256 app.frame.Show(True)
2257 app.MainLoop()
2258
2263
2265 app = wx.PyWidgetTester(size = (600, 400))
2266 widget = cPersonDemographicsEditorNb(app.frame, -1)
2267 widget.identity = activate_patient()
2268 widget.refresh()
2269 app.frame.Show(True)
2270 app.MainLoop()
2271
2280
2281 if len(sys.argv) > 1 and sys.argv[1] == 'test':
2282
2283 gmI18N.activate_locale()
2284 gmI18N.install_domain(domain='gnumed')
2285 gmPG2.get_connection()
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297 test_person_ids_pnl()
2298
2299
2300
2301
2302
2303
2304