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