| 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 try:
55 _('dummy-no-need-to-translate-but-make-epydoc-happy')
56 except NameError:
57 _ = lambda x:x
58
59 #============================================================
60 # image tags related widgets
61 #------------------------------------------------------------
63 if tag_image is not None:
64 if tag_image['is_in_use']:
65 gmGuiHelpers.gm_show_info (
66 aTitle = _('Editing tag'),
67 aMessage = _(
68 'Cannot edit the image tag\n'
69 '\n'
70 ' "%s"\n'
71 '\n'
72 'because it is currently in use.\n'
73 ) % tag_image['l10n_description']
74 )
75 return False
76
77 ea = cTagImageEAPnl(parent, -1)
78 ea.data = tag_image
79 ea.mode = gmTools.coalesce(tag_image, 'new', 'edit')
80 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
81 dlg.SetTitle(gmTools.coalesce(tag_image, _('Adding new tag'), _('Editing tag')))
82 if dlg.ShowModal() == wx.ID_OK:
83 dlg.Destroy()
84 return True
85 dlg.Destroy()
86 return False
87 #------------------------------------------------------------
89
90 if parent is None:
91 parent = wx.GetApp().GetTopWindow()
92
93 #------------------------------------------------------------
94 def go_to_openclipart_org(tag_image):
95 gmNetworkTools.open_url_in_browser(url = 'http://www.openclipart.org')
96 gmNetworkTools.open_url_in_browser(url = 'http://commons.wikimedia.org/wiki/Category:Symbols_of_disabilities')
97 gmNetworkTools.open_url_in_browser(url = 'http://www.duckduckgo.com')
98 gmNetworkTools.open_url_in_browser(url = 'http://images.google.com')
99 return True
100
101 #------------------------------------------------------------
102 def edit(tag_image=None):
103 return edit_tag_image(parent = parent, tag_image = tag_image, single_entry = (tag_image is not None))
104
105 #------------------------------------------------------------
106 def delete(tag):
107 if tag['is_in_use']:
108 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this tag. It is in use.'), beep = True)
109 return False
110
111 return gmDemographicRecord.delete_tag_image(tag_image = tag['pk_tag_image'])
112
113 #------------------------------------------------------------
114 def refresh(lctrl):
115 tags = gmDemographicRecord.get_tag_images(order_by = 'l10n_description')
116 items = [ [
117 t['l10n_description'],
118 gmTools.bool2subst(t['is_in_use'], 'X', ''),
119 '%s' % t['size'],
120 t['pk_tag_image']
121 ] for t in tags ]
122 lctrl.set_string_items(items)
123 lctrl.set_column_widths(widths = [wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE])
124 lctrl.set_data(tags)
125
126 #------------------------------------------------------------
127 msg = _('\nTags with images registered with GNUmed.\n')
128
129 tag = gmListWidgets.get_choices_from_list (
130 parent = parent,
131 msg = msg,
132 caption = _('Showing tags with images.'),
133 columns = [_('Tag name'), _('In use'), _('Image size'), '#'],
134 single_selection = True,
135 new_callback = edit,
136 edit_callback = edit,
137 delete_callback = delete,
138 refresh_callback = refresh,
139 left_extra_button = (_('WWW'), _('Go to www.openclipart.org for images.'), go_to_openclipart_org)
140 )
141
142 return tag
143 #------------------------------------------------------------
144 from Gnumed.wxGladeWidgets import wxgTagImageEAPnl
145
147
149
150 try:
151 data = kwargs['tag_image']
152 del kwargs['tag_image']
153 except KeyError:
154 data = None
155
156 wxgTagImageEAPnl.wxgTagImageEAPnl.__init__(self, *args, **kwargs)
157 gmEditArea.cGenericEditAreaMixin.__init__(self)
158
159 self.mode = 'new'
160 self.data = data
161 if data is not None:
162 self.mode = 'edit'
163
164 self.__selected_image_file = None
165 #----------------------------------------------------------------
166 # generic Edit Area mixin API
167 #----------------------------------------------------------------
169
170 valid = True
171
172 if self.mode == 'new':
173 if self.__selected_image_file is None:
174 valid = False
175 gmDispatcher.send(signal = 'statustext', msg = _('Must pick an image file for a new tag.'), beep = True)
176 self._BTN_pick_image.SetFocus()
177
178 if self.__selected_image_file is not None:
179 try:
180 open(self.__selected_image_file).close()
181 except Exception:
182 valid = False
183 self.__selected_image_file = None
184 gmDispatcher.send(signal = 'statustext', msg = _('Cannot open the image file [%s].') % self.__selected_image_file, beep = True)
185 self._BTN_pick_image.SetFocus()
186
187 if self._TCTRL_description.GetValue().strip() == '':
188 valid = False
189 self.display_tctrl_as_valid(self._TCTRL_description, False)
190 self._TCTRL_description.SetFocus()
191 else:
192 self.display_tctrl_as_valid(self._TCTRL_description, True)
193
194 return (valid is True)
195 #----------------------------------------------------------------
197
198 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Creating tag with image'))
199 if dbo_conn is None:
200 return False
201
202 data = gmDemographicRecord.create_tag_image(description = self._TCTRL_description.GetValue().strip(), link_obj = dbo_conn)
203 dbo_conn.close()
204
205 data['filename'] = self._TCTRL_filename.GetValue().strip()
206 data.save()
207 data.update_image_from_file(filename = self.__selected_image_file)
208
209 # must be done very late or else the property access
210 # will refresh the display such that later field
211 # access will return empty values
212 self.data = data
213 return True
214 #----------------------------------------------------------------
216
217 # this is somewhat fake as it never actually uses the gm-dbo conn
218 # (although it does verify it)
219 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Updating tag with image'))
220 if dbo_conn is None:
221 return False
222 dbo_conn.close()
223
224 self.data['description'] = self._TCTRL_description.GetValue().strip()
225 self.data['filename'] = self._TCTRL_filename.GetValue().strip()
226 self.data.save()
227
228 if self.__selected_image_file is not None:
229 open(self.__selected_image_file).close()
230 self.data.update_image_from_file(filename = self.__selected_image_file)
231 self.__selected_image_file = None
232
233 return True
234 #----------------------------------------------------------------
236 self._TCTRL_description.SetValue('')
237 self._TCTRL_filename.SetValue('')
238 self._BMP_image.SetBitmap(bitmap = wx.Bitmap(100, 100))
239
240 self.__selected_image_file = None
241
242 self._TCTRL_description.SetFocus()
243 #----------------------------------------------------------------
246 #----------------------------------------------------------------
248 self._TCTRL_description.SetValue(self.data['l10n_description'])
249 self._TCTRL_filename.SetValue(gmTools.coalesce(self.data['filename'], ''))
250 fname = self.data.export_image2file()
251 if fname is None:
252 self._BMP_image.SetBitmap(bitmap = wx.Bitmap(100, 100))
253 else:
254 self._BMP_image.SetBitmap(bitmap = gmGuiHelpers.file2scaled_image(filename = fname, height = 100))
255
256 self.__selected_image_file = None
257
258 self._TCTRL_description.SetFocus()
259 #----------------------------------------------------------------
260 # event handlers
261 #----------------------------------------------------------------
273
274 #============================================================
289 #--------------------------------------------------------
290 def delete(tag):
291 do_delete = gmGuiHelpers.gm_show_question (
292 title = _('Deleting patient tag'),
293 question = _('Do you really want to delete this patient tag ?')
294 )
295 if not do_delete:
296 return False
297 patient.remove_tag(tag = tag['pk_identity_tag'])
298 return True
299 #--------------------------------------------------------
300 def manage_available_tags(tag):
301 manage_tag_images(parent = parent)
302 return False
303 #--------------------------------------------------------
304 msg = _('Tags of patient: %s\n') % patient['description_gender']
305
306 return gmListWidgets.get_choices_from_list (
307 parent = parent,
308 msg = msg,
309 caption = _('Showing patient tags'),
310 columns = [_('Tag'), _('Comment')],
311 single_selection = False,
312 delete_callback = delete,
313 refresh_callback = refresh,
314 left_extra_button = (_('Manage'), _('Manage available tags.'), manage_available_tags)
315 )
316 #============================================================
317 from Gnumed.wxGladeWidgets import wxgVisualSoapPresenterPnl
318
320
322 wxgVisualSoapPresenterPnl.wxgVisualSoapPresenterPnl.__init__(self, *args, **kwargs)
323 self._SZR_bitmaps = self.GetSizer()
324 self.__bitmaps = []
325
326 self.__context_popup = wx.Menu()
327
328 item = self.__context_popup.Append(-1, _('&Edit comment'))
329 self.Bind(wx.EVT_MENU, self.__edit_tag, item)
330
331 item = self.__context_popup.Append(-1, _('&Remove tag'))
332 self.Bind(wx.EVT_MENU, self.__remove_tag, item)
333 #--------------------------------------------------------
334 # external API
335 #--------------------------------------------------------
337
338 self.clear()
339
340 for tag in patient.get_tags(order_by = 'l10n_description'):
341 fname = tag.export_image2file()
342 if fname is None:
343 _log.warning('cannot export image data of tag [%s]', tag['l10n_description'])
344 continue
345 img = gmGuiHelpers.file2scaled_image(filename = fname, height = 20)
346 bmp = wx_genstatbmp.GenStaticBitmap(self, -1, img, style = wx.NO_BORDER)
347 bmp.SetToolTip('%s%s' % (
348 tag['l10n_description'],
349 gmTools.coalesce(tag['comment'], '', '\n\n%s')
350 ))
351 bmp.tag = tag
352 bmp.Bind(wx.EVT_RIGHT_UP, self._on_bitmap_rightclicked)
353 # FIXME: add context menu for Delete/Clone/Add/Configure
354 self._SZR_bitmaps.Add(bmp, 0, wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, 1) # | wx.EXPAND
355 self.__bitmaps.append(bmp)
356
357 self.GetParent().Layout()
358
359 #--------------------------------------------------------
361 while len(self._SZR_bitmaps.GetChildren()) > 0:
362 self._SZR_bitmaps.Detach(0)
363 # for child_idx in range(len(self._SZR_bitmaps.GetChildren())):
364 # self._SZR_bitmaps.Detach(child_idx)
365 for bmp in self.__bitmaps:
366 bmp.Destroy()
367 self.__bitmaps = []
368 #--------------------------------------------------------
369 # internal helpers
370 #--------------------------------------------------------
372 if self.__current_tag is None:
373 return
374 pat = gmPerson.gmCurrentPatient()
375 if not pat.connected:
376 return
377 pat.remove_tag(tag = self.__current_tag['pk_identity_tag'])
378 #--------------------------------------------------------
380 if self.__current_tag is None:
381 return
382
383 msg = _('Edit the comment on tag [%s]') % self.__current_tag['l10n_description']
384 comment = wx.GetTextFromUser (
385 message = msg,
386 caption = _('Editing tag comment'),
387 default_value = gmTools.coalesce(self.__current_tag['comment'], ''),
388 parent = self
389 )
390
391 if comment == '':
392 return
393
394 if comment.strip() == self.__current_tag['comment']:
395 return
396
397 if comment == ' ':
398 self.__current_tag['comment'] = None
399 else:
400 self.__current_tag['comment'] = comment.strip()
401
402 self.__current_tag.save()
403 #--------------------------------------------------------
404 # event handlers
405 #--------------------------------------------------------
407 self.__current_tag = evt.GetEventObject().tag
408 self.PopupMenu(self.__context_popup, pos = wx.DefaultPosition)
409 self.__current_tag = None
410
411 #============================================================
412 #============================================================
414
416
417 kwargs['message'] = _("Today's KOrganizer appointments ...")
418 kwargs['button_defs'] = [
419 {'label': _('Reload'), 'tooltip': _('Reload appointments from KOrganizer')},
420 {'label': ''},
421 {'label': ''},
422 {'label': ''},
423 {'label': 'KOrganizer', 'tooltip': _('Launch KOrganizer')}
424 ]
425 gmDataMiningWidgets.cPatientListingPnl.__init__(self, *args, **kwargs)
426
427 self.fname = os.path.expanduser(os.path.join(gmTools.gmPaths().tmp_dir, 'korganizer2gnumed.csv'))
428 self.reload_cmd = 'konsolekalendar --view --export-type csv --export-file %s' % self.fname
429
430 #--------------------------------------------------------
434 #--------------------------------------------------------
436 """Reload appointments from KOrganizer."""
437 found, cmd = gmShellAPI.detect_external_binary(binary = 'korganizer')
438
439 if not found:
440 gmDispatcher.send(signal = 'statustext', msg = _('KOrganizer is not installed.'), beep = True)
441 return
442
443 gmShellAPI.run_command_in_shell(command = cmd, blocking = False)
444 #--------------------------------------------------------
446 try: os.remove(self.fname)
447 except OSError: pass
448 gmShellAPI.run_command_in_shell(command=self.reload_cmd, blocking=True)
449 try:
450 csv_file = io.open(self.fname , mode = 'rt', encoding = 'utf8', errors = 'replace')
451 except IOError:
452 gmDispatcher.send(signal = 'statustext', msg = _('Cannot access KOrganizer transfer file [%s]') % self.fname, beep = True)
453 return
454
455 csv_lines = gmTools.unicode_csv_reader (
456 csv_file,
457 delimiter = ','
458 )
459 # start_date, start_time, end_date, end_time, title (patient), ort, comment, UID
460 self._LCTRL_items.set_columns ([
461 _('Place'),
462 _('Start'),
463 '',
464 '',
465 _('Patient'),
466 _('Comment')
467 ])
468 items = []
469 data = []
470 for line in csv_lines:
471 items.append([line[5], line[0], line[1], line[3], line[4], line[6]])
472 data.append([line[4], line[7]])
473
474 self._LCTRL_items.set_string_items(items = items)
475 self._LCTRL_items.set_column_widths()
476 self._LCTRL_items.set_data(data = data)
477 self._LCTRL_items.patient_key = 0
478 #--------------------------------------------------------
479 # notebook plugins API
480 #--------------------------------------------------------
482 self.reload_appointments()
483
484 #============================================================
485 # occupation related widgets / functions
486 #============================================================
488
489 pat = gmPerson.gmCurrentPatient()
490 curr_jobs = pat.get_occupations()
491 if len(curr_jobs) > 0:
492 old_job = curr_jobs[0]['l10n_occupation']
493 update = curr_jobs[0]['modified_when'].strftime('%m/%Y')
494 else:
495 old_job = ''
496 update = ''
497
498 msg = _(
499 'Please enter the primary occupation of the patient.\n'
500 '\n'
501 'Currently recorded:\n'
502 '\n'
503 ' %s (last updated %s)'
504 ) % (old_job, update)
505
506 new_job = wx.GetTextFromUser (
507 message = msg,
508 caption = _('Editing primary occupation'),
509 default_value = old_job,
510 parent = None
511 )
512 if new_job.strip() == '':
513 return
514
515 for job in curr_jobs:
516 # unlink all but the new job
517 if job['l10n_occupation'] != new_job:
518 pat.unlink_occupation(occupation = job['l10n_occupation'])
519 # and link the new one
520 pat.link_occupation(occupation = new_job)
521
522 #------------------------------------------------------------
524
526 query = "SELECT distinct name, _(name) from dem.occupation where _(name) %(fragment_condition)s"
527 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
528 mp.setThresholds(1, 3, 5)
529 gmPhraseWheel.cPhraseWheel.__init__ (
530 self,
531 *args,
532 **kwargs
533 )
534 self.SetToolTip(_("Type or select an occupation."))
535 self.capitalisation_mode = gmTools.CAPS_FIRST
536 self.matcher = mp
537
538 #============================================================
539 # identity widgets / functions
540 #============================================================
543
544 #------------------------------------------------------------
546
547 # already disabled ?
548 if identity['is_deleted']:
549 _log.debug('identity already deleted: %s', identity)
550 return True
551
552 # logged in staff ?
553 # if so -> return
554 prov = gmStaff.gmCurrentProvider()
555 if prov['pk_identity'] == identity['pk_identity']:
556 _log.warning('identity cannot delete itself while being logged on as staff member')
557 _log.debug('identity to delete: %s', identity)
558 _log.debug('logged on staff: %s', prov)
559 return False
560
561 # ask user for assurance
562 go_ahead = gmGuiHelpers.gm_show_question (
563 _('Are you sure you really, positively want\n'
564 'to disable the following person ?\n'
565 '\n'
566 ' %s %s %s\n'
567 ' born %s\n'
568 '\n'
569 '%s\n'
570 ) % (
571 identity['firstnames'],
572 identity['lastnames'],
573 identity['gender'],
574 identity.get_formatted_dob(),
575 gmTools.bool2subst (
576 identity.is_patient,
577 _('This patient DID receive care here.'),
578 _('This person did NOT receive care here.')
579 )
580 ),
581 _('Disabling person')
582 )
583 if not go_ahead:
584 return False
585
586 # get admin connection
587 conn = gmAuthWidgets.get_dbowner_connection (
588 procedure = _('Disabling person')
589 )
590 # - user cancelled
591 if conn is False:
592 return False
593 # - error
594 if conn is None:
595 return None
596
597 # disable patient
598 gmPerson.disable_identity(identity['pk_identity'])
599
600 # change active patient to logged on staff = myself
601 from Gnumed.wxpython.gmPatSearchWidgets import set_active_patient
602 wx.CallAfter(set_active_patient, patient = prov.identity)
603
604 return True
605
606 #------------------------------------------------------------
607 # phrasewheels
608 #------------------------------------------------------------
610
612 query = "SELECT distinct lastnames, lastnames from dem.names where lastnames %(fragment_condition)s order by lastnames limit 25"
613 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
614 mp.setThresholds(3, 5, 9)
615 gmPhraseWheel.cPhraseWheel.__init__ (
616 self,
617 *args,
618 **kwargs
619 )
620 self.SetToolTip(_("Type or select a last name (family name/surname)."))
621 self.capitalisation_mode = gmTools.CAPS_NAMES
622 self.matcher = mp
623 #------------------------------------------------------------
625
627 query = """
628 (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
629 union
630 (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
631 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
632 mp.setThresholds(3, 5, 9)
633 gmPhraseWheel.cPhraseWheel.__init__ (
634 self,
635 *args,
636 **kwargs
637 )
638 self.SetToolTip(_("Type or select a first name (forename/Christian name/given name)."))
639 self.capitalisation_mode = gmTools.CAPS_NAMES
640 self.matcher = mp
641 #------------------------------------------------------------
643
645 query = """
646 (SELECT distinct preferred, preferred from dem.names where preferred %(fragment_condition)s order by preferred limit 20)
647 union
648 (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
649 union
650 (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
651 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
652 mp.setThresholds(3, 5, 9)
653 gmPhraseWheel.cPhraseWheel.__init__ (
654 self,
655 *args,
656 **kwargs
657 )
658 self.SetToolTip(_("Type or select an alias (nick name, preferred name, call name, warrior name, artist name)."))
659 # nicknames CAN start with lower case !
660 #self.capitalisation_mode = gmTools.CAPS_NAMES
661 self.matcher = mp
662 #------------------------------------------------------------
664
666 query = "SELECT distinct title, title from dem.identity where title %(fragment_condition)s"
667 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
668 mp.setThresholds(1, 3, 9)
669 gmPhraseWheel.cPhraseWheel.__init__ (
670 self,
671 *args,
672 **kwargs
673 )
674 self.SetToolTip(_("Type or select a title. Note that the title applies to the person, not to a particular name !"))
675 self.matcher = mp
676 #------------------------------------------------------------
678 """Let user select a gender."""
679
680 _gender_map = None
681
683
684 if cGenderSelectionPhraseWheel._gender_map is None:
685 cmd = """
686 SELECT tag, l10n_label, sort_weight
687 from dem.v_gender_labels
688 order by sort_weight desc"""
689 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
690 cGenderSelectionPhraseWheel._gender_map = {}
691 for gender in rows:
692 cGenderSelectionPhraseWheel._gender_map[gender[idx['tag']]] = {
693 'data': gender[idx['tag']],
694 'field_label': gender[idx['l10n_label']],
695 'list_label': gender[idx['l10n_label']],
696 'weight': gender[idx['sort_weight']]
697 }
698
699 mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = list(cGenderSelectionPhraseWheel._gender_map.values()))
700 mp.setThresholds(1, 1, 3)
701
702 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
703 self.selection_only = True
704 self.matcher = mp
705 self.picklist_delay = 50
706 #------------------------------------------------------------
708
710 query = """
711 SELECT DISTINCT ON (list_label)
712 pk AS data,
713 name AS field_label,
714 name || coalesce(' (' || issuer || ')', '') as list_label
715 FROM dem.enum_ext_id_types
716 WHERE name %(fragment_condition)s
717 ORDER BY list_label
718 LIMIT 25
719 """
720 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
721 mp.setThresholds(1, 3, 5)
722 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
723 self.SetToolTip(_("Enter or select a type for the external ID."))
724 self.matcher = mp
725 #--------------------------------------------------------
730 #------------------------------------------------------------
732
734 query = """
735 SELECT distinct issuer, issuer
736 from dem.enum_ext_id_types
737 where issuer %(fragment_condition)s
738 order by issuer limit 25"""
739 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
740 mp.setThresholds(1, 3, 5)
741 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
742 self.SetToolTip(_("Type or select an ID issuer."))
743 self.capitalisation_mode = gmTools.CAPS_FIRST
744 self.matcher = mp
745 #------------------------------------------------------------
746 # edit areas
747 #------------------------------------------------------------
748 from Gnumed.wxGladeWidgets import wxgExternalIDEditAreaPnl
749
750 -class cExternalIDEditAreaPnl(wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
751 """An edit area for editing/creating external IDs.
752
753 Does NOT act on/listen to the current patient.
754 """
756
757 try:
758 data = kwargs['external_id']
759 del kwargs['external_id']
760 except:
761 data = None
762
763 wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl.__init__(self, *args, **kwargs)
764 gmEditArea.cGenericEditAreaMixin.__init__(self)
765
766 self.id_holder = None
767
768 self.mode = 'new'
769 self.data = data
770 if data is not None:
771 self.mode = 'edit'
772
773 self.__init_ui()
774 #--------------------------------------------------------
776 self._PRW_type.add_callback_on_lose_focus(self._on_type_set)
777 #----------------------------------------------------------------
778 # generic Edit Area mixin API
779 #----------------------------------------------------------------
781 validity = True
782
783 # do not test .GetData() because adding external
784 # IDs will create types as necessary
785 #if self._PRW_type.GetData() is None:
786 if self._PRW_type.GetValue().strip() == '':
787 validity = False
788 self._PRW_type.display_as_valid(False)
789 self._PRW_type.SetFocus()
790 else:
791 self._PRW_type.display_as_valid(True)
792
793 if self._TCTRL_value.GetValue().strip() == '':
794 validity = False
795 self.display_tctrl_as_valid(tctrl = self._TCTRL_value, valid = False)
796 else:
797 self.display_tctrl_as_valid(tctrl = self._TCTRL_value, valid = True)
798
799 return validity
800 #----------------------------------------------------------------
802 data = {}
803 data['pk_type'] = None
804 data['name'] = self._PRW_type.GetValue().strip()
805 data['value'] = self._TCTRL_value.GetValue().strip()
806 data['issuer'] = gmTools.none_if(self._PRW_issuer.GetValue().strip(), '')
807 data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
808
809 self.id_holder.add_external_id (
810 type_name = data['name'],
811 value = data['value'],
812 issuer = data['issuer'],
813 comment = data['comment']
814 )
815
816 self.data = data
817 return True
818 #----------------------------------------------------------------
820 self.data['name'] = self._PRW_type.GetValue().strip()
821 self.data['value'] = self._TCTRL_value.GetValue().strip()
822 self.data['issuer'] = gmTools.none_if(self._PRW_issuer.GetValue().strip(), '')
823 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
824
825 self.id_holder.update_external_id (
826 pk_id = self.data['pk_id'],
827 type = self.data['name'],
828 value = self.data['value'],
829 issuer = self.data['issuer'],
830 comment = self.data['comment']
831 )
832
833 return True
834 #----------------------------------------------------------------
836 self._PRW_type.SetText(value = '', data = None)
837 self._TCTRL_value.SetValue('')
838 self._PRW_issuer.SetText(value = '', data = None)
839 self._TCTRL_comment.SetValue('')
840 #----------------------------------------------------------------
844 #----------------------------------------------------------------
846 self._PRW_type.SetText(value = self.data['name'], data = self.data['pk_type'])
847 self._TCTRL_value.SetValue(self.data['value'])
848 self._PRW_issuer.SetText(self.data['issuer'])
849 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
850 #----------------------------------------------------------------
851 # internal helpers
852 #----------------------------------------------------------------
854 """Set the issuer according to the selected type.
855
856 Matches are fetched from existing records in backend.
857 """
858 pk_curr_type = self._PRW_type.GetData()
859 if pk_curr_type is None:
860 return True
861 rows, idx = gmPG2.run_ro_queries(queries = [{
862 'cmd': "SELECT issuer FROM dem.enum_ext_id_types WHERE pk = %s",
863 'args': [pk_curr_type]
864 }])
865 if len(rows) == 0:
866 return True
867 wx.CallAfter(self._PRW_issuer.SetText, rows[0][0])
868 return True
869
870 #============================================================
871 # identity widgets
872 #------------------------------------------------------------
874 allow_empty_dob = gmGuiHelpers.gm_show_question (
875 _(
876 'Are you sure you want to leave this person\n'
877 'without a valid date of birth ?\n'
878 '\n'
879 'This can be useful for temporary staff members\n'
880 'but will provoke nag screens if this person\n'
881 'becomes a patient.\n'
882 ),
883 _('Validating date of birth')
884 )
885 return allow_empty_dob
886 #------------------------------------------------------------
888
889 # valid timestamp ?
890 if dob_prw.is_valid_timestamp(allow_empty = False): # properly colors the field
891 dob = dob_prw.date
892 # but year also usable ?
893 if (dob.year > 1899) and (dob < gmDateTime.pydt_now_here()):
894 return True
895
896 if dob.year < 1900:
897 msg = _(
898 'DOB: %s\n'
899 '\n'
900 'While this is a valid point in time Python does\n'
901 'not know how to deal with it.\n'
902 '\n'
903 'We suggest using January 1st 1901 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(allow_empty = True):
1000 gmDispatcher.send(signal = 'statustext', msg = _('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 exc = gmPG2.make_pg_exception_fields_unicode(exc)
1137 gmGuiHelpers.gm_show_error (
1138 aTitle = _('Adding name'),
1139 aMessage = _(
1140 'Cannot add this name to the patient !\n'
1141 '\n'
1142 ' %s'
1143 ) % exc.u_pgerror
1144 )
1145 return False
1146
1147 old_nick = self.__identity['active_name']['preferred']
1148 new_nick = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
1149 if active:
1150 data['preferred'] = gmTools.coalesce(new_nick, old_nick)
1151 else:
1152 data['preferred'] = new_nick
1153 data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1154 data.save()
1155
1156 self.data = data
1157 return True
1158 #----------------------------------------------------------------
1160 """The knack here is that we can only update a few fields.
1161
1162 Otherwise we need to clone the name and update that.
1163 """
1164 first = self._PRW_firstname.GetValue().strip()
1165 last = self._PRW_lastname.GetValue().strip()
1166 active = self._CHBOX_active.GetValue()
1167
1168 current_name = self.data['firstnames'].strip() + self.data['lastnames'].strip()
1169 new_name = first + last
1170
1171 # editable fields only ?
1172 if new_name == current_name:
1173 self.data['active_name'] = self._CHBOX_active.GetValue()
1174 self.data['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
1175 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1176 self.data.save()
1177 # else clone name and update that
1178 else:
1179 try:
1180 name = self.__identity.add_name(first, last, active)
1181 except gmPG2.dbapi.IntegrityError as exc:
1182 _log.exception('cannot clone name when editing existing name')
1183 exc = gmPG2.make_pg_exception_fields_unicode(exc)
1184 gmGuiHelpers.gm_show_error (
1185 aTitle = _('Editing name'),
1186 aMessage = _(
1187 'Cannot clone a copy of this name !\n'
1188 '\n'
1189 ' %s'
1190 ) % exc.u_pgerror
1191 # ) % str(exc)
1192 )
1193 return False
1194 name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
1195 name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
1196 name.save()
1197 self.data = name
1198
1199 return True
1200 #----------------------------------------------------------------
1202 self._PRW_firstname.SetText(value = '', data = None)
1203 self._PRW_lastname.SetText(value = '', data = None)
1204 self._PRW_nick.SetText(value = '', data = None)
1205 self._TCTRL_comment.SetValue('')
1206 self._CHBOX_active.SetValue(False)
1207
1208 self._PRW_firstname.SetFocus()
1209 #----------------------------------------------------------------
1211 self._refresh_as_new()
1212 self._PRW_firstname.SetText(value = '', data = None)
1213 self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], ''))
1214
1215 self._PRW_lastname.SetFocus()
1216 #----------------------------------------------------------------
1218 self._PRW_firstname.SetText(self.data['firstnames'])
1219 self._PRW_lastname.SetText(self.data['lastnames'])
1220 self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], ''))
1221 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
1222 self._CHBOX_active.SetValue(self.data['active_name'])
1223
1224 self._TCTRL_comment.SetFocus()
1225 #------------------------------------------------------------
1226 # list manager
1227 #------------------------------------------------------------
1229 """A list for managing a person's names.
1230
1231 Does NOT act on/listen to the current patient.
1232 """
1234
1235 try:
1236 self.__identity = kwargs['identity']
1237 del kwargs['identity']
1238 except KeyError:
1239 self.__identity = None
1240
1241 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1242
1243 self.refresh_callback = self.refresh
1244 self.new_callback = self._add_name
1245 self.edit_callback = self._edit_name
1246 self.delete_callback = self._del_name
1247
1248 self.__init_ui()
1249 self.refresh()
1250 #--------------------------------------------------------
1251 # external API
1252 #--------------------------------------------------------
1254 if self.__identity is None:
1255 self._LCTRL_items.set_string_items()
1256 return
1257
1258 names = self.__identity.get_names()
1259 self._LCTRL_items.set_string_items (
1260 items = [ [
1261 gmTools.bool2str(n['active_name'], 'X', ''),
1262 n['lastnames'],
1263 n['firstnames'],
1264 gmTools.coalesce(n['preferred'], ''),
1265 gmTools.coalesce(n['comment'], '')
1266 ] for n in names ]
1267 )
1268 self._LCTRL_items.set_column_widths()
1269 self._LCTRL_items.set_data(data = names)
1270 #--------------------------------------------------------
1271 # internal helpers
1272 #--------------------------------------------------------
1274 self._LCTRL_items.set_columns(columns = [
1275 _('Active'),
1276 _('Lastname'),
1277 _('Firstname(s)'),
1278 _('Preferred Name'),
1279 _('Comment')
1280 ])
1281 #--------------------------------------------------------
1283 #ea = cPersonNameEAPnl(self, -1, name = self.__identity.get_active_name())
1284 ea = cPersonNameEAPnl(self, -1, identity = self.__identity)
1285 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1286 dlg.SetTitle(_('Adding new name'))
1287 if dlg.ShowModal() == wx.ID_OK:
1288 dlg.Destroy()
1289 return True
1290 dlg.Destroy()
1291 return False
1292 #--------------------------------------------------------
1294 ea = cPersonNameEAPnl(self, -1, name = name)
1295 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1296 dlg.SetTitle(_('Editing name'))
1297 if dlg.ShowModal() == wx.ID_OK:
1298 dlg.Destroy()
1299 return True
1300 dlg.Destroy()
1301 return False
1302 #--------------------------------------------------------
1304
1305 if len(self.__identity.get_names()) == 1:
1306 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the only name of a person.'), beep = True)
1307 return False
1308
1309 if name['active_name']:
1310 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the active name of a person.'), beep = True)
1311 return False
1312
1313 go_ahead = gmGuiHelpers.gm_show_question (
1314 _( 'It is often advisable to keep old names around and\n'
1315 'just create a new "currently active" name.\n'
1316 '\n'
1317 'This allows finding the patient by both the old\n'
1318 'and the new name (think before/after marriage).\n'
1319 '\n'
1320 'Do you still want to really delete\n'
1321 "this name from the patient ?"
1322 ),
1323 _('Deleting name')
1324 )
1325 if not go_ahead:
1326 return False
1327
1328 self.__identity.delete_name(name = name)
1329 return True
1330 #--------------------------------------------------------
1331 # properties
1332 #--------------------------------------------------------
1335
1339
1340 identity = property(_get_identity, _set_identity)
1341
1342 #------------------------------------------------------------
1344 """A list for managing a person's external IDs.
1345
1346 Does NOT act on/listen to the current patient.
1347 """
1349
1350 try:
1351 self.__identity = kwargs['identity']
1352 del kwargs['identity']
1353 except KeyError:
1354 self.__identity = None
1355
1356 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1357
1358 self.refresh_callback = self.refresh
1359 self.new_callback = self._add_id
1360 self.edit_callback = self._edit_id
1361 self.delete_callback = self._del_id
1362
1363 self.__init_ui()
1364 self.refresh()
1365 #--------------------------------------------------------
1366 # external API
1367 #--------------------------------------------------------
1369 if self.__identity is None:
1370 self._LCTRL_items.set_string_items()
1371 return
1372
1373 ids = self.__identity.get_external_ids()
1374 self._LCTRL_items.set_string_items (
1375 items = [ [
1376 i['name'],
1377 i['value'],
1378 gmTools.coalesce(i['issuer'], ''),
1379 gmTools.coalesce(i['comment'], '')
1380 ] for i in ids
1381 ]
1382 )
1383 self._LCTRL_items.set_column_widths()
1384 self._LCTRL_items.set_data(data = ids)
1385 #--------------------------------------------------------
1386 # internal helpers
1387 #--------------------------------------------------------
1389 self._LCTRL_items.set_columns(columns = [
1390 _('ID type'),
1391 _('Value'),
1392 _('Issuer'),
1393 _('Comment')
1394 ])
1395 #--------------------------------------------------------
1397 ea = cExternalIDEditAreaPnl(self, -1)
1398 ea.id_holder = self.__identity
1399 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea)
1400 dlg.SetTitle(_('Adding new external ID'))
1401 if dlg.ShowModal() == wx.ID_OK:
1402 dlg.Destroy()
1403 return True
1404 dlg.Destroy()
1405 return False
1406 #--------------------------------------------------------
1408 ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id)
1409 ea.id_holder = self.__identity
1410 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1411 dlg.SetTitle(_('Editing external ID'))
1412 if dlg.ShowModal() == wx.ID_OK:
1413 dlg.Destroy()
1414 return True
1415 dlg.Destroy()
1416 return False
1417 #--------------------------------------------------------
1419 go_ahead = gmGuiHelpers.gm_show_question (
1420 _( 'Do you really want to delete this\n'
1421 'external ID from the patient ?'),
1422 _('Deleting external ID')
1423 )
1424 if not go_ahead:
1425 return False
1426 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id'])
1427 return True
1428 #--------------------------------------------------------
1429 # properties
1430 #--------------------------------------------------------
1433
1437
1438 identity = property(_get_identity, _set_identity)
1439 #------------------------------------------------------------
1440 # integrated panels
1441 #------------------------------------------------------------
1442 from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl
1443
1445 """A panel for editing identity data for a person.
1446
1447 - provides access to:
1448 - identity EA
1449 - name list manager
1450 - external IDs list manager
1451
1452 Does NOT act on/listen to the current patient.
1453 """
1455
1456 wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs)
1457
1458 self.__identity = None
1459 self.refresh()
1460 #--------------------------------------------------------
1461 # external API
1462 #--------------------------------------------------------
1464 self._PNL_names.identity = self.__identity
1465 self._PNL_ids.identity = self.__identity
1466 # this is an Edit Area:
1467 self._PNL_identity.mode = 'new'
1468 self._PNL_identity.data = self.__identity
1469 if self.__identity is not None:
1470 self._PNL_identity.mode = 'edit'
1471 self._PNL_identity._refresh_from_existing()
1472 #--------------------------------------------------------
1473 # properties
1474 #--------------------------------------------------------
1477
1481
1482 identity = property(_get_identity, _set_identity)
1483 #--------------------------------------------------------
1484 # event handlers
1485 #--------------------------------------------------------
1489 #self._PNL_identity.refresh()
1490 #--------------------------------------------------------
1493
1494 #============================================================
1495 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl
1496
1497 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
1499
1500 wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs)
1501
1502 self.__identity = None
1503 self._PRW_provider.selection_only = False
1504 self.refresh()
1505 #--------------------------------------------------------
1506 # external API
1507 #--------------------------------------------------------
1509
1510 tt = _('Link another person in this database as the emergency contact:\n\nEnter person name part or identifier and hit <enter>.')
1511
1512 if self.__identity is None:
1513 self._TCTRL_er_contact.SetValue('')
1514 self._TCTRL_person.person = None
1515 self._TCTRL_person.SetToolTip(tt)
1516
1517 self._PRW_provider.SetText(value = '', data = None)
1518 return
1519
1520 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], ''))
1521 if self.__identity['pk_emergency_contact'] is not None:
1522 ident = gmPerson.cPerson(aPK_obj = self.__identity['pk_emergency_contact'])
1523 self._TCTRL_person.person = ident
1524 tt = '%s\n\n%s\n\n%s' % (
1525 tt,
1526 ident['description_gender'],
1527 '\n'.join([
1528 '%s: %s%s' % (
1529 c['l10n_comm_type'],
1530 c['url'],
1531 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), '', '')
1532 )
1533 for c in ident.get_comm_channels()
1534 ])
1535 )
1536 else:
1537 self._TCTRL_person.person = None
1538
1539 self._TCTRL_person.SetToolTip(tt)
1540
1541 if self.__identity['pk_primary_provider'] is None:
1542 self._PRW_provider.SetText(value = '', data = None)
1543 else:
1544 self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
1545
1546 self._PNL_external_care.identity = self.__identity
1547 #--------------------------------------------------------
1548 # properties
1549 #--------------------------------------------------------
1552
1556
1557 identity = property(_get_identity, _set_identity)
1558 #--------------------------------------------------------
1559 # event handlers
1560 #--------------------------------------------------------
1575 #--------------------------------------------------------
1578 #--------------------------------------------------------
1589 #--------------------------------------------------------
1597
1598 #============================================================
1599 # patient demographics editing classes
1600 #============================================================
1602 """Notebook displaying demographics editing pages:
1603
1604 - Identity (as per Jim/Rogerio 12/2011)
1605 - Contacts (addresses, phone numbers, etc)
1606 - Social network (significant others, GP, etc)
1607
1608 Does NOT act on/listen to the current patient.
1609 """
1610 #--------------------------------------------------------
1612
1613 wx.Notebook.__init__ (
1614 self,
1615 parent = parent,
1616 id = id,
1617 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
1618 name = self.__class__.__name__
1619 )
1620 _log.debug('created wx.Notebook: %s with ID %s', self.__class__.__name__, self.Id)
1621
1622 self.__identity = None
1623 self.__do_layout()
1624 self.SetSelection(0)
1625 #--------------------------------------------------------
1626 # public API
1627 #--------------------------------------------------------
1629 """Populate fields in pages with data from model."""
1630 for page_idx in range(self.GetPageCount()):
1631 page = self.GetPage(page_idx)
1632 page.identity = self.__identity
1633
1634 return True
1635 #--------------------------------------------------------
1636 # internal API
1637 #--------------------------------------------------------
1639 """Build patient edition notebook pages."""
1640
1641 # identity page
1642 new_page = cPersonIdentityManagerPnl(self, -1)
1643 new_page.identity = self.__identity
1644 self.AddPage (
1645 page = new_page,
1646 text = _('Identity'),
1647 select = False
1648 )
1649
1650 # contacts page
1651 new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1)
1652 new_page.identity = self.__identity
1653 self.AddPage (
1654 page = new_page,
1655 text = _('Contacts'),
1656 select = True
1657 )
1658
1659 # social network page
1660 new_page = cPersonSocialNetworkManagerPnl(self, -1)
1661 new_page.identity = self.__identity
1662 self.AddPage (
1663 page = new_page,
1664 text = _('Social network'),
1665 select = False
1666 )
1667 #--------------------------------------------------------
1668 # properties
1669 #--------------------------------------------------------
1672
1674 self.__identity = identity
1675
1676 identity = property(_get_identity, _set_identity)
1677
1678 #============================================================
1679 # old occupation widgets
1680 #============================================================
1681 # FIXME: support multiple occupations
1682 # FIXME: redo with wxGlade
1683
1685 """Page containing patient occupations edition fields.
1686 """
1688 """
1689 Creates a new instance of BasicPatDetailsPage
1690 @param parent - The parent widget
1691 @type parent - A wx.Window instance
1692 @param id - The widget id
1693 @type id - An integer
1694 """
1695 wx.Panel.__init__(self, parent, id)
1696 self.__ident = ident
1697 self.__do_layout()
1698 #--------------------------------------------------------
1700 PNL_form = wx.Panel(self, -1)
1701 # occupation
1702 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
1703 self.PRW_occupation = cOccupationPhraseWheel(PNL_form, -1)
1704 self.PRW_occupation.SetToolTip(_("primary occupation of the patient"))
1705 # known since
1706 STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated'))
1707 self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY)
1708
1709 # layout input widgets
1710 SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4)
1711 SZR_input.AddGrowableCol(1)
1712 SZR_input.Add(STT_occupation, 0, wx.SHAPED)
1713 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
1714 SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED)
1715 SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND)
1716 PNL_form.SetSizerAndFit(SZR_input)
1717
1718 # layout page
1719 SZR_main = wx.BoxSizer(wx.VERTICAL)
1720 SZR_main.Add(PNL_form, 1, wx.EXPAND)
1721 self.SetSizer(SZR_main)
1722 #--------------------------------------------------------
1725 #--------------------------------------------------------
1727 if identity is not None:
1728 self.__ident = identity
1729 jobs = self.__ident.get_occupations()
1730 if len(jobs) > 0:
1731 self.PRW_occupation.SetText(jobs[0]['l10n_occupation'])
1732 self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y'))
1733 return True
1734 #--------------------------------------------------------
1736 if self.PRW_occupation.IsModified():
1737 new_job = self.PRW_occupation.GetValue().strip()
1738 jobs = self.__ident.get_occupations()
1739 for job in jobs:
1740 if job['l10n_occupation'] == new_job:
1741 continue
1742 self.__ident.unlink_occupation(occupation = job['l10n_occupation'])
1743 self.__ident.link_occupation(occupation = new_job)
1744 return True
1745
1746 #============================================================
1748 """Patient demographics plugin for main notebook.
1749
1750 Hosts another notebook with pages for Identity, Contacts, etc.
1751
1752 Acts on/listens to the currently active patient.
1753 """
1754 #--------------------------------------------------------
1756 wx.Panel.__init__ (self, parent = parent, id = id, style = wx.NO_BORDER)
1757 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1758 self.__do_layout()
1759 self.__register_interests()
1760 #--------------------------------------------------------
1761 # public API
1762 #--------------------------------------------------------
1763 #--------------------------------------------------------
1764 # internal helpers
1765 #--------------------------------------------------------
1767 """Arrange widgets."""
1768 self.__patient_notebook = cPersonDemographicsEditorNb(self, -1)
1769
1770 szr_main = wx.BoxSizer(wx.VERTICAL)
1771 szr_main.Add(self.__patient_notebook, 1, wx.EXPAND)
1772 self.SetSizerAndFit(szr_main)
1773 #--------------------------------------------------------
1774 # event handling
1775 #--------------------------------------------------------
1777 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
1778 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
1779 #--------------------------------------------------------
1783 #--------------------------------------------------------
1786 #--------------------------------------------------------
1787 # reget mixin API
1788 #--------------------------------------------------------
1798
1799 #============================================================
1800 #============================================================
1801 if __name__ == "__main__":
1802
1803 #--------------------------------------------------------
1805 app = wx.PyWidgetTester(size = (600, 400))
1806 app.SetWidget(cKOrganizerSchedulePnl)
1807 app.MainLoop()
1808 #--------------------------------------------------------
1810 app = wx.PyWidgetTester(size = (600, 400))
1811 widget = cPersonNamesManagerPnl(app.frame, -1)
1812 widget.identity = activate_patient()
1813 app.frame.Show(True)
1814 app.MainLoop()
1815 #--------------------------------------------------------
1817 app = wx.PyWidgetTester(size = (600, 400))
1818 widget = cPersonIDsManagerPnl(app.frame, -1)
1819 widget.identity = activate_patient()
1820 app.frame.Show(True)
1821 app.MainLoop()
1822 #--------------------------------------------------------
1824 app = wx.PyWidgetTester(size = (600, 400))
1825 widget = cPersonIdentityManagerPnl(app.frame, -1)
1826 widget.identity = activate_patient()
1827 app.frame.Show(True)
1828 app.MainLoop()
1829 #--------------------------------------------------------
1831 app = wx.PyWidgetTester(size = (600, 400))
1832 app.SetWidget(cPersonNameEAPnl, name = activate_patient().get_active_name())
1833 app.MainLoop()
1834 #--------------------------------------------------------
1836 app = wx.PyWidgetTester(size = (600, 400))
1837 widget = cPersonDemographicsEditorNb(app.frame, -1)
1838 widget.identity = activate_patient()
1839 widget.refresh()
1840 app.frame.Show(True)
1841 app.MainLoop()
1842 #--------------------------------------------------------
1844 patient = gmPersonSearch.ask_for_patient()
1845 if patient is None:
1846 print("No patient. Exiting gracefully...")
1847 sys.exit(0)
1848 from Gnumed.wxpython.gmPatSearchWidgets import set_active_patient
1849 set_active_patient(patient = patient)
1850 return patient
1851 #--------------------------------------------------------
1852 if len(sys.argv) > 1 and sys.argv[1] == 'test':
1853
1854 gmI18N.activate_locale()
1855 gmI18N.install_domain(domain='gnumed')
1856 gmPG2.get_connection()
1857
1858 # app = wx.PyWidgetTester(size = (400, 300))
1859 # app.SetWidget(cNotebookedPatEditionPanel, -1)
1860 # app.frame.Show(True)
1861 # app.MainLoop()
1862
1863 # phrasewheels
1864 # test_organizer_pnl()
1865
1866 # identity related widgets
1867 #test_person_names_pnl()
1868 test_person_ids_pnl()
1869 #test_pat_ids_pnl()
1870 #test_name_ea_pnl()
1871
1872 #test_cPersonDemographicsEditorNb()
1873
1874 #============================================================
1875
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu May 10 01:55:20 2018 | http://epydoc.sourceforge.net |