| 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 time, string, sys, os, datetime as pyDT, csv, codecs, re as regex, psycopg2, 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
21 from Gnumed.wxpython import gmPlugin, gmPhraseWheel, gmGuiHelpers, gmDateTimeInput
22 from Gnumed.wxpython import gmRegetMixin, gmDataMiningWidgets, gmListWidgets, gmEditArea
23 from Gnumed.wxpython import gmAuthWidgets, gmCfgWidgets
24 from Gnumed.wxGladeWidgets import wxgGenericAddressEditAreaPnl, wxgPersonContactsManagerPnl, wxgPersonIdentityManagerPnl
25 from Gnumed.wxGladeWidgets import wxgCommChannelEditAreaPnl, wxgExternalIDEditAreaPnl
26
27
28 # constant defs
29 _log = logging.getLogger('gm.ui')
30
31
32 try:
33 _('dummy-no-need-to-translate-but-make-epydoc-happy')
34 except NameError:
35 _ = lambda x:x
36
37 #============================================================
38 # country related widgets / functions
39 #============================================================
41
42 if parent is None:
43 parent = wx.GetApp().GetTopWindow()
44
45 countries = gmDemographicRecord.get_countries()
46
47 gmCfgWidgets.configure_string_from_list_option (
48 parent = parent,
49 message = _('Select the default country for new persons.\n'),
50 option = 'person.create.default_country',
51 bias = 'user',
52 choices = [ (c['l10n_country'], c['code']) for c in countries ],
53 columns = [_('Country'), _('Code')],
54 data = [ c['name'] for c in countries ]
55 )
56 #============================================================
58
60
61 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
62
63 context = {
64 u'ctxt_zip': {
65 u'where_part': u'and zip ilike %(zip)s',
66 u'placeholder': u'zip'
67 }
68 }
69 query = u"""
70 select code, name from (
71 select distinct on (code, name) code, (name || ' (' || code || ')') as name, rank from (
72
73 -- localized to user
74
75 select
76 code_country as code, l10n_country as name, 1 as rank
77 from dem.v_zip2data
78 where
79 l10n_country %(fragment_condition)s
80 %(ctxt_zip)s
81
82 union all
83
84 select
85 code as code, _(name) as name, 2 as rank
86 from dem.country
87 where
88 _(name) %(fragment_condition)s
89
90 union all
91
92 -- non-localized
93
94 select
95 code_country as code, country as name, 3 as rank
96 from dem.v_zip2data
97 where
98 country %(fragment_condition)s
99 %(ctxt_zip)s
100
101 union all
102
103 select
104 code as code, name as name, 4 as rank
105 from dem.country
106 where
107 name %(fragment_condition)s
108
109 union all
110
111 -- abbreviation
112
113 select
114 code as code, name as name, 5 as rank
115 from dem.country
116 where
117 code %(fragment_condition)s
118
119 ) as q2
120 ) as q1 order by rank, name limit 25"""
121 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context)
122 mp.setThresholds(2, 5, 9)
123 self.matcher = mp
124
125 self.unset_context(context = u'zip')
126 self.SetToolTipString(_('Type or select a country.'))
127 self.capitalisation_mode = gmTools.CAPS_FIRST
128 self.selection_only = True
129
130 #============================================================
131 # province related widgets / functions
132 #============================================================
134
135 if parent is None:
136 parent = wx.GetApp().GetTopWindow()
137
138 provs = gmDemographicRecord.get_provinces()
139
140 gmCfgWidgets.configure_string_from_list_option (
141 parent = parent,
142 message = _('Select the default region/province/state/territory for new persons.\n'),
143 option = 'person.create.default_region',
144 bias = 'user',
145 choices = [ (p['l10n_country'], p['l10n_state'], p['code_state']) for p in provs ],
146 columns = [_('Country'), _('Region'), _('Code')],
147 data = [ p['state'] for p in provs ]
148 )
149 #============================================================
151 ea = cProvinceEAPnl(parent = parent, id = -1, province = province)
152 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (province is not None))
153 dlg.SetTitle(gmTools.coalesce(province, _('Adding province'), _('Editing province')))
154 result = dlg.ShowModal()
155 dlg.Destroy()
156 return (result == wx.ID_OK)
157 #============================================================
159
160 msg = _(
161 'Are you sure you want to delete this province ?\n'
162 '\n'
163 'Deletion will only work if this province is not\n'
164 'yet in use in any patient addresses.'
165 )
166
167 tt = _(
168 'Also delete any towns/cities/villages known\n'
169 'to be situated in this state as long as\n'
170 'no patients are recorded to live there.'
171 )
172
173 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
174 parent,
175 -1,
176 caption = _('Deleting province'),
177 question = msg,
178 show_checkbox = True,
179 checkbox_msg = _('delete related townships'),
180 checkbox_tooltip = tt,
181 button_defs = [
182 {'label': _('Yes, delete'), 'tooltip': _('Delete province and possibly related townships.'), 'default': False},
183 {'label': _('No'), 'tooltip': _('No, do NOT delete anything.'), 'default': True}
184 ]
185 )
186
187 decision = dlg.ShowModal()
188 if decision != wx.ID_YES:
189 dlg.Destroy()
190 return False
191
192 include_urbs = dlg.checkbox_is_checked()
193 dlg.Destroy()
194
195 return gmDemographicRecord.delete_province(province = province, delete_urbs = include_urbs)
196 #============================================================
198
199 if parent is None:
200 parent = wx.GetApp().GetTopWindow()
201
202 #------------------------------------------------------------
203 def delete(province=None):
204 return delete_province(parent = parent, province = province['pk_state'])
205 #------------------------------------------------------------
206 def edit(province=None):
207 return edit_province(parent = parent, province = province)
208 #------------------------------------------------------------
209 def refresh(lctrl):
210 wx.BeginBusyCursor()
211 provinces = gmDemographicRecord.get_provinces()
212 lctrl.set_string_items([ (p['l10n_country'], p['l10n_state']) for p in provinces ])
213 lctrl.set_data(provinces)
214 wx.EndBusyCursor()
215 #------------------------------------------------------------
216 msg = _(
217 '\n'
218 'This list shows the provinces known to GNUmed.\n'
219 '\n'
220 'In your jurisdiction "province" may correspond to either of "state",\n'
221 '"county", "region", "territory", or some such term.\n'
222 '\n'
223 'Select the province you want to edit !\n'
224 )
225
226 gmListWidgets.get_choices_from_list (
227 parent = parent,
228 msg = msg,
229 caption = _('Editing provinces ...'),
230 columns = [_('Country'), _('Province')],
231 single_selection = True,
232 new_callback = edit,
233 #edit_callback = edit,
234 delete_callback = delete,
235 refresh_callback = refresh
236 )
237 #============================================================
239
241
242 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
243
244 context = {
245 u'ctxt_country_name': {
246 u'where_part': u'and l10n_country ilike %(country_name)s or country ilike %(country_name)s',
247 u'placeholder': u'country_name'
248 },
249 u'ctxt_zip': {
250 u'where_part': u'and zip ilike %(zip)s',
251 u'placeholder': u'zip'
252 },
253 u'ctxt_country_code': {
254 u'where_part': u'and country in (select code from dem.country where _(name) ilike %(country_name)s or name ilike %(country_name)s)',
255 u'placeholder': u'country_name'
256 }
257 }
258
259 query = u"""
260 select code, name from (
261 select distinct on (name) code, name, rank from (
262 -- 1: find states based on name, context: zip and country name
263 select
264 code_state as code, state as name, 1 as rank
265 from dem.v_zip2data
266 where
267 state %(fragment_condition)s
268 %(ctxt_country_name)s
269 %(ctxt_zip)s
270
271 union all
272
273 -- 2: find states based on code, context: zip and country name
274 select
275 code_state as code, state as name, 2 as rank
276 from dem.v_zip2data
277 where
278 code_state %(fragment_condition)s
279 %(ctxt_country_name)s
280 %(ctxt_zip)s
281
282 union all
283
284 -- 3: find states based on name, context: country
285 select
286 code as code, name as name, 3 as rank
287 from dem.state
288 where
289 name %(fragment_condition)s
290 %(ctxt_country_code)s
291
292 union all
293
294 -- 4: find states based on code, context: country
295 select
296 code as code, name as name, 3 as rank
297 from dem.state
298 where
299 code %(fragment_condition)s
300 %(ctxt_country_code)s
301
302 ) as q2
303 ) as q1 order by rank, name limit 50"""
304
305 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context)
306 mp.setThresholds(2, 5, 6)
307 mp.word_separators = u'[ \t]+'
308 self.matcher = mp
309
310 self.unset_context(context = u'zip')
311 self.unset_context(context = u'country_name')
312 self.SetToolTipString(_('Type or select a state/region/province/territory.'))
313 self.capitalisation_mode = gmTools.CAPS_FIRST
314 self.selection_only = True
315 #====================================================================
316 from Gnumed.wxGladeWidgets import wxgProvinceEAPnl
317
319
321
322 try:
323 data = kwargs['province']
324 del kwargs['province']
325 except KeyError:
326 data = None
327
328 wxgProvinceEAPnl.wxgProvinceEAPnl.__init__(self, *args, **kwargs)
329 gmEditArea.cGenericEditAreaMixin.__init__(self)
330
331 self.mode = 'new'
332 self.data = data
333 if data is not None:
334 self.mode = 'edit'
335
336 self.__init_ui()
337 #----------------------------------------------------------------
340 #----------------------------------------------------------------
341 # generic Edit Area mixin API
342 #----------------------------------------------------------------
344
345 validity = True
346
347 if self._PRW_province.GetData() is None:
348 if self._PRW_province.GetValue().strip() == u'':
349 validity = False
350 self._PRW_province.display_as_valid(False)
351 else:
352 self._PRW_province.display_as_valid(True)
353 else:
354 self._PRW_province.display_as_valid(True)
355
356 if self._PRW_province.GetData() is None:
357 if self._TCTRL_code.GetValue().strip() == u'':
358 validity = False
359 self._TCTRL_code.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
360 else:
361 self._TCTRL_code.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
362
363 if self._PRW_country.GetData() is None:
364 validity = False
365 self._PRW_country.display_as_valid(False)
366 else:
367 self._PRW_country.display_as_valid(True)
368
369 return validity
370 #----------------------------------------------------------------
372 gmDemographicRecord.create_province (
373 name = self._PRW_province.GetValue().strip(),
374 code = self._TCTRL_code.GetValue().strip(),
375 country = self._PRW_country.GetData()
376 )
377
378 # EA is refreshed automatically after save, so need this ...
379 self.data = {
380 'l10n_state' : self._PRW_province.GetValue().strip(),
381 'code_state' : self._TCTRL_code.GetValue().strip(),
382 'l10n_country' : self._PRW_country.GetValue().strip()
383 }
384
385 return True
386 #----------------------------------------------------------------
388 # update self.data and save the changes
389 #self.data[''] =
390 #self.data[''] =
391 #self.data[''] =
392 #self.data.save()
393
394 # do nothing for now (IOW, don't support updates)
395 return True
396 #----------------------------------------------------------------
398 self._PRW_province.SetText()
399 self._TCTRL_code.SetValue(u'')
400 self._PRW_country.SetText()
401
402 self._PRW_province.SetFocus()
403 #----------------------------------------------------------------
405 self._PRW_province.SetText(self.data['l10n_state'], self.data['code_state'])
406 self._TCTRL_code.SetValue(self.data['code_state'])
407 self._PRW_country.SetText(self.data['l10n_country'], self.data['code_country'])
408
409 self._PRW_province.SetFocus()
410 #----------------------------------------------------------------
417 #============================================================
418 #============================================================
420
422
423 kwargs['message'] = _("Today's KOrganizer appointments ...")
424 kwargs['button_defs'] = [
425 {'label': _('Reload'), 'tooltip': _('Reload appointments from KOrganizer')},
426 {'label': u''},
427 {'label': u''},
428 {'label': u''},
429 {'label': u'KOrganizer', 'tooltip': _('Launch KOrganizer')}
430 ]
431 gmDataMiningWidgets.cPatientListingPnl.__init__(self, *args, **kwargs)
432
433 self.fname = os.path.expanduser(os.path.join('~', '.gnumed', 'tmp', 'korganizer2gnumed.csv'))
434 self.reload_cmd = 'konsolekalendar --view --export-type csv --export-file %s' % self.fname
435
436 #--------------------------------------------------------
440 #--------------------------------------------------------
442 """Reload appointments from KOrganizer."""
443 found, cmd = gmShellAPI.detect_external_binary(binary = 'korganizer')
444
445 if not found:
446 gmDispatcher.send(signal = 'statustext', msg = _('KOrganizer is not installed.'), beep = True)
447 return
448
449 gmShellAPI.run_command_in_shell(command = cmd, blocking = False)
450 #--------------------------------------------------------
452 try: os.remove(self.fname)
453 except OSError: pass
454 gmShellAPI.run_command_in_shell(command=self.reload_cmd, blocking=True)
455 try:
456 csv_file = codecs.open(self.fname , mode = 'rU', encoding = 'utf8', errors = 'replace')
457 except IOError:
458 gmDispatcher.send(signal = u'statustext', msg = _('Cannot access KOrganizer transfer file [%s]') % self.fname, beep = True)
459 return
460
461 csv_lines = gmTools.unicode_csv_reader (
462 csv_file,
463 delimiter = ','
464 )
465 # start_date, start_time, end_date, end_time, title (patient), ort, comment, UID
466 self._LCTRL_items.set_columns ([
467 _('Place'),
468 _('Start'),
469 u'',
470 u'',
471 _('Patient'),
472 _('Comment')
473 ])
474 items = []
475 data = []
476 for line in csv_lines:
477 items.append([line[5], line[0], line[1], line[3], line[4], line[6]])
478 data.append([line[4], line[7]])
479
480 self._LCTRL_items.set_string_items(items = items)
481 self._LCTRL_items.set_column_widths()
482 self._LCTRL_items.set_data(data = data)
483 self._LCTRL_items.patient_key = 0
484 #--------------------------------------------------------
485 # notebook plugins API
486 #--------------------------------------------------------
488 self.reload_appointments()
489 #============================================================
491
492 pat = gmPerson.gmCurrentPatient()
493 curr_jobs = pat.get_occupations()
494 if len(curr_jobs) > 0:
495 old_job = curr_jobs[0]['l10n_occupation']
496 update = curr_jobs[0]['modified_when'].strftime('%m/%Y')
497 else:
498 old_job = u''
499 update = u''
500
501 msg = _(
502 'Please enter the primary occupation of the patient.\n'
503 '\n'
504 'Currently recorded:\n'
505 '\n'
506 ' %s (last updated %s)'
507 ) % (old_job, update)
508
509 new_job = wx.GetTextFromUser (
510 message = msg,
511 caption = _('Editing primary occupation'),
512 default_value = old_job,
513 parent = None
514 )
515 if new_job.strip() == u'':
516 return
517
518 for job in curr_jobs:
519 # unlink all but the new job
520 if job['l10n_occupation'] != new_job:
521 pat.unlink_occupation(occupation = job['l10n_occupation'])
522 # and link the new one
523 pat.link_occupation(occupation = new_job)
524 #============================================================
526 # ask user for assurance
527 go_ahead = gmGuiHelpers.gm_show_question (
528 _('Are you sure you really, positively want\n'
529 'to disable the following person ?\n'
530 '\n'
531 ' %s %s %s\n'
532 ' born %s\n'
533 '\n'
534 '%s\n'
535 ) % (
536 identity['firstnames'],
537 identity['lastnames'],
538 identity['gender'],
539 identity['dob'],
540 gmTools.bool2subst (
541 identity.is_patient,
542 _('This patient DID receive care.'),
543 _('This person did NOT receive care.')
544 )
545 ),
546 _('Disabling person')
547 )
548 if not go_ahead:
549 return True
550
551 # get admin connection
552 conn = gmAuthWidgets.get_dbowner_connection (
553 procedure = _('Disabling patient')
554 )
555 # - user cancelled
556 if conn is False:
557 return True
558 # - error
559 if conn is None:
560 return False
561
562 # now disable patient
563 gmPG2.run_rw_queries(queries = [{'cmd': u"update dem.identity set deleted=True where pk=%s", 'args': [identity['pk_identity']]}])
564
565 return True
566 #============================================================
567 # address phrasewheels and widgets
568 #============================================================
570 """A list for managing a person's addresses.
571
572 Does NOT act on/listen to the current patient.
573 """
575
576 try:
577 self.__identity = kwargs['identity']
578 del kwargs['identity']
579 except KeyError:
580 self.__identity = None
581
582 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
583
584 self.new_callback = self._add_address
585 self.edit_callback = self._edit_address
586 self.delete_callback = self._del_address
587 self.refresh_callback = self.refresh
588
589 self.__init_ui()
590 self.refresh()
591 #--------------------------------------------------------
592 # external API
593 #--------------------------------------------------------
595 if self.__identity is None:
596 self._LCTRL_items.set_string_items()
597 return
598
599 adrs = self.__identity.get_addresses()
600 self._LCTRL_items.set_string_items (
601 items = [ [
602 a['l10n_address_type'],
603 a['street'],
604 gmTools.coalesce(a['notes_street'], u''),
605 a['number'],
606 gmTools.coalesce(a['subunit'], u''),
607 a['postcode'],
608 a['urb'],
609 gmTools.coalesce(a['suburb'], u''),
610 a['l10n_state'],
611 a['l10n_country'],
612 gmTools.coalesce(a['notes_subunit'], u'')
613 ] for a in adrs
614 ]
615 )
616 self._LCTRL_items.set_column_widths()
617 self._LCTRL_items.set_data(data = adrs)
618 #--------------------------------------------------------
619 # internal helpers
620 #--------------------------------------------------------
622 self._LCTRL_items.SetToolTipString(_('List of known addresses.'))
623 self._LCTRL_items.set_columns(columns = [
624 _('Type'),
625 _('Street'),
626 _('Street info'),
627 _('Number'),
628 _('Subunit'),
629 _('Postal code'),
630 _('Place'),
631 _('Suburb'),
632 _('Region'),
633 _('Country'),
634 _('Comment')
635 ])
636 #--------------------------------------------------------
638 ea = cAddressEditAreaPnl(self, -1)
639 ea.identity = self.__identity
640 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
641 dlg.SetTitle(_('Adding new address'))
642 if dlg.ShowModal() == wx.ID_OK:
643 return True
644 return False
645 #--------------------------------------------------------
647 ea = cAddressEditAreaPnl(self, -1, address = address)
648 ea.identity = self.__identity
649 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
650 dlg.SetTitle(_('Editing address'))
651 if dlg.ShowModal() == wx.ID_OK:
652 # did we add an entirely new address ?
653 # if so then unlink the old one as implied by "edit"
654 if ea.address['pk_address'] != address['pk_address']:
655 self.__identity.unlink_address(address = address)
656 return True
657 return False
658 #--------------------------------------------------------
660 go_ahead = gmGuiHelpers.gm_show_question (
661 _( 'Are you sure you want to remove this\n'
662 "address from the patient's addresses ?\n"
663 '\n'
664 'The address itself will not be deleted\n'
665 'but it will no longer be associated with\n'
666 'this patient.'
667 ),
668 _('Removing address')
669 )
670 if not go_ahead:
671 return False
672 self.__identity.unlink_address(address = address)
673 return True
674 #--------------------------------------------------------
675 # properties
676 #--------------------------------------------------------
679
683
684 identity = property(_get_identity, _set_identity)
685 #============================================================
687 """A panel for editing contact data for a person.
688
689 - provides access to:
690 - addresses
691 - communication paths
692
693 Does NOT act on/listen to the current patient.
694 """
696
697 wxgPersonContactsManagerPnl.wxgPersonContactsManagerPnl.__init__(self, *args, **kwargs)
698
699 self.__identity = None
700 self.refresh()
701 #--------------------------------------------------------
702 # external API
703 #--------------------------------------------------------
707 #--------------------------------------------------------
708 # properties
709 #--------------------------------------------------------
712
716
717 identity = property(_get_identity, _set_identity)
718 #============================================================
720 """An edit area for editing/creating an address.
721
722 Does NOT act on/listen to the current patient.
723 """
725 try:
726 self.address = kwargs['address']
727 del kwargs['address']
728 except KeyError:
729 self.address = None
730
731 wxgGenericAddressEditAreaPnl.wxgGenericAddressEditAreaPnl.__init__(self, *args, **kwargs)
732
733 self.identity = None
734
735 self.__register_interests()
736 self.refresh()
737 #--------------------------------------------------------
738 # external API
739 #--------------------------------------------------------
741 if address is not None:
742 self.address = address
743
744 if self.address is not None:
745 self._PRW_type.SetText(self.address['l10n_address_type'])
746 self._PRW_zip.SetText(self.address['postcode'])
747 self._PRW_street.SetText(self.address['street'], data = self.address['street'])
748 self._TCTRL_notes_street.SetValue(gmTools.coalesce(self.address['notes_street'], ''))
749 self._TCTRL_number.SetValue(self.address['number'])
750 self._TCTRL_subunit.SetValue(gmTools.coalesce(self.address['subunit'], ''))
751 self._PRW_suburb.SetText(gmTools.coalesce(self.address['suburb'], ''))
752 self._PRW_urb.SetText(self.address['urb'], data = self.address['urb'])
753 self._PRW_state.SetText(self.address['l10n_state'], data = self.address['code_state'])
754 self._PRW_country.SetText(self.address['l10n_country'], data = self.address['code_country'])
755 self._TCTRL_notes_subunit.SetValue(gmTools.coalesce(self.address['notes_subunit'], ''))
756 # FIXME: clear fields
757 # else:
758 # pass
759 #--------------------------------------------------------
761 """Links address to patient, creating new address if necessary"""
762
763 if not self.__valid_for_save():
764 return False
765
766 # link address to patient
767 try:
768 adr = self.identity.link_address (
769 number = self._TCTRL_number.GetValue().strip(),
770 street = self._PRW_street.GetValue().strip(),
771 postcode = self._PRW_zip.GetValue().strip(),
772 urb = self._PRW_urb.GetValue().strip(),
773 state = self._PRW_state.GetData(),
774 country = self._PRW_country.GetData(),
775 subunit = gmTools.none_if(self._TCTRL_subunit.GetValue().strip(), u''),
776 suburb = gmTools.none_if(self._PRW_suburb.GetValue().strip(), u''),
777 id_type = self._PRW_type.GetData()
778 )
779 except:
780 _log.exception('cannot save address')
781 gmGuiHelpers.gm_show_error (
782 _('Cannot save address.\n\n'
783 'Does the state [%s]\n'
784 'exist in country [%s] ?'
785 ) % (
786 self._PRW_state.GetValue().strip(),
787 self._PRW_country.GetValue().strip()
788 ),
789 _('Saving address')
790 )
791 return False
792
793 notes = self._TCTRL_notes_street.GetValue().strip()
794 if notes != u'':
795 adr['notes_street'] = notes
796 notes = self._TCTRL_notes_subunit.GetValue().strip()
797 if notes != u'':
798 adr['notes_subunit'] = notes
799 adr.save_payload()
800
801 self.address = adr
802
803 return True
804 #--------------------------------------------------------
805 # event handling
806 #--------------------------------------------------------
808 self._PRW_zip.add_callback_on_lose_focus(self._on_zip_set)
809 self._PRW_country.add_callback_on_lose_focus(self._on_country_set)
810 #--------------------------------------------------------
812 """Set the street, town, state and country according to entered zip code."""
813 zip_code = self._PRW_zip.GetValue()
814 if zip_code.strip() == u'':
815 self._PRW_street.unset_context(context = u'zip')
816 self._PRW_urb.unset_context(context = u'zip')
817 self._PRW_state.unset_context(context = u'zip')
818 self._PRW_country.unset_context(context = u'zip')
819 else:
820 self._PRW_street.set_context(context = u'zip', val = zip_code)
821 self._PRW_urb.set_context(context = u'zip', val = zip_code)
822 self._PRW_state.set_context(context = u'zip', val = zip_code)
823 self._PRW_country.set_context(context = u'zip', val = zip_code)
824 #--------------------------------------------------------
826 """Set the states according to entered country."""
827 country = self._PRW_country.GetData()
828 if country is None:
829 self._PRW_state.unset_context(context = 'country')
830 else:
831 self._PRW_state.set_context(context = 'country', val = country)
832 #--------------------------------------------------------
833 # internal helpers
834 #--------------------------------------------------------
836
837 # validate required fields
838 is_any_field_filled = False
839
840 required_fields = (
841 self._PRW_type,
842 self._PRW_zip,
843 self._PRW_street,
844 self._TCTRL_number,
845 self._PRW_urb
846 )
847 for field in required_fields:
848 if len(field.GetValue().strip()) == 0:
849 if is_any_field_filled:
850 field.SetBackgroundColour('pink')
851 field.SetFocus()
852 field.Refresh()
853 gmGuiHelpers.gm_show_error (
854 _('Address details must be filled in completely or not at all.'),
855 _('Saving contact data')
856 )
857 return False
858 else:
859 is_any_field_filled = True
860 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
861 field.Refresh()
862
863 required_fields = (
864 self._PRW_state,
865 self._PRW_country
866 )
867 for field in required_fields:
868 if field.GetData() is None:
869 if is_any_field_filled:
870 field.SetBackgroundColour('pink')
871 field.SetFocus()
872 field.Refresh()
873 gmGuiHelpers.gm_show_error (
874 _('Address details must be filled in completely or not at all.'),
875 _('Saving contact data')
876 )
877 return False
878 else:
879 is_any_field_filled = True
880 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
881 field.Refresh()
882
883 return True
884 #============================================================
886
888
889 query = u"""
890 select * from (
891 (select
892 pk_address,
893 (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
894 || urb || coalesce(' (' || suburb || ')', '') || ', '
895 || postcode
896 || coalesce(', ' || notes_street, '')
897 || coalesce(', ' || notes_subunit, '')
898 ) as address
899 from
900 dem.v_address
901 where
902 street %(fragment_condition)s
903
904 ) union (
905
906 select
907 pk_address,
908 (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
909 || urb || coalesce(' (' || suburb || ')', '') || ', '
910 || postcode
911 || coalesce(', ' || notes_street, '')
912 || coalesce(', ' || notes_subunit, '')
913 ) as address
914 from
915 dem.v_address
916 where
917 postcode_street %(fragment_condition)s
918
919 ) union (
920
921 select
922 pk_address,
923 (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
924 || urb || coalesce(' (' || suburb || ')', '') || ', '
925 || postcode
926 || coalesce(', ' || notes_street, '')
927 || coalesce(', ' || notes_subunit, '')
928 ) as address
929 from
930 dem.v_address
931 where
932 postcode_urb %(fragment_condition)s
933 )
934 ) as union_result
935 order by union_result.address limit 50"""
936
937 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = query)
938
939 self.setThresholds(2, 4, 6)
940 # self.word_separators = u'[ \t]+'
941
942 #============================================================
944
946
947 mp = cAddressMatchProvider()
948 gmPhraseWheel.cPhraseWheel.__init__ (
949 self,
950 *args,
951 **kwargs
952 )
953 self.matcher = cAddressMatchProvider()
954 self.SetToolTipString(_('Select an address by postcode or street name.'))
955 self.selection_only = True
956 self.__address = None
957 self.__old_pk = None
958 #--------------------------------------------------------
960
961 pk = self.GetData()
962
963 if pk is None:
964 self.__address = None
965 return None
966
967 if self.__address is None:
968 self.__old_pk = pk
969 self.__address = gmDemographicRecord.cAddress(aPK_obj = pk)
970 else:
971 if pk != self.__old_pk:
972 self.__old_pk = pk
973 self.__address = gmDemographicRecord.cAddress(aPK_obj = pk)
974
975 return self.__address
976 #============================================================
978
980
981 query = u"""
982 select id, type from ((
983 select id, _(name) as type, 1 as rank
984 from dem.address_type
985 where _(name) %(fragment_condition)s
986 ) union (
987 select id, name as type, 2 as rank
988 from dem.address_type
989 where name %(fragment_condition)s
990 )) as ur
991 order by
992 ur.rank, ur.type
993 """
994 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
995 mp.setThresholds(1, 2, 4)
996 mp.word_separators = u'[ \t]+'
997 gmPhraseWheel.cPhraseWheel.__init__ (
998 self,
999 *args,
1000 **kwargs
1001 )
1002 self.matcher = mp
1003 self.SetToolTipString(_('Select the type of address.'))
1004 # self.capitalisation_mode = gmTools.CAPS_FIRST
1005 self.selection_only = True
1006 #--------------------------------------------------------
1007 # def GetData(self, can_create=False):
1008 # if self.data is None:
1009 # if can_create:
1010 # self.data = gmDocuments.create_document_type(self.GetValue().strip())['pk_doc_type'] # FIXME: error handling
1011 # return self.data
1012 #============================================================
1014
1016 # FIXME: add possible context
1017 query = u"""
1018 (select distinct postcode, postcode from dem.street where postcode %(fragment_condition)s limit 20)
1019 union
1020 (select distinct postcode, postcode from dem.urb where postcode %(fragment_condition)s limit 20)"""
1021 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1022 mp.setThresholds(2, 3, 15)
1023 gmPhraseWheel.cPhraseWheel.__init__ (
1024 self,
1025 *args,
1026 **kwargs
1027 )
1028 self.SetToolTipString(_("Type or select a zip code (postcode)."))
1029 self.matcher = mp
1030 #============================================================
1032
1034 context = {
1035 u'ctxt_zip': {
1036 u'where_part': u'and zip ilike %(zip)s',
1037 u'placeholder': u'zip'
1038 }
1039 }
1040 query = u"""
1041 select s1, s2 from (
1042 select s1, s2, rank from (
1043 select distinct on (street)
1044 street as s1, street as s2, 1 as rank
1045 from dem.v_zip2data
1046 where
1047 street %(fragment_condition)s
1048 %(ctxt_zip)s
1049
1050 union all
1051
1052 select distinct on (name)
1053 name as s1, name as s2, 2 as rank
1054 from dem.street
1055 where
1056 name %(fragment_condition)s
1057
1058 ) as q2
1059 ) as q1 order by rank, s2 limit 50"""
1060 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context)
1061 mp.setThresholds(3, 5, 8)
1062 gmPhraseWheel.cPhraseWheel.__init__ (
1063 self,
1064 *args,
1065 **kwargs
1066 )
1067 self.unset_context(context = u'zip')
1068
1069 self.SetToolTipString(_('Type or select a street.'))
1070 self.capitalisation_mode = gmTools.CAPS_FIRST
1071 self.matcher = mp
1072 #============================================================
1074
1076
1077 query = """
1078 select distinct on (suburb) suburb, suburb
1079 from dem.street
1080 where suburb %(fragment_condition)s
1081 order by suburb
1082 limit 50
1083 """
1084 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1085 mp.setThresholds(2, 3, 6)
1086 gmPhraseWheel.cPhraseWheel.__init__ (
1087 self,
1088 *args,
1089 **kwargs
1090 )
1091
1092 self.SetToolTipString(_('Type or select the suburb.'))
1093 self.capitalisation_mode = gmTools.CAPS_FIRST
1094 self.matcher = mp
1095 #============================================================
1097
1099 context = {
1100 u'ctxt_zip': {
1101 u'where_part': u'and zip ilike %(zip)s',
1102 u'placeholder': u'zip'
1103 }
1104 }
1105 query = u"""
1106 select u1, u2 from (
1107 select distinct on (rank, u1)
1108 u1, u2, rank
1109 from (
1110 select
1111 urb as u1, urb as u2, 1 as rank
1112 from dem.v_zip2data
1113 where
1114 urb %(fragment_condition)s
1115 %(ctxt_zip)s
1116
1117 union all
1118
1119 select
1120 name as u1, name as u2, 2 as rank
1121 from dem.urb
1122 where
1123 name %(fragment_condition)s
1124 ) as union_result
1125 order by rank, u1
1126 ) as distincted_union
1127 limit 50
1128 """
1129 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context)
1130 mp.setThresholds(3, 5, 7)
1131 gmPhraseWheel.cPhraseWheel.__init__ (
1132 self,
1133 *args,
1134 **kwargs
1135 )
1136 self.unset_context(context = u'zip')
1137
1138 self.SetToolTipString(_('Type or select a city/town/village/dwelling.'))
1139 self.capitalisation_mode = gmTools.CAPS_FIRST
1140 self.matcher = mp
1141 #============================================================
1142 # communications channel related widgets
1143 #============================================================
1145
1147
1148 query = u"""
1149 select pk, type from ((
1150 select pk, _(description) as type, 1 as rank
1151 from dem.enum_comm_types
1152 where _(description) %(fragment_condition)s
1153 ) union (
1154 select pk, description as type, 2 as rank
1155 from dem.enum_comm_types
1156 where description %(fragment_condition)s
1157 )) as ur
1158 order by
1159 ur.rank, ur.type
1160 """
1161 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1162 mp.setThresholds(1, 2, 4)
1163 mp.word_separators = u'[ \t]+'
1164 gmPhraseWheel.cPhraseWheel.__init__ (
1165 self,
1166 *args,
1167 **kwargs
1168 )
1169 self.matcher = mp
1170 self.SetToolTipString(_('Select the type of communications channel.'))
1171 self.selection_only = True
1172 #------------------------------------------------------------
1174 """An edit area for editing/creating a comms channel.
1175
1176 Does NOT act on/listen to the current patient.
1177 """
1179 try:
1180 self.channel = kwargs['comm_channel']
1181 del kwargs['comm_channel']
1182 except KeyError:
1183 self.channel = None
1184
1185 wxgCommChannelEditAreaPnl.wxgCommChannelEditAreaPnl.__init__(self, *args, **kwargs)
1186
1187 self.identity = None
1188
1189 self.refresh()
1190 #--------------------------------------------------------
1191 # external API
1192 #--------------------------------------------------------
1194 if comm_channel is not None:
1195 self.channel = comm_channel
1196
1197 if self.channel is None:
1198 self._PRW_type.SetText(u'')
1199 self._TCTRL_url.SetValue(u'')
1200 self._PRW_address.SetText(value = u'', data = None)
1201 self._CHBOX_confidential.SetValue(False)
1202 else:
1203 self._PRW_type.SetText(self.channel['l10n_comm_type'])
1204 self._TCTRL_url.SetValue(self.channel['url'])
1205 self._PRW_address.SetData(data = self.channel['pk_address'])
1206 self._CHBOX_confidential.SetValue(self.channel['is_confidential'])
1207 #--------------------------------------------------------
1209 """Links comm channel to patient."""
1210 if self.channel is None:
1211 if not self.__valid_for_save():
1212 return False
1213 try:
1214 self.channel = self.identity.link_comm_channel (
1215 pk_channel_type = self._PRW_type.GetData(),
1216 url = self._TCTRL_url.GetValue().strip(),
1217 is_confidential = self._CHBOX_confidential.GetValue(),
1218 )
1219 except psycopg2.IntegrityError:
1220 _log.exception('error saving comm channel')
1221 gmDispatcher.send(signal = u'statustext', msg = _('Cannot save communications channel.'), beep = True)
1222 return False
1223 else:
1224 comm_type = self._PRW_type.GetValue().strip()
1225 if comm_type != u'':
1226 self.channel['comm_type'] = comm_type
1227 url = self._TCTRL_url.GetValue().strip()
1228 if url != u'':
1229 self.channel['url'] = url
1230 self.channel['is_confidential'] = self._CHBOX_confidential.GetValue()
1231
1232 self.channel['pk_address'] = self._PRW_address.GetData()
1233 self.channel.save_payload()
1234
1235 return True
1236 #--------------------------------------------------------
1237 # internal helpers
1238 #--------------------------------------------------------
1240
1241 no_errors = True
1242
1243 if self._PRW_type.GetData() is None:
1244 self._PRW_type.SetBackgroundColour('pink')
1245 self._PRW_type.SetFocus()
1246 self._PRW_type.Refresh()
1247 no_errors = False
1248 else:
1249 self._PRW_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1250 self._PRW_type.Refresh()
1251
1252 if self._TCTRL_url.GetValue().strip() == u'':
1253 self._TCTRL_url.SetBackgroundColour('pink')
1254 self._TCTRL_url.SetFocus()
1255 self._TCTRL_url.Refresh()
1256 no_errors = False
1257 else:
1258 self._TCTRL_url.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1259 self._TCTRL_url.Refresh()
1260
1261 return no_errors
1262 #------------------------------------------------------------
1264 """A list for managing a person's comm channels.
1265
1266 Does NOT act on/listen to the current patient.
1267 """
1269
1270 try:
1271 self.__identity = kwargs['identity']
1272 del kwargs['identity']
1273 except KeyError:
1274 self.__identity = None
1275
1276 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1277
1278 self.new_callback = self._add_comm
1279 self.edit_callback = self._edit_comm
1280 self.delete_callback = self._del_comm
1281 self.refresh_callback = self.refresh
1282
1283 self.__init_ui()
1284 self.refresh()
1285 #--------------------------------------------------------
1286 # external API
1287 #--------------------------------------------------------
1289 if self.__identity is None:
1290 self._LCTRL_items.set_string_items()
1291 return
1292
1293 comms = self.__identity.get_comm_channels()
1294 self._LCTRL_items.set_string_items (
1295 items = [ [ gmTools.bool2str(c['is_confidential'], u'X', u''), c['l10n_comm_type'], c['url'] ] for c in comms ]
1296 )
1297 self._LCTRL_items.set_column_widths()
1298 self._LCTRL_items.set_data(data = comms)
1299 #--------------------------------------------------------
1300 # internal helpers
1301 #--------------------------------------------------------
1303 self._LCTRL_items.SetToolTipString(_('List of known communication channels.'))
1304 self._LCTRL_items.set_columns(columns = [
1305 _('confidential'),
1306 _('Type'),
1307 _('Value')
1308 ])
1309 #--------------------------------------------------------
1311 ea = cCommChannelEditAreaPnl(self, -1)
1312 ea.identity = self.__identity
1313 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
1314 dlg.SetTitle(_('Adding new communications channel'))
1315 if dlg.ShowModal() == wx.ID_OK:
1316 return True
1317 return False
1318 #--------------------------------------------------------
1320 ea = cCommChannelEditAreaPnl(self, -1, comm_channel = comm_channel)
1321 ea.identity = self.__identity
1322 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
1323 dlg.SetTitle(_('Editing communications channel'))
1324 if dlg.ShowModal() == wx.ID_OK:
1325 return True
1326 return False
1327 #--------------------------------------------------------
1329 go_ahead = gmGuiHelpers.gm_show_question (
1330 _( 'Are you sure this patient can no longer\n'
1331 "be contacted via this channel ?"
1332 ),
1333 _('Removing communication channel')
1334 )
1335 if not go_ahead:
1336 return False
1337 self.__identity.unlink_comm_channel(comm_channel = comm)
1338 return True
1339 #--------------------------------------------------------
1340 # properties
1341 #--------------------------------------------------------
1344
1348
1349 identity = property(_get_identity, _set_identity)
1350 #============================================================
1351 # identity widgets
1352 #============================================================
1353 # phrasewheels
1354 #------------------------------------------------------------
1356
1358 query = u"select distinct lastnames, lastnames from dem.names where lastnames %(fragment_condition)s order by lastnames limit 25"
1359 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1360 mp.setThresholds(3, 5, 9)
1361 gmPhraseWheel.cPhraseWheel.__init__ (
1362 self,
1363 *args,
1364 **kwargs
1365 )
1366 self.SetToolTipString(_("Type or select a last name (family name/surname)."))
1367 self.capitalisation_mode = gmTools.CAPS_NAMES
1368 self.matcher = mp
1369 #------------------------------------------------------------
1371
1373 query = u"""
1374 (select distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
1375 union
1376 (select distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
1377 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1378 mp.setThresholds(3, 5, 9)
1379 gmPhraseWheel.cPhraseWheel.__init__ (
1380 self,
1381 *args,
1382 **kwargs
1383 )
1384 self.SetToolTipString(_("Type or select a first name (forename/Christian name/given name)."))
1385 self.capitalisation_mode = gmTools.CAPS_NAMES
1386 self.matcher = mp
1387 #------------------------------------------------------------
1389
1391 query = u"""
1392 (select distinct preferred, preferred from dem.names where preferred %(fragment_condition)s order by preferred limit 20)
1393 union
1394 (select distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
1395 union
1396 (select distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
1397 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1398 mp.setThresholds(3, 5, 9)
1399 gmPhraseWheel.cPhraseWheel.__init__ (
1400 self,
1401 *args,
1402 **kwargs
1403 )
1404 self.SetToolTipString(_("Type or select an alias (nick name, preferred name, call name, warrior name, artist name)."))
1405 # nicknames CAN start with lower case !
1406 #self.capitalisation_mode = gmTools.CAPS_NAMES
1407 self.matcher = mp
1408 #------------------------------------------------------------
1410
1412 query = u"select distinct title, title from dem.identity where title %(fragment_condition)s"
1413 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1414 mp.setThresholds(1, 3, 9)
1415 gmPhraseWheel.cPhraseWheel.__init__ (
1416 self,
1417 *args,
1418 **kwargs
1419 )
1420 self.SetToolTipString(_("Type or select a title. Note that the title applies to the person, not to a particular name !"))
1421 self.matcher = mp
1422 #------------------------------------------------------------
1424 """Let user select a gender."""
1425
1426 _gender_map = None
1427
1429
1430 if cGenderSelectionPhraseWheel._gender_map is None:
1431 cmd = u"""
1432 select tag, l10n_label, sort_weight
1433 from dem.v_gender_labels
1434 order by sort_weight desc"""
1435 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
1436 cGenderSelectionPhraseWheel._gender_map = {}
1437 for gender in rows:
1438 cGenderSelectionPhraseWheel._gender_map[gender[idx['tag']]] = {
1439 'data': gender[idx['tag']],
1440 'label': gender[idx['l10n_label']],
1441 'weight': gender[idx['sort_weight']]
1442 }
1443
1444 mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = cGenderSelectionPhraseWheel._gender_map.values())
1445 mp.setThresholds(1, 1, 3)
1446
1447 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1448 self.selection_only = True
1449 self.matcher = mp
1450 self.picklist_delay = 50
1451 #------------------------------------------------------------
1453
1455 query = u"select distinct name, _(name) from dem.occupation where _(name) %(fragment_condition)s"
1456 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1457 mp.setThresholds(1, 3, 5)
1458 gmPhraseWheel.cPhraseWheel.__init__ (
1459 self,
1460 *args,
1461 **kwargs
1462 )
1463 self.SetToolTipString(_("Type or select an occupation."))
1464 self.capitalisation_mode = gmTools.CAPS_FIRST
1465 self.matcher = mp
1466 #------------------------------------------------------------
1468
1470 query = u"""
1471 select distinct pk, (name || coalesce(' (%s ' || issuer || ')', '')) as label
1472 from dem.enum_ext_id_types
1473 where name %%(fragment_condition)s
1474 order by label limit 25""" % _('issued by')
1475 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1476 mp.setThresholds(1, 3, 5)
1477 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1478 self.SetToolTipString(_("Enter or select a type for the external ID."))
1479 self.matcher = mp
1480 #------------------------------------------------------------
1482
1484 query = u"""
1485 select distinct issuer, issuer
1486 from dem.enum_ext_id_types
1487 where issuer %(fragment_condition)s
1488 order by issuer limit 25"""
1489 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1490 mp.setThresholds(1, 3, 5)
1491 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1492 self.SetToolTipString(_("Type or select an occupation."))
1493 self.capitalisation_mode = gmTools.CAPS_FIRST
1494 self.matcher = mp
1495 #------------------------------------------------------------
1496 # edit areas
1497 #------------------------------------------------------------
1499 """An edit area for editing/creating external IDs.
1500
1501 Does NOT act on/listen to the current patient.
1502 """
1504
1505 try:
1506 self.ext_id = kwargs['external_id']
1507 del kwargs['external_id']
1508 except:
1509 self.ext_id = None
1510
1511 wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl.__init__(self, *args, **kwargs)
1512
1513 self.identity = None
1514
1515 self.__register_events()
1516
1517 self.refresh()
1518 #--------------------------------------------------------
1519 # external API
1520 #--------------------------------------------------------
1522 if ext_id is not None:
1523 self.ext_id = ext_id
1524
1525 if self.ext_id is not None:
1526 self._PRW_type.SetText(value = self.ext_id['name'], data = self.ext_id['pk_type'])
1527 self._TCTRL_value.SetValue(self.ext_id['value'])
1528 self._PRW_issuer.SetText(self.ext_id['issuer'])
1529 self._TCTRL_comment.SetValue(gmTools.coalesce(self.ext_id['comment'], u''))
1530 # FIXME: clear fields
1531 # else:
1532 # pass
1533 #--------------------------------------------------------
1535
1536 if not self.__valid_for_save():
1537 return False
1538
1539 # strip out " (issued by ...)" added by phrasewheel
1540 type = regex.split(' \(%s .+\)$' % _('issued by'), self._PRW_type.GetValue().strip(), 1)[0]
1541
1542 # add new external ID
1543 if self.ext_id is None:
1544 self.identity.add_external_id (
1545 type_name = type,
1546 value = self._TCTRL_value.GetValue().strip(),
1547 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''),
1548 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1549 )
1550 # edit old external ID
1551 else:
1552 self.identity.update_external_id (
1553 pk_id = self.ext_id['pk_id'],
1554 type = type,
1555 value = self._TCTRL_value.GetValue().strip(),
1556 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''),
1557 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1558 )
1559
1560 return True
1561 #--------------------------------------------------------
1562 # internal helpers
1563 #--------------------------------------------------------
1565 self._PRW_type.add_callback_on_lose_focus(self._on_type_set)
1566 #--------------------------------------------------------
1568 """Set the issuer according to the selected type.
1569
1570 Matches are fetched from existing records in backend.
1571 """
1572 pk_curr_type = self._PRW_type.GetData()
1573 if pk_curr_type is None:
1574 return True
1575 rows, idx = gmPG2.run_ro_queries(queries = [{
1576 'cmd': u"select issuer from dem.enum_ext_id_types where pk = %s",
1577 'args': [pk_curr_type]
1578 }])
1579 if len(rows) == 0:
1580 return True
1581 wx.CallAfter(self._PRW_issuer.SetText, rows[0][0])
1582 return True
1583 #--------------------------------------------------------
1585
1586 no_errors = True
1587
1588 # do not test .GetData() because adding external IDs
1589 # will create types if necessary
1590 # if self._PRW_type.GetData() is None:
1591 if self._PRW_type.GetValue().strip() == u'':
1592 self._PRW_type.SetBackgroundColour('pink')
1593 self._PRW_type.SetFocus()
1594 self._PRW_type.Refresh()
1595 else:
1596 self._PRW_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1597 self._PRW_type.Refresh()
1598
1599 if self._TCTRL_value.GetValue().strip() == u'':
1600 self._TCTRL_value.SetBackgroundColour('pink')
1601 self._TCTRL_value.SetFocus()
1602 self._TCTRL_value.Refresh()
1603 no_errors = False
1604 else:
1605 self._TCTRL_value.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1606 self._TCTRL_value.Refresh()
1607
1608 return no_errors
1609 #------------------------------------------------------------
1610 from Gnumed.wxGladeWidgets import wxgIdentityEAPnl
1611
1613 """An edit area for editing/creating title/gender/dob/dod etc."""
1614
1616
1617 try:
1618 data = kwargs['identity']
1619 del kwargs['identity']
1620 except KeyError:
1621 data = None
1622
1623 wxgIdentityEAPnl.wxgIdentityEAPnl.__init__(self, *args, **kwargs)
1624 gmEditArea.cGenericEditAreaMixin.__init__(self)
1625
1626 self.mode = 'new'
1627 self.data = data
1628 if data is not None:
1629 self.mode = 'edit'
1630
1631 # self.__init_ui()
1632 #----------------------------------------------------------------
1633 # def __init_ui(self):
1634 # # adjust phrasewheels etc
1635 #----------------------------------------------------------------
1636 # generic Edit Area mixin API
1637 #----------------------------------------------------------------
1639
1640 has_error = False
1641
1642 if self._PRW_gender.GetData() is None:
1643 self._PRW_gender.SetFocus()
1644 has_error = True
1645
1646 if not self._PRW_dob.is_valid_timestamp():
1647 val = self._PRW_dob.GetValue().strip()
1648 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val)
1649 self._PRW_dob.SetBackgroundColour('pink')
1650 self._PRW_dob.Refresh()
1651 self._PRW_dob.SetFocus()
1652 has_error = True
1653 else:
1654 self._PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1655 self._PRW_dob.Refresh()
1656
1657 if not self._DP_dod.is_valid_timestamp(allow_none=True):
1658 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.'))
1659 self._DP_dod.SetFocus()
1660 has_error = True
1661
1662 return (has_error is False)
1663 #----------------------------------------------------------------
1665 # save the data as a new instance
1666 # data = 1
1667
1668 # data[''] = 1
1669 # data[''] = 1
1670
1671 # data.save()
1672
1673 # must be done very late or else the property access
1674 # will refresh the display such that later field
1675 # access will return empty values
1676 # self.data = data
1677 return False
1678 return True
1679 #----------------------------------------------------------------
1681
1682 self.data['gender'] = self._PRW_gender.GetData()
1683
1684 if self._PRW_dob.GetValue().strip() == u'':
1685 self.data['dob'] = None
1686 else:
1687 self.data['dob'] = self._PRW_dob.GetData().get_pydt()
1688
1689 self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'')
1690 self.data['deceased'] = self._DP_dod.GetValue(as_pydt = True)
1691 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1692
1693 self.data.save()
1694 return True
1695 #----------------------------------------------------------------
1698 #----------------------------------------------------------------
1700
1701 self._LBL_info.SetLabel(u'ID: #%s' % (
1702 self.data.ID
1703 # FIXME: add 'deleted' status
1704 ))
1705 self._PRW_dob.SetText (
1706 value = self.data.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()),
1707 data = self.data['dob']
1708 )
1709 self._DP_dod.SetValue(self.data['deceased'])
1710 self._PRW_gender.SetData(self.data['gender'])
1711 #self._PRW_ethnicity.SetValue()
1712 self._PRW_title.SetText(gmTools.coalesce(self.data['title'], u''))
1713 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
1714 #----------------------------------------------------------------
1717 #----------------------------------------------------------------
1718
1719 #------------------------------------------------------------
1720 from Gnumed.wxGladeWidgets import wxgNameGenderDOBEditAreaPnl
1721
1723 """An edit area for editing/creating name/gender/dob.
1724
1725 Does NOT act on/listen to the current patient.
1726 """
1728
1729 self.__name = kwargs['name']
1730 del kwargs['name']
1731 self.__identity = gmPerson.cIdentity(aPK_obj = self.__name['pk_identity'])
1732
1733 wxgNameGenderDOBEditAreaPnl.wxgNameGenderDOBEditAreaPnl.__init__(self, *args, **kwargs)
1734
1735 self.__register_interests()
1736 self.refresh()
1737 #--------------------------------------------------------
1738 # external API
1739 #--------------------------------------------------------
1741 if self.__name is None:
1742 return
1743
1744 self._PRW_title.SetText(gmTools.coalesce(self.__name['title'], u''))
1745 self._PRW_firstname.SetText(self.__name['firstnames'])
1746 self._PRW_lastname.SetText(self.__name['lastnames'])
1747 self._PRW_nick.SetText(gmTools.coalesce(self.__name['preferred'], u''))
1748 self._PRW_dob.SetText (
1749 value = self.__identity.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()),
1750 data = self.__identity['dob']
1751 )
1752 self._PRW_gender.SetData(self.__name['gender'])
1753 self._CHBOX_active.SetValue(self.__name['active_name'])
1754 self._DP_dod.SetValue(self.__identity['deceased'])
1755 self._TCTRL_comment.SetValue(gmTools.coalesce(self.__name['comment'], u''))
1756 # FIXME: clear fields
1757 # else:
1758 # pass
1759 #--------------------------------------------------------
1761
1762 if not self.__valid_for_save():
1763 return False
1764
1765 self.__identity['gender'] = self._PRW_gender.GetData()
1766 if self._PRW_dob.GetValue().strip() == u'':
1767 self.__identity['dob'] = None
1768 else:
1769 self.__identity['dob'] = self._PRW_dob.GetData().get_pydt()
1770 self.__identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'')
1771 self.__identity['deceased'] = self._DP_dod.GetValue(as_pydt = True)
1772 self.__identity.save_payload()
1773
1774 active = self._CHBOX_active.GetValue()
1775 first = self._PRW_firstname.GetValue().strip()
1776 last = self._PRW_lastname.GetValue().strip()
1777 old_nick = self.__name['preferred']
1778
1779 # is it a new name ?
1780 old_name = self.__name['firstnames'] + self.__name['lastnames']
1781 if (first + last) != old_name:
1782 self.__name = self.__identity.add_name(first, last, active)
1783
1784 self.__name['active_name'] = active
1785 self.__name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
1786 self.__name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
1787
1788 self.__name.save_payload()
1789
1790 return True
1791 #--------------------------------------------------------
1792 # event handling
1793 #--------------------------------------------------------
1795 self._PRW_firstname.add_callback_on_lose_focus(self._on_name_set)
1796 #--------------------------------------------------------
1798 """Set the gender according to entered firstname.
1799
1800 Matches are fetched from existing records in backend.
1801 """
1802 firstname = self._PRW_firstname.GetValue().strip()
1803 if firstname == u'':
1804 return True
1805 rows, idx = gmPG2.run_ro_queries(queries = [{
1806 'cmd': u"select gender from dem.name_gender_map where name ilike %s",
1807 'args': [firstname]
1808 }])
1809 if len(rows) == 0:
1810 return True
1811 wx.CallAfter(self._PRW_gender.SetData, rows[0][0])
1812 return True
1813 #--------------------------------------------------------
1814 # internal helpers
1815 #--------------------------------------------------------
1817
1818 has_error = False
1819
1820 if self._PRW_gender.GetData() is None:
1821 self._PRW_gender.SetBackgroundColour('pink')
1822 self._PRW_gender.Refresh()
1823 self._PRW_gender.SetFocus()
1824 has_error = True
1825 else:
1826 self._PRW_gender.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1827 self._PRW_gender.Refresh()
1828
1829 if not self._PRW_dob.is_valid_timestamp():
1830 val = self._PRW_dob.GetValue().strip()
1831 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val)
1832 self._PRW_dob.SetBackgroundColour('pink')
1833 self._PRW_dob.Refresh()
1834 self._PRW_dob.SetFocus()
1835 has_error = True
1836 else:
1837 self._PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1838 self._PRW_dob.Refresh()
1839
1840 if not self._DP_dod.is_valid_timestamp():
1841 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.'))
1842 self._DP_dod.SetBackgroundColour('pink')
1843 self._DP_dod.Refresh()
1844 self._DP_dod.SetFocus()
1845 has_error = True
1846 else:
1847 self._DP_dod.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1848 self._DP_dod.Refresh()
1849
1850 if self._PRW_lastname.GetValue().strip() == u'':
1851 self._PRW_lastname.SetBackgroundColour('pink')
1852 self._PRW_lastname.Refresh()
1853 self._PRW_lastname.SetFocus()
1854 has_error = True
1855 else:
1856 self._PRW_lastname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1857 self._PRW_lastname.Refresh()
1858
1859 if self._PRW_firstname.GetValue().strip() == u'':
1860 self._PRW_firstname.SetBackgroundColour('pink')
1861 self._PRW_firstname.Refresh()
1862 self._PRW_firstname.SetFocus()
1863 has_error = True
1864 else:
1865 self._PRW_firstname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1866 self._PRW_firstname.Refresh()
1867
1868 return (has_error is False)
1869 #------------------------------------------------------------
1870 # list manager
1871 #------------------------------------------------------------
1873 """A list for managing a person's names.
1874
1875 Does NOT act on/listen to the current patient.
1876 """
1878
1879 try:
1880 self.__identity = kwargs['identity']
1881 del kwargs['identity']
1882 except KeyError:
1883 self.__identity = None
1884
1885 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1886
1887 self.new_callback = self._add_name
1888 self.edit_callback = self._edit_name
1889 self.delete_callback = self._del_name
1890 self.refresh_callback = self.refresh
1891
1892 self.__init_ui()
1893 self.refresh()
1894 #--------------------------------------------------------
1895 # external API
1896 #--------------------------------------------------------
1898 if self.__identity is None:
1899 self._LCTRL_items.set_string_items()
1900 return
1901
1902 names = self.__identity.get_names()
1903 self._LCTRL_items.set_string_items (
1904 items = [ [
1905 gmTools.bool2str(n['active_name'], 'X', ''),
1906 n['lastnames'],
1907 n['firstnames'],
1908 gmTools.coalesce(n['preferred'], u''),
1909 gmTools.coalesce(n['comment'], u'')
1910 ] for n in names ]
1911 )
1912 self._LCTRL_items.set_column_widths()
1913 self._LCTRL_items.set_data(data = names)
1914 #--------------------------------------------------------
1915 # internal helpers
1916 #--------------------------------------------------------
1918 self._LCTRL_items.set_columns(columns = [
1919 _('Active'),
1920 _('Lastname'),
1921 _('Firstname(s)'),
1922 _('Preferred Name'),
1923 _('Comment')
1924 ])
1925 #--------------------------------------------------------
1927 ea = cNameGenderDOBEditAreaPnl(self, -1, name = self.__identity.get_active_name())
1928 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
1929 dlg.SetTitle(_('Adding new name'))
1930 if dlg.ShowModal() == wx.ID_OK:
1931 dlg.Destroy()
1932 return True
1933 dlg.Destroy()
1934 return False
1935 #--------------------------------------------------------
1937 ea = cNameGenderDOBEditAreaPnl(self, -1, name = name)
1938 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
1939 dlg.SetTitle(_('Editing name'))
1940 if dlg.ShowModal() == wx.ID_OK:
1941 dlg.Destroy()
1942 return True
1943 dlg.Destroy()
1944 return False
1945 #--------------------------------------------------------
1947
1948 if len(self.__identity.get_names()) == 1:
1949 gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the only name of a person.'), beep = True)
1950 return False
1951
1952 go_ahead = gmGuiHelpers.gm_show_question (
1953 _( 'It is often advisable to keep old names around and\n'
1954 'just create a new "currently active" name.\n'
1955 '\n'
1956 'This allows finding the patient by both the old\n'
1957 'and the new name (think before/after marriage).\n'
1958 '\n'
1959 'Do you still want to really delete\n'
1960 "this name from the patient ?"
1961 ),
1962 _('Deleting name')
1963 )
1964 if not go_ahead:
1965 return False
1966
1967 self.__identity.delete_name(name = name)
1968 return True
1969 #--------------------------------------------------------
1970 # properties
1971 #--------------------------------------------------------
1974
1978
1979 identity = property(_get_identity, _set_identity)
1980 #------------------------------------------------------------
1982 """A list for managing a person's external IDs.
1983
1984 Does NOT act on/listen to the current patient.
1985 """
1987
1988 try:
1989 self.__identity = kwargs['identity']
1990 del kwargs['identity']
1991 except KeyError:
1992 self.__identity = None
1993
1994 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1995
1996 self.new_callback = self._add_id
1997 self.edit_callback = self._edit_id
1998 self.delete_callback = self._del_id
1999 self.refresh_callback = self.refresh
2000
2001 self.__init_ui()
2002 self.refresh()
2003 #--------------------------------------------------------
2004 # external API
2005 #--------------------------------------------------------
2007 if self.__identity is None:
2008 self._LCTRL_items.set_string_items()
2009 return
2010
2011 ids = self.__identity.get_external_ids()
2012 self._LCTRL_items.set_string_items (
2013 items = [ [
2014 i['name'],
2015 i['value'],
2016 gmTools.coalesce(i['issuer'], u''),
2017 i['context'],
2018 gmTools.coalesce(i['comment'], u'')
2019 ] for i in ids
2020 ]
2021 )
2022 self._LCTRL_items.set_column_widths()
2023 self._LCTRL_items.set_data(data = ids)
2024 #--------------------------------------------------------
2025 # internal helpers
2026 #--------------------------------------------------------
2028 self._LCTRL_items.set_columns(columns = [
2029 _('ID type'),
2030 _('Value'),
2031 _('Issuer'),
2032 _('Context'),
2033 _('Comment')
2034 ])
2035 #--------------------------------------------------------
2037 ea = cExternalIDEditAreaPnl(self, -1)
2038 ea.identity = self.__identity
2039 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
2040 dlg.SetTitle(_('Adding new external ID'))
2041 if dlg.ShowModal() == wx.ID_OK:
2042 dlg.Destroy()
2043 return True
2044 dlg.Destroy()
2045 return False
2046 #--------------------------------------------------------
2048 ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id)
2049 ea.identity = self.__identity
2050 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea)
2051 dlg.SetTitle(_('Editing external ID'))
2052 if dlg.ShowModal() == wx.ID_OK:
2053 dlg.Destroy()
2054 return True
2055 dlg.Destroy()
2056 return False
2057 #--------------------------------------------------------
2059 go_ahead = gmGuiHelpers.gm_show_question (
2060 _( 'Do you really want to delete this\n'
2061 'external ID from the patient ?'),
2062 _('Deleting external ID')
2063 )
2064 if not go_ahead:
2065 return False
2066 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id'])
2067 return True
2068 #--------------------------------------------------------
2069 # properties
2070 #--------------------------------------------------------
2073
2077
2078 identity = property(_get_identity, _set_identity)
2079 #------------------------------------------------------------
2080 # integrated panels
2081 #------------------------------------------------------------
2083 """A panel for editing identity data for a person.
2084
2085 - provides access to:
2086 - name
2087 - external IDs
2088
2089 Does NOT act on/listen to the current patient.
2090 """
2092
2093 wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs)
2094
2095 self.__identity = None
2096 self.refresh()
2097 #--------------------------------------------------------
2098 # external API
2099 #--------------------------------------------------------
2101 self._PNL_names.identity = self.__identity
2102 self._PNL_ids.identity = self.__identity
2103 # this is an Edit Area:
2104 self._PNL_identity.mode = 'new'
2105 self._PNL_identity.data = self.__identity
2106 if self.__identity is not None:
2107 self._PNL_identity.mode = 'edit'
2108 #--------------------------------------------------------
2109 # properties
2110 #--------------------------------------------------------
2113
2117
2118 identity = property(_get_identity, _set_identity)
2119 #--------------------------------------------------------
2120 # event handlers
2121 #--------------------------------------------------------
2125 #--------------------------------------------------------
2128
2129 #============================================================
2130 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl
2131
2132 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
2134
2135 wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs)
2136
2137 self.__identity = None
2138 self.refresh()
2139 #--------------------------------------------------------
2140 # external API
2141 #--------------------------------------------------------
2143
2144 tt = _("Link another person in this database as the emergency contact.")
2145
2146 if self.__identity is None:
2147 self._TCTRL_er_contact.SetValue(u'')
2148 self._TCTRL_person.person = None
2149 self._TCTRL_person.SetToolTipString(tt)
2150 return
2151
2152 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], u''))
2153 if self.__identity['pk_emergency_contact'] is not None:
2154 ident = gmPerson.cIdentity(aPK_obj = self.__identity['pk_emergency_contact'])
2155 self._TCTRL_person.person = ident
2156 tt = u'%s\n\n%s\n\n%s' % (
2157 tt,
2158 ident['description_gender'],
2159 u'\n'.join([
2160 u'%s: %s%s' % (
2161 c['l10n_comm_type'],
2162 c['url'],
2163 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), u'', u'')
2164 )
2165 for c in ident.get_comm_channels()
2166 ])
2167 )
2168 else:
2169 self._TCTRL_person.person = None
2170
2171 self._TCTRL_person.SetToolTipString(tt)
2172 #--------------------------------------------------------
2173 # properties
2174 #--------------------------------------------------------
2177
2181
2182 identity = property(_get_identity, _set_identity)
2183 #--------------------------------------------------------
2184 # event handlers
2185 #--------------------------------------------------------
2194 #--------------------------------------------------------
2205 #--------------------------------------------------------
2213 #============================================================
2214 # new-patient widgets
2215 #============================================================
2217
2218 dbcfg = gmCfg.cCfgSQL()
2219
2220 def_region = dbcfg.get2 (
2221 option = u'person.create.default_region',
2222 workplace = gmSurgery.gmCurrentPractice().active_workplace,
2223 bias = u'user'
2224 )
2225
2226 if def_region is None:
2227 def_country = dbcfg.get2 (
2228 option = u'person.create.default_country',
2229 workplace = gmSurgery.gmCurrentPractice().active_workplace,
2230 bias = u'user'
2231 )
2232 else:
2233 countries = gmDemographicRecord.get_country_for_region(region = def_region)
2234 if len(countries) == 1:
2235 def_country = countries[0]['l10n_country']
2236
2237 if parent is None:
2238 parent = wx.GetApp().GetTopWindow()
2239
2240 ea = cNewPatientEAPnl(parent = parent, id = -1, country = def_country, region = def_region)
2241 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
2242 dlg.SetTitle(_('Adding new person'))
2243 ea._PRW_lastname.SetFocus()
2244 result = dlg.ShowModal()
2245 pat = ea.data
2246 dlg.Destroy()
2247
2248 if result != wx.ID_OK:
2249 return False
2250
2251 if activate:
2252 from Gnumed.wxpython import gmPatSearchWidgets
2253 gmPatSearchWidgets.set_active_patient(patient = pat)
2254
2255 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
2256
2257 return True
2258 #============================================================
2259 from Gnumed.wxGladeWidgets import wxgNewPatientEAPnl
2260
2261 -class cNewPatientEAPnl(wxgNewPatientEAPnl.wxgNewPatientEAPnl, gmEditArea.cGenericEditAreaMixin):
2262
2264
2265 try:
2266 self.default_region = kwargs['region']
2267 del kwargs['region']
2268 except KeyError:
2269 self.default_region = None
2270
2271 try:
2272 self.default_country = kwargs['country']
2273 del kwargs['country']
2274 except KeyError:
2275 self.default_country = None
2276
2277 wxgNewPatientEAPnl.wxgNewPatientEAPnl.__init__(self, *args, **kwargs)
2278 gmEditArea.cGenericEditAreaMixin.__init__(self)
2279
2280 self.mode = 'new'
2281 self.data = None
2282 self._address = None
2283
2284 self.__init_ui()
2285 self.__register_interests()
2286 #----------------------------------------------------------------
2287 # internal helpers
2288 #----------------------------------------------------------------
2290 self._PRW_lastname.final_regex = '.+'
2291 self._PRW_firstnames.final_regex = '.+'
2292 self._PRW_address_searcher.selection_only = False
2293 low = wx.DateTimeFromDMY(1,0,1900)
2294 hi = wx.DateTime()
2295 self._DP_dob.SetRange(low, hi.SetToCurrent())
2296 # only if we would support None on selection_only's
2297 #self._PRW_external_id_type.selection_only = True
2298
2299 if self.default_country is not None:
2300 self._PRW_country.SetText(value = self.default_country)
2301
2302 if self.default_region is not None:
2303 self._PRW_region.SetText(value = self.default_region)
2304 #----------------------------------------------------------------
2306
2307 adr = self._PRW_address_searcher.get_address()
2308 if adr is None:
2309 return True
2310
2311 if ctrl.GetValue().strip() != adr[field]:
2312 wx.CallAfter(self._PRW_address_searcher.SetText, value = u'', data = None)
2313 return True
2314
2315 return False
2316 #----------------------------------------------------------------
2318 adr = self._PRW_address_searcher.get_address()
2319 if adr is None:
2320 return True
2321
2322 self._PRW_zip.SetText(value = adr['postcode'], data = adr['postcode'])
2323
2324 self._PRW_street.SetText(value = adr['street'], data = adr['street'])
2325 self._PRW_street.set_context(context = u'zip', val = adr['postcode'])
2326
2327 self._TCTRL_number.SetValue(adr['number'])
2328
2329 self._PRW_urb.SetText(value = adr['urb'], data = adr['urb'])
2330 self._PRW_urb.set_context(context = u'zip', val = adr['postcode'])
2331
2332 self._PRW_region.SetText(value = adr['l10n_state'], data = adr['code_state'])
2333 self._PRW_region.set_context(context = u'zip', val = adr['postcode'])
2334
2335 self._PRW_country.SetText(value = adr['l10n_country'], data = adr['code_country'])
2336 self._PRW_country.set_context(context = u'zip', val = adr['postcode'])
2337 #----------------------------------------------------------------
2339 error = False
2340
2341 # name fields
2342 if self._PRW_lastname.GetValue().strip() == u'':
2343 error = True
2344 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.'))
2345 self._PRW_lastname.display_as_valid(False)
2346 else:
2347 self._PRW_lastname.display_as_valid(True)
2348
2349 if self._PRW_firstnames.GetValue().strip() == '':
2350 error = True
2351 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.'))
2352 self._PRW_firstnames.display_as_valid(False)
2353 else:
2354 self._PRW_firstnames.display_as_valid(True)
2355
2356 # gender
2357 if self._PRW_gender.GetData() is None:
2358 error = True
2359 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.'))
2360 self._PRW_gender.display_as_valid(False)
2361 else:
2362 self._PRW_gender.display_as_valid(True)
2363
2364 # dob validation
2365 if not self._DP_dob.is_valid_timestamp():
2366
2367 gmDispatcher.send(signal = 'statustext', msg = _('Cannot use this date of birth. Does it lie before 1900 ?'))
2368
2369 do_it_anyway = gmGuiHelpers.gm_show_question (
2370 _(
2371 'Are you sure you want to register this person\n'
2372 'without a valid date of birth ?\n'
2373 '\n'
2374 'This can be useful for temporary staff members\n'
2375 'but will provoke nag screens if this person\n'
2376 'becomes a patient.\n'
2377 '\n'
2378 'Note that the date of birth cannot technically\n'
2379 'be before 1900, either :-(\n'
2380 ),
2381 _('Registering new person')
2382 )
2383
2384 if not do_it_anyway:
2385 error = True
2386
2387 if self._DP_dob.GetValue() is None:
2388 self._DP_dob.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
2389 elif self._DP_dob.GetValue().GetYear() < 1900:
2390 error = True
2391 gmDispatcher.send(signal = 'statustext', msg = _('The year of birth must lie after 1900.'), beep = True)
2392 self._DP_dob.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
2393 self._DP_dob.SetFocus()
2394 else:
2395 self._DP_dob.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
2396 self._DP_dob.Refresh()
2397
2398 # TOB validation if non-empty
2399 # if self._TCTRL_tob.GetValue().strip() != u'':
2400
2401 return (not error)
2402 #----------------------------------------------------------------
2404
2405 # existing address ? if so set other fields
2406 if self._PRW_address_searcher.GetData() is not None:
2407 wx.CallAfter(self.__set_fields_from_address_searcher)
2408 return True
2409
2410 # must either all contain something or none of them
2411 fields_to_fill = (
2412 self._TCTRL_number,
2413 self._PRW_zip,
2414 self._PRW_street,
2415 self._PRW_urb,
2416 self._PRW_region,
2417 self._PRW_country
2418 )
2419 no_of_filled_fields = 0
2420
2421 for field in fields_to_fill:
2422 if field.GetValue().strip() != u'':
2423 no_of_filled_fields += 1
2424 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
2425 field.Refresh()
2426
2427 # empty address ?
2428 if no_of_filled_fields == 0:
2429 if empty_address_is_valid:
2430 return True
2431 else:
2432 return None
2433
2434 # incompletely filled address ?
2435 if no_of_filled_fields != len(fields_to_fill):
2436 for field in fields_to_fill:
2437 if field.GetValue().strip() == u'':
2438 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
2439 field.SetFocus()
2440 field.Refresh()
2441 msg = _('To properly create an address, all the related fields must be filled in.')
2442 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
2443 return False
2444
2445 # fields which must contain a selected item
2446 # FIXME: they must also contain an *acceptable combination* which
2447 # FIXME: can only be tested against the database itself ...
2448 strict_fields = (
2449 self._PRW_region,
2450 self._PRW_country
2451 )
2452 error = False
2453 for field in strict_fields:
2454 if field.GetData() is None:
2455 error = True
2456 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
2457 field.SetFocus()
2458 else:
2459 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
2460 field.Refresh()
2461
2462 if error:
2463 msg = _('This field must contain an item selected from the dropdown list.')
2464 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
2465 return False
2466
2467 return True
2468 #----------------------------------------------------------------
2470
2471 # identity
2472 self._PRW_firstnames.add_callback_on_lose_focus(self._on_leaving_firstname)
2473
2474 # address
2475 self._PRW_address_searcher.add_callback_on_lose_focus(self._on_leaving_adress_searcher)
2476
2477 # invalidate address searcher when any field edited
2478 self._PRW_street.add_callback_on_lose_focus(self._invalidate_address_searcher)
2479 wx.EVT_KILL_FOCUS(self._TCTRL_number, self._invalidate_address_searcher)
2480 self._PRW_urb.add_callback_on_lose_focus(self._invalidate_address_searcher)
2481 self._PRW_region.add_callback_on_lose_focus(self._invalidate_address_searcher)
2482
2483 self._PRW_zip.add_callback_on_lose_focus(self._on_leaving_zip)
2484 self._PRW_country.add_callback_on_lose_focus(self._on_leaving_country)
2485 #----------------------------------------------------------------
2486 # event handlers
2487 #----------------------------------------------------------------
2489 """Set the gender according to entered firstname.
2490
2491 Matches are fetched from existing records in backend.
2492 """
2493 # only set if not already set so as to not
2494 # overwrite a change by the user
2495 if self._PRW_gender.GetData() is not None:
2496 return True
2497
2498 firstname = self._PRW_firstnames.GetValue().strip()
2499 if firstname == u'':
2500 return True
2501
2502 gender = gmPerson.map_firstnames2gender(firstnames = firstname)
2503 if gender is None:
2504 return True
2505
2506 wx.CallAfter(self._PRW_gender.SetData, gender)
2507 return True
2508 #----------------------------------------------------------------
2510 self.__perhaps_invalidate_address_searcher(self._PRW_zip, 'postcode')
2511
2512 zip_code = gmTools.none_if(self._PRW_zip.GetValue().strip(), u'')
2513 self._PRW_street.set_context(context = u'zip', val = zip_code)
2514 self._PRW_urb.set_context(context = u'zip', val = zip_code)
2515 self._PRW_region.set_context(context = u'zip', val = zip_code)
2516 self._PRW_country.set_context(context = u'zip', val = zip_code)
2517
2518 return True
2519 #----------------------------------------------------------------
2521 self.__perhaps_invalidate_address_searcher(self._PRW_country, 'l10n_country')
2522
2523 country = gmTools.none_if(self._PRW_country.GetValue().strip(), u'')
2524 self._PRW_region.set_context(context = u'country', val = country)
2525
2526 return True
2527 #----------------------------------------------------------------
2529 mapping = [
2530 (self._PRW_street, 'street'),
2531 (self._TCTRL_number, 'number'),
2532 (self._PRW_urb, 'urb'),
2533 (self._PRW_region, 'l10n_state')
2534 ]
2535
2536 # loop through fields and invalidate address searcher if different
2537 for ctrl, field in mapping:
2538 if self.__perhaps_invalidate_address_searcher(ctrl, field):
2539 return True
2540
2541 return True
2542 #----------------------------------------------------------------
2544 adr = self._PRW_address_searcher.get_address()
2545 if adr is None:
2546 return True
2547
2548 wx.CallAfter(self.__set_fields_from_address_searcher)
2549 return True
2550 #----------------------------------------------------------------
2551 # generic Edit Area mixin API
2552 #----------------------------------------------------------------
2554 return (self.__identity_valid_for_save() and self.__address_valid_for_save(empty_address_is_valid = True))
2555 #----------------------------------------------------------------
2557
2558 # identity
2559 new_identity = gmPerson.create_identity (
2560 gender = self._PRW_gender.GetData(),
2561 dob = self._DP_dob.get_pydt(),
2562 lastnames = self._PRW_lastname.GetValue().strip(),
2563 firstnames = self._PRW_firstnames.GetValue().strip()
2564 )
2565 _log.debug('identity created: %s' % new_identity)
2566
2567 new_identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip())
2568 new_identity.set_nickname(nickname = gmTools.none_if(self._PRW_nickname.GetValue().strip(), u''))
2569 #TOB
2570 new_identity.save()
2571
2572 name = new_identity.get_active_name()
2573 name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
2574 name.save()
2575
2576 # address
2577 is_valid = self.__address_valid_for_save(empty_address_is_valid = False)
2578 if is_valid is True:
2579 # because we currently only check for non-emptiness
2580 # we must still deal with database errors
2581 try:
2582 new_identity.link_address (
2583 number = self._TCTRL_number.GetValue().strip(),
2584 street = self._PRW_street.GetValue().strip(),
2585 postcode = self._PRW_zip.GetValue().strip(),
2586 urb = self._PRW_urb.GetValue().strip(),
2587 state = self._PRW_region.GetData(),
2588 country = self._PRW_country.GetData()
2589 )
2590 except psycopg2.InternalError:
2591 #except StandardError:
2592 _log.debug('number: >>%s<<', self._TCTRL_number.GetValue().strip())
2593 _log.debug('street: >>%s<<', self._PRW_street.GetValue().strip())
2594 _log.debug('postcode: >>%s<<', self._PRW_zip.GetValue().strip())
2595 _log.debug('urb: >>%s<<', self._PRW_urb.GetValue().strip())
2596 _log.debug('state: >>%s<<', self._PRW_region.GetData().strip())
2597 _log.debug('country: >>%s<<', self._PRW_country.GetData().strip())
2598 _log.exception('cannot link address')
2599 gmGuiHelpers.gm_show_error (
2600 aTitle = _('Saving address'),
2601 aMessage = _(
2602 'Cannot save this address.\n'
2603 '\n'
2604 'You will have to add it via the Demographics plugin.\n'
2605 )
2606 )
2607 elif is_valid is False:
2608 gmGuiHelpers.gm_show_error (
2609 aTitle = _('Saving address'),
2610 aMessage = _(
2611 'Address not saved.\n'
2612 '\n'
2613 'You will have to add it via the Demographics plugin.\n'
2614 )
2615 )
2616 # else it is None which means empty address which we ignore
2617
2618 # phone
2619 new_identity.link_comm_channel (
2620 comm_medium = u'homephone',
2621 url = gmTools.none_if(self._TCTRL_phone.GetValue().strip(), u''),
2622 is_confidential = False
2623 )
2624
2625 # external ID
2626 pk_type = self._PRW_external_id_type.GetData()
2627 id_value = self._TCTRL_external_id_value.GetValue().strip()
2628 if (pk_type is not None) and (id_value != u''):
2629 new_identity.add_external_id(value = id_value, pk_type = pk_type)
2630
2631 # occupation
2632 new_identity.link_occupation (
2633 occupation = gmTools.none_if(self._PRW_occupation.GetValue().strip(), u'')
2634 )
2635
2636 self.data = new_identity
2637 return True
2638 #----------------------------------------------------------------
2641 #----------------------------------------------------------------
2645 #----------------------------------------------------------------
2648 #----------------------------------------------------------------
2651 #============================================================
2652 # new-patient wizard classes
2653 #============================================================
2655 """
2656 Wizard page for entering patient's basic demographic information
2657 """
2658
2659 form_fields = (
2660 'firstnames', 'lastnames', 'nick', 'dob', 'gender', 'title', 'occupation',
2661 'address_number', 'zip_code', 'street', 'town', 'state', 'country', 'phone', 'comment'
2662 )
2663
2665 """
2666 Creates a new instance of BasicPatDetailsPage
2667 @param parent - The parent widget
2668 @type parent - A wx.Window instance
2669 @param tile - The title of the page
2670 @type title - A StringType instance
2671 """
2672 wx.wizard.WizardPageSimple.__init__(self, parent) #, bitmap = gmGuiHelpers.gm_icon(_('oneperson'))
2673 self.__title = title
2674 self.__do_layout()
2675 self.__register_interests()
2676 #--------------------------------------------------------
2678 PNL_form = wx.Panel(self, -1)
2679
2680 # last name
2681 STT_lastname = wx.StaticText(PNL_form, -1, _('Last name'))
2682 STT_lastname.SetForegroundColour('red')
2683 self.PRW_lastname = cLastnamePhraseWheel(parent = PNL_form, id = -1)
2684 self.PRW_lastname.SetToolTipString(_('Required: lastname (family name)'))
2685
2686 # first name
2687 STT_firstname = wx.StaticText(PNL_form, -1, _('First name(s)'))
2688 STT_firstname.SetForegroundColour('red')
2689 self.PRW_firstname = cFirstnamePhraseWheel(parent = PNL_form, id = -1)
2690 self.PRW_firstname.SetToolTipString(_('Required: surname/given name/first name'))
2691
2692 # nickname
2693 STT_nick = wx.StaticText(PNL_form, -1, _('Nick name'))
2694 self.PRW_nick = cNicknamePhraseWheel(parent = PNL_form, id = -1)
2695
2696 # DOB
2697 STT_dob = wx.StaticText(PNL_form, -1, _('Date of birth'))
2698 STT_dob.SetForegroundColour('red')
2699 self.PRW_dob = gmDateTimeInput.cFuzzyTimestampInput(parent = PNL_form, id = -1)
2700 self.PRW_dob.SetToolTipString(_("Required: date of birth, if unknown or aliasing wanted then invent one"))
2701
2702 # gender
2703 STT_gender = wx.StaticText(PNL_form, -1, _('Gender'))
2704 STT_gender.SetForegroundColour('red')
2705 self.PRW_gender = cGenderSelectionPhraseWheel(parent = PNL_form, id=-1)
2706 self.PRW_gender.SetToolTipString(_("Required: gender of patient"))
2707
2708 # title
2709 STT_title = wx.StaticText(PNL_form, -1, _('Title'))
2710 self.PRW_title = cTitlePhraseWheel(parent = PNL_form, id = -1)
2711
2712 # zip code
2713 STT_zip_code = wx.StaticText(PNL_form, -1, _('Postal code'))
2714 STT_zip_code.SetForegroundColour('orange')
2715 self.PRW_zip_code = cZipcodePhraseWheel(parent = PNL_form, id = -1)
2716 self.PRW_zip_code.SetToolTipString(_("primary/home address: zip/postal code"))
2717
2718 # street
2719 STT_street = wx.StaticText(PNL_form, -1, _('Street'))
2720 STT_street.SetForegroundColour('orange')
2721 self.PRW_street = cStreetPhraseWheel(parent = PNL_form, id = -1)
2722 self.PRW_street.SetToolTipString(_("primary/home address: name of street"))
2723
2724 # address number
2725 STT_address_number = wx.StaticText(PNL_form, -1, _('Number'))
2726 STT_address_number.SetForegroundColour('orange')
2727 self.TTC_address_number = wx.TextCtrl(PNL_form, -1)
2728 self.TTC_address_number.SetToolTipString(_("primary/home address: address number"))
2729
2730 # town
2731 STT_town = wx.StaticText(PNL_form, -1, _('Place'))
2732 STT_town.SetForegroundColour('orange')
2733 self.PRW_town = cUrbPhraseWheel(parent = PNL_form, id = -1)
2734 self.PRW_town.SetToolTipString(_("primary/home address: city/town/village/dwelling/..."))
2735
2736 # state
2737 STT_state = wx.StaticText(PNL_form, -1, _('Region'))
2738 STT_state.SetForegroundColour('orange')
2739 self.PRW_state = cStateSelectionPhraseWheel(parent=PNL_form, id=-1)
2740 self.PRW_state.SetToolTipString(_("primary/home address: state/province/county/..."))
2741
2742 # country
2743 STT_country = wx.StaticText(PNL_form, -1, _('Country'))
2744 STT_country.SetForegroundColour('orange')
2745 self.PRW_country = cCountryPhraseWheel(parent = PNL_form, id = -1)
2746 self.PRW_country.SetToolTipString(_("primary/home address: country"))
2747
2748 # phone
2749 STT_phone = wx.StaticText(PNL_form, -1, _('Phone'))
2750 self.TTC_phone = wx.TextCtrl(PNL_form, -1)
2751 self.TTC_phone.SetToolTipString(_("phone number at home"))
2752
2753 # occupation
2754 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
2755 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1)
2756
2757 # comment
2758 STT_comment = wx.StaticText(PNL_form, -1, _('Comment'))
2759 self.TCTRL_comment = wx.TextCtrl(PNL_form, -1)
2760 self.TCTRL_comment.SetToolTipString(_('A comment on this patient.'))
2761
2762 # form main validator
2763 self.form_DTD = cFormDTD(fields = self.__class__.form_fields)
2764 PNL_form.SetValidator(cBasicPatDetailsPageValidator(dtd = self.form_DTD))
2765
2766 # layout input widgets
2767 SZR_input = wx.FlexGridSizer(cols = 2, rows = 16, vgap = 4, hgap = 4)
2768 SZR_input.AddGrowableCol(1)
2769 SZR_input.Add(STT_lastname, 0, wx.SHAPED)
2770 SZR_input.Add(self.PRW_lastname, 1, wx.EXPAND)
2771 SZR_input.Add(STT_firstname, 0, wx.SHAPED)
2772 SZR_input.Add(self.PRW_firstname, 1, wx.EXPAND)
2773 SZR_input.Add(STT_nick, 0, wx.SHAPED)
2774 SZR_input.Add(self.PRW_nick, 1, wx.EXPAND)
2775 SZR_input.Add(STT_dob, 0, wx.SHAPED)
2776 SZR_input.Add(self.PRW_dob, 1, wx.EXPAND)
2777 SZR_input.Add(STT_gender, 0, wx.SHAPED)
2778 SZR_input.Add(self.PRW_gender, 1, wx.EXPAND)
2779 SZR_input.Add(STT_title, 0, wx.SHAPED)
2780 SZR_input.Add(self.PRW_title, 1, wx.EXPAND)
2781 SZR_input.Add(STT_zip_code, 0, wx.SHAPED)
2782 SZR_input.Add(self.PRW_zip_code, 1, wx.EXPAND)
2783 SZR_input.Add(STT_street, 0, wx.SHAPED)
2784 SZR_input.Add(self.PRW_street, 1, wx.EXPAND)
2785 SZR_input.Add(STT_address_number, 0, wx.SHAPED)
2786 SZR_input.Add(self.TTC_address_number, 1, wx.EXPAND)
2787 SZR_input.Add(STT_town, 0, wx.SHAPED)
2788 SZR_input.Add(self.PRW_town, 1, wx.EXPAND)
2789 SZR_input.Add(STT_state, 0, wx.SHAPED)
2790 SZR_input.Add(self.PRW_state, 1, wx.EXPAND)
2791 SZR_input.Add(STT_country, 0, wx.SHAPED)
2792 SZR_input.Add(self.PRW_country, 1, wx.EXPAND)
2793 SZR_input.Add(STT_phone, 0, wx.SHAPED)
2794 SZR_input.Add(self.TTC_phone, 1, wx.EXPAND)
2795 SZR_input.Add(STT_occupation, 0, wx.SHAPED)
2796 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
2797 SZR_input.Add(STT_comment, 0, wx.SHAPED)
2798 SZR_input.Add(self.TCTRL_comment, 1, wx.EXPAND)
2799
2800 PNL_form.SetSizerAndFit(SZR_input)
2801
2802 # layout page
2803 SZR_main = gmGuiHelpers.makePageTitle(self, self.__title)
2804 SZR_main.Add(PNL_form, 1, wx.EXPAND)
2805 #--------------------------------------------------------
2806 # event handling
2807 #--------------------------------------------------------
2809 self.PRW_firstname.add_callback_on_lose_focus(self.on_name_set)
2810 self.PRW_country.add_callback_on_selection(self.on_country_selected)
2811 self.PRW_zip_code.add_callback_on_lose_focus(self.on_zip_set)
2812 #--------------------------------------------------------
2814 """Set the states according to entered country."""
2815 self.PRW_state.set_context(context=u'country', val=data)
2816 return True
2817 #--------------------------------------------------------
2819 """Set the gender according to entered firstname.
2820
2821 Matches are fetched from existing records in backend.
2822 """
2823 firstname = self.PRW_firstname.GetValue().strip()
2824 rows, idx = gmPG2.run_ro_queries(queries = [{
2825 'cmd': u"select gender from dem.name_gender_map where name ilike %s",
2826 'args': [firstname]
2827 }])
2828 if len(rows) == 0:
2829 return True
2830 wx.CallAfter(self.PRW_gender.SetData, rows[0][0])
2831 return True
2832 #--------------------------------------------------------
2834 """Set the street, town, state and country according to entered zip code."""
2835 zip_code = self.PRW_zip_code.GetValue().strip()
2836 self.PRW_street.set_context(context=u'zip', val=zip_code)
2837 self.PRW_town.set_context(context=u'zip', val=zip_code)
2838 self.PRW_state.set_context(context=u'zip', val=zip_code)
2839 self.PRW_country.set_context(context=u'zip', val=zip_code)
2840 return True
2841 #============================================================
2843 """
2844 Wizard to create a new patient.
2845
2846 TODO:
2847 - write pages for different "themes" of patient creation
2848 - make it configurable which pages are loaded
2849 - make available sets of pages that apply to a country
2850 - make loading of some pages depend upon values in earlier pages, eg
2851 when the patient is female and older than 13 include a page about
2852 "female" data (number of kids etc)
2853
2854 FIXME: use: wizard.FindWindowById(wx.ID_FORWARD).Disable()
2855 """
2856 #--------------------------------------------------------
2857 - def __init__(self, parent, title = _('Register new person'), subtitle = _('Basic demographic details') ):
2858 """
2859 Creates a new instance of NewPatientWizard
2860 @param parent - The parent widget
2861 @type parent - A wx.Window instance
2862 """
2863 id_wiz = wx.NewId()
2864 wx.wizard.Wizard.__init__(self, parent, id_wiz, title) #images.getWizTest1Bitmap()
2865 self.SetExtraStyle(wx.WS_EX_VALIDATE_RECURSIVELY)
2866 self.__subtitle = subtitle
2867 self.__do_layout()
2868 #--------------------------------------------------------
2870 """Create new patient.
2871
2872 activate, too, if told to do so (and patient successfully created)
2873 """
2874 while True:
2875
2876 if not wx.wizard.Wizard.RunWizard(self, self.basic_pat_details):
2877 return False
2878
2879 try:
2880 # retrieve DTD and create patient
2881 ident = create_identity_from_dtd(dtd = self.basic_pat_details.form_DTD)
2882 except:
2883 _log.exception('cannot add new patient - missing identity fields')
2884 gmGuiHelpers.gm_show_error (
2885 _('Cannot create new patient.\n'
2886 'Missing parts of the identity.'
2887 ),
2888 _('Adding new patient')
2889 )
2890 continue
2891
2892 update_identity_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD)
2893
2894 try:
2895 link_contacts_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD)
2896 except:
2897 _log.exception('cannot finalize new patient - missing address fields')
2898 gmGuiHelpers.gm_show_error (
2899 _('Cannot add address for the new patient.\n'
2900 'You must either enter all of the address fields or\n'
2901 'none at all. The relevant fields are marked in yellow.\n'
2902 '\n'
2903 'You will need to add the address details in the\n'
2904 'demographics module.'
2905 ),
2906 _('Adding new patient')
2907 )
2908 break
2909
2910 link_occupation_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD)
2911
2912 break
2913
2914 if activate:
2915 from Gnumed.wxpython import gmPatSearchWidgets
2916 gmPatSearchWidgets.set_active_patient(patient = ident)
2917
2918 return ident
2919 #--------------------------------------------------------
2920 # internal helpers
2921 #--------------------------------------------------------
2923 """Arrange widgets.
2924 """
2925 # Create the wizard pages
2926 self.basic_pat_details = cBasicPatDetailsPage(self, self.__subtitle )
2927 self.FitToPage(self.basic_pat_details)
2928 #============================================================
2929 #============================================================
2931 """
2932 This validator is used to ensure that the user has entered all
2933 the required conditional values in the page (eg., to properly
2934 create an address, all the related fields must be filled).
2935 """
2936 #--------------------------------------------------------
2938 """
2939 Validator initialization.
2940 @param dtd The object containing the data model.
2941 @type dtd A cFormDTD instance
2942 """
2943 # initialize parent class
2944 wx.PyValidator.__init__(self)
2945 # validator's storage object
2946 self.form_DTD = dtd
2947 #--------------------------------------------------------
2949 """
2950 Standard cloner.
2951 Note that every validator must implement the Clone() method.
2952 """
2953 return cBasicPatDetailsPageValidator(dtd = self.form_DTD) # FIXME: probably need new instance of DTD ?
2954 #--------------------------------------------------------
2956 """
2957 Validate the contents of the given text control.
2958 """
2959 _pnl_form = self.GetWindow().GetParent()
2960
2961 error = False
2962
2963 # name fields
2964 if _pnl_form.PRW_lastname.GetValue().strip() == '':
2965 error = True
2966 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.'))
2967 _pnl_form.PRW_lastname.SetBackgroundColour('pink')
2968 _pnl_form.PRW_lastname.Refresh()
2969 else:
2970 _pnl_form.PRW_lastname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2971 _pnl_form.PRW_lastname.Refresh()
2972
2973 if _pnl_form.PRW_firstname.GetValue().strip() == '':
2974 error = True
2975 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.'))
2976 _pnl_form.PRW_firstname.SetBackgroundColour('pink')
2977 _pnl_form.PRW_firstname.Refresh()
2978 else:
2979 _pnl_form.PRW_firstname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2980 _pnl_form.PRW_firstname.Refresh()
2981
2982 # gender
2983 if _pnl_form.PRW_gender.GetData() is None:
2984 error = True
2985 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.'))
2986 _pnl_form.PRW_gender.SetBackgroundColour('pink')
2987 _pnl_form.PRW_gender.Refresh()
2988 else:
2989 _pnl_form.PRW_gender.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2990 _pnl_form.PRW_gender.Refresh()
2991
2992 # dob validation
2993 if (
2994 (_pnl_form.PRW_dob.GetValue().strip() == u'')
2995 or (not _pnl_form.PRW_dob.is_valid_timestamp())
2996 or (_pnl_form.PRW_dob.GetData().timestamp.year < 1900)
2997 ):
2998 error = True
2999 msg = _('Cannot parse <%s> into proper timestamp.') % _pnl_form.PRW_dob.GetValue()
3000 gmDispatcher.send(signal = 'statustext', msg = msg)
3001 _pnl_form.PRW_dob.SetBackgroundColour('pink')
3002 _pnl_form.PRW_dob.Refresh()
3003 else:
3004 _pnl_form.PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
3005 _pnl_form.PRW_dob.Refresh()
3006
3007 # address
3008 is_any_field_filled = False
3009 address_fields = (
3010 _pnl_form.TTC_address_number,
3011 _pnl_form.PRW_zip_code,
3012 _pnl_form.PRW_street,
3013 _pnl_form.PRW_town
3014 )
3015 for field in address_fields:
3016 if field.GetValue().strip() == u'':
3017 if is_any_field_filled:
3018 error = True
3019 msg = _('To properly create an address, all the related fields must be filled in.')
3020 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
3021 field.SetBackgroundColour('pink')
3022 field.SetFocus()
3023 field.Refresh()
3024 else:
3025 is_any_field_filled = True
3026 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
3027 field.Refresh()
3028
3029 address_fields = (
3030 _pnl_form.PRW_state,
3031 _pnl_form.PRW_country
3032 )
3033 for field in address_fields:
3034 if field.GetData() is None:
3035 if is_any_field_filled:
3036 error = True
3037 msg = _('To properly create an address, all the related fields must be filled in.')
3038 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
3039 field.SetBackgroundColour('pink')
3040 field.SetFocus()
3041 field.Refresh()
3042 else:
3043 is_any_field_filled = True
3044 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
3045 field.Refresh()
3046
3047 return (not error)
3048 #--------------------------------------------------------
3050 """
3051 Transfer data from validator to window.
3052 The default implementation returns False, indicating that an error
3053 occurred. We simply return True, as we don't do any data transfer.
3054 """
3055 _pnl_form = self.GetWindow().GetParent()
3056 # fill in controls with values from self.form_DTD
3057 _pnl_form.PRW_gender.SetData(self.form_DTD['gender'])
3058 _pnl_form.PRW_dob.SetText(self.form_DTD['dob'])
3059 _pnl_form.PRW_lastname.SetText(self.form_DTD['lastnames'])
3060 _pnl_form.PRW_firstname.SetText(self.form_DTD['firstnames'])
3061 _pnl_form.PRW_title.SetText(self.form_DTD['title'])
3062 _pnl_form.PRW_nick.SetText(self.form_DTD['nick'])
3063 _pnl_form.PRW_occupation.SetText(self.form_DTD['occupation'])
3064 _pnl_form.TTC_address_number.SetValue(self.form_DTD['address_number'])
3065 _pnl_form.PRW_street.SetText(self.form_DTD['street'])
3066 _pnl_form.PRW_zip_code.SetText(self.form_DTD['zip_code'])
3067 _pnl_form.PRW_town.SetText(self.form_DTD['town'])
3068 _pnl_form.PRW_state.SetData(self.form_DTD['state'])
3069 _pnl_form.PRW_country.SetData(self.form_DTD['country'])
3070 _pnl_form.TTC_phone.SetValue(self.form_DTD['phone'])
3071 _pnl_form.TCTRL_comment.SetValue(self.form_DTD['comment'])
3072 return True # Prevent wxDialog from complaining
3073 #--------------------------------------------------------
3075 """
3076 Transfer data from window to validator.
3077 The default implementation returns False, indicating that an error
3078 occurred. We simply return True, as we don't do any data transfer.
3079 """
3080 # FIXME: should be called automatically
3081 if not self.GetWindow().GetParent().Validate():
3082 return False
3083 try:
3084 _pnl_form = self.GetWindow().GetParent()
3085 # fill in self.form_DTD with values from controls
3086 self.form_DTD['gender'] = _pnl_form.PRW_gender.GetData()
3087 self.form_DTD['dob'] = _pnl_form.PRW_dob.GetData()
3088
3089 self.form_DTD['lastnames'] = _pnl_form.PRW_lastname.GetValue()
3090 self.form_DTD['firstnames'] = _pnl_form.PRW_firstname.GetValue()
3091 self.form_DTD['title'] = _pnl_form.PRW_title.GetValue()
3092 self.form_DTD['nick'] = _pnl_form.PRW_nick.GetValue()
3093
3094 self.form_DTD['occupation'] = _pnl_form.PRW_occupation.GetValue()
3095
3096 self.form_DTD['address_number'] = _pnl_form.TTC_address_number.GetValue()
3097 self.form_DTD['street'] = _pnl_form.PRW_street.GetValue()
3098 self.form_DTD['zip_code'] = _pnl_form.PRW_zip_code.GetValue()
3099 self.form_DTD['town'] = _pnl_form.PRW_town.GetValue()
3100 self.form_DTD['state'] = _pnl_form.PRW_state.GetData()
3101 self.form_DTD['country'] = _pnl_form.PRW_country.GetData()
3102
3103 self.form_DTD['phone'] = _pnl_form.TTC_phone.GetValue()
3104
3105 self.form_DTD['comment'] = _pnl_form.TCTRL_comment.GetValue()
3106 except:
3107 return False
3108 return True
3109 #============================================================
3110 #class cFormDTD:
3111 # """
3112 # Simple Data Transfer Dictionary class to make easy the trasfer of
3113 # data between the form (view) and the business logic.
3114 #
3115 # Maybe later consider turning this into a standard dict by
3116 # {}.fromkeys([key, key, ...], default) when it becomes clear that
3117 # we really don't need the added potential of a full-fledged class.
3118 # """
3119 # def __init__(self, fields):
3120 # """
3121 # Initialize the DTD with the supplied field names.
3122 # @param fields The names of the fields.
3123 # @type fields A TupleType instance.
3124 # """
3125 # self.data = {}
3126 # for a_field in fields:
3127 # self.data[a_field] = ''
3128 #
3129 # def __getitem__(self, attribute):
3130 # """
3131 # Retrieve the value of the given attribute (key)
3132 # @param attribute The attribute (key) to retrieve its value for.
3133 # @type attribute a StringType instance.
3134 # """
3135 # if not self.data[attribute]:
3136 # return ''
3137 # return self.data[attribute]
3138 #
3139 # def __setitem__(self, attribute, value):
3140 # """
3141 # Set the value of a given attribute (key).
3142 # @param attribute The attribute (key) to set its value for.
3143 # @type attribute a StringType instance.
3144 # @param avaluee The value to set.
3145 # @rtpe attribute a StringType instance.
3146 # """
3147 # self.data[attribute] = value
3148 #
3149 # def __str__(self):
3150 # """
3151 # Print string representation of the DTD object.
3152 # """
3153 # return str(self.data)
3154 #============================================================
3155 # patient demographics editing classes
3156 #============================================================
3158 """Notebook displaying demographics editing pages:
3159
3160 - Identity
3161 - Contacts (addresses, phone numbers, etc)
3162 - Social Network (significant others, GP, etc)
3163
3164 Does NOT act on/listen to the current patient.
3165 """
3166 #--------------------------------------------------------
3168
3169 wx.Notebook.__init__ (
3170 self,
3171 parent = parent,
3172 id = id,
3173 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
3174 name = self.__class__.__name__
3175 )
3176
3177 self.__identity = None
3178 self.__do_layout()
3179 self.SetSelection(0)
3180 #--------------------------------------------------------
3181 # public API
3182 #--------------------------------------------------------
3184 """Populate fields in pages with data from model."""
3185 for page_idx in range(self.GetPageCount()):
3186 page = self.GetPage(page_idx)
3187 page.identity = self.__identity
3188
3189 return True
3190 #--------------------------------------------------------
3191 # internal API
3192 #--------------------------------------------------------
3194 """Build patient edition notebook pages."""
3195
3196 # contacts page
3197 new_page = cPersonContactsManagerPnl(self, -1)
3198 new_page.identity = self.__identity
3199 self.AddPage (
3200 page = new_page,
3201 text = _('Contacts'),
3202 select = True
3203 )
3204
3205 # identity page
3206 new_page = cPersonIdentityManagerPnl(self, -1)
3207 new_page.identity = self.__identity
3208 self.AddPage (
3209 page = new_page,
3210 text = _('Identity'),
3211 select = False
3212 )
3213
3214 # social network page
3215 new_page = cPersonSocialNetworkManagerPnl(self, -1)
3216 new_page.identity = self.__identity
3217 self.AddPage (
3218 page = new_page,
3219 text = _('Social Network'),
3220 select = False
3221 )
3222 #--------------------------------------------------------
3223 # properties
3224 #--------------------------------------------------------
3227
3229 self.__identity = identity
3230
3231 identity = property(_get_identity, _set_identity)
3232 #============================================================
3233 #============================================================
3234 # FIXME: support multiple occupations
3235 # FIXME: redo with wxGlade
3236
3238 """Page containing patient occupations edition fields.
3239 """
3241 """
3242 Creates a new instance of BasicPatDetailsPage
3243 @param parent - The parent widget
3244 @type parent - A wx.Window instance
3245 @param id - The widget id
3246 @type id - An integer
3247 """
3248 wx.Panel.__init__(self, parent, id)
3249 self.__ident = ident
3250 self.__do_layout()
3251 #--------------------------------------------------------
3253 PNL_form = wx.Panel(self, -1)
3254 # occupation
3255 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
3256 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1)
3257 self.PRW_occupation.SetToolTipString(_("primary occupation of the patient"))
3258 # known since
3259 STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated'))
3260 self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY)
3261
3262 # layout input widgets
3263 SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4)
3264 SZR_input.AddGrowableCol(1)
3265 SZR_input.Add(STT_occupation, 0, wx.SHAPED)
3266 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
3267 SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED)
3268 SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND)
3269 PNL_form.SetSizerAndFit(SZR_input)
3270
3271 # layout page
3272 SZR_main = wx.BoxSizer(wx.VERTICAL)
3273 SZR_main.Add(PNL_form, 1, wx.EXPAND)
3274 self.SetSizer(SZR_main)
3275 #--------------------------------------------------------
3278 #--------------------------------------------------------
3280 if identity is not None:
3281 self.__ident = identity
3282 jobs = self.__ident.get_occupations()
3283 if len(jobs) > 0:
3284 self.PRW_occupation.SetText(jobs[0]['l10n_occupation'])
3285 self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y'))
3286 return True
3287 #--------------------------------------------------------
3289 if self.PRW_occupation.IsModified():
3290 new_job = self.PRW_occupation.GetValue().strip()
3291 jobs = self.__ident.get_occupations()
3292 for job in jobs:
3293 if job['l10n_occupation'] == new_job:
3294 continue
3295 self.__ident.unlink_occupation(occupation = job['l10n_occupation'])
3296 self.__ident.link_occupation(occupation = new_job)
3297 return True
3298 #============================================================
3300 """Patient demographics plugin for main notebook.
3301
3302 Hosts another notebook with pages for Identity, Contacts, etc.
3303
3304 Acts on/listens to the currently active patient.
3305 """
3306 #--------------------------------------------------------
3308 wx.Panel.__init__ (self, parent = parent, id = id, style = wx.NO_BORDER)
3309 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
3310 self.__do_layout()
3311 self.__register_interests()
3312 #--------------------------------------------------------
3313 # public API
3314 #--------------------------------------------------------
3315 #--------------------------------------------------------
3316 # internal helpers
3317 #--------------------------------------------------------
3319 """Arrange widgets."""
3320 self.__patient_notebook = cPersonDemographicsEditorNb(self, -1)
3321
3322 szr_main = wx.BoxSizer(wx.VERTICAL)
3323 szr_main.Add(self.__patient_notebook, 1, wx.EXPAND)
3324 self.SetSizerAndFit(szr_main)
3325 #--------------------------------------------------------
3326 # event handling
3327 #--------------------------------------------------------
3329 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
3330 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
3331 #--------------------------------------------------------
3334 #--------------------------------------------------------
3337 #--------------------------------------------------------
3338 # reget mixin API
3339 #--------------------------------------------------------
3349 #============================================================
3350 #def create_identity_from_dtd(dtd=None):
3351 # """
3352 # Register a new patient, given the data supplied in the
3353 # Data Transfer Dictionary object.
3354 #
3355 # @param basic_details_DTD Data Transfer Dictionary encapsulating all the
3356 # supplied data.
3357 # @type basic_details_DTD A cFormDTD instance.
3358 # """
3359 # new_identity = gmPerson.create_identity (
3360 # gender = dtd['gender'],
3361 # dob = dtd['dob'].get_pydt(),
3362 # lastnames = dtd['lastnames'],
3363 # firstnames = dtd['firstnames']
3364 # )
3365 # if new_identity is None:
3366 # _log.error('cannot create identity from %s' % str(dtd))
3367 # return None
3368 # _log.debug('identity created: %s' % new_identity)
3369 #
3370 # if dtd['comment'] is not None:
3371 # if dtd['comment'].strip() != u'':
3372 # name = new_identity.get_active_name()
3373 # name['comment'] = dtd['comment']
3374 # name.save_payload()
3375 #
3376 # return new_identity
3377 #============================================================
3378 #def update_identity_from_dtd(identity, dtd=None):
3379 # """
3380 # Update patient details with data supplied by
3381 # Data Transfer Dictionary object.
3382 #
3383 # @param basic_details_DTD Data Transfer Dictionary encapsulating all the
3384 # supplied data.
3385 # @type basic_details_DTD A cFormDTD instance.
3386 # """
3387 # # identity
3388 # if identity['gender'] != dtd['gender']:
3389 # identity['gender'] = dtd['gender']
3390 # if identity['dob'] != dtd['dob'].get_pydt():
3391 # identity['dob'] = dtd['dob'].get_pydt()
3392 # if len(dtd['title']) > 0 and identity['title'] != dtd['title']:
3393 # identity['title'] = dtd['title']
3394 # # FIXME: error checking
3395 # # FIXME: we need a trigger to update the values of the
3396 # # view, identity['keys'], eg. lastnames and firstnames
3397 # # are not refreshed.
3398 # identity.save_payload()
3399 #
3400 # # names
3401 # # FIXME: proper handling of "active"
3402 # if identity['firstnames'] != dtd['firstnames'] or identity['lastnames'] != dtd['lastnames']:
3403 # new_name = identity.add_name(firstnames = dtd['firstnames'], lastnames = dtd['lastnames'], active = True)
3404 # # nickname
3405 # if len(dtd['nick']) > 0 and identity['preferred'] != dtd['nick']:
3406 # identity.set_nickname(nickname = dtd['nick'])
3407 #
3408 # return True
3409 #============================================================
3410 #def link_contacts_from_dtd(identity, dtd=None):
3411 # """
3412 # Update patient details with data supplied by
3413 # Data Transfer Dictionary object.
3414 #
3415 # @param basic_details_DTD Data Transfer Dictionary encapsulating all the
3416 # supplied data.
3417 # @type basic_details_DTD A cFormDTD instance.
3418 # """
3419 # lng = len (
3420 # dtd['address_number'].strip() +
3421 # dtd['street'].strip() +
3422 # dtd['zip_code'].strip() +
3423 # dtd['town'].strip() +
3424 # dtd['state'].strip() +
3425 # dtd['country'].strip()
3426 # )
3427 # # FIXME: improve error checking
3428 # if lng > 5:
3429 # # FIXME: support address type
3430 # success = identity.link_address (
3431 # number = dtd['address_number'].strip(),
3432 # street = dtd['street'].strip(),
3433 # postcode = dtd['zip_code'].strip(),
3434 # urb = dtd['town'].strip(),
3435 # state = dtd['state'].strip(),
3436 # country = dtd['country'].strip()
3437 # )
3438 # if not success:
3439 # gmDispatcher.send(signal='statustext', msg = _('Cannot add patient address.'))
3440 # else:
3441 # gmDispatcher.send(signal='statustext', msg = _('Cannot add patient address. Missing fields.'))
3442 #
3443 # if len(dtd['phone']) > 0:
3444 # identity.link_comm_channel (
3445 # comm_medium = 'homephone',
3446 # url = dtd['phone'],
3447 # is_confidential = False
3448 # )
3449 #
3450 # # FIXME: error checking
3451 ## identity.save_payload()
3452 # return True
3453 #============================================================
3454 #def link_occupation_from_dtd(identity, dtd=None):
3455 # """
3456 # Update patient details with data supplied by
3457 # Data Transfer Dictionary object.
3458 #
3459 # @param basic_details_DTD Data Transfer Dictionary encapsulating all the
3460 # supplied data.
3461 # @type basic_details_DTD A cFormDTD instance.
3462 # """
3463 # identity.link_occupation(occupation = dtd['occupation'])
3464 #
3465 # return True
3466 #============================================================
3468 """
3469 Utility class to test the new patient wizard.
3470 """
3471 #--------------------------------------------------------
3473 """
3474 Create a new instance of TestPanel.
3475 @param parent The parent widget
3476 @type parent A wx.Window instance
3477 """
3478 wx.Panel.__init__(self, parent, id)
3479 wizard = cNewPatientWizard(self)
3480 print wizard.RunWizard()
3481 #============================================================
3482 if __name__ == "__main__":
3483
3484 #--------------------------------------------------------
3486 app = wx.PyWidgetTester(size = (200, 50))
3487 pw = cZipcodePhraseWheel(app.frame, -1)
3488 app.frame.Show(True)
3489 app.MainLoop()
3490 #--------------------------------------------------------
3492 app = wx.PyWidgetTester(size = (200, 50))
3493 pw = cStateSelectionPhraseWheel(app.frame, -1)
3494 # pw.set_context(context = u'zip', val = u'04318')
3495 # pw.set_context(context = u'country', val = u'Deutschland')
3496 app.frame.Show(True)
3497 app.MainLoop()
3498 #--------------------------------------------------------
3500 app = wx.PyWidgetTester(size = (200, 50))
3501 pw = cUrbPhraseWheel(app.frame, -1)
3502 app.frame.Show(True)
3503 pw.set_context(context = u'zip', val = u'04317')
3504 app.MainLoop()
3505 #--------------------------------------------------------
3507 app = wx.PyWidgetTester(size = (200, 50))
3508 pw = cSuburbPhraseWheel(app.frame, -1)
3509 app.frame.Show(True)
3510 app.MainLoop()
3511 #--------------------------------------------------------
3513 app = wx.PyWidgetTester(size = (200, 50))
3514 pw = cAddressTypePhraseWheel(app.frame, -1)
3515 app.frame.Show(True)
3516 app.MainLoop()
3517 #--------------------------------------------------------
3519 app = wx.PyWidgetTester(size = (200, 50))
3520 pw = cAddressPhraseWheel(app.frame, -1)
3521 app.frame.Show(True)
3522 app.MainLoop()
3523 #--------------------------------------------------------
3525 app = wx.PyWidgetTester(size = (200, 50))
3526 pw = cStreetPhraseWheel(app.frame, -1)
3527 # pw.set_context(context = u'zip', val = u'04318')
3528 app.frame.Show(True)
3529 app.MainLoop()
3530 #--------------------------------------------------------
3532 app = wx.PyWidgetTester(size = (600, 400))
3533 app.SetWidget(cKOrganizerSchedulePnl)
3534 app.MainLoop()
3535 #--------------------------------------------------------
3537 app = wx.PyWidgetTester(size = (600, 400))
3538 widget = cPersonNamesManagerPnl(app.frame, -1)
3539 widget.identity = activate_patient()
3540 app.frame.Show(True)
3541 app.MainLoop()
3542 #--------------------------------------------------------
3544 app = wx.PyWidgetTester(size = (600, 400))
3545 widget = cPersonIDsManagerPnl(app.frame, -1)
3546 widget.identity = activate_patient()
3547 app.frame.Show(True)
3548 app.MainLoop()
3549 #--------------------------------------------------------
3551 app = wx.PyWidgetTester(size = (600, 400))
3552 widget = cPersonIdentityManagerPnl(app.frame, -1)
3553 widget.identity = activate_patient()
3554 app.frame.Show(True)
3555 app.MainLoop()
3556 #--------------------------------------------------------
3558 app = wx.PyWidgetTester(size = (600, 400))
3559 app.SetWidget(cNameGenderDOBEditAreaPnl, name = activate_patient().get_active_name())
3560 app.MainLoop()
3561 #--------------------------------------------------------
3563 app = wx.PyWidgetTester(size = (600, 400))
3564 app.SetWidget(cAddressEditAreaPnl, address = gmDemographicRecord.cAddress(aPK_obj = 1))
3565 app.MainLoop()
3566 #--------------------------------------------------------
3568 app = wx.PyWidgetTester(size = (600, 400))
3569 widget = cPersonAddressesManagerPnl(app.frame, -1)
3570 widget.identity = activate_patient()
3571 app.frame.Show(True)
3572 app.MainLoop()
3573 #--------------------------------------------------------
3575 app = wx.PyWidgetTester(size = (600, 400))
3576 widget = cPersonCommsManagerPnl(app.frame, -1)
3577 widget.identity = activate_patient()
3578 app.frame.Show(True)
3579 app.MainLoop()
3580 #--------------------------------------------------------
3582 app = wx.PyWidgetTester(size = (600, 400))
3583 widget = cPersonContactsManagerPnl(app.frame, -1)
3584 widget.identity = activate_patient()
3585 app.frame.Show(True)
3586 app.MainLoop()
3587 #--------------------------------------------------------
3589 app = wx.PyWidgetTester(size = (600, 400))
3590 widget = cPersonDemographicsEditorNb(app.frame, -1)
3591 widget.identity = activate_patient()
3592 widget.refresh()
3593 app.frame.Show(True)
3594 app.MainLoop()
3595 #--------------------------------------------------------
3597 patient = gmPerson.ask_for_patient()
3598 if patient is None:
3599 print "No patient. Exiting gracefully..."
3600 sys.exit(0)
3601 from Gnumed.wxpython import gmPatSearchWidgets
3602 gmPatSearchWidgets.set_active_patient(patient=patient)
3603 return patient
3604 #--------------------------------------------------------
3605 if len(sys.argv) > 1 and sys.argv[1] == 'test':
3606
3607 gmI18N.activate_locale()
3608 gmI18N.install_domain(domain='gnumed')
3609 gmPG2.get_connection()
3610
3611 # a = cFormDTD(fields = cBasicPatDetailsPage.form_fields)
3612
3613 # app = wx.PyWidgetTester(size = (400, 300))
3614 # app.SetWidget(cNotebookedPatEditionPanel, -1)
3615 # app.SetWidget(TestWizardPanel, -1)
3616 # app.frame.Show(True)
3617 # app.MainLoop()
3618
3619 # phrasewheels
3620 # test_zipcode_prw()
3621 # test_state_prw()
3622 # test_street_prw()
3623 # test_organizer_pnl()
3624 #test_address_type_prw()
3625 #test_suburb_prw()
3626 test_urb_prw()
3627 #test_address_prw()
3628
3629 # contacts related widgets
3630 #test_address_ea_pnl()
3631 #test_person_adrs_pnl()
3632 #test_person_comms_pnl()
3633 #test_pat_contacts_pnl()
3634
3635 # identity related widgets
3636 #test_person_names_pnl()
3637 #test_person_ids_pnl()
3638 #test_pat_ids_pnl()
3639 #test_name_ea_pnl()
3640
3641 #test_cPersonDemographicsEditorNb()
3642
3643 #============================================================
3644
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 28 04:12:44 2010 | http://epydoc.sourceforge.net |