| Home | Trees | Indices | Help |
|
|---|
|
|
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 # standard library
7 import sys
8 import sys
9 import io
10 import re as regex
11 import logging
12 import os
13 import datetime as pydt
14
15
16 import wx
17 import wx.lib.imagebrowser as wx_imagebrowser
18 import wx.lib.statbmp as wx_genstatbmp
19
20
21 # GNUmed specific
22 if __name__ == '__main__':
23 sys.path.insert(0, '../../')
24 from Gnumed.pycommon import gmDispatcher
25 from Gnumed.pycommon import gmI18N
26 from Gnumed.pycommon import gmMatchProvider
27 from Gnumed.pycommon import gmPG2
28 from Gnumed.pycommon import gmTools
29 from Gnumed.pycommon import gmCfg
30 from Gnumed.pycommon import gmDateTime
31 from Gnumed.pycommon import gmShellAPI
32 from Gnumed.pycommon import gmNetworkTools
33
34 from Gnumed.business import gmDemographicRecord
35 from Gnumed.business import gmPersonSearch
36 from Gnumed.business import gmPerson
37 from Gnumed.business import gmStaff
38
39 from Gnumed.wxpython import gmPhraseWheel
40 from Gnumed.wxpython import gmRegetMixin
41 from Gnumed.wxpython import gmAuthWidgets
42 from Gnumed.wxpython import gmPersonContactWidgets
43 from Gnumed.wxpython import gmEditArea
44 from Gnumed.wxpython import gmListWidgets
45 from Gnumed.wxpython import gmDateTimeInput
46 from Gnumed.wxpython import gmDataMiningWidgets
47 from Gnumed.wxpython import gmGuiHelpers
48
49
50 # constant defs
51 _log = logging.getLogger('gm.ui')
52
53 #============================================================
54 # image tags related widgets
55 #------------------------------------------------------------
57 if tag_image is not None:
58 if tag_image['is_in_use']:
59 gmGuiHelpers.gm_show_info (
60 aTitle = _('Editing tag'),
61 aMessage = _(
62 'Cannot edit the image tag\n'
63 '\n'
64 ' "%s"\n'
65 '\n'
66 'because it is currently in use.\n'
67 ) % tag_image['l10n_description']
68 )
69 return False
70
71 ea = cTagImageEAPnl(parent, -1)
72 ea.data = tag_image
73 ea.mode = gmTools.coalesce(tag_image, 'new', 'edit')
74 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
75 dlg.SetTitle(gmTools.coalesce(tag_image, _('Adding new tag'), _('Editing tag')))
76 if dlg.ShowModal() == wx.ID_OK:
77 dlg.DestroyLater()
78 return True
79 dlg.DestroyLater()
80 return False
81 #------------------------------------------------------------
83
84 if parent is None:
85 parent = wx.GetApp().GetTopWindow()
86
87 #------------------------------------------------------------
88 def go_to_openclipart_org(tag_image):
89 gmNetworkTools.open_url_in_browser(url = 'http://www.openclipart.org')
90 gmNetworkTools.open_url_in_browser(url = 'http://commons.wikimedia.org/wiki/Category:Symbols_of_disabilities')
91 gmNetworkTools.open_url_in_browser(url = 'http://www.duckduckgo.com')
92 gmNetworkTools.open_url_in_browser(url = 'http://images.google.com')
93 return True
94
95 #------------------------------------------------------------
96 def edit(tag_image=None):
97 return edit_tag_image(parent = parent, tag_image = tag_image, single_entry = (tag_image is not None))
98
99 #------------------------------------------------------------
100 def delete(tag):
101 if tag['is_in_use']:
102 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this tag. It is in use.'), beep = True)
103 return False
104
105 return gmDemographicRecord.delete_tag_image(tag_image = tag['pk_tag_image'])
106
107 #------------------------------------------------------------
108 def refresh(lctrl):
109 tags = gmDemographicRecord.get_tag_images(order_by = 'l10n_description')
110 items = [ [
111 t['l10n_description'],
112 gmTools.bool2subst(t['is_in_use'], 'X', ''),
113 '%s' % t['size'],
114 t['pk_tag_image']
115 ] for t in tags ]
116 lctrl.set_string_items(items)
117 lctrl.set_column_widths(widths = [wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE])
118 lctrl.set_data(tags)
119
120 #------------------------------------------------------------
121 msg = _('\nTags with images registered with GNUmed.\n')
122
123 tag = gmListWidgets.get_choices_from_list (
124 parent = parent,
125 msg = msg,
126 caption = _('Showing tags with images.'),
127 columns = [_('Tag name'), _('In use'), _('Image size'), '#'],
128 single_selection = True,
129 new_callback = edit,
130 edit_callback = edit,
131 delete_callback = delete,
132 refresh_callback = refresh,
133 left_extra_button = (_('WWW'), _('Go to www.openclipart.org for images.'), go_to_openclipart_org)
134 )
135
136 return tag
137 #------------------------------------------------------------
138 from Gnumed.wxGladeWidgets import wxgTagImageEAPnl
139
141
143
144 try:
145 data = kwargs['tag_image']
146 del kwargs['tag_image']
147 except KeyError:
148 data = None
149
150 wxgTagImageEAPnl.wxgTagImageEAPnl.__init__(self, *args, **kwargs)
151 gmEditArea.cGenericEditAreaMixin.__init__(self)
152
153 self.mode = 'new'
154 self.data = data
155 if data is not None:
156 self.mode = 'edit'
157
158 self.__selected_image_file = None
159 #----------------------------------------------------------------
160 # generic Edit Area mixin API
161 #----------------------------------------------------------------
163
164 valid = True
165
166 if self.mode == 'new':
167 if self.__selected_image_file is None:
168 valid = False
169 self.StatusText = _('Must pick an image file for a new tag.')
170 self._BTN_pick_image.SetFocus()
171
172 if self.__selected_image_file is not None:
173 try:
174 open(self.__selected_image_file).close()
175 except Exception:
176 valid = False
177 self.__selected_image_file = None
178 self.StatusText = _('Cannot open the image file [%s].')
179 self._BTN_pick_image.SetFocus()
180
181 if self._TCTRL_description.GetValue().strip() == '':
182 valid = False
183 self.display_tctrl_as_valid(self._TCTRL_description, False)
184 self._TCTRL_description.SetFocus()
185 else:
186 self.display_tctrl_as_valid(self._TCTRL_description, True)
187
188 return (valid is True)
189 #----------------------------------------------------------------
191
192 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Creating tag with image'))
193 if dbo_conn is None:
194 return False
195
196 data = gmDemographicRecord.create_tag_image(description = self._TCTRL_description.GetValue().strip(), link_obj = dbo_conn)
197 dbo_conn.close()
198
199 data['filename'] = self._TCTRL_filename.GetValue().strip()
200 data.save()
201 data.update_image_from_file(filename = self.__selected_image_file)
202
203 # must be done very late or else the property access
204 # will refresh the display such that later field
205 # access will return empty values
206 self.data = data
207 return True
208 #----------------------------------------------------------------
210
211 # this is somewhat fake as it never actually uses the gm-dbo conn
212 # (although it does verify it)
213 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Updating tag with image'))
214 if dbo_conn is None:
215 return False
216 dbo_conn.close()
217
218 self.data['description'] = self._TCTRL_description.GetValue().strip()
219 self.data['filename'] = self._TCTRL_filename.GetValue().strip()
220 self.data.save()
221
222 if self.__selected_image_file is not None:
223 open(self.__selected_image_file).close()
224 self.data.update_image_from_file(filename = self.__selected_image_file)
225 self.__selected_image_file = None
226
227 return True
228 #----------------------------------------------------------------
230 self._TCTRL_description.SetValue('')
231 self._TCTRL_filename.SetValue('')
232 self._BMP_image.SetBitmap(bitmap = wx.Bitmap(100, 100))
233
234 self.__selected_image_file = None
235
236 self._TCTRL_description.SetFocus()
237 #----------------------------------------------------------------
240 #----------------------------------------------------------------
242 self._TCTRL_description.SetValue(self.data['l10n_description'])
243 self._TCTRL_filename.SetValue(gmTools.coalesce(self.data['filename'], ''))
244 fname = self.data.export_image2file()
245 if fname is None:
246 self._BMP_image.SetBitmap(bitmap = wx.Bitmap(100, 100))
247 else:
248 self._BMP_image.SetBitmap(bitmap = gmGuiHelpers.file2scaled_image(filename = fname, height = 100))
249
250 self.__selected_image_file = None
251
252 self._TCTRL_description.SetFocus()
253 #----------------------------------------------------------------
254 # event handlers
255 #----------------------------------------------------------------
267
268 #============================================================
283 #--------------------------------------------------------
284 def delete(tag):
285 do_delete = gmGuiHelpers.gm_show_question (
286 title = _('Deleting patient tag'),
287 question = _('Do you really want to delete this patient tag ?')
288 )
289 if not do_delete:
290 return False
291 patient.remove_tag(tag = tag['pk_identity_tag'])
292 return True
293 #--------------------------------------------------------
294 def manage_available_tags(tag):
295 manage_tag_images(parent = parent)
296 return False
297 #--------------------------------------------------------
298 msg = _('Tags of patient: %s\n') % patient['description_gender']
299
300 return gmListWidgets.get_choices_from_list (
301 parent = parent,
302 msg = msg,
303 caption = _('Showing patient tags'),
304 columns = [_('Tag'), _('Comment')],
305 single_selection = False,
306 delete_callback = delete,
307 refresh_callback = refresh,
308 left_extra_button = (_('Manage'), _('Manage available tags.'), manage_available_tags)
309 )
310 #============================================================
311 from Gnumed.wxGladeWidgets import wxgVisualSoapPresenterPnl
312
314
316 wxgVisualSoapPresenterPnl.wxgVisualSoapPresenterPnl.__init__(self, *args, **kwargs)
317 self._SZR_bitmaps = self.GetSizer()
318 self.__bitmaps = []
319
320 self.__context_popup = wx.Menu()
321
322 item = self.__context_popup.Append(-1, _('&Edit comment'))
323 self.Bind(wx.EVT_MENU, self.__edit_tag, item)
324
325 item = self.__context_popup.Append(-1, _('&Remove tag'))
326 self.Bind(wx.EVT_MENU, self.__remove_tag, item)
327 #--------------------------------------------------------
328 # external API
329 #--------------------------------------------------------
331
332 self.clear()
333
334 for tag in patient.get_tags(order_by = 'l10n_description'):
335 fname = tag.export_image2file()
336 if fname is None:
337 _log.warning('cannot export image data of tag [%s]', tag['l10n_description'])
338 continue
339 img = gmGuiHelpers.file2scaled_image(filename = fname, height = 20)
340 bmp = wx_genstatbmp.GenStaticBitmap(self, -1, img, style = wx.NO_BORDER)
341 bmp.SetToolTip('%s%s' % (
342 tag['l10n_description'],
343 gmTools.coalesce(tag['comment'], '', '\n\n%s')
344 ))
345 bmp.tag = tag
346 bmp.Bind(wx.EVT_RIGHT_UP, self._on_bitmap_rightclicked)
347 # FIXME: add context menu for Delete/Clone/Add/Configure
348 self._SZR_bitmaps.Add(bmp, 0, wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, 1) # | wx.EXPAND
349 self.__bitmaps.append(bmp)
350
351 self.GetParent().Layout()
352
353 #--------------------------------------------------------
355 while len(self._SZR_bitmaps.GetChildren()) > 0:
356 self._SZR_bitmaps.Detach(0)
357 # for child_idx in range(len(self._SZR_bitmaps.GetChildren())):
358 # self._SZR_bitmaps.Detach(child_idx)
359 for bmp in self.__bitmaps:
360 bmp.DestroyLater()
361 self.__bitmaps = []
362 #--------------------------------------------------------
363 # internal helpers
364 #--------------------------------------------------------
366 if self.__current_tag is None:
367 return
368 pat = gmPerson.gmCurrentPatient()
369 if not pat.connected:
370 return
371 pat.remove_tag(tag = self.__current_tag['pk_identity_tag'])
372 #--------------------------------------------------------
374 if self.__current_tag is None:
375 return
376
377 msg = _('Edit the comment on tag [%s]') % self.__current_tag['l10n_description']
378 comment = wx.GetTextFromUser (
379 message = msg,
380 caption = _('Editing tag comment'),
381 default_value = gmTools.coalesce(self.__current_tag['comment'], ''),
382 parent = self
383 )
384
385 if comment == '':
386 return
387
388 if comment.strip() == self.__current_tag['comment']:
389 return
390
391 if comment == ' ':
392 self.__current_tag['comment'] = None
393 else:
394 self.__current_tag['comment'] = comment.strip()
395
396 self.__current_tag.save()
397 #--------------------------------------------------------
398 # event handlers
399 #--------------------------------------------------------
401 self.__current_tag = evt.GetEventObject().tag
402 self.PopupMenu(self.__context_popup, pos = wx.DefaultPosition)
403 self.__current_tag = None
404
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': ''},
415 {'label': ''},
416 {'label': ''},
417 {'label': 'KOrganizer', 'tooltip': _('Launch KOrganizer')}
418 ]
419 gmDataMiningWidgets.cPatientListingPnl.__init__(self, *args, **kwargs)
420
421 self.fname = os.path.expanduser(os.path.join(gmTools.gmPaths().tmp_dir, 'korganizer2gnumed.csv'))
422 self.reload_cmd = 'konsolekalendar --view --export-type csv --export-file %s' % self.fname
423
424 #--------------------------------------------------------
428 #--------------------------------------------------------
430 """Reload appointments from KOrganizer."""
431 found, cmd = gmShellAPI.detect_external_binary(binary = 'korganizer')
432
433 if not found:
434 gmDispatcher.send(signal = 'statustext', msg = _('KOrganizer is not installed.'), beep = True)
435 return
436
437 gmShellAPI.run_command_in_shell(command = cmd, blocking = False)
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 = io.open(self.fname , mode = 'rt', encoding = 'utf8', errors = 'replace')
445 except IOError:
446 gmDispatcher.send(signal = '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 # start_date, start_time, end_date, end_time, title (patient), ort, comment, UID
454 self._LCTRL_items.set_columns ([
455 _('Place'),
456 _('Start'),
457 '',
458 '',
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 # notebook plugins API
474 #--------------------------------------------------------
476 self.reload_appointments()
477
478 #============================================================
479 # occupation related widgets / functions
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 = ''
490 update = ''
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() == '':
507 return
508
509 for job in curr_jobs:
510 # unlink all but the new job
511 if job['l10n_occupation'] != new_job:
512 pat.unlink_occupation(occupation = job['l10n_occupation'])
513 # and link the new one
514 pat.link_occupation(occupation = new_job)
515
516 #------------------------------------------------------------
518
520 query = "SELECT distinct name, _(name) from dem.occupation where _(name) %(fragment_condition)s"
521 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
522 mp.setThresholds(1, 3, 5)
523 gmPhraseWheel.cPhraseWheel.__init__ (
524 self,
525 *args,
526 **kwargs
527 )
528 self.SetToolTip(_("Type or select an occupation."))
529 self.capitalisation_mode = gmTools.CAPS_FIRST
530 self.matcher = mp
531
532 #============================================================
533 # identity widgets / functions
534 #============================================================
537
538 #------------------------------------------------------------
540
541 # already disabled ?
542 if identity['is_deleted']:
543 _log.debug('identity already deleted: %s', identity)
544 return True
545
546 # logged in staff ?
547 # if so -> return
548 prov = gmStaff.gmCurrentProvider()
549 if prov['pk_identity'] == identity['pk_identity']:
550 _log.warning('identity cannot delete itself while being logged on as staff member')
551 _log.debug('identity to delete: %s', identity)
552 _log.debug('logged on staff: %s', prov)
553 return False
554
555 # ask user for assurance
556 go_ahead = gmGuiHelpers.gm_show_question (
557 _('Are you sure you really, positively want\n'
558 'to disable the following person ?\n'
559 '\n'
560 ' %s %s %s\n'
561 ' born %s\n'
562 '\n'
563 '%s\n'
564 ) % (
565 identity['firstnames'],
566 identity['lastnames'],
567 identity['gender'],
568 identity.get_formatted_dob(),
569 gmTools.bool2subst (
570 identity.is_patient,
571 _('This patient DID receive care here.'),
572 _('This person did NOT receive care here.')
573 )
574 ),
575 _('Disabling person')
576 )
577 if not go_ahead:
578 return False
579
580 # get admin connection
581 conn = gmAuthWidgets.get_dbowner_connection (
582 procedure = _('Disabling person')
583 )
584 # - user cancelled
585 if conn is False:
586 return False
587 # - error
588 if conn is None:
589 return None
590
591 # disable patient
592 gmPerson.disable_identity(identity['pk_identity'])
593
594 # change active patient to logged on staff = myself
595 from Gnumed.wxpython.gmPatSearchWidgets import set_active_patient
596 wx.CallAfter(set_active_patient, patient = prov.identity)
597
598 return True
599
600 #------------------------------------------------------------
601 # phrasewheels
602 #------------------------------------------------------------
604
606 query = "SELECT distinct lastnames, lastnames from dem.names where lastnames %(fragment_condition)s order by lastnames limit 25"
607 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
608 mp.setThresholds(3, 5, 9)
609 gmPhraseWheel.cPhraseWheel.__init__ (
610 self,
611 *args,
612 **kwargs
613 )
614 self.SetToolTip(_("Type or select a last name (family name/surname)."))
615 self.capitalisation_mode = gmTools.CAPS_NAMES
616 self.matcher = mp
617 #------------------------------------------------------------
619
621 query = """
622 (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
623 union
624 (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
625 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
626 mp.setThresholds(3, 5, 9)
627 gmPhraseWheel.cPhraseWheel.__init__ (
628 self,
629 *args,
630 **kwargs
631 )
632 self.SetToolTip(_("Type or select a first name (forename/Christian name/given name)."))
633 self.capitalisation_mode = gmTools.CAPS_NAMES
634 self.matcher = mp
635 #------------------------------------------------------------
637
639 query = """
640 (SELECT distinct preferred, preferred from dem.names where preferred %(fragment_condition)s order by preferred limit 20)
641 union
642 (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
643 union
644 (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
645 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
646 mp.setThresholds(3, 5, 9)
647 gmPhraseWheel.cPhraseWheel.__init__ (
648 self,
649 *args,
650 **kwargs
651 )
652 self.SetToolTip(_("Type or select an alias (nick name, preferred name, call name, warrior name, artist name)."))
653 # nicknames CAN start with lower case !
654 #self.capitalisation_mode = gmTools.CAPS_NAMES
655 self.matcher = mp
656 #------------------------------------------------------------
658
660 query = "SELECT distinct title, title from dem.identity where title %(fragment_condition)s"
661 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
662 mp.setThresholds(1, 3, 9)
663 gmPhraseWheel.cPhraseWheel.__init__ (
664 self,
665 *args,
666 **kwargs
667 )
668 self.SetToolTip(_("Type or select a title. Note that the title applies to the person, not to a particular name !"))
669 self.matcher = mp
670 #------------------------------------------------------------
672 """Let user select a gender."""
673
674 _gender_map = None
675
677
678 if cGenderSelectionPhraseWheel._gender_map is None:
679 cmd = """
680 SELECT tag, l10n_label, sort_weight
681 from dem.v_gender_labels
682 order by sort_weight desc"""
683 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
684 cGenderSelectionPhraseWheel._gender_map = {}
685 for gender in rows:
686 cGenderSelectionPhraseWheel._gender_map[gender[idx['tag']]] = {
687 'data': gender[idx['tag']],
688 'field_label': gender[idx['l10n_label']],
689 'list_label': gender[idx['l10n_label']],
690 'weight': gender[idx['sort_weight']]
691 }
692
693 mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = list(cGenderSelectionPhraseWheel._gender_map.values()))
694 mp.setThresholds(1, 1, 3)
695
696 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
697 self.selection_only = True
698 self.matcher = mp
699 self.picklist_delay = 50
700 #------------------------------------------------------------
702
704 query = """
705 SELECT DISTINCT ON (list_label)
706 pk AS data,
707 name AS field_label,
708 name || coalesce(' (' || issuer || ')', '') as list_label
709 FROM dem.enum_ext_id_types
710 WHERE name %(fragment_condition)s
711 ORDER BY list_label
712 LIMIT 25
713 """
714 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
715 mp.setThresholds(1, 3, 5)
716 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
717 self.SetToolTip(_("Enter or select a type for the external ID."))
718 self.matcher = mp
719 #--------------------------------------------------------
724 #------------------------------------------------------------
726
728 query = """
729 SELECT distinct issuer, issuer
730 from dem.enum_ext_id_types
731 where issuer %(fragment_condition)s
732 order by issuer limit 25"""
733 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
734 mp.setThresholds(1, 3, 5)
735 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
736 self.SetToolTip(_("Type or select an ID issuer."))
737 self.capitalisation_mode = gmTools.CAPS_FIRST
738 self.matcher = mp
739 #------------------------------------------------------------
740 # edit areas
741 #------------------------------------------------------------
742 from Gnumed.wxGladeWidgets import wxgExternalIDEditAreaPnl
743
744 -class cExternalIDEditAreaPnl(wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
745 """An edit area for editing/creating external IDs.
746
747 Does NOT act on/listen to the current patient.
748 """
750
751 try:
752 data = kwargs['external_id']
753 del kwargs['external_id']
754 except Exception:
755 data = None
756
757 wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl.__init__(self, *args, **kwargs)
758 gmEditArea.cGenericEditAreaMixin.__init__(self)
759
760 self.id_holder = None
761
762 self.mode = 'new'
763 self.data = data
764 if data is not None:
765 self.mode = 'edit'
766
767 self.__init_ui()
768 #--------------------------------------------------------
770 self._PRW_type.add_callback_on_lose_focus(self._on_type_set)
771 #----------------------------------------------------------------
772 # generic Edit Area mixin API
773 #----------------------------------------------------------------
775 validity = True
776
777 # do not test .GetData() because adding external
778 # IDs will create types as necessary
779 #if self._PRW_type.GetData() is None:
780 if self._PRW_type.GetValue().strip() == '':
781 validity = False
782 self._PRW_type.display_as_valid(False)
783 self._PRW_type.SetFocus()
784 else:
785 self._PRW_type.display_as_valid(True)
786
787 if self._TCTRL_value.GetValue().strip() == '':
788 validity = False
789 self.display_tctrl_as_valid(tctrl = self._TCTRL_value, valid = False)
790 else:
791 self.display_tctrl_as_valid(tctrl = self._TCTRL_value, valid = True)
792
793 return validity
794 #----------------------------------------------------------------
796 data = {}
797 data['pk_type'] = None
798 data['name'] = self._PRW_type.GetValue().strip()
799 data['value'] = self._TCTRL_value.GetValue().strip()
800 data['issuer'] = gmTools.none_if(self._PRW_issuer.GetValue().strip(), '')
801 data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
802
803 self.id_holder.add_external_id (
804 type_name = data['name'],
805 value = data['value'],
806 issuer = data['issuer'],
807 comment = data['comment']
808 )
809
810 self.data = data
811 return True
812 #----------------------------------------------------------------
814 self.data['name'] = self._PRW_type.GetValue().strip()
815 self.data['value'] = self._TCTRL_value.GetValue().strip()
816 self.data['issuer'] = gmTools.none_if(self._PRW_issuer.GetValue().strip(), '')
817 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
818
819 self.id_holder.update_external_id (
820 pk_id = self.data['pk_id'],
821 type = self.data['name'],
822 value = self.data['value'],
823 issuer = self.data['issuer'],
824 comment = self.data['comment']
825 )
826
827 return True
828 #----------------------------------------------------------------
830 self._PRW_type.SetText(value = '', data = None)
831 self._TCTRL_value.SetValue('')
832 self._PRW_issuer.SetText(value = '', data = None)
833 self._TCTRL_comment.SetValue('')
834 #----------------------------------------------------------------
838 #----------------------------------------------------------------
840 self._PRW_type.SetText(value = self.data['name'], data = self.data['pk_type'])
841 self._TCTRL_value.SetValue(self.data['value'])
842 self._PRW_issuer.SetText(self.data['issuer'])
843 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
844 #----------------------------------------------------------------
845 # internal helpers
846 #----------------------------------------------------------------
848 """Set the issuer according to the selected type.
849
850 Matches are fetched from existing records in backend.
851 """
852 pk_curr_type = self._PRW_type.GetData()
853 if pk_curr_type is None:
854 return True
855 rows, idx = gmPG2.run_ro_queries(queries = [{
856 'cmd': "SELECT issuer FROM dem.enum_ext_id_types WHERE pk = %s",
857 'args': [pk_curr_type]
858 }])
859 if len(rows) == 0:
860 return True
861 wx.CallAfter(self._PRW_issuer.SetText, rows[0][0])
862 return True
863
864 #============================================================
865 # identity widgets
866 #------------------------------------------------------------
868 allow_empty_dob = gmGuiHelpers.gm_show_question (
869 _(
870 'Are you sure you want to leave this person\n'
871 'without a valid date of birth ?\n'
872 '\n'
873 'This can be useful for temporary staff members\n'
874 'but will provoke nag screens if this person\n'
875 'becomes a patient.\n'
876 ),
877 _('Validating date of birth')
878 )
879 return allow_empty_dob
880 #------------------------------------------------------------
882
883 # valid timestamp ?
884 if dob_prw.is_valid_timestamp(empty_is_valid = False): # properly colors the field
885 dob = dob_prw.date
886 # but year also usable ?
887 if (dob.year > 1899) and (dob < gmDateTime.pydt_now_here()):
888 return True
889
890 if dob.year < 1900:
891 msg = _(
892 'DOB: %s\n'
893 '\n'
894 'While this is a valid point in time Python does\n'
895 'not know how to deal with it.\n'
896 '\n'
897 'We suggest using January 1st 1901 instead and adding\n'
898 'the true date of birth to the patient comment.\n'
899 '\n'
900 'Sorry for the inconvenience %s'
901 ) % (dob, gmTools.u_frowning_face)
902 else:
903 msg = _(
904 'DOB: %s\n'
905 '\n'
906 'Date of birth in the future !'
907 ) % dob
908 gmGuiHelpers.gm_show_error (
909 msg,
910 _('Validating date of birth')
911 )
912 dob_prw.display_as_valid(False)
913 dob_prw.SetFocus()
914 return False
915
916 # invalid timestamp but not empty
917 if dob_prw.GetValue().strip() != '':
918 dob_prw.display_as_valid(False)
919 gmDispatcher.send(signal = 'statustext', msg = _('Invalid date of birth.'))
920 dob_prw.SetFocus()
921 return False
922
923 # empty DOB field
924 dob_prw.display_as_valid(False)
925 return True
926
927 #------------------------------------------------------------
929
930 val = ctrl.GetValue().strip()
931
932 if val == '':
933 return True
934
935 converted, hours = gmTools.input2int(val[:2], 0, 23)
936 if not converted:
937 return False
938
939 converted, minutes = gmTools.input2int(val[3:5], 0, 59)
940 if not converted:
941 return False
942
943 return True
944
945 #------------------------------------------------------------
946 from Gnumed.wxGladeWidgets import wxgIdentityEAPnl
947
949 """An edit area for editing/creating title/gender/dob/dod etc."""
950
952
953 try:
954 data = kwargs['identity']
955 del kwargs['identity']
956 except KeyError:
957 data = None
958
959 wxgIdentityEAPnl.wxgIdentityEAPnl.__init__(self, *args, **kwargs)
960 gmEditArea.cGenericEditAreaMixin.__init__(self)
961
962 self.mode = 'new'
963 self.data = data
964 if data is not None:
965 self.mode = 'edit'
966
967 # self.__init_ui()
968 #----------------------------------------------------------------
969 # def __init_ui(self):
970 # # adjust phrasewheels etc
971 #----------------------------------------------------------------
972 # generic Edit Area mixin API
973 #----------------------------------------------------------------
975
976 has_error = False
977
978 if self._PRW_gender.GetData() is None:
979 self._PRW_gender.SetFocus()
980 has_error = True
981
982 if self.data is not None:
983 if not _validate_dob_field(self._PRW_dob):
984 has_error = True
985
986 # TOB validation
987 if _validate_tob_field(self._TCTRL_tob):
988 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True)
989 else:
990 has_error = True
991 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False)
992
993 if not self._PRW_dod.is_valid_timestamp(empty_is_valid = True):
994 self.StatusText = _('Invalid date of death.')
995 self._PRW_dod.SetFocus()
996 has_error = True
997
998 return (has_error is False)
999 #----------------------------------------------------------------
1003 #----------------------------------------------------------------
1005
1006 if self._PRW_dob.GetValue().strip() == '':
1007 if not _empty_dob_allowed():
1008 return False
1009 self.data['dob'] = None
1010 else:
1011 self.data['dob'] = self._PRW_dob.GetData()
1012 self.data['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
1013 val = self._TCTRL_tob.GetValue().strip()
1014 if val == '':
1015 self.data['tob'] = None
1016 else:
1017 self.data['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
1018 self.data['gender'] = self._PRW_gender.GetData()
1019 self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), '')
1020 self.data['deceased'] = self._PRW_dod.GetData()
1021 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1022
1023 self.data.save()
1024 return True
1025 #----------------------------------------------------------------
1028 #----------------------------------------------------------------
1030
1031 self._LBL_info.SetLabel('ID: #%s' % (
1032 self.data.ID
1033 # FIXME: add 'deleted' status
1034 ))
1035 if self.data['dob'] is None:
1036 val = ''
1037 else:
1038 val = gmDateTime.pydt_strftime (
1039 self.data['dob'],
1040 format = '%Y-%m-%d',
1041 accuracy = gmDateTime.acc_minutes
1042 )
1043 self._PRW_dob.SetText(value = val, data = self.data['dob'])
1044 self._CHBOX_estimated_dob.SetValue(self.data['dob_is_estimated'])
1045 if self.data['tob'] is None:
1046 self._TCTRL_tob.SetValue('')
1047 else:
1048 self._TCTRL_tob.SetValue(self.data['tob'].strftime('%H:%M'))
1049 if self.data['deceased'] is None:
1050 val = ''
1051 else:
1052 val = gmDateTime.pydt_strftime (
1053 self.data['deceased'],
1054 format = '%Y-%m-%d %H:%M',
1055 accuracy = gmDateTime.acc_minutes
1056 )
1057 self._PRW_dod.SetText(value = val, data = self.data['deceased'])
1058 self._PRW_gender.SetData(self.data['gender'])
1059 #self._PRW_ethnicity.SetValue()
1060 self._PRW_title.SetText(gmTools.coalesce(self.data['title'], ''))
1061 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
1062 #----------------------------------------------------------------
1065 #------------------------------------------------------------
1066 from Gnumed.wxGladeWidgets import wxgPersonNameEAPnl
1067
1068 -class cPersonNameEAPnl(wxgPersonNameEAPnl.wxgPersonNameEAPnl, gmEditArea.cGenericEditAreaMixin):
1069 """An edit area for editing/creating names of people.
1070
1071 Does NOT act on/listen to the current patient.
1072 """
1074
1075 try:
1076 data = kwargs['name']
1077 identity = gmPerson.cPerson(aPK_obj = data['pk_identity'])
1078 del kwargs['name']
1079 except KeyError:
1080 data = None
1081 identity = kwargs['identity']
1082 del kwargs['identity']
1083
1084 wxgPersonNameEAPnl.wxgPersonNameEAPnl.__init__(self, *args, **kwargs)
1085 gmEditArea.cGenericEditAreaMixin.__init__(self)
1086
1087 self.__identity = identity
1088
1089 self.mode = 'new'
1090 self.data = data
1091 if data is not None:
1092 self.mode = 'edit'
1093
1094 #self.__init_ui()
1095 #----------------------------------------------------------------
1096 # def __init_ui(self):
1097 # # adjust phrasewheels etc
1098 #----------------------------------------------------------------
1099 # generic Edit Area mixin API
1100 #----------------------------------------------------------------
1102 validity = True
1103
1104 if self._PRW_lastname.GetValue().strip() == '':
1105 validity = False
1106 self._PRW_lastname.display_as_valid(False)
1107 self._PRW_lastname.SetFocus()
1108 else:
1109 self._PRW_lastname.display_as_valid(True)
1110
1111 if self._PRW_firstname.GetValue().strip() == '':
1112 validity = False
1113 self._PRW_firstname.display_as_valid(False)
1114 self._PRW_firstname.SetFocus()
1115 else:
1116 self._PRW_firstname.display_as_valid(True)
1117
1118 return validity
1119 #----------------------------------------------------------------
1121
1122 first = self._PRW_firstname.GetValue().strip()
1123 last = self._PRW_lastname.GetValue().strip()
1124 active = self._CHBOX_active.GetValue()
1125
1126 try:
1127 data = self.__identity.add_name(first, last, active)
1128 except gmPG2.dbapi.IntegrityError as exc:
1129 _log.exception('cannot save new name')
1130 gmGuiHelpers.gm_show_error (
1131 aTitle = _('Adding name'),
1132 aMessage = _(
1133 'Cannot add this name to the patient !\n'
1134 '\n'
1135 ' %s'
1136 ) % exc.pgerror
1137 )
1138 return False
1139
1140 old_nick = self.__identity['active_name']['preferred']
1141 new_nick = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
1142 if active:
1143 data['preferred'] = gmTools.coalesce(new_nick, old_nick)
1144 else:
1145 data['preferred'] = new_nick
1146 data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1147 data.save()
1148
1149 self.data = data
1150 return True
1151 #----------------------------------------------------------------
1153 """The knack here is that we can only update a few fields.
1154
1155 Otherwise we need to clone the name and update that.
1156 """
1157 first = self._PRW_firstname.GetValue().strip()
1158 last = self._PRW_lastname.GetValue().strip()
1159 active = self._CHBOX_active.GetValue()
1160
1161 current_name = self.data['firstnames'].strip() + self.data['lastnames'].strip()
1162 new_name = first + last
1163
1164 # editable fields only ?
1165 if new_name == current_name:
1166 self.data['active_name'] = self._CHBOX_active.GetValue()
1167 self.data['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
1168 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1169 self.data.save()
1170 # else clone name and update that
1171 else:
1172 try:
1173 name = self.__identity.add_name(first, last, active)
1174 except gmPG2.dbapi.IntegrityError as exc:
1175 _log.exception('cannot clone name when editing existing name')
1176 gmGuiHelpers.gm_show_error (
1177 aTitle = _('Editing name'),
1178 aMessage = _(
1179 'Cannot clone a copy of this name !\n'
1180 '\n'
1181 ' %s'
1182 ) % exc.pgerror
1183 )
1184 return False
1185 name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
1186 name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1187 name.save()
1188 self.data = name
1189
1190 return True
1191 #----------------------------------------------------------------
1193 self._PRW_firstname.SetText(value = '', data = None)
1194 self._PRW_lastname.SetText(value = '', data = None)
1195 self._PRW_nick.SetText(value = '', data = None)
1196 self._TCTRL_comment.SetValue('')
1197 self._CHBOX_active.SetValue(False)
1198
1199 self._PRW_firstname.SetFocus()
1200 #----------------------------------------------------------------
1202 self._refresh_as_new()
1203 self._PRW_firstname.SetText(value = '', data = None)
1204 self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], ''))
1205
1206 self._PRW_lastname.SetFocus()
1207 #----------------------------------------------------------------
1209 self._PRW_firstname.SetText(self.data['firstnames'])
1210 self._PRW_lastname.SetText(self.data['lastnames'])
1211 self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], ''))
1212 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
1213 self._CHBOX_active.SetValue(self.data['active_name'])
1214
1215 self._TCTRL_comment.SetFocus()
1216 #------------------------------------------------------------
1217 # list manager
1218 #------------------------------------------------------------
1220 """A list for managing a person's names.
1221
1222 Does NOT act on/listen to the current patient.
1223 """
1225
1226 try:
1227 self.__identity = kwargs['identity']
1228 del kwargs['identity']
1229 except KeyError:
1230 self.__identity = None
1231
1232 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1233
1234 self.refresh_callback = self.refresh
1235 self.new_callback = self._add_name
1236 self.edit_callback = self._edit_name
1237 self.delete_callback = self._del_name
1238
1239 self.__init_ui()
1240 self.refresh()
1241 #--------------------------------------------------------
1242 # external API
1243 #--------------------------------------------------------
1245 if self.__identity is None:
1246 self._LCTRL_items.set_string_items()
1247 return
1248
1249 names = self.__identity.get_names()
1250 self._LCTRL_items.set_string_items (
1251 items = [ [
1252 gmTools.bool2str(n['active_name'], 'X', ''),
1253 n['lastnames'],
1254 n['firstnames'],
1255 gmTools.coalesce(n['preferred'], ''),
1256 gmTools.coalesce(n['comment'], '')
1257 ] for n in names ]
1258 )
1259 self._LCTRL_items.set_column_widths()
1260 self._LCTRL_items.set_data(data = names)
1261 #--------------------------------------------------------
1262 # internal helpers
1263 #--------------------------------------------------------
1265 self._LCTRL_items.set_columns(columns = [
1266 _('Active'),
1267 _('Lastname'),
1268 _('Firstname(s)'),
1269 _('Preferred Name'),
1270 _('Comment')
1271 ])
1272 #--------------------------------------------------------
1274 #ea = cPersonNameEAPnl(self, -1, name = self.__identity.get_active_name())
1275 ea = cPersonNameEAPnl(self, -1, identity = self.__identity)
1276 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1277 dlg.SetTitle(_('Adding new name'))
1278 if dlg.ShowModal() == wx.ID_OK:
1279 dlg.DestroyLater()
1280 return True
1281 dlg.DestroyLater()
1282 return False
1283 #--------------------------------------------------------
1285 ea = cPersonNameEAPnl(self, -1, name = name)
1286 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1287 dlg.SetTitle(_('Editing name'))
1288 if dlg.ShowModal() == wx.ID_OK:
1289 dlg.DestroyLater()
1290 return True
1291 dlg.DestroyLater()
1292 return False
1293 #--------------------------------------------------------
1295
1296 if len(self.__identity.get_names()) == 1:
1297 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the only name of a person.'), beep = True)
1298 return False
1299
1300 if name['active_name']:
1301 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the active name of a person.'), beep = True)
1302 return False
1303
1304 go_ahead = gmGuiHelpers.gm_show_question (
1305 _( 'It is often advisable to keep old names around and\n'
1306 'just create a new "currently active" name.\n'
1307 '\n'
1308 'This allows finding the patient by both the old\n'
1309 'and the new name (think before/after marriage).\n'
1310 '\n'
1311 'Do you still want to really delete\n'
1312 "this name from the patient ?"
1313 ),
1314 _('Deleting name')
1315 )
1316 if not go_ahead:
1317 return False
1318
1319 self.__identity.delete_name(name = name)
1320 return True
1321 #--------------------------------------------------------
1322 # properties
1323 #--------------------------------------------------------
1326
1330
1331 identity = property(_get_identity, _set_identity)
1332
1333 #------------------------------------------------------------
1335 """A list for managing a person's external IDs.
1336
1337 Does NOT act on/listen to the current patient.
1338 """
1340
1341 try:
1342 self.__identity = kwargs['identity']
1343 del kwargs['identity']
1344 except KeyError:
1345 self.__identity = None
1346
1347 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1348
1349 self.refresh_callback = self.refresh
1350 self.new_callback = self._add_id
1351 self.edit_callback = self._edit_id
1352 self.delete_callback = self._del_id
1353
1354 self.__init_ui()
1355 self.refresh()
1356 #--------------------------------------------------------
1357 # external API
1358 #--------------------------------------------------------
1360 if self.__identity is None:
1361 self._LCTRL_items.set_string_items()
1362 return
1363
1364 ids = self.__identity.get_external_ids()
1365 self._LCTRL_items.set_string_items (
1366 items = [ [
1367 i['name'],
1368 i['value'],
1369 gmTools.coalesce(i['issuer'], ''),
1370 gmTools.coalesce(i['comment'], '')
1371 ] for i in ids
1372 ]
1373 )
1374 self._LCTRL_items.set_column_widths()
1375 self._LCTRL_items.set_data(data = ids)
1376 #--------------------------------------------------------
1377 # internal helpers
1378 #--------------------------------------------------------
1380 self._LCTRL_items.set_columns(columns = [
1381 _('ID type'),
1382 _('Value'),
1383 _('Issuer'),
1384 _('Comment')
1385 ])
1386 #--------------------------------------------------------
1388 ea = cExternalIDEditAreaPnl(self, -1)
1389 ea.id_holder = self.__identity
1390 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea)
1391 dlg.SetTitle(_('Adding new external ID'))
1392 if dlg.ShowModal() == wx.ID_OK:
1393 dlg.DestroyLater()
1394 return True
1395 dlg.DestroyLater()
1396 return False
1397 #--------------------------------------------------------
1399 ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id)
1400 ea.id_holder = self.__identity
1401 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1402 dlg.SetTitle(_('Editing external ID'))
1403 if dlg.ShowModal() == wx.ID_OK:
1404 dlg.DestroyLater()
1405 return True
1406 dlg.DestroyLater()
1407 return False
1408 #--------------------------------------------------------
1410 go_ahead = gmGuiHelpers.gm_show_question (
1411 _( 'Do you really want to delete this\n'
1412 'external ID from the patient ?'),
1413 _('Deleting external ID')
1414 )
1415 if not go_ahead:
1416 return False
1417 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id'])
1418 return True
1419 #--------------------------------------------------------
1420 # properties
1421 #--------------------------------------------------------
1424
1428
1429 identity = property(_get_identity, _set_identity)
1430 #------------------------------------------------------------
1431 # integrated panels
1432 #------------------------------------------------------------
1433 from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl
1434
1436 """A panel for editing identity data for a person.
1437
1438 - provides access to:
1439 - identity EA
1440 - name list manager
1441 - external IDs list manager
1442
1443 Does NOT act on/listen to the current patient.
1444 """
1446
1447 wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs)
1448
1449 self.__identity = None
1450 self.refresh()
1451 #--------------------------------------------------------
1452 # external API
1453 #--------------------------------------------------------
1455 self._PNL_names.identity = self.__identity
1456 self._PNL_ids.identity = self.__identity
1457 # this is an Edit Area:
1458 self._PNL_identity.mode = 'new'
1459 self._PNL_identity.data = self.__identity
1460 if self.__identity is not None:
1461 self._PNL_identity.mode = 'edit'
1462 self._PNL_identity._refresh_from_existing()
1463 #--------------------------------------------------------
1464 # properties
1465 #--------------------------------------------------------
1468
1472
1473 identity = property(_get_identity, _set_identity)
1474 #--------------------------------------------------------
1475 # event handlers
1476 #--------------------------------------------------------
1480 #self._PNL_identity.refresh()
1481 #--------------------------------------------------------
1484
1485 #============================================================
1486 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl
1487
1488 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
1490
1491 wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs)
1492
1493 self.__identity = None
1494 self._PRW_provider.selection_only = False
1495 self.refresh()
1496 #--------------------------------------------------------
1497 # external API
1498 #--------------------------------------------------------
1500
1501 tt = _('Link another person in this database as the emergency contact:\n\nEnter person name part or identifier and hit <enter>.')
1502
1503 if self.__identity is None:
1504 self._TCTRL_er_contact.SetValue('')
1505 self._TCTRL_person.person = None
1506 self._TCTRL_person.SetToolTip(tt)
1507
1508 self._PRW_provider.SetText(value = '', data = None)
1509 return
1510
1511 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], ''))
1512 if self.__identity['pk_emergency_contact'] is not None:
1513 ident = gmPerson.cPerson(aPK_obj = self.__identity['pk_emergency_contact'])
1514 self._TCTRL_person.person = ident
1515 tt = '%s\n\n%s\n\n%s' % (
1516 tt,
1517 ident['description_gender'],
1518 '\n'.join([
1519 '%s: %s%s' % (
1520 c['l10n_comm_type'],
1521 c['url'],
1522 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), '', '')
1523 )
1524 for c in ident.get_comm_channels()
1525 ])
1526 )
1527 else:
1528 self._TCTRL_person.person = None
1529
1530 self._TCTRL_person.SetToolTip(tt)
1531
1532 if self.__identity['pk_primary_provider'] is None:
1533 self._PRW_provider.SetText(value = '', data = None)
1534 else:
1535 self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
1536
1537 self._PNL_external_care.identity = self.__identity
1538 #--------------------------------------------------------
1539 # properties
1540 #--------------------------------------------------------
1543
1547
1548 identity = property(_get_identity, _set_identity)
1549 #--------------------------------------------------------
1550 # event handlers
1551 #--------------------------------------------------------
1566 #--------------------------------------------------------
1569 #--------------------------------------------------------
1580 #--------------------------------------------------------
1588
1589 #============================================================
1590 # patient demographics editing classes
1591 #============================================================
1593 """Notebook displaying demographics editing pages:
1594
1595 - Identity (as per Jim/Rogerio 12/2011)
1596 - Contacts (addresses, phone numbers, etc)
1597 - Social network (significant others, GP, etc)
1598
1599 Does NOT act on/listen to the current patient.
1600 """
1601 #--------------------------------------------------------
1603
1604 wx.Notebook.__init__ (
1605 self,
1606 parent = parent,
1607 id = id,
1608 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
1609 name = self.__class__.__name__
1610 )
1611 _log.debug('created wx.Notebook: %s with ID %s', self.__class__.__name__, self.Id)
1612
1613 self.__identity = None
1614 self.__do_layout()
1615 self.SetSelection(0)
1616 #--------------------------------------------------------
1617 # public API
1618 #--------------------------------------------------------
1620 """Populate fields in pages with data from model."""
1621 for page_idx in range(self.GetPageCount()):
1622 page = self.GetPage(page_idx)
1623 page.identity = self.__identity
1624
1625 return True
1626 #--------------------------------------------------------
1627 # internal API
1628 #--------------------------------------------------------
1630 """Build patient edition notebook pages."""
1631
1632 # identity page
1633 new_page = cPersonIdentityManagerPnl(self, -1)
1634 new_page.identity = self.__identity
1635 self.AddPage (
1636 page = new_page,
1637 text = _('Identity'),
1638 select = False
1639 )
1640
1641 # contacts page
1642 new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1)
1643 new_page.identity = self.__identity
1644 self.AddPage (
1645 page = new_page,
1646 text = _('Contacts'),
1647 select = True
1648 )
1649
1650 # social network page
1651 new_page = cPersonSocialNetworkManagerPnl(self, -1)
1652 new_page.identity = self.__identity
1653 self.AddPage (
1654 page = new_page,
1655 text = _('Social network'),
1656 select = False
1657 )
1658 #--------------------------------------------------------
1659 # properties
1660 #--------------------------------------------------------
1663
1665 self.__identity = identity
1666
1667 identity = property(_get_identity, _set_identity)
1668
1669 #============================================================
1670 # old occupation widgets
1671 #============================================================
1672 # FIXME: support multiple occupations
1673 # FIXME: redo with wxGlade
1674
1676 """Page containing patient occupations edition fields.
1677 """
1679 """
1680 Creates a new instance of BasicPatDetailsPage
1681 @param parent - The parent widget
1682 @type parent - A wx.Window instance
1683 @param id - The widget id
1684 @type id - An integer
1685 """
1686 wx.Panel.__init__(self, parent, id)
1687 self.__ident = ident
1688 self.__do_layout()
1689 #--------------------------------------------------------
1691 PNL_form = wx.Panel(self, -1)
1692 # occupation
1693 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
1694 self.PRW_occupation = cOccupationPhraseWheel(PNL_form, -1)
1695 self.PRW_occupation.SetToolTip(_("primary occupation of the patient"))
1696 # known since
1697 STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated'))
1698 self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY)
1699
1700 # layout input widgets
1701 SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4)
1702 SZR_input.AddGrowableCol(1)
1703 SZR_input.Add(STT_occupation, 0, wx.SHAPED)
1704 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
1705 SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED)
1706 SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND)
1707 PNL_form.SetSizerAndFit(SZR_input)
1708
1709 # layout page
1710 SZR_main = wx.BoxSizer(wx.VERTICAL)
1711 SZR_main.Add(PNL_form, 1, wx.EXPAND)
1712 self.SetSizer(SZR_main)
1713 #--------------------------------------------------------
1716 #--------------------------------------------------------
1718 if identity is not None:
1719 self.__ident = identity
1720 jobs = self.__ident.get_occupations()
1721 if len(jobs) > 0:
1722 self.PRW_occupation.SetText(jobs[0]['l10n_occupation'])
1723 self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y'))
1724 return True
1725 #--------------------------------------------------------
1727 if self.PRW_occupation.IsModified():
1728 new_job = self.PRW_occupation.GetValue().strip()
1729 jobs = self.__ident.get_occupations()
1730 for job in jobs:
1731 if job['l10n_occupation'] == new_job:
1732 continue
1733 self.__ident.unlink_occupation(occupation = job['l10n_occupation'])
1734 self.__ident.link_occupation(occupation = new_job)
1735 return True
1736
1737 #============================================================
1739 """Patient demographics plugin for main notebook.
1740
1741 Hosts another notebook with pages for Identity, Contacts, etc.
1742
1743 Acts on/listens to the currently active patient.
1744 """
1745 #--------------------------------------------------------
1747 wx.Panel.__init__ (self, parent = parent, id = id, style = wx.NO_BORDER)
1748 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1749 self.__do_layout()
1750 self.__register_interests()
1751 #--------------------------------------------------------
1752 # public API
1753 #--------------------------------------------------------
1754 #--------------------------------------------------------
1755 # internal helpers
1756 #--------------------------------------------------------
1758 """Arrange widgets."""
1759 self.__patient_notebook = cPersonDemographicsEditorNb(self, -1)
1760
1761 szr_main = wx.BoxSizer(wx.VERTICAL)
1762 szr_main.Add(self.__patient_notebook, 1, wx.EXPAND)
1763 self.SetSizerAndFit(szr_main)
1764 #--------------------------------------------------------
1765 # event handling
1766 #--------------------------------------------------------
1768 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
1769 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
1770 #--------------------------------------------------------
1774 #--------------------------------------------------------
1777 #--------------------------------------------------------
1778 # reget mixin API
1779 #--------------------------------------------------------
1789
1790 #============================================================
1791 #============================================================
1792 if __name__ == "__main__":
1793
1794 #--------------------------------------------------------
1796 app = wx.PyWidgetTester(size = (600, 400))
1797 app.SetWidget(cKOrganizerSchedulePnl)
1798 app.MainLoop()
1799 #--------------------------------------------------------
1801 app = wx.PyWidgetTester(size = (600, 400))
1802 widget = cPersonNamesManagerPnl(app.frame, -1)
1803 widget.identity = activate_patient()
1804 app.frame.Show(True)
1805 app.MainLoop()
1806 #--------------------------------------------------------
1808 app = wx.PyWidgetTester(size = (600, 400))
1809 widget = cPersonIDsManagerPnl(app.frame, -1)
1810 widget.identity = activate_patient()
1811 app.frame.Show(True)
1812 app.MainLoop()
1813 #--------------------------------------------------------
1815 app = wx.PyWidgetTester(size = (600, 400))
1816 widget = cPersonIdentityManagerPnl(app.frame, -1)
1817 widget.identity = activate_patient()
1818 app.frame.Show(True)
1819 app.MainLoop()
1820 #--------------------------------------------------------
1822 app = wx.PyWidgetTester(size = (600, 400))
1823 app.SetWidget(cPersonNameEAPnl, name = activate_patient().get_active_name())
1824 app.MainLoop()
1825 #--------------------------------------------------------
1827 app = wx.PyWidgetTester(size = (600, 400))
1828 widget = cPersonDemographicsEditorNb(app.frame, -1)
1829 widget.identity = activate_patient()
1830 widget.refresh()
1831 app.frame.Show(True)
1832 app.MainLoop()
1833 #--------------------------------------------------------
1835 patient = gmPersonSearch.ask_for_patient()
1836 if patient is None:
1837 print("No patient. Exiting gracefully...")
1838 sys.exit(0)
1839 from Gnumed.wxpython.gmPatSearchWidgets import set_active_patient
1840 set_active_patient(patient = patient)
1841 return patient
1842 #--------------------------------------------------------
1843 if len(sys.argv) > 1 and sys.argv[1] == 'test':
1844
1845 gmI18N.activate_locale()
1846 gmI18N.install_domain(domain='gnumed')
1847 gmPG2.get_connection()
1848
1849 # app = wx.PyWidgetTester(size = (400, 300))
1850 # app.SetWidget(cNotebookedPatEditionPanel, -1)
1851 # app.frame.Show(True)
1852 # app.MainLoop()
1853
1854 # phrasewheels
1855 # test_organizer_pnl()
1856
1857 # identity related widgets
1858 #test_person_names_pnl()
1859 test_person_ids_pnl()
1860 #test_pat_ids_pnl()
1861 #test_name_ea_pnl()
1862
1863 #test_cPersonDemographicsEditorNb()
1864
1865 #============================================================
1866
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sun Nov 10 02:55:34 2019 | http://epydoc.sourceforge.net |