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:
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
124
126 self._PRW_lastname.final_regex = '.+'
127 self._PRW_firstnames.final_regex = '.+'
128 self._PRW_address_searcher.selection_only = False
129
130
131
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
143
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.get_potential_person_dupes(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
273
275
276
277 if self._PRW_address_searcher.GetData() is not None:
278 wx.CallAfter(self.__set_fields_from_address_searcher)
279 return True
280
281
282 fields_to_fill = (
283 self._TCTRL_number,
284 self._PRW_zip,
285 self._PRW_street,
286 self._PRW_urb,
287 self._PRW_type
288 )
289 no_of_filled_fields = 0
290
291 for field in fields_to_fill:
292 if field.GetValue().strip() != '':
293 no_of_filled_fields += 1
294 field.display_as_valid(True)
295
296
297 if no_of_filled_fields == 0:
298 if empty_address_is_valid:
299 return True
300 else:
301 return None
302
303
304 if no_of_filled_fields != len(fields_to_fill):
305 for field in fields_to_fill:
306 if field.GetValue().strip() == '':
307 field.display_as_valid(False)
308 field.SetFocus()
309 msg = _('To properly create an address, all the related fields must be filled in.')
310 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
311 return False
312
313
314
315
316 strict_fields = (
317 self._PRW_type,
318 self._PRW_region,
319 self._PRW_country
320 )
321 error = False
322 for field in strict_fields:
323 if field.GetData() is None:
324 error = True
325 field.display_as_valid(False)
326 field.SetFocus()
327 else:
328 field.display_as_valid(True)
329
330 if error:
331 msg = _('This field must contain an item selected from the dropdown list.')
332 gmGuiHelpers.gm_show_error(msg, _('Required fields'))
333 return False
334
335 return True
336
359
360
361
362
364 wx.CallAfter(self._refresh_ext_id_warning)
365
367 wx.CallAfter(self._refresh_ext_id_warning)
368
370 wx.CallAfter(self._refresh_dupe_warning)
371
373 """Set the gender according to entered firstname.
374
375 Matches are fetched from existing records in backend.
376 """
377 wx.CallAfter(self._refresh_dupe_warning)
378
379
380
381 if self._PRW_gender.GetData() is not None:
382 return True
383
384 firstname = self._PRW_firstnames.GetValue().strip()
385 if firstname == '':
386 return True
387
388 gender = gmPerson.map_firstnames2gender(firstnames = firstname)
389 if gender is None:
390 return True
391
392 wx.CallAfter(self._PRW_gender.SetData, gender)
393
394 return True
395
397 _validate_dob_field(self._PRW_dob)
398 wx.CallAfter(self._refresh_dupe_warning)
399
401 self.__perhaps_invalidate_address_searcher(self._PRW_zip, 'postcode')
402
403 zip_code = gmTools.none_if(self._PRW_zip.GetValue().strip(), '')
404 self._PRW_street.set_context(context = 'zip', val = zip_code)
405 self._PRW_urb.set_context(context = 'zip', val = zip_code)
406 self._PRW_region.set_context(context = 'zip', val = zip_code)
407 self._PRW_country.set_context(context = 'zip', val = zip_code)
408
409 return True
410
412 self.__perhaps_invalidate_address_searcher(self._PRW_country, 'l10n_country')
413
414 country = gmTools.none_if(self._PRW_country.GetValue().strip(), '')
415 self._PRW_region.set_context(context = 'country', val = country)
416
417 return True
418
420 if self._TCTRL_number.GetValue().strip() == '':
421 adr = self._PRW_address_searcher.address
422 if adr is None:
423 return True
424 self._TCTRL_number.SetValue(adr['number'])
425 return True
426
427 self.__perhaps_invalidate_address_searcher(self._TCTRL_number, 'number')
428 return True
429
431 if self._TCTRL_unit.GetValue().strip() == '':
432 adr = self._PRW_address_searcher.address
433 if adr is None:
434 return True
435 self._TCTRL_unit.SetValue(gmTools.coalesce(adr['subunit'], ''))
436 return True
437
438 self.__perhaps_invalidate_address_searcher(self._TCTRL_unit, 'subunit')
439 return True
440
442 mapping = [
443 (self._PRW_street, 'street'),
444 (self._PRW_urb, 'urb'),
445 (self._PRW_region, 'l10n_region')
446 ]
447
448 for ctrl, field in mapping:
449 if self.__perhaps_invalidate_address_searcher(ctrl, field):
450 return True
451
452 return True
453
455 if self._PRW_address_searcher.address is None:
456 return True
457
458 wx.CallAfter(self.__set_fields_from_address_searcher)
459 return True
460
461
462
464 if self._PRW_primary_provider.GetValue().strip() == '':
465 self._PRW_primary_provider.display_as_valid(True)
466 else:
467 if self._PRW_primary_provider.GetData() is None:
468 self._PRW_primary_provider.display_as_valid(False)
469 else:
470 self._PRW_primary_provider.display_as_valid(True)
471 return (self.__identity_valid_for_save() and self.__address_valid_for_save(empty_address_is_valid = True))
472
474
475 if self._PRW_dob.GetValue().strip() == '':
476 if not _empty_dob_allowed():
477 self._PRW_dob.display_as_valid(False)
478 self._PRW_dob.SetFocus()
479 return False
480
481
482 new_identity = gmPerson.create_identity (
483 gender = self._PRW_gender.GetData(),
484 dob = self._PRW_dob.GetData(),
485 lastnames = self._PRW_lastname.GetValue().strip(),
486 firstnames = self._PRW_firstnames.GetValue().strip(),
487 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
488 )
489 if new_identity is None:
490 gmGuiHelpers.gm_show_error (
491 title = _('Creating person.'),
492 error = _(
493 'Failed to create person. Does it already exist ?\n'
494 '\n'
495 'If so you need to add a unique comment.'
496 )
497 )
498 return False
499 _log.info('identity created: %s' % new_identity)
500
501 new_identity['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
502 val = self._TCTRL_tob.GetValue().strip()
503 if val != '':
504 new_identity['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
505 new_identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip())
506
507 prov = self._PRW_primary_provider.GetData()
508 if prov is not None:
509 new_identity['pk_primary_provider'] = prov
510
511 new_identity.save()
512 _log.info('new identity updated: %s' % new_identity)
513
514 new_identity.set_nickname(nickname = gmTools.none_if(self._PRW_nickname.GetValue().strip(), ''))
515 _log.info('nickname set on new identity: %s' % new_identity)
516
517
518
519 is_valid = self.__address_valid_for_save(empty_address_is_valid = False)
520 if is_valid is True:
521
522
523 try:
524 new_identity.link_address (
525 number = self._TCTRL_number.GetValue().strip(),
526 street = self._PRW_street.GetValue().strip(),
527 postcode = self._PRW_zip.GetValue().strip(),
528 urb = self._PRW_urb.GetValue().strip(),
529 region_code = self._PRW_region.GetData(),
530 country_code = self._PRW_country.GetData(),
531 subunit = gmTools.none_if(self._TCTRL_unit.GetValue().strip(), ''),
532 id_type = self._PRW_type.GetData()
533 )
534 except gmPG2.dbapi.InternalError:
535 _log.debug('number: >>%s<<', self._TCTRL_number.GetValue().strip())
536 _log.debug('(sub)unit: >>%s<<', self._TCTRL_unit.GetValue().strip())
537 _log.debug('street: >>%s<<', self._PRW_street.GetValue().strip())
538 _log.debug('postcode: >>%s<<', self._PRW_zip.GetValue().strip())
539 _log.debug('urb: >>%s<<', self._PRW_urb.GetValue().strip())
540 _log.debug('region: >>%s<<', self._PRW_region.GetData().strip())
541 _log.debug('country: >>%s<<', self._PRW_country.GetData().strip())
542 _log.exception('cannot link address')
543 gmGuiHelpers.gm_show_error (
544 aTitle = _('Saving address'),
545 aMessage = _(
546 'Cannot save this address.\n'
547 '\n'
548 'You will have to add it via the Demographics plugin.\n'
549 )
550 )
551 elif is_valid is False:
552 gmGuiHelpers.gm_show_error (
553 aTitle = _('Saving address'),
554 aMessage = _(
555 'Address not saved.\n'
556 '\n'
557 'You will have to add it via the Demographics plugin.\n'
558 )
559 )
560
561
562
563 channel_name = self._PRW_channel_type.GetValue().strip()
564 pk_channel_type = self._PRW_channel_type.GetData()
565 if pk_channel_type is None:
566 if channel_name == '':
567 channel_name = 'homephone'
568 new_identity.link_comm_channel (
569 comm_medium = channel_name,
570 pk_channel_type = pk_channel_type,
571 url = gmTools.none_if(self._TCTRL_phone.GetValue().strip(), ''),
572 is_confidential = False
573 )
574
575
576 pk_type = self._PRW_external_id_type.GetData()
577 id_value = self._TCTRL_external_id_value.GetValue().strip()
578 if (pk_type is not None) and (id_value != ''):
579 new_identity.add_external_id(value = id_value, pk_type = pk_type)
580
581
582 new_identity.link_occupation (
583 occupation = gmTools.none_if(self._PRW_occupation.GetValue().strip(), '')
584 )
585
586 self.data = new_identity
587 return True
588
590 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
591
595
598
600 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
601
602
603
604
605 if __name__ == "__main__":
606
607 if len(sys.argv) < 2:
608 sys.exit()
609
610 if sys.argv[1] != 'test':
611 sys.exit()
612
613
614
615
616
617
618
619
620