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