| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed patient creation widgets.
2
3 copyright: authors
4 """
5 #============================================================
6 __author__ = "K.Hilbert"
7 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
8
9 import logging
10 import sys
11 import datetime as pydt
12
13
14 import wx
15
16
17 if __name__ == '__main__':
18 sys.path.insert(0, '../../')
19 from Gnumed.pycommon import gmCfg
20 from Gnumed.pycommon import gmPG2
21 from Gnumed.pycommon import gmTools
22 from Gnumed.pycommon import gmDateTime
23 from Gnumed.pycommon import gmDispatcher
24
25 from Gnumed.business import gmPraxis
26 from Gnumed.business import gmPerson
27 from Gnumed.business import gmStaff
28 from Gnumed.business import gmDemographicRecord
29
30 from Gnumed.wxpython import gmEditArea
31 from Gnumed.wxpython import gmGuiHelpers
32 from Gnumed.wxpython import gmEncounterWidgets
33 from Gnumed.wxpython.gmDemographicsWidgets import _validate_dob_field, _validate_tob_field, _empty_dob_allowed
34
35
36 _log = logging.getLogger('gm.patient')
37
38 #============================================================
40
41 if parent is None:
42 parent = wx.GetApp().GetTopWindow()
43
44 if activate: # meaning we will switch away from the current patient if any
45 msg = _(
46 'Before creating a new person review the encounter details\n'
47 'of the patient you just worked on:\n'
48 )
49 gmEncounterWidgets.sanity_check_encounter_of_active_patient(parent = parent, msg = msg)
50
51 msg = _('Edit the current encounter of the patient you are ABOUT TO LEAVE:')
52
53 dbcfg = gmCfg.cCfgSQL()
54
55 def_region = dbcfg.get2 (
56 option = 'person.create.default_region',
57 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
58 bias = 'user'
59 )
60 def_country = None
61
62 if def_region is None:
63 def_country = dbcfg.get2 (
64 option = 'person.create.default_country',
65 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
66 bias = 'user'
67 )
68 else:
69 countries = gmDemographicRecord.get_country_for_region(region = def_region)
70 if len(countries) == 1:
71 def_country = countries[0]['code_country']
72
73 ea = cNewPatientEAPnl(parent, -1, country = def_country, region = def_region)
74 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = True)
75 dlg.SetTitle(_('Adding new person'))
76 ea._PRW_lastname.SetFocus()
77 result = dlg.ShowModal()
78 pat = ea.data
79 dlg.Destroy()
80
81 if result != wx.ID_OK:
82 return False
83
84 _log.debug('created new person [%s]', pat.ID)
85
86 if activate:
87 from Gnumed.wxpython import gmPatSearchWidgets
88 gmPatSearchWidgets.set_active_patient(patient = pat)
89
90 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
91
92 return True
93
94 #============================================================
95 from Gnumed.wxGladeWidgets import wxgNewPatientEAPnl
96
97 -class cNewPatientEAPnl(wxgNewPatientEAPnl.wxgNewPatientEAPnl, gmEditArea.cGenericEditAreaMixin):
98
100
101 try:
102 self.default_region = kwargs['region']
103 del kwargs['region']
104 except KeyError:
105 self.default_region = None
106
107 try:
108 self.default_country = kwargs['country']
109 del kwargs['country']
110 except KeyError:
111 self.default_country = None
112
113 wxgNewPatientEAPnl.wxgNewPatientEAPnl.__init__(self, *args, **kwargs)
114 gmEditArea.cGenericEditAreaMixin.__init__(self)
115
116 self.mode = 'new'
117 self.data = None
118 self._address = None
119
120 self.__init_ui()
121 self.__register_interests()
122 #----------------------------------------------------------------
123 # internal helpers
124 #----------------------------------------------------------------
126 self._PRW_lastname.final_regex = '.+'
127 self._PRW_firstnames.final_regex = '.+'
128 self._PRW_address_searcher.selection_only = False
129
130 # only if we would support None on selection_only's:
131 # self._PRW_external_id_type.selection_only = True
132
133 if self.default_country is not None:
134 match = self._PRW_country._data2match(data = self.default_country)
135 if match is not None:
136 self._PRW_country.SetText(value = match['field_label'], data = match['data'])
137
138 if self.default_region is not None:
139 self._PRW_region.SetText(value = self.default_region)
140
141 self._PRW_type.SetText(value = 'home')
142 # FIXME: only use this if member of gm-doctors,
143 # FIXME: other than that check fallback_primary_provider
144 self._PRW_primary_provider.SetData(data = gmStaff.gmCurrentProvider()['pk_staff'])
145
146 self._PRW_lastname.SetFocus()
147 #----------------------------------------------------------------
149 id_type = self._PRW_external_id_type.GetData()
150 if id_type is None:
151 self._LBL_id_exists.SetLabel('')
152 return
153 val = self._TCTRL_external_id_value.GetValue().strip()
154 if val == '':
155 self._LBL_id_exists.SetLabel('')
156 return
157 if gmPerson.external_id_exists(pk_issuer = id_type, value = val) > 0:
158 self._LBL_id_exists.SetLabel(_('ID exists !'))
159 else:
160 self._LBL_id_exists.SetLabel('')
161 #----------------------------------------------------------------
163 lname = self._PRW_lastname.GetValue().strip()
164 if lname == '':
165 self._LBL_person_exists.SetLabel('')
166 return
167
168 dob = self._PRW_dob.GetData()
169 if dob is None:
170 self._LBL_person_exists.SetLabel('')
171 return
172
173 fname = gmTools.none_if(self._PRW_firstnames.GetValue().strip()[:1], '')
174
175 no_of_dupes = gmPerson.person_exists(lastnames = lname, firstnames = fname, dob = dob)
176 if no_of_dupes == 0:
177 lbl = ''
178 elif no_of_dupes == 1:
179 lbl = _('One "%s, %s (%s)" already exists !') % (
180 lname,
181 gmTools.coalesce(fname, '?', '%s %%s. %s' % (gmTools.u_ellipsis, gmTools.u_ellipsis)),
182 gmDateTime.pydt_strftime(dob, '%Y %b %d', 'utf8')
183 )
184 else:
185 lbl = _('%s "%s, %s (%s)" already exist !') % (
186 no_of_dupes,
187 lname,
188 gmTools.coalesce(fname, '?', '%s %%s. %s' % (gmTools.u_ellipsis, gmTools.u_ellipsis)),
189 gmDateTime.pydt_strftime(dob, '%Y %b %d', 'utf8')
190 )
191
192 self._LBL_person_exists.SetLabel(lbl)
193 #----------------------------------------------------------------
195
196 adr = self._PRW_address_searcher.address
197 if adr is None:
198 return True
199
200 if ctrl.GetValue().strip() != adr[field]:
201 wx.CallAfter(self._PRW_address_searcher.SetText, value = '', data = None)
202 return True
203
204 return False
205 #----------------------------------------------------------------
207 adr = self._PRW_address_searcher.address
208 if adr is None:
209 return True
210
211 self._PRW_zip.SetText(value = adr['postcode'], data = adr['postcode'])
212
213 self._PRW_street.SetText(value = adr['street'], data = adr['street'])
214 self._PRW_street.set_context(context = 'zip', val = adr['postcode'])
215
216 self._PRW_urb.SetText(value = adr['urb'], data = adr['urb'])
217 self._PRW_urb.set_context(context = 'zip', val = adr['postcode'])
218
219 self._PRW_region.SetText(value = adr['l10n_region'], data = adr['code_region'])
220 self._PRW_region.set_context(context = 'zip', val = adr['postcode'])
221
222 self._PRW_country.SetText(value = adr['l10n_country'], data = adr['code_country'])
223 self._PRW_country.set_context(context = 'zip', val = adr['postcode'])
224 #----------------------------------------------------------------
226 error = False
227
228 # name fields
229 if self._PRW_lastname.GetValue().strip() == '':
230 error = True
231 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.'))
232 self._PRW_lastname.display_as_valid(False)
233 else:
234 self._PRW_lastname.display_as_valid(True)
235
236 if self._PRW_firstnames.GetValue().strip() == '':
237 error = True
238 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.'))
239 self._PRW_firstnames.display_as_valid(False)
240 else:
241 self._PRW_firstnames.display_as_valid(True)
242
243 # gender
244 if self._PRW_gender.GetData() is None:
245 error = True
246 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.'))
247 self._PRW_gender.display_as_valid(False)
248 else:
249 self._PRW_gender.display_as_valid(True)
250
251 # dob validation
252 if not _validate_dob_field(self._PRW_dob):
253 error = True
254
255 # TOB validation
256 if _validate_tob_field(self._TCTRL_tob):
257 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True)
258 else:
259 error = True
260 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False)
261
262 return (not error)
263 #----------------------------------------------------------------
265
266 # existing address ? if so set other fields
267 if self._PRW_address_searcher.GetData() is not None:
268 wx.CallAfter(self.__set_fields_from_address_searcher)
269 return True
270
271 # must either all contain something or none of them
272 fields_to_fill = (
273 self._TCTRL_number,
274 self._PRW_zip,
275 self._PRW_street,
276 self._PRW_urb,
277 self._PRW_type
278 )
279 no_of_filled_fields = 0
280
281 for field in fields_to_fill:
282 if field.GetValue().strip() != '':
283 no_of_filled_fields += 1
284 field.display_as_valid(True)
285
286 # empty address ?
287 if no_of_filled_fields == 0:
288 if empty_address_is_valid:
289 return True
290 else:
291 return None
292
293 # incompletely filled address ?
294 if no_of_filled_fields != len(fields_to_fill):
295 for field in fields_to_fill:
296 if field.GetValue().strip() == '':
297 field.display_as_valid(False)
298 field.SetFocus()
299 msg = _('To properly create an address, all the related fields must be filled in.')
300 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
301 return False
302
303 # fields which must contain a selected item
304 # FIXME: they must also contain an *acceptable combination* which
305 # FIXME: can only be tested against the database itself ...
306 strict_fields = (
307 self._PRW_type,
308 self._PRW_region,
309 self._PRW_country
310 )
311 error = False
312 for field in strict_fields:
313 if field.GetData() is None:
314 error = True
315 field.display_as_valid(False)
316 field.SetFocus()
317 else:
318 field.display_as_valid(True)
319
320 if error:
321 msg = _('This field must contain an item selected from the dropdown list.')
322 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
323 return False
324
325 return True
326 #----------------------------------------------------------------
328
329 # identity
330 self._PRW_lastname.add_callback_on_lose_focus(self._on_leaving_lastname)
331 self._PRW_firstnames.add_callback_on_lose_focus(self._on_leaving_firstname)
332 self._PRW_dob.add_callback_on_lose_focus(self._on_leaving_dob)
333
334 # address
335 self._PRW_address_searcher.add_callback_on_lose_focus(self._on_leaving_adress_searcher)
336
337 # invalidate address searcher when any field edited
338 self._PRW_street.add_callback_on_lose_focus(self._invalidate_address_searcher)
339 self._TCTRL_number.Bind(wx.EVT_KILL_FOCUS, self._on_leaving_number)
340 self._TCTRL_unit.Bind(wx.EVT_KILL_FOCUS, self._on_leaving_unit)
341 self._PRW_urb.add_callback_on_lose_focus(self._invalidate_address_searcher)
342 self._PRW_region.add_callback_on_lose_focus(self._invalidate_address_searcher)
343
344 self._PRW_zip.add_callback_on_lose_focus(self._on_leaving_zip)
345 self._PRW_country.add_callback_on_lose_focus(self._on_leaving_country)
346
347 self._PRW_external_id_type.add_callback_on_lose_focus(callback = self._on_leaving_ext_id_type)
348 self._TCTRL_external_id_value.add_callback_on_lose_focus(callback = self._on_leaving_ext_id_val)
349
350 #----------------------------------------------------------------
351 # event handlers
352 #----------------------------------------------------------------
355 #----------------------------------------------------------------
358 #----------------------------------------------------------------
361 #----------------------------------------------------------------
363 """Set the gender according to entered firstname.
364
365 Matches are fetched from existing records in backend.
366 """
367 wx.CallAfter(self._refresh_dupe_warning)
368
369 # only set if not already set so as to not
370 # overwrite a change by the user
371 if self._PRW_gender.GetData() is not None:
372 return True
373
374 firstname = self._PRW_firstnames.GetValue().strip()
375 if firstname == '':
376 return True
377
378 gender = gmPerson.map_firstnames2gender(firstnames = firstname)
379 if gender is None:
380 return True
381
382 wx.CallAfter(self._PRW_gender.SetData, gender)
383
384 return True
385 #----------------------------------------------------------------
388 #----------------------------------------------------------------
390 self.__perhaps_invalidate_address_searcher(self._PRW_zip, 'postcode')
391
392 zip_code = gmTools.none_if(self._PRW_zip.GetValue().strip(), '')
393 self._PRW_street.set_context(context = 'zip', val = zip_code)
394 self._PRW_urb.set_context(context = 'zip', val = zip_code)
395 self._PRW_region.set_context(context = 'zip', val = zip_code)
396 self._PRW_country.set_context(context = 'zip', val = zip_code)
397
398 return True
399 #----------------------------------------------------------------
401 self.__perhaps_invalidate_address_searcher(self._PRW_country, 'l10n_country')
402
403 country = gmTools.none_if(self._PRW_country.GetValue().strip(), '')
404 self._PRW_region.set_context(context = 'country', val = country)
405
406 return True
407 #----------------------------------------------------------------
409 if self._TCTRL_number.GetValue().strip() == '':
410 adr = self._PRW_address_searcher.address
411 if adr is None:
412 return True
413 self._TCTRL_number.SetValue(adr['number'])
414 return True
415
416 self.__perhaps_invalidate_address_searcher(self._TCTRL_number, 'number')
417 return True
418 #----------------------------------------------------------------
420 if self._TCTRL_unit.GetValue().strip() == '':
421 adr = self._PRW_address_searcher.address
422 if adr is None:
423 return True
424 self._TCTRL_unit.SetValue(gmTools.coalesce(adr['subunit'], ''))
425 return True
426
427 self.__perhaps_invalidate_address_searcher(self._TCTRL_unit, 'subunit')
428 return True
429 #----------------------------------------------------------------
431 mapping = [
432 (self._PRW_street, 'street'),
433 (self._PRW_urb, 'urb'),
434 (self._PRW_region, 'l10n_region')
435 ]
436 # loop through fields and invalidate address searcher if different
437 for ctrl, field in mapping:
438 if self.__perhaps_invalidate_address_searcher(ctrl, field):
439 return True
440
441 return True
442 #----------------------------------------------------------------
444 if self._PRW_address_searcher.address is None:
445 return True
446
447 wx.CallAfter(self.__set_fields_from_address_searcher)
448 return True
449 #----------------------------------------------------------------
450 # generic Edit Area mixin API
451 #----------------------------------------------------------------
453 if self._PRW_primary_provider.GetValue().strip() == '':
454 self._PRW_primary_provider.display_as_valid(True)
455 else:
456 if self._PRW_primary_provider.GetData() is None:
457 self._PRW_primary_provider.display_as_valid(False)
458 else:
459 self._PRW_primary_provider.display_as_valid(True)
460 return (self.__identity_valid_for_save() and self.__address_valid_for_save(empty_address_is_valid = True))
461 #----------------------------------------------------------------
463
464 if self._PRW_dob.GetValue().strip() == '':
465 if not _empty_dob_allowed():
466 self._PRW_dob.display_as_valid(False)
467 self._PRW_dob.SetFocus()
468 return False
469
470 # identity
471 new_identity = gmPerson.create_identity (
472 gender = self._PRW_gender.GetData(),
473 dob = self._PRW_dob.GetData(),
474 lastnames = self._PRW_lastname.GetValue().strip(),
475 firstnames = self._PRW_firstnames.GetValue().strip()
476 )
477 _log.info('identity created: %s' % new_identity)
478
479 new_identity['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
480 val = self._TCTRL_tob.GetValue().strip()
481 if val != '':
482 new_identity['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
483 new_identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip())
484
485 prov = self._PRW_primary_provider.GetData()
486 if prov is not None:
487 new_identity['pk_primary_provider'] = prov
488 new_identity['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
489 new_identity.save()
490 _log.info('new identity updated: %s' % new_identity)
491
492 new_identity.set_nickname(nickname = gmTools.none_if(self._PRW_nickname.GetValue().strip(), ''))
493 _log.info('nickname set on new identity: %s' % new_identity)
494
495 # address
496 # if we reach this the address cannot be completely empty
497 is_valid = self.__address_valid_for_save(empty_address_is_valid = False)
498 if is_valid is True:
499 # because we currently only check for non-emptiness
500 # we must still deal with database errors
501 try:
502 new_identity.link_address (
503 number = self._TCTRL_number.GetValue().strip(),
504 street = self._PRW_street.GetValue().strip(),
505 postcode = self._PRW_zip.GetValue().strip(),
506 urb = self._PRW_urb.GetValue().strip(),
507 region_code = self._PRW_region.GetData(),
508 country_code = self._PRW_country.GetData(),
509 subunit = gmTools.none_if(self._TCTRL_unit.GetValue().strip(), ''),
510 id_type = self._PRW_type.GetData()
511 )
512 except gmPG2.dbapi.InternalError:
513 _log.debug('number: >>%s<<', self._TCTRL_number.GetValue().strip())
514 _log.debug('(sub)unit: >>%s<<', self._TCTRL_unit.GetValue().strip())
515 _log.debug('street: >>%s<<', self._PRW_street.GetValue().strip())
516 _log.debug('postcode: >>%s<<', self._PRW_zip.GetValue().strip())
517 _log.debug('urb: >>%s<<', self._PRW_urb.GetValue().strip())
518 _log.debug('region: >>%s<<', self._PRW_region.GetData().strip())
519 _log.debug('country: >>%s<<', self._PRW_country.GetData().strip())
520 _log.exception('cannot link address')
521 gmGuiHelpers.gm_show_error (
522 aTitle = _('Saving address'),
523 aMessage = _(
524 'Cannot save this address.\n'
525 '\n'
526 'You will have to add it via the Demographics plugin.\n'
527 )
528 )
529 elif is_valid is False:
530 gmGuiHelpers.gm_show_error (
531 aTitle = _('Saving address'),
532 aMessage = _(
533 'Address not saved.\n'
534 '\n'
535 'You will have to add it via the Demographics plugin.\n'
536 )
537 )
538 # else it is None which means empty address which we ignore
539
540 # phone
541 channel_name = self._PRW_channel_type.GetValue().strip()
542 pk_channel_type = self._PRW_channel_type.GetData()
543 if pk_channel_type is None:
544 if channel_name == '':
545 channel_name = 'homephone'
546 new_identity.link_comm_channel (
547 comm_medium = channel_name,
548 pk_channel_type = pk_channel_type,
549 url = gmTools.none_if(self._TCTRL_phone.GetValue().strip(), ''),
550 is_confidential = False
551 )
552
553 # external ID
554 pk_type = self._PRW_external_id_type.GetData()
555 id_value = self._TCTRL_external_id_value.GetValue().strip()
556 if (pk_type is not None) and (id_value != ''):
557 new_identity.add_external_id(value = id_value, pk_type = pk_type)
558
559 # occupation
560 new_identity.link_occupation (
561 occupation = gmTools.none_if(self._PRW_occupation.GetValue().strip(), '')
562 )
563
564 self.data = new_identity
565 return True
566 #----------------------------------------------------------------
569 #----------------------------------------------------------------
573 #----------------------------------------------------------------
576 #----------------------------------------------------------------
579
580 #============================================================
581 # main
582 #------------------------------------------------------------
583 if __name__ == "__main__":
584
585 if len(sys.argv) < 2:
586 sys.exit()
587
588 if sys.argv[1] != 'test':
589 sys.exit()
590
591 # from Gnumed.pycommon import gmPG2
592 # from Gnumed.pycommon import gmI18N
593 # gmI18N.activate_locale()
594 # gmI18N.install_domain()
595
596 #--------------------------------------------------------
597 #test_org_unit_prw()
598
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu May 10 01:55:20 2018 | http://epydoc.sourceforge.net |