| 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
885 #------------------------------------------------------------
887
888 # valid timestamp ?
889 if dob_prw.is_valid_timestamp(empty_is_valid = False): # properly colors the field
890 dob = dob_prw.date
891 # but year also usable ?
892 #if (dob.year > 1899) and (dob < gmDateTime.pydt_now_here()):
893 if (dob.year > pydt.MINYEAR) and (dob < gmDateTime.pydt_now_here()):
894 return True
895
896 if dob.year < 1800:
897 msg = _(
898 'DOB: %s\n'
899 '\n'
900 'While this is a valid point in time Python\n'
901 'may not know how to deal with it.\n'
902 '\n'
903 'We suggest using January 1st 1801 instead and adding\n'
904 'the true date of birth to the patient comment.\n'
905 '\n'
906 'Sorry for the inconvenience %s'
907 ) % (dob, gmTools.u_frowning_face)
908 else:
909 msg = _(
910 'DOB: %s\n'
911 '\n'
912 'Date of birth in the future !'
913 ) % dob
914 gmGuiHelpers.gm_show_error (
915 msg,
916 _('Validating date of birth')
917 )
918 dob_prw.display_as_valid(False)
919 dob_prw.SetFocus()
920 return False
921
922 # invalid timestamp but not empty
923 if dob_prw.GetValue().strip() != '':
924 dob_prw.display_as_valid(False)
925 gmDispatcher.send(signal = 'statustext', msg = _('Invalid date of birth.'))
926 dob_prw.SetFocus()
927 return False
928
929 # empty DOB field
930 dob_prw.display_as_valid(False)
931 return True
932
933 #------------------------------------------------------------
935
936 val = ctrl.GetValue().strip()
937
938 if val == '':
939 return True
940
941 converted, hours = gmTools.input2int(val[:2], 0, 23)
942 if not converted:
943 return False
944
945 converted, minutes = gmTools.input2int(val[3:5], 0, 59)
946 if not converted:
947 return False
948
949 return True
950
951 #------------------------------------------------------------
952 from Gnumed.wxGladeWidgets import wxgIdentityEAPnl
953
955 """An edit area for editing/creating title/gender/dob/dod etc."""
956
958
959 try:
960 data = kwargs['identity']
961 del kwargs['identity']
962 except KeyError:
963 data = None
964
965 wxgIdentityEAPnl.wxgIdentityEAPnl.__init__(self, *args, **kwargs)
966 gmEditArea.cGenericEditAreaMixin.__init__(self)
967
968 self.mode = 'new'
969 self.data = data
970 if data is not None:
971 self.mode = 'edit'
972
973 # self.__init_ui()
974 #----------------------------------------------------------------
975 # def __init_ui(self):
976 # # adjust phrasewheels etc
977 #----------------------------------------------------------------
978 # generic Edit Area mixin API
979 #----------------------------------------------------------------
981
982 has_error = False
983
984 if self._PRW_gender.GetData() is None:
985 self._PRW_gender.SetFocus()
986 has_error = True
987
988 if self.data is not None:
989 if not _validate_dob_field(self._PRW_dob):
990 has_error = True
991
992 # TOB validation
993 if _validate_tob_field(self._TCTRL_tob):
994 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True)
995 else:
996 has_error = True
997 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False)
998
999 if not self._PRW_dod.is_valid_timestamp(empty_is_valid = True):
1000 self.StatusText = _('Invalid date of death.')
1001 self._PRW_dod.SetFocus()
1002 has_error = True
1003
1004 return (has_error is False)
1005 #----------------------------------------------------------------
1009 #----------------------------------------------------------------
1011
1012 if self._PRW_dob.GetValue().strip() == '':
1013 if not _empty_dob_allowed():
1014 return False
1015 self.data['dob'] = None
1016 else:
1017 self.data['dob'] = self._PRW_dob.GetData()
1018 self.data['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
1019 val = self._TCTRL_tob.GetValue().strip()
1020 if val == '':
1021 self.data['tob'] = None
1022 else:
1023 self.data['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
1024 self.data['gender'] = self._PRW_gender.GetData()
1025 self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), '')
1026 self.data['deceased'] = self._PRW_dod.GetData()
1027 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1028
1029 self.data.save()
1030 return True
1031 #----------------------------------------------------------------
1034 #----------------------------------------------------------------
1036
1037 self._LBL_info.SetLabel('ID: #%s' % (
1038 self.data.ID
1039 # FIXME: add 'deleted' status
1040 ))
1041 if self.data['dob'] is None:
1042 val = ''
1043 else:
1044 val = gmDateTime.pydt_strftime (
1045 self.data['dob'],
1046 format = '%Y-%m-%d',
1047 accuracy = gmDateTime.acc_minutes
1048 )
1049 self._PRW_dob.SetText(value = val, data = self.data['dob'])
1050 self._CHBOX_estimated_dob.SetValue(self.data['dob_is_estimated'])
1051 if self.data['tob'] is None:
1052 self._TCTRL_tob.SetValue('')
1053 else:
1054 self._TCTRL_tob.SetValue(self.data['tob'].strftime('%H:%M'))
1055 if self.data['deceased'] is None:
1056 val = ''
1057 else:
1058 val = gmDateTime.pydt_strftime (
1059 self.data['deceased'],
1060 format = '%Y-%m-%d %H:%M',
1061 accuracy = gmDateTime.acc_minutes
1062 )
1063 self._PRW_dod.SetText(value = val, data = self.data['deceased'])
1064 self._PRW_gender.SetData(self.data['gender'])
1065 #self._PRW_ethnicity.SetValue()
1066 self._PRW_title.SetText(gmTools.coalesce(self.data['title'], ''))
1067 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
1068 #----------------------------------------------------------------
1071 #------------------------------------------------------------
1072 from Gnumed.wxGladeWidgets import wxgPersonNameEAPnl
1073
1074 -class cPersonNameEAPnl(wxgPersonNameEAPnl.wxgPersonNameEAPnl, gmEditArea.cGenericEditAreaMixin):
1075 """An edit area for editing/creating names of people.
1076
1077 Does NOT act on/listen to the current patient.
1078 """
1080
1081 try:
1082 data = kwargs['name']
1083 identity = gmPerson.cPerson(aPK_obj = data['pk_identity'])
1084 del kwargs['name']
1085 except KeyError:
1086 data = None
1087 identity = kwargs['identity']
1088 del kwargs['identity']
1089
1090 wxgPersonNameEAPnl.wxgPersonNameEAPnl.__init__(self, *args, **kwargs)
1091 gmEditArea.cGenericEditAreaMixin.__init__(self)
1092
1093 self.__identity = identity
1094
1095 self.mode = 'new'
1096 self.data = data
1097 if data is not None:
1098 self.mode = 'edit'
1099
1100 #self.__init_ui()
1101 #----------------------------------------------------------------
1102 # def __init_ui(self):
1103 # # adjust phrasewheels etc
1104 #----------------------------------------------------------------
1105 # generic Edit Area mixin API
1106 #----------------------------------------------------------------
1108 validity = True
1109
1110 if self._PRW_lastname.GetValue().strip() == '':
1111 validity = False
1112 self._PRW_lastname.display_as_valid(False)
1113 self._PRW_lastname.SetFocus()
1114 else:
1115 self._PRW_lastname.display_as_valid(True)
1116
1117 if self._PRW_firstname.GetValue().strip() == '':
1118 validity = False
1119 self._PRW_firstname.display_as_valid(False)
1120 self._PRW_firstname.SetFocus()
1121 else:
1122 self._PRW_firstname.display_as_valid(True)
1123
1124 return validity
1125 #----------------------------------------------------------------
1127
1128 first = self._PRW_firstname.GetValue().strip()
1129 last = self._PRW_lastname.GetValue().strip()
1130 active = self._CHBOX_active.GetValue()
1131
1132 try:
1133 data = self.__identity.add_name(first, last, active)
1134 except gmPG2.dbapi.IntegrityError as exc:
1135 _log.exception('cannot save new name')
1136 gmGuiHelpers.gm_show_error (
1137 aTitle = _('Adding name'),
1138 aMessage = _(
1139 'Cannot add this name to the patient !\n'
1140 '\n'
1141 ' %s'
1142 ) % exc.pgerror
1143 )
1144 return False
1145
1146 old_nick = self.__identity['active_name']['preferred']
1147 new_nick = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
1148 if active:
1149 data['preferred'] = gmTools.coalesce(new_nick, old_nick)
1150 else:
1151 data['preferred'] = new_nick
1152 data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1153 data.save()
1154
1155 self.data = data
1156 return True
1157 #----------------------------------------------------------------
1159 """The knack here is that we can only update a few fields.
1160
1161 Otherwise we need to clone the name and update that.
1162 """
1163 first = self._PRW_firstname.GetValue().strip()
1164 last = self._PRW_lastname.GetValue().strip()
1165 active = self._CHBOX_active.GetValue()
1166
1167 current_name = self.data['firstnames'].strip() + self.data['lastnames'].strip()
1168 new_name = first + last
1169
1170 # editable fields only ?
1171 if new_name == current_name:
1172 self.data['active_name'] = self._CHBOX_active.GetValue()
1173 self.data['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
1174 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1175 self.data.save()
1176 # else clone name and update that
1177 else:
1178 try:
1179 name = self.__identity.add_name(first, last, active)
1180 except gmPG2.dbapi.IntegrityError as exc:
1181 _log.exception('cannot clone name when editing existing name')
1182 gmGuiHelpers.gm_show_error (
1183 aTitle = _('Editing name'),
1184 aMessage = _(
1185 'Cannot clone a copy of this name !\n'
1186 '\n'
1187 ' %s'
1188 ) % exc.pgerror
1189 )
1190 return False
1191 name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
1192 name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1193 name.save()
1194 self.data = name
1195
1196 return True
1197 #----------------------------------------------------------------
1199 self._PRW_firstname.SetText(value = '', data = None)
1200 self._PRW_lastname.SetText(value = '', data = None)
1201 self._PRW_nick.SetText(value = '', data = None)
1202 self._TCTRL_comment.SetValue('')
1203 self._CHBOX_active.SetValue(False)
1204
1205 self._PRW_firstname.SetFocus()
1206 #----------------------------------------------------------------
1208 self._refresh_as_new()
1209 self._PRW_firstname.SetText(value = '', data = None)
1210 self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], ''))
1211
1212 self._PRW_lastname.SetFocus()
1213 #----------------------------------------------------------------
1215 self._PRW_firstname.SetText(self.data['firstnames'])
1216 self._PRW_lastname.SetText(self.data['lastnames'])
1217 self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], ''))
1218 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
1219 self._CHBOX_active.SetValue(self.data['active_name'])
1220
1221 self._TCTRL_comment.SetFocus()
1222 #------------------------------------------------------------
1223 # list manager
1224 #------------------------------------------------------------
1226 """A list for managing a person's names.
1227
1228 Does NOT act on/listen to the current patient.
1229 """
1231
1232 try:
1233 self.__identity = kwargs['identity']
1234 del kwargs['identity']
1235 except KeyError:
1236 self.__identity = None
1237
1238 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1239
1240 self.refresh_callback = self.refresh
1241 self.new_callback = self._add_name
1242 self.edit_callback = self._edit_name
1243 self.delete_callback = self._del_name
1244
1245 self.__init_ui()
1246 self.refresh()
1247 #--------------------------------------------------------
1248 # external API
1249 #--------------------------------------------------------
1251 if self.__identity is None:
1252 self._LCTRL_items.set_string_items()
1253 return
1254
1255 names = self.__identity.get_names()
1256 self._LCTRL_items.set_string_items (
1257 items = [ [
1258 gmTools.bool2str(n['active_name'], 'X', ''),
1259 n['lastnames'],
1260 n['firstnames'],
1261 gmTools.coalesce(n['preferred'], ''),
1262 gmTools.coalesce(n['comment'], '')
1263 ] for n in names ]
1264 )
1265 self._LCTRL_items.set_column_widths()
1266 self._LCTRL_items.set_data(data = names)
1267 #--------------------------------------------------------
1268 # internal helpers
1269 #--------------------------------------------------------
1271 self._LCTRL_items.set_columns(columns = [
1272 _('Active'),
1273 _('Lastname'),
1274 _('Firstname(s)'),
1275 _('Preferred Name'),
1276 _('Comment')
1277 ])
1278 #--------------------------------------------------------
1280 #ea = cPersonNameEAPnl(self, -1, name = self.__identity.get_active_name())
1281 ea = cPersonNameEAPnl(self, -1, identity = self.__identity)
1282 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1283 dlg.SetTitle(_('Adding new name'))
1284 if dlg.ShowModal() == wx.ID_OK:
1285 dlg.DestroyLater()
1286 return True
1287 dlg.DestroyLater()
1288 return False
1289 #--------------------------------------------------------
1291 ea = cPersonNameEAPnl(self, -1, name = name)
1292 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1293 dlg.SetTitle(_('Editing name'))
1294 if dlg.ShowModal() == wx.ID_OK:
1295 dlg.DestroyLater()
1296 return True
1297 dlg.DestroyLater()
1298 return False
1299 #--------------------------------------------------------
1301
1302 if len(self.__identity.get_names()) == 1:
1303 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the only name of a person.'), beep = True)
1304 return False
1305
1306 if name['active_name']:
1307 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the active name of a person.'), beep = True)
1308 return False
1309
1310 go_ahead = gmGuiHelpers.gm_show_question (
1311 _( 'It is often advisable to keep old names around and\n'
1312 'just create a new "currently active" name.\n'
1313 '\n'
1314 'This allows finding the patient by both the old\n'
1315 'and the new name (think before/after marriage).\n'
1316 '\n'
1317 'Do you still want to really delete\n'
1318 "this name from the patient ?"
1319 ),
1320 _('Deleting name')
1321 )
1322 if not go_ahead:
1323 return False
1324
1325 self.__identity.delete_name(name = name)
1326 return True
1327 #--------------------------------------------------------
1328 # properties
1329 #--------------------------------------------------------
1332
1336
1337 identity = property(_get_identity, _set_identity)
1338
1339 #------------------------------------------------------------
1341 """A list for managing a person's external IDs.
1342
1343 Does NOT act on/listen to the current patient.
1344 """
1346
1347 try:
1348 self.__identity = kwargs['identity']
1349 del kwargs['identity']
1350 except KeyError:
1351 self.__identity = None
1352
1353 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1354
1355 self.refresh_callback = self.refresh
1356 self.new_callback = self._add_id
1357 self.edit_callback = self._edit_id
1358 self.delete_callback = self._del_id
1359
1360 self.__init_ui()
1361 self.refresh()
1362 #--------------------------------------------------------
1363 # external API
1364 #--------------------------------------------------------
1366 if self.__identity is None:
1367 self._LCTRL_items.set_string_items()
1368 return
1369
1370 ids = self.__identity.get_external_ids()
1371 self._LCTRL_items.set_string_items (
1372 items = [ [
1373 i['name'],
1374 i['value'],
1375 gmTools.coalesce(i['issuer'], ''),
1376 gmTools.coalesce(i['comment'], '')
1377 ] for i in ids
1378 ]
1379 )
1380 self._LCTRL_items.set_column_widths()
1381 self._LCTRL_items.set_data(data = ids)
1382 #--------------------------------------------------------
1383 # internal helpers
1384 #--------------------------------------------------------
1386 self._LCTRL_items.set_columns(columns = [
1387 _('ID type'),
1388 _('Value'),
1389 _('Issuer'),
1390 _('Comment')
1391 ])
1392 #--------------------------------------------------------
1394 ea = cExternalIDEditAreaPnl(self, -1)
1395 ea.id_holder = self.__identity
1396 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea)
1397 dlg.SetTitle(_('Adding new external ID'))
1398 if dlg.ShowModal() == wx.ID_OK:
1399 dlg.DestroyLater()
1400 return True
1401 dlg.DestroyLater()
1402 return False
1403 #--------------------------------------------------------
1405 ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id)
1406 ea.id_holder = self.__identity
1407 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1408 dlg.SetTitle(_('Editing external ID'))
1409 if dlg.ShowModal() == wx.ID_OK:
1410 dlg.DestroyLater()
1411 return True
1412 dlg.DestroyLater()
1413 return False
1414 #--------------------------------------------------------
1416 go_ahead = gmGuiHelpers.gm_show_question (
1417 _( 'Do you really want to delete this\n'
1418 'external ID from the patient ?'),
1419 _('Deleting external ID')
1420 )
1421 if not go_ahead:
1422 return False
1423 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id'])
1424 return True
1425 #--------------------------------------------------------
1426 # properties
1427 #--------------------------------------------------------
1430
1434
1435 identity = property(_get_identity, _set_identity)
1436 #------------------------------------------------------------
1437 # integrated panels
1438 #------------------------------------------------------------
1439 from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl
1440
1442 """A panel for editing identity data for a person.
1443
1444 - provides access to:
1445 - identity EA
1446 - name list manager
1447 - external IDs list manager
1448
1449 Does NOT act on/listen to the current patient.
1450 """
1452
1453 wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs)
1454
1455 self.__identity = None
1456 self.refresh()
1457 #--------------------------------------------------------
1458 # external API
1459 #--------------------------------------------------------
1461 self._PNL_names.identity = self.__identity
1462 self._PNL_ids.identity = self.__identity
1463 # this is an Edit Area:
1464 self._PNL_identity.mode = 'new'
1465 self._PNL_identity.data = self.__identity
1466 if self.__identity is not None:
1467 self._PNL_identity.mode = 'edit'
1468 self._PNL_identity._refresh_from_existing()
1469 #--------------------------------------------------------
1470 # properties
1471 #--------------------------------------------------------
1474
1478
1479 identity = property(_get_identity, _set_identity)
1480 #--------------------------------------------------------
1481 # event handlers
1482 #--------------------------------------------------------
1486 #self._PNL_identity.refresh()
1487 #--------------------------------------------------------
1490
1491 #============================================================
1492 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl
1493
1494 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
1496
1497 wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs)
1498
1499 self.__identity = None
1500 self._PRW_provider.selection_only = False
1501 self.refresh()
1502 #--------------------------------------------------------
1503 # external API
1504 #--------------------------------------------------------
1506
1507 tt = _('Link another person in this database as the emergency contact:\n\nEnter person name part or identifier and hit <enter>.')
1508
1509 if self.__identity is None:
1510 self._TCTRL_er_contact.SetValue('')
1511 self._TCTRL_person.person = None
1512 self._TCTRL_person.SetToolTip(tt)
1513
1514 self._PRW_provider.SetText(value = '', data = None)
1515 return
1516
1517 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], ''))
1518 if self.__identity['pk_emergency_contact'] is not None:
1519 ident = gmPerson.cPerson(aPK_obj = self.__identity['pk_emergency_contact'])
1520 self._TCTRL_person.person = ident
1521 tt = '%s\n\n%s\n\n%s' % (
1522 tt,
1523 ident['description_gender'],
1524 '\n'.join([
1525 '%s: %s%s' % (
1526 c['l10n_comm_type'],
1527 c['url'],
1528 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), '', '')
1529 )
1530 for c in ident.get_comm_channels()
1531 ])
1532 )
1533 else:
1534 self._TCTRL_person.person = None
1535
1536 self._TCTRL_person.SetToolTip(tt)
1537
1538 if self.__identity['pk_primary_provider'] is None:
1539 self._PRW_provider.SetText(value = '', data = None)
1540 else:
1541 self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
1542
1543 self._PNL_external_care.identity = self.__identity
1544 #--------------------------------------------------------
1545 # properties
1546 #--------------------------------------------------------
1549
1553
1554 identity = property(_get_identity, _set_identity)
1555 #--------------------------------------------------------
1556 # event handlers
1557 #--------------------------------------------------------
1572 #--------------------------------------------------------
1575 #--------------------------------------------------------
1586 #--------------------------------------------------------
1594
1595 #============================================================
1596 # patient demographics editing classes
1597 #============================================================
1599 """Notebook displaying demographics editing pages:
1600
1601 - Identity (as per Jim/Rogerio 12/2011)
1602 - Contacts (addresses, phone numbers, etc)
1603 - Social network (significant others, GP, etc)
1604
1605 Does NOT act on/listen to the current patient.
1606 """
1607 #--------------------------------------------------------
1609
1610 wx.Notebook.__init__ (
1611 self,
1612 parent = parent,
1613 id = id,
1614 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
1615 name = self.__class__.__name__
1616 )
1617 _log.debug('created wx.Notebook: %s with ID %s', self.__class__.__name__, self.Id)
1618
1619 self.__identity = None
1620 self.__do_layout()
1621 self.SetSelection(0)
1622 #--------------------------------------------------------
1623 # public API
1624 #--------------------------------------------------------
1626 """Populate fields in pages with data from model."""
1627 for page_idx in range(self.GetPageCount()):
1628 page = self.GetPage(page_idx)
1629 page.identity = self.__identity
1630
1631 return True
1632 #--------------------------------------------------------
1633 # internal API
1634 #--------------------------------------------------------
1636 """Build patient edition notebook pages."""
1637
1638 # identity page
1639 new_page = cPersonIdentityManagerPnl(self, -1)
1640 new_page.identity = self.__identity
1641 self.AddPage (
1642 page = new_page,
1643 text = _('Identity'),
1644 select = False
1645 )
1646
1647 # contacts page
1648 new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1)
1649 new_page.identity = self.__identity
1650 self.AddPage (
1651 page = new_page,
1652 text = _('Contacts'),
1653 select = True
1654 )
1655
1656 # social network page
1657 new_page = cPersonSocialNetworkManagerPnl(self, -1)
1658 new_page.identity = self.__identity
1659 self.AddPage (
1660 page = new_page,
1661 text = _('Social network'),
1662 select = False
1663 )
1664 #--------------------------------------------------------
1665 # properties
1666 #--------------------------------------------------------
1669
1671 self.__identity = identity
1672
1673 identity = property(_get_identity, _set_identity)
1674
1675 #============================================================
1676 # old occupation widgets
1677 #============================================================
1678 # FIXME: support multiple occupations
1679 # FIXME: redo with wxGlade
1680
1682 """Page containing patient occupations edition fields.
1683 """
1685 """
1686 Creates a new instance of BasicPatDetailsPage
1687 @param parent - The parent widget
1688 @type parent - A wx.Window instance
1689 @param id - The widget id
1690 @type id - An integer
1691 """
1692 wx.Panel.__init__(self, parent, id)
1693 self.__ident = ident
1694 self.__do_layout()
1695 #--------------------------------------------------------
1697 PNL_form = wx.Panel(self, -1)
1698 # occupation
1699 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
1700 self.PRW_occupation = cOccupationPhraseWheel(PNL_form, -1)
1701 self.PRW_occupation.SetToolTip(_("primary occupation of the patient"))
1702 # known since
1703 STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated'))
1704 self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY)
1705
1706 # layout input widgets
1707 SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4)
1708 SZR_input.AddGrowableCol(1)
1709 SZR_input.Add(STT_occupation, 0, wx.SHAPED)
1710 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
1711 SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED)
1712 SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND)
1713 PNL_form.SetSizerAndFit(SZR_input)
1714
1715 # layout page
1716 SZR_main = wx.BoxSizer(wx.VERTICAL)
1717 SZR_main.Add(PNL_form, 1, wx.EXPAND)
1718 self.SetSizer(SZR_main)
1719 #--------------------------------------------------------
1722 #--------------------------------------------------------
1724 if identity is not None:
1725 self.__ident = identity
1726 jobs = self.__ident.get_occupations()
1727 if len(jobs) > 0:
1728 self.PRW_occupation.SetText(jobs[0]['l10n_occupation'])
1729 self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y'))
1730 return True
1731 #--------------------------------------------------------
1733 if self.PRW_occupation.IsModified():
1734 new_job = self.PRW_occupation.GetValue().strip()
1735 jobs = self.__ident.get_occupations()
1736 for job in jobs:
1737 if job['l10n_occupation'] == new_job:
1738 continue
1739 self.__ident.unlink_occupation(occupation = job['l10n_occupation'])
1740 self.__ident.link_occupation(occupation = new_job)
1741 return True
1742
1743 #============================================================
1745 """Patient demographics plugin for main notebook.
1746
1747 Hosts another notebook with pages for Identity, Contacts, etc.
1748
1749 Acts on/listens to the currently active patient.
1750 """
1751 #--------------------------------------------------------
1753 wx.Panel.__init__ (self, parent = parent, id = id, style = wx.NO_BORDER)
1754 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1755 self.__do_layout()
1756 self.__register_interests()
1757 #--------------------------------------------------------
1758 # public API
1759 #--------------------------------------------------------
1760 #--------------------------------------------------------
1761 # internal helpers
1762 #--------------------------------------------------------
1764 """Arrange widgets."""
1765 self.__patient_notebook = cPersonDemographicsEditorNb(self, -1)
1766
1767 szr_main = wx.BoxSizer(wx.VERTICAL)
1768 szr_main.Add(self.__patient_notebook, 1, wx.EXPAND)
1769 self.SetSizerAndFit(szr_main)
1770 #--------------------------------------------------------
1771 # event handling
1772 #--------------------------------------------------------
1774 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
1775 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
1776 #--------------------------------------------------------
1780 #--------------------------------------------------------
1783 #--------------------------------------------------------
1784 # reget mixin API
1785 #--------------------------------------------------------
1795
1796 #============================================================
1797 #============================================================
1798 if __name__ == "__main__":
1799
1800 #--------------------------------------------------------
1802 app = wx.PyWidgetTester(size = (600, 400))
1803 app.SetWidget(cKOrganizerSchedulePnl)
1804 app.MainLoop()
1805 #--------------------------------------------------------
1807 app = wx.PyWidgetTester(size = (600, 400))
1808 widget = cPersonNamesManagerPnl(app.frame, -1)
1809 widget.identity = activate_patient()
1810 app.frame.Show(True)
1811 app.MainLoop()
1812 #--------------------------------------------------------
1814 app = wx.PyWidgetTester(size = (600, 400))
1815 widget = cPersonIDsManagerPnl(app.frame, -1)
1816 widget.identity = activate_patient()
1817 app.frame.Show(True)
1818 app.MainLoop()
1819 #--------------------------------------------------------
1821 app = wx.PyWidgetTester(size = (600, 400))
1822 widget = cPersonIdentityManagerPnl(app.frame, -1)
1823 widget.identity = activate_patient()
1824 app.frame.Show(True)
1825 app.MainLoop()
1826 #--------------------------------------------------------
1828 app = wx.PyWidgetTester(size = (600, 400))
1829 app.SetWidget(cPersonNameEAPnl, name = activate_patient().get_active_name())
1830 app.MainLoop()
1831 #--------------------------------------------------------
1833 app = wx.PyWidgetTester(size = (600, 400))
1834 widget = cPersonDemographicsEditorNb(app.frame, -1)
1835 widget.identity = activate_patient()
1836 widget.refresh()
1837 app.frame.Show(True)
1838 app.MainLoop()
1839 #--------------------------------------------------------
1841 patient = gmPersonSearch.ask_for_patient()
1842 if patient is None:
1843 print("No patient. Exiting gracefully...")
1844 sys.exit(0)
1845 from Gnumed.wxpython.gmPatSearchWidgets import set_active_patient
1846 set_active_patient(patient = patient)
1847 return patient
1848 #--------------------------------------------------------
1849 if len(sys.argv) > 1 and sys.argv[1] == 'test':
1850
1851 gmI18N.activate_locale()
1852 gmI18N.install_domain(domain='gnumed')
1853 gmPG2.get_connection()
1854
1855 # app = wx.PyWidgetTester(size = (400, 300))
1856 # app.SetWidget(cNotebookedPatEditionPanel, -1)
1857 # app.frame.Show(True)
1858 # app.MainLoop()
1859
1860 # phrasewheels
1861 # test_organizer_pnl()
1862
1863 # identity related widgets
1864 #test_person_names_pnl()
1865 test_person_ids_pnl()
1866 #test_pat_ids_pnl()
1867 #test_name_ea_pnl()
1868
1869 #test_cPersonDemographicsEditorNb()
1870
1871 #============================================================
1872
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Jul 23 01:55:31 2020 | http://epydoc.sourceforge.net |