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.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
263
265
266
267 if self._PRW_address_searcher.GetData() is not None:
268 wx.CallAfter(self.__set_fields_from_address_searcher)
269 return True
270
271
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
287 if no_of_filled_fields == 0:
288 if empty_address_is_valid:
289 return True
290 else:
291 return None
292
293
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
304
305
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
349
350
351
352
354 wx.CallAfter(self._refresh_ext_id_warning)
355
357 wx.CallAfter(self._refresh_ext_id_warning)
358
360 wx.CallAfter(self._refresh_dupe_warning)
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
370
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
387 wx.CallAfter(self._refresh_dupe_warning)
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
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
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
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
496
497 is_valid = self.__address_valid_for_save(empty_address_is_valid = False)
498 if is_valid is True:
499
500
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
539
540
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
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
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
568 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
569
573
576
578 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
579
580
581
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
592
593
594
595
596
597
598