| Home | Trees | Indices | Help |
|
|---|
|
|
1 """Widgets dealing with patient demographics."""
2 #============================================================
3 __version__ = "$Revision: 1.175 $"
4 __author__ = "R.Terry, SJ Tan, I Haywood, Carlos Moro <cfmoro1976@yahoo.es>"
5 __license__ = 'GPL (details at http://www.gnu.org)'
6
7 # standard library
8 import sys, os, codecs, re as regex, logging
9
10
11 import wx
12 import wx.wizard
13
14
15 # GNUmed specific
16 if __name__ == '__main__':
17 sys.path.insert(0, '../../')
18 from Gnumed.pycommon import gmDispatcher, gmI18N, gmMatchProvider, gmPG2, gmTools, gmCfg
19 from Gnumed.pycommon import gmDateTime, gmShellAPI
20 from Gnumed.business import gmDemographicRecord, gmPerson, gmSurgery, gmPersonSearch
21 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmDateTimeInput
22 from Gnumed.wxpython import gmRegetMixin, gmDataMiningWidgets, gmListWidgets, gmEditArea
23 from Gnumed.wxpython import gmAuthWidgets, gmPersonContactWidgets
24
25
26 # constant defs
27 _log = logging.getLogger('gm.ui')
28
29
30 try:
31 _('dummy-no-need-to-translate-but-make-epydoc-happy')
32 except NameError:
33 _ = lambda x:x
34
35 #============================================================
36 #============================================================
38
40
41 kwargs['message'] = _("Today's KOrganizer appointments ...")
42 kwargs['button_defs'] = [
43 {'label': _('Reload'), 'tooltip': _('Reload appointments from KOrganizer')},
44 {'label': u''},
45 {'label': u''},
46 {'label': u''},
47 {'label': u'KOrganizer', 'tooltip': _('Launch KOrganizer')}
48 ]
49 gmDataMiningWidgets.cPatientListingPnl.__init__(self, *args, **kwargs)
50
51 self.fname = os.path.expanduser(os.path.join('~', '.gnumed', 'tmp', 'korganizer2gnumed.csv'))
52 self.reload_cmd = 'konsolekalendar --view --export-type csv --export-file %s' % self.fname
53
54 #--------------------------------------------------------
58 #--------------------------------------------------------
60 """Reload appointments from KOrganizer."""
61 found, cmd = gmShellAPI.detect_external_binary(binary = 'korganizer')
62
63 if not found:
64 gmDispatcher.send(signal = 'statustext', msg = _('KOrganizer is not installed.'), beep = True)
65 return
66
67 gmShellAPI.run_command_in_shell(command = cmd, blocking = False)
68 #--------------------------------------------------------
70 try: os.remove(self.fname)
71 except OSError: pass
72 gmShellAPI.run_command_in_shell(command=self.reload_cmd, blocking=True)
73 try:
74 csv_file = codecs.open(self.fname , mode = 'rU', encoding = 'utf8', errors = 'replace')
75 except IOError:
76 gmDispatcher.send(signal = u'statustext', msg = _('Cannot access KOrganizer transfer file [%s]') % self.fname, beep = True)
77 return
78
79 csv_lines = gmTools.unicode_csv_reader (
80 csv_file,
81 delimiter = ','
82 )
83 # start_date, start_time, end_date, end_time, title (patient), ort, comment, UID
84 self._LCTRL_items.set_columns ([
85 _('Place'),
86 _('Start'),
87 u'',
88 u'',
89 _('Patient'),
90 _('Comment')
91 ])
92 items = []
93 data = []
94 for line in csv_lines:
95 items.append([line[5], line[0], line[1], line[3], line[4], line[6]])
96 data.append([line[4], line[7]])
97
98 self._LCTRL_items.set_string_items(items = items)
99 self._LCTRL_items.set_column_widths()
100 self._LCTRL_items.set_data(data = data)
101 self._LCTRL_items.patient_key = 0
102 #--------------------------------------------------------
103 # notebook plugins API
104 #--------------------------------------------------------
106 self.reload_appointments()
107 #============================================================
108 # occupation related widgets / functions
109 #============================================================
111
112 pat = gmPerson.gmCurrentPatient()
113 curr_jobs = pat.get_occupations()
114 if len(curr_jobs) > 0:
115 old_job = curr_jobs[0]['l10n_occupation']
116 update = curr_jobs[0]['modified_when'].strftime('%m/%Y')
117 else:
118 old_job = u''
119 update = u''
120
121 msg = _(
122 'Please enter the primary occupation of the patient.\n'
123 '\n'
124 'Currently recorded:\n'
125 '\n'
126 ' %s (last updated %s)'
127 ) % (old_job, update)
128
129 new_job = wx.GetTextFromUser (
130 message = msg,
131 caption = _('Editing primary occupation'),
132 default_value = old_job,
133 parent = None
134 )
135 if new_job.strip() == u'':
136 return
137
138 for job in curr_jobs:
139 # unlink all but the new job
140 if job['l10n_occupation'] != new_job:
141 pat.unlink_occupation(occupation = job['l10n_occupation'])
142 # and link the new one
143 pat.link_occupation(occupation = new_job)
144
145 #------------------------------------------------------------
147
149 query = u"select distinct name, _(name) from dem.occupation where _(name) %(fragment_condition)s"
150 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
151 mp.setThresholds(1, 3, 5)
152 gmPhraseWheel.cPhraseWheel.__init__ (
153 self,
154 *args,
155 **kwargs
156 )
157 self.SetToolTipString(_("Type or select an occupation."))
158 self.capitalisation_mode = gmTools.CAPS_FIRST
159 self.matcher = mp
160
161 #============================================================
162 # identity widgets / functions
163 #============================================================
165 # ask user for assurance
166 go_ahead = gmGuiHelpers.gm_show_question (
167 _('Are you sure you really, positively want\n'
168 'to disable the following person ?\n'
169 '\n'
170 ' %s %s %s\n'
171 ' born %s\n'
172 '\n'
173 '%s\n'
174 ) % (
175 identity['firstnames'],
176 identity['lastnames'],
177 identity['gender'],
178 identity['dob'],
179 gmTools.bool2subst (
180 identity.is_patient,
181 _('This patient DID receive care.'),
182 _('This person did NOT receive care.')
183 )
184 ),
185 _('Disabling person')
186 )
187 if not go_ahead:
188 return True
189
190 # get admin connection
191 conn = gmAuthWidgets.get_dbowner_connection (
192 procedure = _('Disabling patient')
193 )
194 # - user cancelled
195 if conn is False:
196 return True
197 # - error
198 if conn is None:
199 return False
200
201 # now disable patient
202 gmPG2.run_rw_queries(queries = [{'cmd': u"update dem.identity set deleted=True where pk=%s", 'args': [identity['pk_identity']]}])
203
204 return True
205
206 #------------------------------------------------------------
207 # phrasewheels
208 #------------------------------------------------------------
210
212 query = u"select distinct lastnames, lastnames from dem.names where lastnames %(fragment_condition)s order by lastnames limit 25"
213 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
214 mp.setThresholds(3, 5, 9)
215 gmPhraseWheel.cPhraseWheel.__init__ (
216 self,
217 *args,
218 **kwargs
219 )
220 self.SetToolTipString(_("Type or select a last name (family name/surname)."))
221 self.capitalisation_mode = gmTools.CAPS_NAMES
222 self.matcher = mp
223 #------------------------------------------------------------
225
227 query = u"""
228 (select distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
229 union
230 (select distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
231 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
232 mp.setThresholds(3, 5, 9)
233 gmPhraseWheel.cPhraseWheel.__init__ (
234 self,
235 *args,
236 **kwargs
237 )
238 self.SetToolTipString(_("Type or select a first name (forename/Christian name/given name)."))
239 self.capitalisation_mode = gmTools.CAPS_NAMES
240 self.matcher = mp
241 #------------------------------------------------------------
243
245 query = u"""
246 (select distinct preferred, preferred from dem.names where preferred %(fragment_condition)s order by preferred limit 20)
247 union
248 (select distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
249 union
250 (select distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
251 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
252 mp.setThresholds(3, 5, 9)
253 gmPhraseWheel.cPhraseWheel.__init__ (
254 self,
255 *args,
256 **kwargs
257 )
258 self.SetToolTipString(_("Type or select an alias (nick name, preferred name, call name, warrior name, artist name)."))
259 # nicknames CAN start with lower case !
260 #self.capitalisation_mode = gmTools.CAPS_NAMES
261 self.matcher = mp
262 #------------------------------------------------------------
264
266 query = u"select distinct title, title from dem.identity where title %(fragment_condition)s"
267 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
268 mp.setThresholds(1, 3, 9)
269 gmPhraseWheel.cPhraseWheel.__init__ (
270 self,
271 *args,
272 **kwargs
273 )
274 self.SetToolTipString(_("Type or select a title. Note that the title applies to the person, not to a particular name !"))
275 self.matcher = mp
276 #------------------------------------------------------------
278 """Let user select a gender."""
279
280 _gender_map = None
281
283
284 if cGenderSelectionPhraseWheel._gender_map is None:
285 cmd = u"""
286 select tag, l10n_label, sort_weight
287 from dem.v_gender_labels
288 order by sort_weight desc"""
289 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
290 cGenderSelectionPhraseWheel._gender_map = {}
291 for gender in rows:
292 cGenderSelectionPhraseWheel._gender_map[gender[idx['tag']]] = {
293 'data': gender[idx['tag']],
294 'label': gender[idx['l10n_label']],
295 'weight': gender[idx['sort_weight']]
296 }
297
298 mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = cGenderSelectionPhraseWheel._gender_map.values())
299 mp.setThresholds(1, 1, 3)
300
301 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
302 self.selection_only = True
303 self.matcher = mp
304 self.picklist_delay = 50
305 #------------------------------------------------------------
307
309 query = u"""
310 select distinct pk, (name || coalesce(' (%s ' || issuer || ')', '')) as label
311 from dem.enum_ext_id_types
312 where name %%(fragment_condition)s
313 order by label limit 25""" % _('issued by')
314 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
315 mp.setThresholds(1, 3, 5)
316 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
317 self.SetToolTipString(_("Enter or select a type for the external ID."))
318 self.matcher = mp
319 #------------------------------------------------------------
321
323 query = u"""
324 select distinct issuer, issuer
325 from dem.enum_ext_id_types
326 where issuer %(fragment_condition)s
327 order by issuer limit 25"""
328 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
329 mp.setThresholds(1, 3, 5)
330 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
331 self.SetToolTipString(_("Type or select an ID issuer."))
332 self.capitalisation_mode = gmTools.CAPS_FIRST
333 self.matcher = mp
334 #------------------------------------------------------------
335 # edit areas
336 #------------------------------------------------------------
337 from Gnumed.wxGladeWidgets import wxgExternalIDEditAreaPnl
338
340 """An edit area for editing/creating external IDs.
341
342 Does NOT act on/listen to the current patient.
343 """
345
346 try:
347 self.ext_id = kwargs['external_id']
348 del kwargs['external_id']
349 except:
350 self.ext_id = None
351
352 wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl.__init__(self, *args, **kwargs)
353
354 self.identity = None
355
356 self.__register_events()
357
358 self.refresh()
359 #--------------------------------------------------------
360 # external API
361 #--------------------------------------------------------
363 if ext_id is not None:
364 self.ext_id = ext_id
365
366 if self.ext_id is not None:
367 self._PRW_type.SetText(value = self.ext_id['name'], data = self.ext_id['pk_type'])
368 self._TCTRL_value.SetValue(self.ext_id['value'])
369 self._PRW_issuer.SetText(self.ext_id['issuer'])
370 self._TCTRL_comment.SetValue(gmTools.coalesce(self.ext_id['comment'], u''))
371 # FIXME: clear fields
372 # else:
373 # pass
374 #--------------------------------------------------------
376
377 if not self.__valid_for_save():
378 return False
379
380 # strip out " (issued by ...)" added by phrasewheel
381 type = regex.split(' \(%s .+\)$' % _('issued by'), self._PRW_type.GetValue().strip(), 1)[0]
382
383 # add new external ID
384 if self.ext_id is None:
385 self.identity.add_external_id (
386 type_name = type,
387 value = self._TCTRL_value.GetValue().strip(),
388 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''),
389 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
390 )
391 # edit old external ID
392 else:
393 self.identity.update_external_id (
394 pk_id = self.ext_id['pk_id'],
395 type = type,
396 value = self._TCTRL_value.GetValue().strip(),
397 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''),
398 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
399 )
400
401 return True
402 #--------------------------------------------------------
403 # internal helpers
404 #--------------------------------------------------------
406 self._PRW_type.add_callback_on_lose_focus(self._on_type_set)
407 #--------------------------------------------------------
409 """Set the issuer according to the selected type.
410
411 Matches are fetched from existing records in backend.
412 """
413 pk_curr_type = self._PRW_type.GetData()
414 if pk_curr_type is None:
415 return True
416 rows, idx = gmPG2.run_ro_queries(queries = [{
417 'cmd': u"select issuer from dem.enum_ext_id_types where pk = %s",
418 'args': [pk_curr_type]
419 }])
420 if len(rows) == 0:
421 return True
422 wx.CallAfter(self._PRW_issuer.SetText, rows[0][0])
423 return True
424 #--------------------------------------------------------
426
427 no_errors = True
428
429 # do not test .GetData() because adding external IDs
430 # will create types if necessary
431 # if self._PRW_type.GetData() is None:
432 if self._PRW_type.GetValue().strip() == u'':
433 self._PRW_type.SetBackgroundColour('pink')
434 self._PRW_type.SetFocus()
435 self._PRW_type.Refresh()
436 else:
437 self._PRW_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
438 self._PRW_type.Refresh()
439
440 if self._TCTRL_value.GetValue().strip() == u'':
441 self._TCTRL_value.SetBackgroundColour('pink')
442 self._TCTRL_value.SetFocus()
443 self._TCTRL_value.Refresh()
444 no_errors = False
445 else:
446 self._TCTRL_value.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
447 self._TCTRL_value.Refresh()
448
449 return no_errors
450 #------------------------------------------------------------
451 from Gnumed.wxGladeWidgets import wxgIdentityEAPnl
452
454 """An edit area for editing/creating title/gender/dob/dod etc."""
455
457
458 try:
459 data = kwargs['identity']
460 del kwargs['identity']
461 except KeyError:
462 data = None
463
464 wxgIdentityEAPnl.wxgIdentityEAPnl.__init__(self, *args, **kwargs)
465 gmEditArea.cGenericEditAreaMixin.__init__(self)
466
467 self.mode = 'new'
468 self.data = data
469 if data is not None:
470 self.mode = 'edit'
471
472 # self.__init_ui()
473 #----------------------------------------------------------------
474 # def __init_ui(self):
475 # # adjust phrasewheels etc
476 #----------------------------------------------------------------
477 # generic Edit Area mixin API
478 #----------------------------------------------------------------
480
481 has_error = False
482
483 if self._PRW_gender.GetData() is None:
484 self._PRW_gender.SetFocus()
485 has_error = True
486
487 if not self._PRW_dob.is_valid_timestamp():
488 val = self._PRW_dob.GetValue().strip()
489 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val)
490 self._PRW_dob.SetBackgroundColour('pink')
491 self._PRW_dob.Refresh()
492 self._PRW_dob.SetFocus()
493 has_error = True
494 else:
495 self._PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
496 self._PRW_dob.Refresh()
497
498 if not self._DP_dod.is_valid_timestamp(allow_none=True):
499 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.'))
500 self._DP_dod.SetFocus()
501 has_error = True
502
503 return (has_error is False)
504 #----------------------------------------------------------------
508 #----------------------------------------------------------------
510
511 self.data['gender'] = self._PRW_gender.GetData()
512
513 if self._PRW_dob.GetValue().strip() == u'':
514 self.data['dob'] = None
515 else:
516 self.data['dob'] = self._PRW_dob.GetData().get_pydt()
517
518 self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'')
519 self.data['deceased'] = self._DP_dod.GetValue(as_pydt = True)
520 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
521
522 self.data.save()
523 return True
524 #----------------------------------------------------------------
527 #----------------------------------------------------------------
529
530 self._LBL_info.SetLabel(u'ID: #%s' % (
531 self.data.ID
532 # FIXME: add 'deleted' status
533 ))
534 self._PRW_dob.SetText (
535 value = self.data.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()),
536 data = self.data['dob']
537 )
538 self._DP_dod.SetValue(self.data['deceased'])
539 self._PRW_gender.SetData(self.data['gender'])
540 #self._PRW_ethnicity.SetValue()
541 self._PRW_title.SetText(gmTools.coalesce(self.data['title'], u''))
542 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
543 #----------------------------------------------------------------
546
547 #------------------------------------------------------------
548 from Gnumed.wxGladeWidgets import wxgNameGenderDOBEditAreaPnl
549
551 """An edit area for editing/creating name/gender/dob.
552
553 Does NOT act on/listen to the current patient.
554 """
556
557 self.__name = kwargs['name']
558 del kwargs['name']
559 self.__identity = gmPerson.cIdentity(aPK_obj = self.__name['pk_identity'])
560
561 wxgNameGenderDOBEditAreaPnl.wxgNameGenderDOBEditAreaPnl.__init__(self, *args, **kwargs)
562
563 self.__register_interests()
564 self.refresh()
565 #--------------------------------------------------------
566 # external API
567 #--------------------------------------------------------
569 if self.__name is None:
570 return
571
572 self._PRW_title.SetText(gmTools.coalesce(self.__name['title'], u''))
573 self._PRW_firstname.SetText(self.__name['firstnames'])
574 self._PRW_lastname.SetText(self.__name['lastnames'])
575 self._PRW_nick.SetText(gmTools.coalesce(self.__name['preferred'], u''))
576 self._PRW_dob.SetText (
577 value = self.__identity.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()),
578 data = self.__identity['dob']
579 )
580 self._PRW_gender.SetData(self.__name['gender'])
581 self._CHBOX_active.SetValue(self.__name['active_name'])
582 self._DP_dod.SetValue(self.__identity['deceased'])
583 self._TCTRL_comment.SetValue(gmTools.coalesce(self.__name['comment'], u''))
584 # FIXME: clear fields
585 # else:
586 # pass
587 #--------------------------------------------------------
589
590 if not self.__valid_for_save():
591 return False
592
593 self.__identity['gender'] = self._PRW_gender.GetData()
594 if self._PRW_dob.GetValue().strip() == u'':
595 self.__identity['dob'] = None
596 else:
597 self.__identity['dob'] = self._PRW_dob.GetData().get_pydt()
598 self.__identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'')
599 self.__identity['deceased'] = self._DP_dod.GetValue(as_pydt = True)
600 self.__identity.save_payload()
601
602 active = self._CHBOX_active.GetValue()
603 first = self._PRW_firstname.GetValue().strip()
604 last = self._PRW_lastname.GetValue().strip()
605 old_nick = self.__name['preferred']
606
607 # is it a new name ?
608 old_name = self.__name['firstnames'] + self.__name['lastnames']
609 if (first + last) != old_name:
610 self.__name = self.__identity.add_name(first, last, active)
611
612 self.__name['active_name'] = active
613 self.__name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
614 self.__name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
615
616 self.__name.save_payload()
617
618 return True
619 #--------------------------------------------------------
620 # event handling
621 #--------------------------------------------------------
623 self._PRW_firstname.add_callback_on_lose_focus(self._on_name_set)
624 #--------------------------------------------------------
626 """Set the gender according to entered firstname.
627
628 Matches are fetched from existing records in backend.
629 """
630 firstname = self._PRW_firstname.GetValue().strip()
631 if firstname == u'':
632 return True
633 rows, idx = gmPG2.run_ro_queries(queries = [{
634 'cmd': u"select gender from dem.name_gender_map where name ilike %s",
635 'args': [firstname]
636 }])
637 if len(rows) == 0:
638 return True
639 wx.CallAfter(self._PRW_gender.SetData, rows[0][0])
640 return True
641 #--------------------------------------------------------
642 # internal helpers
643 #--------------------------------------------------------
645
646 has_error = False
647
648 if self._PRW_gender.GetData() is None:
649 self._PRW_gender.SetBackgroundColour('pink')
650 self._PRW_gender.Refresh()
651 self._PRW_gender.SetFocus()
652 has_error = True
653 else:
654 self._PRW_gender.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
655 self._PRW_gender.Refresh()
656
657 if not self._PRW_dob.is_valid_timestamp():
658 val = self._PRW_dob.GetValue().strip()
659 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val)
660 self._PRW_dob.SetBackgroundColour('pink')
661 self._PRW_dob.Refresh()
662 self._PRW_dob.SetFocus()
663 has_error = True
664 else:
665 self._PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
666 self._PRW_dob.Refresh()
667
668 if not self._DP_dod.is_valid_timestamp():
669 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.'))
670 self._DP_dod.SetBackgroundColour('pink')
671 self._DP_dod.Refresh()
672 self._DP_dod.SetFocus()
673 has_error = True
674 else:
675 self._DP_dod.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
676 self._DP_dod.Refresh()
677
678 if self._PRW_lastname.GetValue().strip() == u'':
679 self._PRW_lastname.SetBackgroundColour('pink')
680 self._PRW_lastname.Refresh()
681 self._PRW_lastname.SetFocus()
682 has_error = True
683 else:
684 self._PRW_lastname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
685 self._PRW_lastname.Refresh()
686
687 if self._PRW_firstname.GetValue().strip() == u'':
688 self._PRW_firstname.SetBackgroundColour('pink')
689 self._PRW_firstname.Refresh()
690 self._PRW_firstname.SetFocus()
691 has_error = True
692 else:
693 self._PRW_firstname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
694 self._PRW_firstname.Refresh()
695
696 return (has_error is False)
697 #------------------------------------------------------------
698 # list manager
699 #------------------------------------------------------------
701 """A list for managing a person's names.
702
703 Does NOT act on/listen to the current patient.
704 """
706
707 try:
708 self.__identity = kwargs['identity']
709 del kwargs['identity']
710 except KeyError:
711 self.__identity = None
712
713 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
714
715 self.new_callback = self._add_name
716 self.edit_callback = self._edit_name
717 self.delete_callback = self._del_name
718 self.refresh_callback = self.refresh
719
720 self.__init_ui()
721 self.refresh()
722 #--------------------------------------------------------
723 # external API
724 #--------------------------------------------------------
726 if self.__identity is None:
727 self._LCTRL_items.set_string_items()
728 return
729
730 names = self.__identity.get_names()
731 self._LCTRL_items.set_string_items (
732 items = [ [
733 gmTools.bool2str(n['active_name'], 'X', ''),
734 n['lastnames'],
735 n['firstnames'],
736 gmTools.coalesce(n['preferred'], u''),
737 gmTools.coalesce(n['comment'], u'')
738 ] for n in names ]
739 )
740 self._LCTRL_items.set_column_widths()
741 self._LCTRL_items.set_data(data = names)
742 #--------------------------------------------------------
743 # internal helpers
744 #--------------------------------------------------------
746 self._LCTRL_items.set_columns(columns = [
747 _('Active'),
748 _('Lastname'),
749 _('Firstname(s)'),
750 _('Preferred Name'),
751 _('Comment')
752 ])
753 #--------------------------------------------------------
755 ea = cNameGenderDOBEditAreaPnl(self, -1, name = self.__identity.get_active_name())
756 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
757 dlg.SetTitle(_('Adding new name'))
758 if dlg.ShowModal() == wx.ID_OK:
759 dlg.Destroy()
760 return True
761 dlg.Destroy()
762 return False
763 #--------------------------------------------------------
765 ea = cNameGenderDOBEditAreaPnl(self, -1, name = name)
766 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
767 dlg.SetTitle(_('Editing name'))
768 if dlg.ShowModal() == wx.ID_OK:
769 dlg.Destroy()
770 return True
771 dlg.Destroy()
772 return False
773 #--------------------------------------------------------
775
776 if len(self.__identity.get_names()) == 1:
777 gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the only name of a person.'), beep = True)
778 return False
779
780 go_ahead = gmGuiHelpers.gm_show_question (
781 _( 'It is often advisable to keep old names around and\n'
782 'just create a new "currently active" name.\n'
783 '\n'
784 'This allows finding the patient by both the old\n'
785 'and the new name (think before/after marriage).\n'
786 '\n'
787 'Do you still want to really delete\n'
788 "this name from the patient ?"
789 ),
790 _('Deleting name')
791 )
792 if not go_ahead:
793 return False
794
795 self.__identity.delete_name(name = name)
796 return True
797 #--------------------------------------------------------
798 # properties
799 #--------------------------------------------------------
802
806
807 identity = property(_get_identity, _set_identity)
808 #------------------------------------------------------------
810 """A list for managing a person's external IDs.
811
812 Does NOT act on/listen to the current patient.
813 """
815
816 try:
817 self.__identity = kwargs['identity']
818 del kwargs['identity']
819 except KeyError:
820 self.__identity = None
821
822 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
823
824 self.new_callback = self._add_id
825 self.edit_callback = self._edit_id
826 self.delete_callback = self._del_id
827 self.refresh_callback = self.refresh
828
829 self.__init_ui()
830 self.refresh()
831 #--------------------------------------------------------
832 # external API
833 #--------------------------------------------------------
835 if self.__identity is None:
836 self._LCTRL_items.set_string_items()
837 return
838
839 ids = self.__identity.get_external_ids()
840 self._LCTRL_items.set_string_items (
841 items = [ [
842 i['name'],
843 i['value'],
844 gmTools.coalesce(i['issuer'], u''),
845 gmTools.coalesce(i['comment'], u'')
846 ] for i in ids
847 ]
848 )
849 self._LCTRL_items.set_column_widths()
850 self._LCTRL_items.set_data(data = ids)
851 #--------------------------------------------------------
852 # internal helpers
853 #--------------------------------------------------------
855 self._LCTRL_items.set_columns(columns = [
856 _('ID type'),
857 _('Value'),
858 _('Issuer'),
859 _('Comment')
860 ])
861 #--------------------------------------------------------
863 ea = cExternalIDEditAreaPnl(self, -1)
864 ea.identity = self.__identity
865 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
866 dlg.SetTitle(_('Adding new external ID'))
867 if dlg.ShowModal() == wx.ID_OK:
868 dlg.Destroy()
869 return True
870 dlg.Destroy()
871 return False
872 #--------------------------------------------------------
874 ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id)
875 ea.identity = self.__identity
876 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
877 dlg.SetTitle(_('Editing external ID'))
878 if dlg.ShowModal() == wx.ID_OK:
879 dlg.Destroy()
880 return True
881 dlg.Destroy()
882 return False
883 #--------------------------------------------------------
885 go_ahead = gmGuiHelpers.gm_show_question (
886 _( 'Do you really want to delete this\n'
887 'external ID from the patient ?'),
888 _('Deleting external ID')
889 )
890 if not go_ahead:
891 return False
892 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id'])
893 return True
894 #--------------------------------------------------------
895 # properties
896 #--------------------------------------------------------
899
903
904 identity = property(_get_identity, _set_identity)
905 #------------------------------------------------------------
906 # integrated panels
907 #------------------------------------------------------------
908 from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl
909
911 """A panel for editing identity data for a person.
912
913 - provides access to:
914 - name
915 - external IDs
916
917 Does NOT act on/listen to the current patient.
918 """
920
921 wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs)
922
923 self.__identity = None
924 self.refresh()
925 #--------------------------------------------------------
926 # external API
927 #--------------------------------------------------------
929 self._PNL_names.identity = self.__identity
930 self._PNL_ids.identity = self.__identity
931 # this is an Edit Area:
932 self._PNL_identity.mode = 'new'
933 self._PNL_identity.data = self.__identity
934 if self.__identity is not None:
935 self._PNL_identity.mode = 'edit'
936 #--------------------------------------------------------
937 # properties
938 #--------------------------------------------------------
941
945
946 identity = property(_get_identity, _set_identity)
947 #--------------------------------------------------------
948 # event handlers
949 #--------------------------------------------------------
953 #--------------------------------------------------------
956
957 #============================================================
958 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl
959
960 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
962
963 wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs)
964
965 self.__identity = None
966 self._PRW_provider.selection_only = False
967 self.refresh()
968 #--------------------------------------------------------
969 # external API
970 #--------------------------------------------------------
972
973 tt = _("Link another person in this database as the emergency contact.")
974
975 if self.__identity is None:
976 self._TCTRL_er_contact.SetValue(u'')
977 self._TCTRL_person.person = None
978 self._TCTRL_person.SetToolTipString(tt)
979
980 self._PRW_provider.SetText(value = u'', data = None)
981 return
982
983 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], u''))
984 if self.__identity['pk_emergency_contact'] is not None:
985 ident = gmPerson.cIdentity(aPK_obj = self.__identity['pk_emergency_contact'])
986 self._TCTRL_person.person = ident
987 tt = u'%s\n\n%s\n\n%s' % (
988 tt,
989 ident['description_gender'],
990 u'\n'.join([
991 u'%s: %s%s' % (
992 c['l10n_comm_type'],
993 c['url'],
994 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), u'', u'')
995 )
996 for c in ident.get_comm_channels()
997 ])
998 )
999 else:
1000 self._TCTRL_person.person = None
1001
1002 self._TCTRL_person.SetToolTipString(tt)
1003
1004 print "refreshing provider"
1005 print self.__identity
1006 print self.__identity['pk_primary_provider']
1007 if self.__identity['pk_primary_provider'] is None:
1008 self._PRW_provider.SetText(value = u'', data = None)
1009 else:
1010 self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
1011 #--------------------------------------------------------
1012 # properties
1013 #--------------------------------------------------------
1016
1020
1021 identity = property(_get_identity, _set_identity)
1022 #--------------------------------------------------------
1023 # event handlers
1024 #--------------------------------------------------------
1038 #--------------------------------------------------------
1049 #--------------------------------------------------------
1057 #============================================================
1058 # new-patient widgets
1059 #============================================================
1061
1062 dbcfg = gmCfg.cCfgSQL()
1063
1064 def_region = dbcfg.get2 (
1065 option = u'person.create.default_region',
1066 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1067 bias = u'user'
1068 )
1069 def_country = None
1070
1071 if def_region is None:
1072 def_country = dbcfg.get2 (
1073 option = u'person.create.default_country',
1074 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1075 bias = u'user'
1076 )
1077 else:
1078 countries = gmDemographicRecord.get_country_for_region(region = def_region)
1079 if len(countries) == 1:
1080 def_country = countries[0]['l10n_country']
1081
1082 if parent is None:
1083 parent = wx.GetApp().GetTopWindow()
1084
1085 ea = cNewPatientEAPnl(parent = parent, id = -1, country = def_country, region = def_region)
1086 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
1087 dlg.SetTitle(_('Adding new person'))
1088 ea._PRW_lastname.SetFocus()
1089 result = dlg.ShowModal()
1090 pat = ea.data
1091 dlg.Destroy()
1092
1093 if result != wx.ID_OK:
1094 return False
1095
1096 _log.debug('created new person [%s]', pat.ID)
1097
1098 if activate:
1099 from Gnumed.wxpython import gmPatSearchWidgets
1100 gmPatSearchWidgets.set_active_patient(patient = pat)
1101
1102 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
1103
1104 return True
1105 #============================================================
1106 from Gnumed.wxGladeWidgets import wxgNewPatientEAPnl
1107
1108 -class cNewPatientEAPnl(wxgNewPatientEAPnl.wxgNewPatientEAPnl, gmEditArea.cGenericEditAreaMixin):
1109
1111
1112 try:
1113 self.default_region = kwargs['region']
1114 del kwargs['region']
1115 except KeyError:
1116 self.default_region = None
1117
1118 try:
1119 self.default_country = kwargs['country']
1120 del kwargs['country']
1121 except KeyError:
1122 self.default_country = None
1123
1124 wxgNewPatientEAPnl.wxgNewPatientEAPnl.__init__(self, *args, **kwargs)
1125 gmEditArea.cGenericEditAreaMixin.__init__(self)
1126
1127 self.mode = 'new'
1128 self.data = None
1129 self._address = None
1130
1131 self.__init_ui()
1132 self.__register_interests()
1133 #----------------------------------------------------------------
1134 # internal helpers
1135 #----------------------------------------------------------------
1137 self._PRW_lastname.final_regex = '.+'
1138 self._PRW_firstnames.final_regex = '.+'
1139 self._PRW_address_searcher.selection_only = False
1140
1141 # don't do that or else it will turn <invalid> into <today> :-(
1142 # low = wx.DateTimeFromDMY(1,0,1900)
1143 # hi = wx.DateTime()
1144 # self._DP_dob.SetRange(low, hi.SetToCurrent())
1145 #self._DP_dob.SetValue(None)
1146
1147 # only if we would support None on selection_only's:
1148 # self._PRW_external_id_type.selection_only = True
1149
1150 if self.default_country is not None:
1151 self._PRW_country.SetText(value = self.default_country)
1152
1153 if self.default_region is not None:
1154 self._PRW_region.SetText(value = self.default_region)
1155 #----------------------------------------------------------------
1157
1158 adr = self._PRW_address_searcher.get_address()
1159 if adr is None:
1160 return True
1161
1162 if ctrl.GetValue().strip() != adr[field]:
1163 wx.CallAfter(self._PRW_address_searcher.SetText, value = u'', data = None)
1164 return True
1165
1166 return False
1167 #----------------------------------------------------------------
1169 adr = self._PRW_address_searcher.get_address()
1170 if adr is None:
1171 return True
1172
1173 self._PRW_zip.SetText(value = adr['postcode'], data = adr['postcode'])
1174
1175 self._PRW_street.SetText(value = adr['street'], data = adr['street'])
1176 self._PRW_street.set_context(context = u'zip', val = adr['postcode'])
1177
1178 self._TCTRL_number.SetValue(adr['number'])
1179
1180 self._PRW_urb.SetText(value = adr['urb'], data = adr['urb'])
1181 self._PRW_urb.set_context(context = u'zip', val = adr['postcode'])
1182
1183 self._PRW_region.SetText(value = adr['l10n_state'], data = adr['code_state'])
1184 self._PRW_region.set_context(context = u'zip', val = adr['postcode'])
1185
1186 self._PRW_country.SetText(value = adr['l10n_country'], data = adr['code_country'])
1187 self._PRW_country.set_context(context = u'zip', val = adr['postcode'])
1188 #----------------------------------------------------------------
1190 error = False
1191
1192 # name fields
1193 if self._PRW_lastname.GetValue().strip() == u'':
1194 error = True
1195 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.'))
1196 self._PRW_lastname.display_as_valid(False)
1197 else:
1198 self._PRW_lastname.display_as_valid(True)
1199
1200 if self._PRW_firstnames.GetValue().strip() == '':
1201 error = True
1202 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.'))
1203 self._PRW_firstnames.display_as_valid(False)
1204 else:
1205 self._PRW_firstnames.display_as_valid(True)
1206
1207 # gender
1208 if self._PRW_gender.GetData() is None:
1209 error = True
1210 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.'))
1211 self._PRW_gender.display_as_valid(False)
1212 else:
1213 self._PRW_gender.display_as_valid(True)
1214
1215 # dob validation
1216 dob = self._DP_dob.GetValue(as_pydt = False, invalid_as_none = True)
1217 # 1) valid timestamp ?
1218 if self._DP_dob.is_valid_timestamp(allow_none = False): # properly colors the field
1219 # but year also usable ?
1220 msg = None
1221 if (dob.GetYear() < 1900):
1222 msg = _(
1223 'DOB: %s\n'
1224 '\n'
1225 'While this is a valid point in time Python does\n'
1226 'not know how to deal with it.\n'
1227 '\n'
1228 'We suggest using January 1st 1901 instead and adding\n'
1229 'the true date of birth to the patient comment.\n'
1230 '\n'
1231 'Sorry for the inconvenience %s'
1232 ) % (dob, gmTools.u_frowning_face)
1233 elif dob > gmDateTime.wx_now_here(wx = wx):
1234 msg = _(
1235 'DOB: %s\n'
1236 '\n'
1237 'Date of birth in the future !'
1238 ) % dob
1239
1240 if msg is not None:
1241 error = True
1242 gmGuiHelpers.gm_show_error (
1243 msg,
1244 _('Registering new person')
1245 )
1246 self._DP_dob.display_as_valid(False)
1247 self._DP_dob.SetFocus()
1248 # 2) invalid timestamp ?
1249 # Do we have to check for u'', ever ?
1250 else:
1251 allow_empty_dob = gmGuiHelpers.gm_show_question (
1252 _(
1253 'Are you sure you want to register this person\n'
1254 'without a valid date of birth ?\n'
1255 '\n'
1256 'This can be useful for temporary staff members\n'
1257 'but will provoke nag screens if this person\n'
1258 'becomes a patient.\n'
1259 ),
1260 _('Registering new person')
1261 )
1262 if allow_empty_dob:
1263 self._DP_dob.display_as_valid(True)
1264 else:
1265 error = True
1266 self._DP_dob.SetFocus()
1267
1268 # TOB validation if non-empty
1269 # if self._TCTRL_tob.GetValue().strip() != u'':
1270
1271 return (not error)
1272 #----------------------------------------------------------------
1274
1275 # existing address ? if so set other fields
1276 if self._PRW_address_searcher.GetData() is not None:
1277 wx.CallAfter(self.__set_fields_from_address_searcher)
1278 return True
1279
1280 # must either all contain something or none of them
1281 fields_to_fill = (
1282 self._TCTRL_number,
1283 self._PRW_zip,
1284 self._PRW_street,
1285 self._PRW_urb,
1286 self._PRW_region,
1287 self._PRW_country
1288 )
1289 no_of_filled_fields = 0
1290
1291 for field in fields_to_fill:
1292 if field.GetValue().strip() != u'':
1293 no_of_filled_fields += 1
1294 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
1295 field.Refresh()
1296
1297 # empty address ?
1298 if no_of_filled_fields == 0:
1299 if empty_address_is_valid:
1300 return True
1301 else:
1302 return None
1303
1304 # incompletely filled address ?
1305 if no_of_filled_fields != len(fields_to_fill):
1306 for field in fields_to_fill:
1307 if field.GetValue().strip() == u'':
1308 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
1309 field.SetFocus()
1310 field.Refresh()
1311 msg = _('To properly create an address, all the related fields must be filled in.')
1312 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
1313 return False
1314
1315 # fields which must contain a selected item
1316 # FIXME: they must also contain an *acceptable combination* which
1317 # FIXME: can only be tested against the database itself ...
1318 strict_fields = (
1319 self._PRW_region,
1320 self._PRW_country
1321 )
1322 error = False
1323 for field in strict_fields:
1324 if field.GetData() is None:
1325 error = True
1326 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
1327 field.SetFocus()
1328 else:
1329 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
1330 field.Refresh()
1331
1332 if error:
1333 msg = _('This field must contain an item selected from the dropdown list.')
1334 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
1335 return False
1336
1337 return True
1338 #----------------------------------------------------------------
1340
1341 # identity
1342 self._PRW_firstnames.add_callback_on_lose_focus(self._on_leaving_firstname)
1343
1344 # address
1345 self._PRW_address_searcher.add_callback_on_lose_focus(self._on_leaving_adress_searcher)
1346
1347 # invalidate address searcher when any field edited
1348 self._PRW_street.add_callback_on_lose_focus(self._invalidate_address_searcher)
1349 wx.EVT_KILL_FOCUS(self._TCTRL_number, self._invalidate_address_searcher)
1350 self._PRW_urb.add_callback_on_lose_focus(self._invalidate_address_searcher)
1351 self._PRW_region.add_callback_on_lose_focus(self._invalidate_address_searcher)
1352
1353 self._PRW_zip.add_callback_on_lose_focus(self._on_leaving_zip)
1354 self._PRW_country.add_callback_on_lose_focus(self._on_leaving_country)
1355 #----------------------------------------------------------------
1356 # event handlers
1357 #----------------------------------------------------------------
1359 """Set the gender according to entered firstname.
1360
1361 Matches are fetched from existing records in backend.
1362 """
1363 # only set if not already set so as to not
1364 # overwrite a change by the user
1365 if self._PRW_gender.GetData() is not None:
1366 return True
1367
1368 firstname = self._PRW_firstnames.GetValue().strip()
1369 if firstname == u'':
1370 return True
1371
1372 gender = gmPerson.map_firstnames2gender(firstnames = firstname)
1373 if gender is None:
1374 return True
1375
1376 wx.CallAfter(self._PRW_gender.SetData, gender)
1377 return True
1378 #----------------------------------------------------------------
1380 self.__perhaps_invalidate_address_searcher(self._PRW_zip, 'postcode')
1381
1382 zip_code = gmTools.none_if(self._PRW_zip.GetValue().strip(), u'')
1383 self._PRW_street.set_context(context = u'zip', val = zip_code)
1384 self._PRW_urb.set_context(context = u'zip', val = zip_code)
1385 self._PRW_region.set_context(context = u'zip', val = zip_code)
1386 self._PRW_country.set_context(context = u'zip', val = zip_code)
1387
1388 return True
1389 #----------------------------------------------------------------
1391 self.__perhaps_invalidate_address_searcher(self._PRW_country, 'l10n_country')
1392
1393 country = gmTools.none_if(self._PRW_country.GetValue().strip(), u'')
1394 self._PRW_region.set_context(context = u'country', val = country)
1395
1396 return True
1397 #----------------------------------------------------------------
1399 mapping = [
1400 (self._PRW_street, 'street'),
1401 (self._TCTRL_number, 'number'),
1402 (self._PRW_urb, 'urb'),
1403 (self._PRW_region, 'l10n_state')
1404 ]
1405
1406 # loop through fields and invalidate address searcher if different
1407 for ctrl, field in mapping:
1408 if self.__perhaps_invalidate_address_searcher(ctrl, field):
1409 return True
1410
1411 return True
1412 #----------------------------------------------------------------
1414 adr = self._PRW_address_searcher.get_address()
1415 if adr is None:
1416 return True
1417
1418 wx.CallAfter(self.__set_fields_from_address_searcher)
1419 return True
1420 #----------------------------------------------------------------
1421 # generic Edit Area mixin API
1422 #----------------------------------------------------------------
1424 return (self.__identity_valid_for_save() and self.__address_valid_for_save(empty_address_is_valid = True))
1425 #----------------------------------------------------------------
1427
1428 # identity
1429 new_identity = gmPerson.create_identity (
1430 gender = self._PRW_gender.GetData(),
1431 dob = self._DP_dob.get_pydt(),
1432 lastnames = self._PRW_lastname.GetValue().strip(),
1433 firstnames = self._PRW_firstnames.GetValue().strip()
1434 )
1435 _log.debug('identity created: %s' % new_identity)
1436
1437 new_identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip())
1438 new_identity.set_nickname(nickname = gmTools.none_if(self._PRW_nickname.GetValue().strip(), u''))
1439 #TOB
1440 new_identity.save()
1441
1442 name = new_identity.get_active_name()
1443 name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1444 name.save()
1445
1446 # address
1447 is_valid = self.__address_valid_for_save(empty_address_is_valid = False)
1448 if is_valid is True:
1449 # because we currently only check for non-emptiness
1450 # we must still deal with database errors
1451 try:
1452 new_identity.link_address (
1453 number = self._TCTRL_number.GetValue().strip(),
1454 street = self._PRW_street.GetValue().strip(),
1455 postcode = self._PRW_zip.GetValue().strip(),
1456 urb = self._PRW_urb.GetValue().strip(),
1457 state = self._PRW_region.GetData(),
1458 country = self._PRW_country.GetData()
1459 )
1460 except gmPG2.dbapi.InternalError:
1461 _log.debug('number: >>%s<<', self._TCTRL_number.GetValue().strip())
1462 _log.debug('street: >>%s<<', self._PRW_street.GetValue().strip())
1463 _log.debug('postcode: >>%s<<', self._PRW_zip.GetValue().strip())
1464 _log.debug('urb: >>%s<<', self._PRW_urb.GetValue().strip())
1465 _log.debug('state: >>%s<<', self._PRW_region.GetData().strip())
1466 _log.debug('country: >>%s<<', self._PRW_country.GetData().strip())
1467 _log.exception('cannot link address')
1468 gmGuiHelpers.gm_show_error (
1469 aTitle = _('Saving address'),
1470 aMessage = _(
1471 'Cannot save this address.\n'
1472 '\n'
1473 'You will have to add it via the Demographics plugin.\n'
1474 )
1475 )
1476 elif is_valid is False:
1477 gmGuiHelpers.gm_show_error (
1478 aTitle = _('Saving address'),
1479 aMessage = _(
1480 'Address not saved.\n'
1481 '\n'
1482 'You will have to add it via the Demographics plugin.\n'
1483 )
1484 )
1485 # else it is None which means empty address which we ignore
1486
1487 # phone
1488 new_identity.link_comm_channel (
1489 comm_medium = u'homephone',
1490 url = gmTools.none_if(self._TCTRL_phone.GetValue().strip(), u''),
1491 is_confidential = False
1492 )
1493
1494 # external ID
1495 pk_type = self._PRW_external_id_type.GetData()
1496 id_value = self._TCTRL_external_id_value.GetValue().strip()
1497 if (pk_type is not None) and (id_value != u''):
1498 new_identity.add_external_id(value = id_value, pk_type = pk_type)
1499
1500 # occupation
1501 new_identity.link_occupation (
1502 occupation = gmTools.none_if(self._PRW_occupation.GetValue().strip(), u'')
1503 )
1504
1505 self.data = new_identity
1506 return True
1507 #----------------------------------------------------------------
1510 #----------------------------------------------------------------
1514 #----------------------------------------------------------------
1517 #----------------------------------------------------------------
1520 #============================================================
1521 # new-patient wizard classes
1522 #============================================================
1524 """
1525 Wizard page for entering patient's basic demographic information
1526 """
1527
1528 form_fields = (
1529 'firstnames', 'lastnames', 'nick', 'dob', 'gender', 'title', 'occupation',
1530 'address_number', 'zip_code', 'street', 'town', 'state', 'country', 'phone', 'comment'
1531 )
1532
1534 """
1535 Creates a new instance of BasicPatDetailsPage
1536 @param parent - The parent widget
1537 @type parent - A wx.Window instance
1538 @param tile - The title of the page
1539 @type title - A StringType instance
1540 """
1541 wx.wizard.WizardPageSimple.__init__(self, parent) #, bitmap = gmGuiHelpers.gm_icon(_('oneperson'))
1542 self.__title = title
1543 self.__do_layout()
1544 self.__register_interests()
1545 #--------------------------------------------------------
1547 PNL_form = wx.Panel(self, -1)
1548
1549 # last name
1550 STT_lastname = wx.StaticText(PNL_form, -1, _('Last name'))
1551 STT_lastname.SetForegroundColour('red')
1552 self.PRW_lastname = cLastnamePhraseWheel(parent = PNL_form, id = -1)
1553 self.PRW_lastname.SetToolTipString(_('Required: lastname (family name)'))
1554
1555 # first name
1556 STT_firstname = wx.StaticText(PNL_form, -1, _('First name(s)'))
1557 STT_firstname.SetForegroundColour('red')
1558 self.PRW_firstname = cFirstnamePhraseWheel(parent = PNL_form, id = -1)
1559 self.PRW_firstname.SetToolTipString(_('Required: surname/given name/first name'))
1560
1561 # nickname
1562 STT_nick = wx.StaticText(PNL_form, -1, _('Nick name'))
1563 self.PRW_nick = cNicknamePhraseWheel(parent = PNL_form, id = -1)
1564
1565 # DOB
1566 STT_dob = wx.StaticText(PNL_form, -1, _('Date of birth'))
1567 STT_dob.SetForegroundColour('red')
1568 self.PRW_dob = gmDateTimeInput.cFuzzyTimestampInput(parent = PNL_form, id = -1)
1569 self.PRW_dob.SetToolTipString(_("Required: date of birth, if unknown or aliasing wanted then invent one"))
1570
1571 # gender
1572 STT_gender = wx.StaticText(PNL_form, -1, _('Gender'))
1573 STT_gender.SetForegroundColour('red')
1574 self.PRW_gender = cGenderSelectionPhraseWheel(parent = PNL_form, id=-1)
1575 self.PRW_gender.SetToolTipString(_("Required: gender of patient"))
1576
1577 # title
1578 STT_title = wx.StaticText(PNL_form, -1, _('Title'))
1579 self.PRW_title = cTitlePhraseWheel(parent = PNL_form, id = -1)
1580
1581 # zip code
1582 STT_zip_code = wx.StaticText(PNL_form, -1, _('Postal code'))
1583 STT_zip_code.SetForegroundColour('orange')
1584 self.PRW_zip_code = gmPersonContactWidgets.cZipcodePhraseWheel(parent = PNL_form, id = -1)
1585 self.PRW_zip_code.SetToolTipString(_("primary/home address: zip/postal code"))
1586
1587 # street
1588 STT_street = wx.StaticText(PNL_form, -1, _('Street'))
1589 STT_street.SetForegroundColour('orange')
1590 self.PRW_street = gmPersonContactWidgets.cStreetPhraseWheel(parent = PNL_form, id = -1)
1591 self.PRW_street.SetToolTipString(_("primary/home address: name of street"))
1592
1593 # address number
1594 STT_address_number = wx.StaticText(PNL_form, -1, _('Number'))
1595 STT_address_number.SetForegroundColour('orange')
1596 self.TTC_address_number = wx.TextCtrl(PNL_form, -1)
1597 self.TTC_address_number.SetToolTipString(_("primary/home address: address number"))
1598
1599 # town
1600 STT_town = wx.StaticText(PNL_form, -1, _('Place'))
1601 STT_town.SetForegroundColour('orange')
1602 self.PRW_town = gmPersonContactWidgets.cUrbPhraseWheel(parent = PNL_form, id = -1)
1603 self.PRW_town.SetToolTipString(_("primary/home address: city/town/village/dwelling/..."))
1604
1605 # state
1606 STT_state = wx.StaticText(PNL_form, -1, _('Region'))
1607 STT_state.SetForegroundColour('orange')
1608 self.PRW_state = gmPersonContactWidgets.cStateSelectionPhraseWheel(parent=PNL_form, id=-1)
1609 self.PRW_state.SetToolTipString(_("primary/home address: state/province/county/..."))
1610
1611 # country
1612 STT_country = wx.StaticText(PNL_form, -1, _('Country'))
1613 STT_country.SetForegroundColour('orange')
1614 self.PRW_country = gmPersonContactWidgets.cCountryPhraseWheel(parent = PNL_form, id = -1)
1615 self.PRW_country.SetToolTipString(_("primary/home address: country"))
1616
1617 # phone
1618 STT_phone = wx.StaticText(PNL_form, -1, _('Phone'))
1619 self.TTC_phone = wx.TextCtrl(PNL_form, -1)
1620 self.TTC_phone.SetToolTipString(_("phone number at home"))
1621
1622 # occupation
1623 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
1624 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1)
1625
1626 # comment
1627 STT_comment = wx.StaticText(PNL_form, -1, _('Comment'))
1628 self.TCTRL_comment = wx.TextCtrl(PNL_form, -1)
1629 self.TCTRL_comment.SetToolTipString(_('A comment on this patient.'))
1630
1631 # form main validator
1632 self.form_DTD = cFormDTD(fields = self.__class__.form_fields)
1633 PNL_form.SetValidator(cBasicPatDetailsPageValidator(dtd = self.form_DTD))
1634
1635 # layout input widgets
1636 SZR_input = wx.FlexGridSizer(cols = 2, rows = 16, vgap = 4, hgap = 4)
1637 SZR_input.AddGrowableCol(1)
1638 SZR_input.Add(STT_lastname, 0, wx.SHAPED)
1639 SZR_input.Add(self.PRW_lastname, 1, wx.EXPAND)
1640 SZR_input.Add(STT_firstname, 0, wx.SHAPED)
1641 SZR_input.Add(self.PRW_firstname, 1, wx.EXPAND)
1642 SZR_input.Add(STT_nick, 0, wx.SHAPED)
1643 SZR_input.Add(self.PRW_nick, 1, wx.EXPAND)
1644 SZR_input.Add(STT_dob, 0, wx.SHAPED)
1645 SZR_input.Add(self.PRW_dob, 1, wx.EXPAND)
1646 SZR_input.Add(STT_gender, 0, wx.SHAPED)
1647 SZR_input.Add(self.PRW_gender, 1, wx.EXPAND)
1648 SZR_input.Add(STT_title, 0, wx.SHAPED)
1649 SZR_input.Add(self.PRW_title, 1, wx.EXPAND)
1650 SZR_input.Add(STT_zip_code, 0, wx.SHAPED)
1651 SZR_input.Add(self.PRW_zip_code, 1, wx.EXPAND)
1652 SZR_input.Add(STT_street, 0, wx.SHAPED)
1653 SZR_input.Add(self.PRW_street, 1, wx.EXPAND)
1654 SZR_input.Add(STT_address_number, 0, wx.SHAPED)
1655 SZR_input.Add(self.TTC_address_number, 1, wx.EXPAND)
1656 SZR_input.Add(STT_town, 0, wx.SHAPED)
1657 SZR_input.Add(self.PRW_town, 1, wx.EXPAND)
1658 SZR_input.Add(STT_state, 0, wx.SHAPED)
1659 SZR_input.Add(self.PRW_state, 1, wx.EXPAND)
1660 SZR_input.Add(STT_country, 0, wx.SHAPED)
1661 SZR_input.Add(self.PRW_country, 1, wx.EXPAND)
1662 SZR_input.Add(STT_phone, 0, wx.SHAPED)
1663 SZR_input.Add(self.TTC_phone, 1, wx.EXPAND)
1664 SZR_input.Add(STT_occupation, 0, wx.SHAPED)
1665 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
1666 SZR_input.Add(STT_comment, 0, wx.SHAPED)
1667 SZR_input.Add(self.TCTRL_comment, 1, wx.EXPAND)
1668
1669 PNL_form.SetSizerAndFit(SZR_input)
1670
1671 # layout page
1672 SZR_main = gmGuiHelpers.makePageTitle(self, self.__title)
1673 SZR_main.Add(PNL_form, 1, wx.EXPAND)
1674 #--------------------------------------------------------
1675 # event handling
1676 #--------------------------------------------------------
1678 self.PRW_firstname.add_callback_on_lose_focus(self.on_name_set)
1679 self.PRW_country.add_callback_on_selection(self.on_country_selected)
1680 self.PRW_zip_code.add_callback_on_lose_focus(self.on_zip_set)
1681 #--------------------------------------------------------
1683 """Set the states according to entered country."""
1684 self.PRW_state.set_context(context=u'country', val=data)
1685 return True
1686 #--------------------------------------------------------
1688 """Set the gender according to entered firstname.
1689
1690 Matches are fetched from existing records in backend.
1691 """
1692 firstname = self.PRW_firstname.GetValue().strip()
1693 rows, idx = gmPG2.run_ro_queries(queries = [{
1694 'cmd': u"select gender from dem.name_gender_map where name ilike %s",
1695 'args': [firstname]
1696 }])
1697 if len(rows) == 0:
1698 return True
1699 wx.CallAfter(self.PRW_gender.SetData, rows[0][0])
1700 return True
1701 #--------------------------------------------------------
1703 """Set the street, town, state and country according to entered zip code."""
1704 zip_code = self.PRW_zip_code.GetValue().strip()
1705 self.PRW_street.set_context(context=u'zip', val=zip_code)
1706 self.PRW_town.set_context(context=u'zip', val=zip_code)
1707 self.PRW_state.set_context(context=u'zip', val=zip_code)
1708 self.PRW_country.set_context(context=u'zip', val=zip_code)
1709 return True
1710 #============================================================
1712 """
1713 Wizard to create a new patient.
1714
1715 TODO:
1716 - write pages for different "themes" of patient creation
1717 - make it configurable which pages are loaded
1718 - make available sets of pages that apply to a country
1719 - make loading of some pages depend upon values in earlier pages, eg
1720 when the patient is female and older than 13 include a page about
1721 "female" data (number of kids etc)
1722
1723 FIXME: use: wizard.FindWindowById(wx.ID_FORWARD).Disable()
1724 """
1725 #--------------------------------------------------------
1726 - def __init__(self, parent, title = _('Register new person'), subtitle = _('Basic demographic details') ):
1727 """
1728 Creates a new instance of NewPatientWizard
1729 @param parent - The parent widget
1730 @type parent - A wx.Window instance
1731 """
1732 id_wiz = wx.NewId()
1733 wx.wizard.Wizard.__init__(self, parent, id_wiz, title) #images.getWizTest1Bitmap()
1734 self.SetExtraStyle(wx.WS_EX_VALIDATE_RECURSIVELY)
1735 self.__subtitle = subtitle
1736 self.__do_layout()
1737 #--------------------------------------------------------
1739 """Create new patient.
1740
1741 activate, too, if told to do so (and patient successfully created)
1742 """
1743 while True:
1744
1745 if not wx.wizard.Wizard.RunWizard(self, self.basic_pat_details):
1746 return False
1747
1748 try:
1749 # retrieve DTD and create patient
1750 ident = create_identity_from_dtd(dtd = self.basic_pat_details.form_DTD)
1751 except:
1752 _log.exception('cannot add new patient - missing identity fields')
1753 gmGuiHelpers.gm_show_error (
1754 _('Cannot create new patient.\n'
1755 'Missing parts of the identity.'
1756 ),
1757 _('Adding new patient')
1758 )
1759 continue
1760
1761 update_identity_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD)
1762
1763 try:
1764 link_contacts_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD)
1765 except:
1766 _log.exception('cannot finalize new patient - missing address fields')
1767 gmGuiHelpers.gm_show_error (
1768 _('Cannot add address for the new patient.\n'
1769 'You must either enter all of the address fields or\n'
1770 'none at all. The relevant fields are marked in yellow.\n'
1771 '\n'
1772 'You will need to add the address details in the\n'
1773 'demographics module.'
1774 ),
1775 _('Adding new patient')
1776 )
1777 break
1778
1779 link_occupation_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD)
1780
1781 break
1782
1783 if activate:
1784 from Gnumed.wxpython import gmPatSearchWidgets
1785 gmPatSearchWidgets.set_active_patient(patient = ident)
1786
1787 return ident
1788 #--------------------------------------------------------
1789 # internal helpers
1790 #--------------------------------------------------------
1792 """Arrange widgets.
1793 """
1794 # Create the wizard pages
1795 self.basic_pat_details = cBasicPatDetailsPage(self, self.__subtitle )
1796 self.FitToPage(self.basic_pat_details)
1797 #============================================================
1798 #============================================================
1800 """
1801 This validator is used to ensure that the user has entered all
1802 the required conditional values in the page (eg., to properly
1803 create an address, all the related fields must be filled).
1804 """
1805 #--------------------------------------------------------
1807 """
1808 Validator initialization.
1809 @param dtd The object containing the data model.
1810 @type dtd A cFormDTD instance
1811 """
1812 # initialize parent class
1813 wx.PyValidator.__init__(self)
1814 # validator's storage object
1815 self.form_DTD = dtd
1816 #--------------------------------------------------------
1818 """
1819 Standard cloner.
1820 Note that every validator must implement the Clone() method.
1821 """
1822 return cBasicPatDetailsPageValidator(dtd = self.form_DTD) # FIXME: probably need new instance of DTD ?
1823 #--------------------------------------------------------
1825 """
1826 Validate the contents of the given text control.
1827 """
1828 _pnl_form = self.GetWindow().GetParent()
1829
1830 error = False
1831
1832 # name fields
1833 if _pnl_form.PRW_lastname.GetValue().strip() == '':
1834 error = True
1835 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.'))
1836 _pnl_form.PRW_lastname.SetBackgroundColour('pink')
1837 _pnl_form.PRW_lastname.Refresh()
1838 else:
1839 _pnl_form.PRW_lastname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1840 _pnl_form.PRW_lastname.Refresh()
1841
1842 if _pnl_form.PRW_firstname.GetValue().strip() == '':
1843 error = True
1844 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.'))
1845 _pnl_form.PRW_firstname.SetBackgroundColour('pink')
1846 _pnl_form.PRW_firstname.Refresh()
1847 else:
1848 _pnl_form.PRW_firstname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1849 _pnl_form.PRW_firstname.Refresh()
1850
1851 # gender
1852 if _pnl_form.PRW_gender.GetData() is None:
1853 error = True
1854 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.'))
1855 _pnl_form.PRW_gender.SetBackgroundColour('pink')
1856 _pnl_form.PRW_gender.Refresh()
1857 else:
1858 _pnl_form.PRW_gender.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1859 _pnl_form.PRW_gender.Refresh()
1860
1861 # dob validation
1862 if (
1863 (_pnl_form.PRW_dob.GetValue().strip() == u'')
1864 or (not _pnl_form.PRW_dob.is_valid_timestamp())
1865 or (_pnl_form.PRW_dob.GetData().timestamp.year < 1900)
1866 ):
1867 error = True
1868 msg = _('Cannot parse <%s> into proper timestamp.') % _pnl_form.PRW_dob.GetValue()
1869 gmDispatcher.send(signal = 'statustext', msg = msg)
1870 _pnl_form.PRW_dob.SetBackgroundColour('pink')
1871 _pnl_form.PRW_dob.Refresh()
1872 else:
1873 _pnl_form.PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1874 _pnl_form.PRW_dob.Refresh()
1875
1876 # address
1877 is_any_field_filled = False
1878 address_fields = (
1879 _pnl_form.TTC_address_number,
1880 _pnl_form.PRW_zip_code,
1881 _pnl_form.PRW_street,
1882 _pnl_form.PRW_town
1883 )
1884 for field in address_fields:
1885 if field.GetValue().strip() == u'':
1886 if is_any_field_filled:
1887 error = True
1888 msg = _('To properly create an address, all the related fields must be filled in.')
1889 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
1890 field.SetBackgroundColour('pink')
1891 field.SetFocus()
1892 field.Refresh()
1893 else:
1894 is_any_field_filled = True
1895 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1896 field.Refresh()
1897
1898 address_fields = (
1899 _pnl_form.PRW_state,
1900 _pnl_form.PRW_country
1901 )
1902 for field in address_fields:
1903 if field.GetData() is None:
1904 if is_any_field_filled:
1905 error = True
1906 msg = _('To properly create an address, all the related fields must be filled in.')
1907 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
1908 field.SetBackgroundColour('pink')
1909 field.SetFocus()
1910 field.Refresh()
1911 else:
1912 is_any_field_filled = True
1913 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1914 field.Refresh()
1915
1916 return (not error)
1917 #--------------------------------------------------------
1919 """
1920 Transfer data from validator to window.
1921 The default implementation returns False, indicating that an error
1922 occurred. We simply return True, as we don't do any data transfer.
1923 """
1924 _pnl_form = self.GetWindow().GetParent()
1925 # fill in controls with values from self.form_DTD
1926 _pnl_form.PRW_gender.SetData(self.form_DTD['gender'])
1927 _pnl_form.PRW_dob.SetText(self.form_DTD['dob'])
1928 _pnl_form.PRW_lastname.SetText(self.form_DTD['lastnames'])
1929 _pnl_form.PRW_firstname.SetText(self.form_DTD['firstnames'])
1930 _pnl_form.PRW_title.SetText(self.form_DTD['title'])
1931 _pnl_form.PRW_nick.SetText(self.form_DTD['nick'])
1932 _pnl_form.PRW_occupation.SetText(self.form_DTD['occupation'])
1933 _pnl_form.TTC_address_number.SetValue(self.form_DTD['address_number'])
1934 _pnl_form.PRW_street.SetText(self.form_DTD['street'])
1935 _pnl_form.PRW_zip_code.SetText(self.form_DTD['zip_code'])
1936 _pnl_form.PRW_town.SetText(self.form_DTD['town'])
1937 _pnl_form.PRW_state.SetData(self.form_DTD['state'])
1938 _pnl_form.PRW_country.SetData(self.form_DTD['country'])
1939 _pnl_form.TTC_phone.SetValue(self.form_DTD['phone'])
1940 _pnl_form.TCTRL_comment.SetValue(self.form_DTD['comment'])
1941 return True # Prevent wxDialog from complaining
1942 #--------------------------------------------------------
1944 """
1945 Transfer data from window to validator.
1946 The default implementation returns False, indicating that an error
1947 occurred. We simply return True, as we don't do any data transfer.
1948 """
1949 # FIXME: should be called automatically
1950 if not self.GetWindow().GetParent().Validate():
1951 return False
1952 try:
1953 _pnl_form = self.GetWindow().GetParent()
1954 # fill in self.form_DTD with values from controls
1955 self.form_DTD['gender'] = _pnl_form.PRW_gender.GetData()
1956 self.form_DTD['dob'] = _pnl_form.PRW_dob.GetData()
1957
1958 self.form_DTD['lastnames'] = _pnl_form.PRW_lastname.GetValue()
1959 self.form_DTD['firstnames'] = _pnl_form.PRW_firstname.GetValue()
1960 self.form_DTD['title'] = _pnl_form.PRW_title.GetValue()
1961 self.form_DTD['nick'] = _pnl_form.PRW_nick.GetValue()
1962
1963 self.form_DTD['occupation'] = _pnl_form.PRW_occupation.GetValue()
1964
1965 self.form_DTD['address_number'] = _pnl_form.TTC_address_number.GetValue()
1966 self.form_DTD['street'] = _pnl_form.PRW_street.GetValue()
1967 self.form_DTD['zip_code'] = _pnl_form.PRW_zip_code.GetValue()
1968 self.form_DTD['town'] = _pnl_form.PRW_town.GetValue()
1969 self.form_DTD['state'] = _pnl_form.PRW_state.GetData()
1970 self.form_DTD['country'] = _pnl_form.PRW_country.GetData()
1971
1972 self.form_DTD['phone'] = _pnl_form.TTC_phone.GetValue()
1973
1974 self.form_DTD['comment'] = _pnl_form.TCTRL_comment.GetValue()
1975 except:
1976 return False
1977 return True
1978 #============================================================
1979 # patient demographics editing classes
1980 #============================================================
1982 """Notebook displaying demographics editing pages:
1983
1984 - Identity
1985 - Contacts (addresses, phone numbers, etc)
1986 - Social Network (significant others, GP, etc)
1987
1988 Does NOT act on/listen to the current patient.
1989 """
1990 #--------------------------------------------------------
1992
1993 wx.Notebook.__init__ (
1994 self,
1995 parent = parent,
1996 id = id,
1997 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
1998 name = self.__class__.__name__
1999 )
2000
2001 self.__identity = None
2002 self.__do_layout()
2003 self.SetSelection(0)
2004 #--------------------------------------------------------
2005 # public API
2006 #--------------------------------------------------------
2008 """Populate fields in pages with data from model."""
2009 for page_idx in range(self.GetPageCount()):
2010 page = self.GetPage(page_idx)
2011 page.identity = self.__identity
2012
2013 return True
2014 #--------------------------------------------------------
2015 # internal API
2016 #--------------------------------------------------------
2018 """Build patient edition notebook pages."""
2019
2020 # contacts page
2021 new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1)
2022 new_page.identity = self.__identity
2023 self.AddPage (
2024 page = new_page,
2025 text = _('Contacts'),
2026 select = True
2027 )
2028
2029 # identity page
2030 new_page = cPersonIdentityManagerPnl(self, -1)
2031 new_page.identity = self.__identity
2032 self.AddPage (
2033 page = new_page,
2034 text = _('Identity'),
2035 select = False
2036 )
2037
2038 # social network page
2039 new_page = cPersonSocialNetworkManagerPnl(self, -1)
2040 new_page.identity = self.__identity
2041 self.AddPage (
2042 page = new_page,
2043 text = _('Social Network'),
2044 select = False
2045 )
2046 #--------------------------------------------------------
2047 # properties
2048 #--------------------------------------------------------
2051
2053 self.__identity = identity
2054
2055 identity = property(_get_identity, _set_identity)
2056 #============================================================
2057 # old occupation widgets
2058 #============================================================
2059 # FIXME: support multiple occupations
2060 # FIXME: redo with wxGlade
2061
2063 """Page containing patient occupations edition fields.
2064 """
2066 """
2067 Creates a new instance of BasicPatDetailsPage
2068 @param parent - The parent widget
2069 @type parent - A wx.Window instance
2070 @param id - The widget id
2071 @type id - An integer
2072 """
2073 wx.Panel.__init__(self, parent, id)
2074 self.__ident = ident
2075 self.__do_layout()
2076 #--------------------------------------------------------
2078 PNL_form = wx.Panel(self, -1)
2079 # occupation
2080 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
2081 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1)
2082 self.PRW_occupation.SetToolTipString(_("primary occupation of the patient"))
2083 # known since
2084 STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated'))
2085 self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY)
2086
2087 # layout input widgets
2088 SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4)
2089 SZR_input.AddGrowableCol(1)
2090 SZR_input.Add(STT_occupation, 0, wx.SHAPED)
2091 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
2092 SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED)
2093 SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND)
2094 PNL_form.SetSizerAndFit(SZR_input)
2095
2096 # layout page
2097 SZR_main = wx.BoxSizer(wx.VERTICAL)
2098 SZR_main.Add(PNL_form, 1, wx.EXPAND)
2099 self.SetSizer(SZR_main)
2100 #--------------------------------------------------------
2103 #--------------------------------------------------------
2105 if identity is not None:
2106 self.__ident = identity
2107 jobs = self.__ident.get_occupations()
2108 if len(jobs) > 0:
2109 self.PRW_occupation.SetText(jobs[0]['l10n_occupation'])
2110 self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y'))
2111 return True
2112 #--------------------------------------------------------
2114 if self.PRW_occupation.IsModified():
2115 new_job = self.PRW_occupation.GetValue().strip()
2116 jobs = self.__ident.get_occupations()
2117 for job in jobs:
2118 if job['l10n_occupation'] == new_job:
2119 continue
2120 self.__ident.unlink_occupation(occupation = job['l10n_occupation'])
2121 self.__ident.link_occupation(occupation = new_job)
2122 return True
2123 #============================================================
2125 """Patient demographics plugin for main notebook.
2126
2127 Hosts another notebook with pages for Identity, Contacts, etc.
2128
2129 Acts on/listens to the currently active patient.
2130 """
2131 #--------------------------------------------------------
2133 wx.Panel.__init__ (self, parent = parent, id = id, style = wx.NO_BORDER)
2134 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
2135 self.__do_layout()
2136 self.__register_interests()
2137 #--------------------------------------------------------
2138 # public API
2139 #--------------------------------------------------------
2140 #--------------------------------------------------------
2141 # internal helpers
2142 #--------------------------------------------------------
2144 """Arrange widgets."""
2145 self.__patient_notebook = cPersonDemographicsEditorNb(self, -1)
2146
2147 szr_main = wx.BoxSizer(wx.VERTICAL)
2148 szr_main.Add(self.__patient_notebook, 1, wx.EXPAND)
2149 self.SetSizerAndFit(szr_main)
2150 #--------------------------------------------------------
2151 # event handling
2152 #--------------------------------------------------------
2154 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2155 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
2156 #--------------------------------------------------------
2159 #--------------------------------------------------------
2162 #--------------------------------------------------------
2163 # reget mixin API
2164 #--------------------------------------------------------
2174 #============================================================
2176 """
2177 Utility class to test the new patient wizard.
2178 """
2179 #--------------------------------------------------------
2181 """
2182 Create a new instance of TestPanel.
2183 @param parent The parent widget
2184 @type parent A wx.Window instance
2185 """
2186 wx.Panel.__init__(self, parent, id)
2187 wizard = cNewPatientWizard(self)
2188 print wizard.RunWizard()
2189 #============================================================
2190 if __name__ == "__main__":
2191
2192 #--------------------------------------------------------
2194 app = wx.PyWidgetTester(size = (600, 400))
2195 app.SetWidget(cKOrganizerSchedulePnl)
2196 app.MainLoop()
2197 #--------------------------------------------------------
2199 app = wx.PyWidgetTester(size = (600, 400))
2200 widget = cPersonNamesManagerPnl(app.frame, -1)
2201 widget.identity = activate_patient()
2202 app.frame.Show(True)
2203 app.MainLoop()
2204 #--------------------------------------------------------
2206 app = wx.PyWidgetTester(size = (600, 400))
2207 widget = cPersonIDsManagerPnl(app.frame, -1)
2208 widget.identity = activate_patient()
2209 app.frame.Show(True)
2210 app.MainLoop()
2211 #--------------------------------------------------------
2213 app = wx.PyWidgetTester(size = (600, 400))
2214 widget = cPersonIdentityManagerPnl(app.frame, -1)
2215 widget.identity = activate_patient()
2216 app.frame.Show(True)
2217 app.MainLoop()
2218 #--------------------------------------------------------
2220 app = wx.PyWidgetTester(size = (600, 400))
2221 app.SetWidget(cNameGenderDOBEditAreaPnl, name = activate_patient().get_active_name())
2222 app.MainLoop()
2223 #--------------------------------------------------------
2225 app = wx.PyWidgetTester(size = (600, 400))
2226 widget = cPersonContactsManagerPnl(app.frame, -1)
2227 widget.identity = activate_patient()
2228 app.frame.Show(True)
2229 app.MainLoop()
2230 #--------------------------------------------------------
2232 app = wx.PyWidgetTester(size = (600, 400))
2233 widget = cPersonDemographicsEditorNb(app.frame, -1)
2234 widget.identity = activate_patient()
2235 widget.refresh()
2236 app.frame.Show(True)
2237 app.MainLoop()
2238 #--------------------------------------------------------
2240 patient = gmPersonSearch.ask_for_patient()
2241 if patient is None:
2242 print "No patient. Exiting gracefully..."
2243 sys.exit(0)
2244 from Gnumed.wxpython import gmPatSearchWidgets
2245 gmPatSearchWidgets.set_active_patient(patient=patient)
2246 return patient
2247 #--------------------------------------------------------
2248 if len(sys.argv) > 1 and sys.argv[1] == 'test':
2249
2250 gmI18N.activate_locale()
2251 gmI18N.install_domain(domain='gnumed')
2252 gmPG2.get_connection()
2253
2254 # a = cFormDTD(fields = cBasicPatDetailsPage.form_fields)
2255
2256 # app = wx.PyWidgetTester(size = (400, 300))
2257 # app.SetWidget(cNotebookedPatEditionPanel, -1)
2258 # app.SetWidget(TestWizardPanel, -1)
2259 # app.frame.Show(True)
2260 # app.MainLoop()
2261
2262 # phrasewheels
2263 # test_zipcode_prw()
2264 # test_state_prw()
2265 # test_street_prw()
2266 # test_organizer_pnl()
2267 #test_address_type_prw()
2268 #test_suburb_prw()
2269 test_urb_prw()
2270 #test_address_prw()
2271
2272 # contacts related widgets
2273 #test_address_ea_pnl()
2274 #test_person_adrs_pnl()
2275 #test_person_comms_pnl()
2276 #test_pat_contacts_pnl()
2277
2278 # identity related widgets
2279 #test_person_names_pnl()
2280 #test_person_ids_pnl()
2281 #test_pat_ids_pnl()
2282 #test_name_ea_pnl()
2283
2284 #test_cPersonDemographicsEditorNb()
2285
2286 #============================================================
2287
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Sep 9 04:07:28 2010 | http://epydoc.sourceforge.net |