1
2 """GNUmed patient objects.
3
4 This is a patient object intended to let a useful client-side
5 API crystallize from actual use in true XP fashion.
6 """
7
8
9
10 __version__ = "$Revision: 1.198 $"
11 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
12 __license__ = "GPL"
13
14
15 import sys, os.path, time, re as regex, string, types, datetime as pyDT, codecs, threading, logging
16
17
18
19 if __name__ == '__main__':
20 sys.path.insert(0, '../../')
21 from Gnumed.pycommon import gmExceptions, gmDispatcher, gmBorg, gmI18N, gmNull, gmBusinessDBObject, gmTools, gmPG2, gmMatchProvider, gmDateTime, gmLog2
22 from Gnumed.business import gmMedDoc, gmDemographicRecord, gmProviderInbox, gmXdtMappings, gmClinicalRecord
23
24
25 _log = logging.getLogger('gm.person')
26 _log.info(__version__)
27
28 __gender_list = None
29 __gender_idx = None
30
31 __gender2salutation_map = None
32
33
35
36
37
38
39
40
42 return 'firstnames lastnames dob gender'.split()
43
46
48 """Generate generic queries.
49
50 - not locale dependant
51 - data -> firstnames, lastnames, dob, gender
52
53 shall we mogrify name parts ? probably not as external
54 sources should know what they do
55
56 finds by inactive name, too, but then shows
57 the corresponding active name ;-)
58
59 Returns list of matching identities (may be empty)
60 or None if it was told to create an identity but couldn't.
61 """
62 where_snippets = []
63 args = {}
64
65 where_snippets.append(u'firstnames = %(first)s')
66 args['first'] = self.firstnames
67
68 where_snippets.append(u'lastnames = %(last)s')
69 args['last'] = self.lastnames
70
71 if self.dob is not None:
72 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %(dob)s)")
73 args['dob'] = self.dob
74
75 if self.gender is not None:
76 where_snippets.append('gender = %(sex)s')
77 args['sex'] = self.gender
78
79 cmd = u"""
80 select *, '%s' as match_type from dem.v_basic_person
81 where pk_identity in (
82 select id_identity from dem.names where %s
83 ) order by lastnames, firstnames, dob""" % (
84 _('external patient source (name, gender, date of birth)'),
85 ' and '.join(where_snippets)
86 )
87
88 try:
89 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx=True)
90 except:
91 _log.error(u'cannot get candidate identities for dto "%s"' % self)
92 _log.exception('query %s' % cmd)
93 rows = []
94
95 if len(rows) == 0:
96 if not can_create:
97 return []
98 ident = self.import_into_database()
99 if ident is None:
100 return None
101 identities = [ident]
102 else:
103 identities = [ cIdentity(row = {'pk_field': 'pk_identity', 'data': row, 'idx': idx}) for row in rows ]
104
105 return identities
106
108 """Imports self into the database.
109
110 Child classes can override this to provide more extensive import.
111 """
112 ident = create_identity (
113 firstnames = self.firstnames,
114 lastnames = self.lastnames,
115 gender = self.gender,
116 dob = self.dob
117 )
118 return ident
119
122
123
124
126 return u'<%s @ %s: %s %s (%s) %s>' % (
127 self.__class__.__name__,
128 id(self),
129 self.firstnames,
130 self.lastnames,
131 self.gender,
132 self.dob
133 )
134
136 """Do some sanity checks on self.* access."""
137
138 if attr == 'gender':
139 glist, idx = get_gender_list()
140 for gender in glist:
141 if str(val) in [gender[0], gender[1], gender[2], gender[3]]:
142 val = gender[idx['tag']]
143 object.__setattr__(self, attr, val)
144 return
145 raise ValueError('invalid gender: [%s]' % val)
146
147 if attr == 'dob':
148 if val is not None:
149 if not isinstance(val, pyDT.datetime):
150 raise TypeError('invalid type for DOB (must be datetime.datetime): %s [%s]' % (type(val), val))
151 if val.tzinfo is None:
152 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % val.isoformat())
153
154 object.__setattr__(self, attr, val)
155 return
156
158 return getattr(self, attr)
159
160 -class cPersonName(gmBusinessDBObject.cBusinessDBObject):
161 _cmd_fetch_payload = u"select * from dem.v_person_names where pk_name = %s"
162 _cmds_store_payload = [
163 u"""update dem.names set
164 active = False
165 where
166 %(active_name)s is True and -- act only when needed and only
167 id_identity = %(pk_identity)s and -- on names of this identity
168 active is True and -- which are active
169 id != %(pk_name)s -- but NOT *this* name
170 """,
171 u"""update dem.names set
172 active = %(active_name)s,
173 preferred = %(preferred)s,
174 comment = %(comment)s
175 where
176 id = %(pk_name)s and
177 id_identity = %(pk_identity)s and -- belt and suspenders
178 xmin = %(xmin_name)s""",
179 u"""select xmin as xmin_name from dem.names where id = %(pk_name)s"""
180 ]
181 _updatable_fields = ['active_name', 'preferred', 'comment']
182
191
193 return '%(last)s, %(title)s %(first)s%(nick)s' % {
194 'last': self._payload[self._idx['lastnames']],
195 'title': gmTools.coalesce (
196 self._payload[self._idx['title']],
197 map_gender2salutation(self._payload[self._idx['gender']])
198 ),
199 'first': self._payload[self._idx['firstnames']],
200 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u' "%s"', u'%s')
201 }
202
203 description = property(_get_description, lambda x:x)
204
205 -class cStaff(gmBusinessDBObject.cBusinessDBObject):
206 _cmd_fetch_payload = u"select * from dem.v_staff where pk_staff=%s"
207 _cmds_store_payload = [
208 u"""update dem.staff set
209 fk_role = %(pk_role)s,
210 short_alias = %(short_alias)s,
211 comment = gm.nullify_empty_string(%(comment)s),
212 is_active = %(is_active)s,
213 db_user = %(db_user)s
214 where
215 pk=%(pk_staff)s and
216 xmin = %(xmin_staff)s""",
217 u"""select xmin_staff from dem.v_staff where pk_identity=%(pk_identity)s"""
218 ]
219 _updatable_fields = ['pk_role', 'short_alias', 'comment', 'is_active', 'db_user']
220
221 - def __init__(self, aPK_obj=None, row=None):
222
223 if (aPK_obj is None) and (row is None):
224 cmd = u"select * from dem.v_staff where db_user = CURRENT_USER"
225 try:
226 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
227 except:
228 _log.exception('cannot instantiate staff instance')
229 gmLog2.log_stack_trace()
230 raise ValueError('cannot instantiate staff instance for database account CURRENT_USER')
231 if len(rows) == 0:
232 raise ValueError('no staff record for database account CURRENT_USER')
233 row = {
234 'pk_field': 'pk_staff',
235 'idx': idx,
236 'data': rows[0]
237 }
238 gmBusinessDBObject.cBusinessDBObject.__init__(self, row = row)
239 else:
240 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj = aPK_obj, row = row)
241
242
243 self.__is_current_user = (gmPG2.get_current_user() == self._payload[self._idx['db_user']])
244
245 self.__inbox = None
246
253
255 rows, idx = gmPG2.run_ro_queries (
256 queries = [{
257 'cmd': u'select i18n.get_curr_lang(%(usr)s)',
258 'args': {'usr': self._payload[self._idx['db_user']]}
259 }]
260 )
261 return rows[0][0]
262
264 if not gmPG2.set_user_language(language = language):
265 raise ValueError (
266 u'Cannot set database language to [%s] for user [%s].' % (language, self._payload[self._idx['db_user']])
267 )
268 return
269
270 database_language = property(_get_db_lang, _set_db_lang)
271
273 if self.__inbox is None:
274 self.__inbox = gmProviderInbox.cProviderInbox(provider_id = self._payload[self._idx['pk_staff']])
275 return self.__inbox
276
279
280 inbox = property(_get_inbox, _set_inbox)
281
284
286 """Staff member Borg to hold currently logged on provider.
287
288 There may be many instances of this but they all share state.
289 """
291 """Change or get currently logged on provider.
292
293 provider:
294 * None: get copy of current instance
295 * cStaff instance: change logged on provider (role)
296 """
297
298 try:
299 self.provider
300 except AttributeError:
301 self.provider = gmNull.cNull()
302
303
304 if provider is None:
305 return None
306
307
308 if not isinstance(provider, cStaff):
309 raise ValueError, 'cannot set logged on provider to [%s], must be either None or cStaff instance' % str(provider)
310
311
312 if self.provider['pk_staff'] == provider['pk_staff']:
313 return None
314
315
316 if isinstance(self.provider, gmNull.cNull):
317 self.provider = provider
318 return None
319
320
321 raise ValueError, 'provider change [%s] -> [%s] not yet supported' % (self.provider['pk_staff'], provider['pk_staff'])
322
323
326
327
328
330 """Return any attribute if known how to retrieve it by proxy.
331 """
332 return self.provider[aVar]
333
334
335
337 if attribute == 'provider':
338 raise AttributeError
339 if not isinstance(self.provider, gmNull.cNull):
340 return getattr(self.provider, attribute)
341
342
343 -class cIdentity(gmBusinessDBObject.cBusinessDBObject):
344 _cmd_fetch_payload = u"select * from dem.v_basic_person where pk_identity = %s"
345 _cmds_store_payload = [
346 u"""update dem.identity set
347 gender = %(gender)s,
348 dob = %(dob)s,
349 tob = %(tob)s,
350 cob = gm.nullify_empty_string(%(cob)s),
351 title = gm.nullify_empty_string(%(title)s),
352 fk_marital_status = %(pk_marital_status)s,
353 karyotype = gm.nullify_empty_string(%(karyotype)s),
354 pupic = gm.nullify_empty_string(%(pupic)s),
355 deceased = %(deceased)s
356 where
357 pk = %(pk_identity)s and
358 xmin = %(xmin_identity)s""",
359 u"""select xmin_identity from dem.v_basic_person where pk_identity = %(pk_identity)s"""
360 ]
361 _updatable_fields = ["title", "dob", "tob", "cob", "gender", "pk_marital_status", "karyotype", "pupic", 'deceased']
362
364 return self._payload[self._idx['pk_identity']]
366 raise AttributeError('setting ID of identity is not allowed')
367 ID = property(_get_ID, _set_ID)
368
370
371 if attribute == 'dob':
372 if value is not None:
373
374 if isinstance(value, pyDT.datetime):
375 if value.tzinfo is None:
376 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % dt.isoformat())
377 else:
378 raise TypeError, '[%s]: type [%s] (%s) invalid for attribute [dob], must be datetime.datetime or None' % (self.__class__.__name__, type(value), value)
379
380
381 if self._payload[self._idx['dob']] is not None:
382 old_dob = self._payload[self._idx['dob']].strftime('%Y %m %d %H %M %S')
383 new_dob = value.strftime('%Y %m %d %H %M %S')
384 if new_dob == old_dob:
385 return
386
387 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
388
391
393 cmd = u"""
394 select exists (
395 select 1
396 from clin.v_emr_journal
397 where
398 pk_patient = %(pat)s
399 and
400 soap_cat is not null
401 )"""
402 args = {'pat': self._payload[self._idx['pk_identity']]}
403 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
404 return rows[0][0]
405
407 raise AttributeError('setting is_patient status of identity is not allowed')
408
409 is_patient = property(_get_is_patient, _set_is_patient)
410
411
412
414 for name in self.get_names():
415 if name['active_name'] is True:
416 return name
417
418 _log.error('cannot retrieve active name for patient [%s]' % self._payload[self._idx['pk_identity']])
419 return None
420
422 cmd = u"select * from dem.v_person_names where pk_identity = %(pk_pat)s"
423 rows, idx = gmPG2.run_ro_queries (
424 queries = [{
425 'cmd': cmd,
426 'args': {'pk_pat': self._payload[self._idx['pk_identity']]}
427 }],
428 get_col_idx = True
429 )
430
431 if len(rows) == 0:
432
433 return []
434
435 names = [ cPersonName(row = {'idx': idx, 'data': r, 'pk_field': 'pk_name'}) for r in rows ]
436 return names
437
448
450 return '%(sex)s%(title)s %(last)s, %(first)s%(nick)s' % {
451 'last': self._payload[self._idx['lastnames']],
452 'first': self._payload[self._idx['firstnames']],
453 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u' (%s)', u'%s'),
454 'sex': map_gender2salutation(self._payload[self._idx['gender']]),
455 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s', u'%s')
456 }
457
459 return '%(last)s,%(title)s %(first)s%(nick)s' % {
460 'last': self._payload[self._idx['lastnames']],
461 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s', u'%s'),
462 'first': self._payload[self._idx['firstnames']],
463 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u' (%s)', u'%s')
464 }
465
466 - def add_name(self, firstnames, lastnames, active=True):
467 """Add a name.
468
469 @param firstnames The first names.
470 @param lastnames The last names.
471 @param active When True, the new name will become the active one (hence setting other names to inactive)
472 @type active A types.BooleanType instance
473 """
474 name = create_name(self.ID, firstnames, lastnames, active)
475 if active:
476 self.refetch_payload()
477 return name
478
480 cmd = u"delete from dem.names where id = %(name)s and id_identity = %(pat)s"
481 args = {'name': name['pk_name'], 'pat': self.ID}
482 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
483
484
485
486
488 """
489 Set the nickname. Setting the nickname only makes sense for the currently
490 active name.
491 @param nickname The preferred/nick/warrior name to set.
492 """
493 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': u"select dem.set_nickname(%s, %s)", 'args': [self.ID, nickname]}])
494 self.refetch_payload()
495 return True
496
497
498
499
500
501
502
503
504
505
506 - def add_external_id(self, type_name=None, value=None, issuer=None, comment=None, context=u'p', pk_type=None):
507 """Adds an external ID to the patient.
508
509 creates ID type if necessary
510 context hardcoded to 'p' for now
511 """
512
513
514 if pk_type is not None:
515 cmd = u"""
516 select * from dem.v_external_ids4identity where
517 pk_identity = %(pat)s and
518 pk_type = %(pk_type)s and
519 value = %(val)s"""
520 else:
521
522 if issuer is None:
523 cmd = u"""
524 select * from dem.v_external_ids4identity where
525 pk_identity = %(pat)s and
526 name = %(name)s and
527 value = %(val)s"""
528 else:
529 cmd = u"""
530 select * from dem.v_external_ids4identity where
531 pk_identity = %(pat)s and
532 name = %(name)s and
533 value = %(val)s and
534 issuer = %(issuer)s"""
535 args = {
536 'pat': self.ID,
537 'name': type_name,
538 'val': value,
539 'issuer': issuer,
540 'pk_type': pk_type
541 }
542 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
543
544
545 if len(rows) == 0:
546
547 args = {
548 'pat': self.ID,
549 'val': value,
550 'type_name': type_name,
551 'pk_type': pk_type,
552 'issuer': issuer,
553 'ctxt': context,
554 'comment': comment
555 }
556
557 if pk_type is None:
558 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
559 %(val)s,
560 (select dem.add_external_id_type(%(type_name)s, %(issuer)s, %(ctxt)s)),
561 %(comment)s,
562 %(pat)s
563 )"""
564 else:
565 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
566 %(val)s,
567 %(pk_type)s,
568 %(comment)s,
569 %(pat)s
570 )"""
571
572 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
573
574
575 else:
576 row = rows[0]
577 if comment is not None:
578
579 if gmTools.coalesce(row['comment'], '').find(comment.strip()) == -1:
580 comment = '%s%s' % (gmTools.coalesce(row['comment'], '', '%s // '), comment.strip)
581 cmd = u"update dem.lnk_identity2ext_id set comment = %(comment)s where id=%(pk)s"
582 args = {'comment': comment, 'pk': row['pk_id']}
583 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
584
585 - def update_external_id(self, pk_id=None, type=None, value=None, issuer=None, comment=None):
586 """Edits an existing external ID.
587
588 creates ID type if necessary
589 context hardcoded to 'p' for now
590 """
591 cmd = u"""
592 update dem.lnk_identity2ext_id set
593 fk_origin = (select dem.add_external_id_type(%(type)s, %(issuer)s, %(ctxt)s)),
594 external_id = %(value)s,
595 comment = %(comment)s
596 where id = %(pk)s"""
597 args = {'pk': pk_id, 'ctxt': u'p', 'value': value, 'type': type, 'issuer': issuer, 'comment': comment}
598 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
599
601 where_parts = ['pk_identity = %(pat)s']
602 args = {'pat': self.ID}
603
604 if id_type is not None:
605 where_parts.append(u'name = %(name)s')
606 args['name'] = id_type.strip()
607
608 if issuer is not None:
609 where_parts.append(u'issuer = %(issuer)s')
610 args['issuer'] = issuer.strip()
611
612 if context is not None:
613 where_parts.append(u'context = %(ctxt)s')
614 args['ctxt'] = context.strip()
615
616 cmd = u"select * from dem.v_external_ids4identity where %s" % ' and '.join(where_parts)
617 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
618
619 return rows
620
622 cmd = u"""
623 delete from dem.lnk_identity2ext_id
624 where id_identity = %(pat)s and id = %(pk)s"""
625 args = {'pat': self.ID, 'pk': pk_ext_id}
626 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
627
629 """Merge another identity into this one.
630
631 Keep this one. Delete other one."""
632
633 if other_identity.ID == self.ID:
634 return True, None
635
636 curr_pat = gmCurrentPatient()
637 if curr_pat.connected:
638 if other_identity.ID == curr_pat.ID:
639 return False, _('Cannot merge active patient into another patient.')
640
641 queries = []
642 args = {'old_pat': other_identity.ID, 'new_pat': self.ID}
643
644
645 queries.append ({
646 'cmd': u'delete from clin.allergy_state where pk = (select pk_allergy_state from clin.v_pat_allergy_state where pk_patient = %(old_pat)s)',
647 'args': args
648 })
649
650
651
652 queries.append ({
653 'cmd': u'update dem.names set active = False where id_identity = %(old_pat)s',
654 'args': args
655 })
656
657
658 FKs = gmPG2.get_foreign_keys2column (
659 schema = u'dem',
660 table = u'identity',
661 column = u'pk'
662 )
663
664
665 cmd_template = u'update %s set %s = %%(new_pat)s where %s = %%(old_pat)s'
666 for FK in FKs:
667 queries.append ({
668 'cmd': cmd_template % (FK['referencing_table'], FK['referencing_column'], FK['referencing_column']),
669 'args': args
670 })
671
672
673 queries.append ({
674 'cmd': u'delete from dem.identity where pk = %(old_pat)s',
675 'args': args
676 })
677
678 _log.warning('identity [%s] is about to assimilate identity [%s]', self.ID, other_identity.ID)
679
680 gmPG2.run_rw_queries(link_obj = link_obj, queries = queries, end_tx = True)
681
682 self.add_external_id (
683 type_name = u'merged GNUmed identity primary key',
684 value = u'GNUmed::pk::%s' % other_identity.ID,
685 issuer = u'GNUmed'
686 )
687
688 return True, None
689
690
692 cmd = u"""
693 insert into clin.waiting_list (fk_patient, urgency, comment, area, list_position)
694 values (
695 %(pat)s,
696 %(urg)s,
697 %(cmt)s,
698 %(area)s,
699 (select coalesce((max(list_position) + 1), 1) from clin.waiting_list)
700 )"""
701 args = {'pat': self.ID, 'urg': urgency, 'cmt': comment, 'area': zone}
702 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], verbose=True)
703
704 - def export_as_gdt(self, filename=None, encoding='iso-8859-15', external_id_type=None):
705
706 template = u'%s%s%s\r\n'
707
708 file = codecs.open (
709 filename = filename,
710 mode = 'wb',
711 encoding = encoding,
712 errors = 'strict'
713 )
714
715 file.write(template % (u'013', u'8000', u'6301'))
716 file.write(template % (u'013', u'9218', u'2.10'))
717 if external_id_type is None:
718 file.write(template % (u'%03d' % (9 + len(str(self.ID))), u'3000', self.ID))
719 else:
720 ext_ids = self.get_external_ids(id_type = external_id_type)
721 if len(ext_ids) > 0:
722 file.write(template % (u'%03d' % (9 + len(ext_ids[0]['value'])), u'3000', ext_ids[0]['value']))
723 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['lastnames']])), u'3101', self._payload[self._idx['lastnames']]))
724 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['firstnames']])), u'3102', self._payload[self._idx['firstnames']]))
725 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['dob']].strftime('%d%m%Y'))), u'3103', self._payload[self._idx['dob']].strftime('%d%m%Y')))
726 file.write(template % (u'010', u'3110', gmXdtMappings.map_gender_gm2xdt[self._payload[self._idx['gender']]]))
727 file.write(template % (u'025', u'6330', 'GNUmed::9206::encoding'))
728 file.write(template % (u'%03d' % (9 + len(encoding)), u'6331', encoding))
729 if external_id_type is None:
730 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
731 file.write(template % (u'017', u'6333', u'internal'))
732 else:
733 if len(ext_ids) > 0:
734 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
735 file.write(template % (u'%03d' % (9 + len(external_id_type)), u'6333', external_id_type))
736
737 file.close()
738
739
740
742 cmd = u"select * from dem.v_person_jobs where pk_identity=%s"
743 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
744 return rows
745
747 """Link an occupation with a patient, creating the occupation if it does not exists.
748
749 @param occupation The name of the occupation to link the patient to.
750 """
751 if (activities is None) and (occupation is None):
752 return True
753
754 occupation = occupation.strip()
755 if len(occupation) == 0:
756 return True
757
758 if activities is not None:
759 activities = activities.strip()
760
761 args = {'act': activities, 'pat_id': self.pk_obj, 'job': occupation}
762
763 cmd = u"select activities from dem.v_person_jobs where pk_identity = %(pat_id)s and l10n_occupation = _(%(job)s)"
764 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
765
766 queries = []
767 if len(rows) == 0:
768 queries.append ({
769 'cmd': u"INSERT INTO dem.lnk_job2person (fk_identity, fk_occupation, activities) VALUES (%(pat_id)s, dem.create_occupation(%(job)s), %(act)s)",
770 'args': args
771 })
772 else:
773 if rows[0]['activities'] != activities:
774 queries.append ({
775 'cmd': u"update dem.lnk_job2person set activities=%(act)s where fk_identity=%(pat_id)s and fk_occupation=(select id from dem.occupation where _(name) = _(%(job)s))",
776 'args': args
777 })
778
779 rows, idx = gmPG2.run_rw_queries(queries = queries)
780
781 return True
782
784 if occupation is None:
785 return True
786 occupation = occupation.strip()
787 cmd = u"delete from dem.lnk_job2person where fk_identity=%(pk)s and fk_occupation in (select id from dem.occupation where _(name) = _(%(job)s))"
788 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj, 'job': occupation}}])
789 return True
790
791
792
794 cmd = u"select * from dem.v_person_comms where pk_identity = %s"
795 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True)
796
797 filtered = rows
798
799 if comm_medium is not None:
800 filtered = []
801 for row in rows:
802 if row['comm_type'] == comm_medium:
803 filtered.append(row)
804
805 return [ gmDemographicRecord.cCommChannel(row = {
806 'pk_field': 'pk_lnk_identity2comm',
807 'data': r,
808 'idx': idx
809 }) for r in filtered
810 ]
811
812 - def link_comm_channel(self, comm_medium=None, url=None, is_confidential=False, pk_channel_type=None):
813 """Link a communication medium with a patient.
814
815 @param comm_medium The name of the communication medium.
816 @param url The communication resource locator.
817 @type url A types.StringType instance.
818 @param is_confidential Wether the data must be treated as confidential.
819 @type is_confidential A types.BooleanType instance.
820 """
821 comm_channel = gmDemographicRecord.create_comm_channel (
822 comm_medium = comm_medium,
823 url = url,
824 is_confidential = is_confidential,
825 pk_channel_type = pk_channel_type,
826 pk_identity = self.pk_obj
827 )
828 return comm_channel
829
835
836
837
839 cmd = u"select * from dem.v_pat_addresses where pk_identity=%s"
840 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx=True)
841 addresses = []
842 for r in rows:
843 addresses.append(gmDemographicRecord.cPatientAddress(row={'idx': idx, 'data': r, 'pk_field': 'pk_address'}))
844
845 filtered = addresses
846
847 if address_type is not None:
848 filtered = []
849 for adr in addresses:
850 if adr['address_type'] == address_type:
851 filtered.append(adr)
852
853 return filtered
854
855 - def link_address(self, number=None, street=None, postcode=None, urb=None, state=None, country=None, subunit=None, suburb=None, id_type=None):
856 """Link an address with a patient, creating the address if it does not exists.
857
858 @param number The number of the address.
859 @param street The name of the street.
860 @param postcode The postal code of the address.
861 @param urb The name of town/city/etc.
862 @param state The code of the state.
863 @param country The code of the country.
864 @param id_type The primary key of the address type.
865 """
866
867 adr = gmDemographicRecord.create_address (
868 country = country,
869 state = state,
870 urb = urb,
871 suburb = suburb,
872 postcode = postcode,
873 street = street,
874 number = number,
875 subunit = subunit
876 )
877
878
879 cmd = u"select * from dem.lnk_person_org_address where id_identity = %s and id_address = %s"
880 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj, adr['pk_address']]}])
881
882 if len(rows) == 0:
883 args = {'id': self.pk_obj, 'adr': adr['pk_address'], 'type': id_type}
884 if id_type is None:
885 cmd = u"""
886 insert into dem.lnk_person_org_address(id_identity, id_address)
887 values (%(id)s, %(adr)s)"""
888 else:
889 cmd = u"""
890 insert into dem.lnk_person_org_address(id_identity, id_address, id_type)
891 values (%(id)s, %(adr)s, %(type)s)"""
892 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
893 else:
894
895 if id_type is not None:
896 r = rows[0]
897 if r['id_type'] != id_type:
898 cmd = "update dem.lnk_person_org_address set id_type = %(type)s where id = %(id)s"
899 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'type': id_type, 'id': r['id']}}])
900
901 return adr
902
904 """Remove an address from the patient.
905
906 The address itself stays in the database.
907 The address can be either cAdress or cPatientAdress.
908 """
909 cmd = u"delete from dem.lnk_person_org_address where id_identity = %(person)s and id_address = %(adr)s"
910 args = {'person': self.pk_obj, 'adr': address['pk_address']}
911 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
912
913
914
916 cmd = u"""
917 select
918 t.description,
919 vbp.pk_identity as id,
920 title,
921 firstnames,
922 lastnames,
923 dob,
924 cob,
925 gender,
926 karyotype,
927 pupic,
928 pk_marital_status,
929 marital_status,+
930 xmin_identity,
931 preferred
932 from
933 dem.v_basic_person vbp, dem.relation_types t, dem.lnk_person2relative l
934 where
935 (
936 l.id_identity = %(pk)s and
937 vbp.pk_identity = l.id_relative and
938 t.id = l.id_relation_type
939 ) or (
940 l.id_relative = %(pk)s and
941 vbp.pk_identity = l.id_identity and
942 t.inverse = l.id_relation_type
943 )"""
944 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}])
945 if len(rows) == 0:
946 return []
947 return [(row[0], cIdentity(row = {'data': row[1:], 'idx':idx, 'pk_field': 'pk'})) for row in rows]
948
950
951 id_new_relative = create_dummy_identity()
952
953 relative = cIdentity(aPK_obj=id_new_relative)
954
955
956 relative.add_name( '**?**', self.get_names()['lastnames'])
957
958 if self._ext_cache.has_key('relatives'):
959 del self._ext_cache['relatives']
960 cmd = u"""
961 insert into dem.lnk_person2relative (
962 id_identity, id_relative, id_relation_type
963 ) values (
964 %s, %s, (select id from dem.relation_types where description = %s)
965 )"""
966 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.ID, id_new_relative, rel_type ]}])
967 return True
968
970
971 self.set_relative(None, relation)
972
973
974
991
992 - def dob_in_range(self, min_distance=u'1 week', max_distance=u'1 week'):
993 cmd = u'select dem.dob_is_in_range(%(dob)s, %(min)s, %(max)s)'
994 rows, idx = gmPG2.run_ro_queries (
995 queries = [{
996 'cmd': cmd,
997 'args': {'dob': self['dob'], 'min': min_distance, 'max': max_distance}
998 }]
999 )
1000 return rows[0][0]
1001
1002
1003
1005 cmd = u'select * from clin.v_most_recent_encounters where pk_patient=%s'
1006 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['pk_identity']]]}])
1007 if len(rows) > 0:
1008 return rows[0]
1009 else:
1010 return None
1011
1014
1017
1018 messages = property(_get_messages, _set_messages)
1019
1022
1023
1024
1026 """Format patient demographics into patient specific path name fragment."""
1027 return '%s-%s%s-%s' % (
1028 self._payload[self._idx['lastnames']].replace(u' ', u'_'),
1029 self._payload[self._idx['firstnames']].replace(u' ', u'_'),
1030 gmTools.coalesce(self._payload[self._idx['preferred']], u'', template_initial = u'-(%s)'),
1031 self._payload[self._idx['dob']].strftime('%Y-%m-%d')
1032 )
1033
1035 """Represents a staff member which is a person.
1036
1037 - a specializing subclass of cIdentity turning it into a staff member
1038 """
1042
1045
1047 """Represents a person which is a patient.
1048
1049 - a specializing subclass of cIdentity turning it into a patient
1050 - its use is to cache subobjects like EMR and document folder
1051 """
1052 - def __init__(self, aPK_obj=None, row=None):
1053 cIdentity.__init__(self, aPK_obj=aPK_obj, row=row)
1054 self.__db_cache = {}
1055 self.__emr_access_lock = threading.Lock()
1056
1058 """Do cleanups before dying.
1059
1060 - note that this may be called in a thread
1061 """
1062 if self.__db_cache.has_key('clinical record'):
1063 self.__db_cache['clinical record'].cleanup()
1064 if self.__db_cache.has_key('document folder'):
1065 self.__db_cache['document folder'].cleanup()
1066 cIdentity.cleanup(self)
1067
1069 if not self.__emr_access_lock.acquire(False):
1070 raise AttributeError('cannot access EMR')
1071 try:
1072 emr = self.__db_cache['clinical record']
1073 self.__emr_access_lock.release()
1074 return emr
1075 except KeyError:
1076 pass
1077
1078 self.__db_cache['clinical record'] = gmClinicalRecord.cClinicalRecord(aPKey = self._payload[self._idx['pk_identity']])
1079 self.__emr_access_lock.release()
1080 return self.__db_cache['clinical record']
1081
1083 try:
1084 return self.__db_cache['document folder']
1085 except KeyError:
1086 pass
1087
1088 self.__db_cache['document folder'] = gmMedDoc.cDocumentFolder(aPKey = self._payload[self._idx['pk_identity']])
1089 return self.__db_cache['document folder']
1090
1092 """Patient Borg to hold currently active patient.
1093
1094 There may be many instances of this but they all share state.
1095 """
1096 - def __init__(self, patient=None, forced_reload=False):
1097 """Change or get currently active patient.
1098
1099 patient:
1100 * None: get currently active patient
1101 * -1: unset currently active patient
1102 * cPatient instance: set active patient if possible
1103 """
1104
1105 try:
1106 tmp = self.patient
1107 except AttributeError:
1108 self.patient = gmNull.cNull()
1109 self.__register_interests()
1110
1111
1112
1113 self.__lock_depth = 0
1114
1115 self.__pre_selection_callbacks = []
1116
1117
1118 if patient is None:
1119 return None
1120
1121
1122 if self.locked:
1123 _log.error('patient [%s] is locked, cannot change to [%s]' % (self.patient['pk_identity'], patient))
1124 return None
1125
1126
1127 if patient == -1:
1128 _log.debug('explicitly unsetting current patient')
1129 if not self.__run_pre_selection_callbacks():
1130 _log.debug('not unsetting current patient')
1131 return None
1132 self.__send_pre_selection_notification()
1133 self.patient.cleanup()
1134 self.patient = gmNull.cNull()
1135 self.__send_selection_notification()
1136 return None
1137
1138
1139 if not isinstance(patient, cPatient):
1140 _log.error('cannot set active patient to [%s], must be either None, -1 or cPatient instance' % str(patient))
1141 raise TypeError, 'gmPerson.gmCurrentPatient.__init__(): <patient> must be None, -1 or cPatient instance but is: %s' % str(patient)
1142
1143
1144 if (self.patient['pk_identity'] == patient['pk_identity']) and not forced_reload:
1145 return None
1146
1147
1148 _log.debug('patient change [%s] -> [%s] requested', self.patient['pk_identity'], patient['pk_identity'])
1149
1150
1151 if not self.__run_pre_selection_callbacks():
1152 _log.debug('not changing current patient')
1153 return None
1154 self.__send_pre_selection_notification()
1155 self.patient.cleanup()
1156 self.patient = patient
1157 self.patient.get_emr()
1158 self.__send_selection_notification()
1159
1160 return None
1161
1165
1169
1170
1171
1173 if not callable(callback):
1174 raise TypeError(u'callback [%s] not callable' % callback)
1175
1176 self.__pre_selection_callbacks.append(callback)
1177
1180
1182 raise AttributeError(u'invalid to set <connected> state')
1183
1184 connected = property(_get_connected, _set_connected)
1185
1187 return (self.__lock_depth > 0)
1188
1190 if locked:
1191 self.__lock_depth = self.__lock_depth + 1
1192 gmDispatcher.send(signal='patient_locked')
1193 else:
1194 if self.__lock_depth == 0:
1195 _log.error('lock/unlock imbalance, trying to refcount lock depth below 0')
1196 return
1197 else:
1198 self.__lock_depth = self.__lock_depth - 1
1199 gmDispatcher.send(signal='patient_unlocked')
1200
1201 locked = property(_get_locked, _set_locked)
1202
1204 _log.info('forced patient unlock at lock depth [%s]' % self.__lock_depth)
1205 self.__lock_depth = 0
1206 gmDispatcher.send(signal='patient_unlocked')
1207
1208
1209
1211 if isinstance(self.patient, gmNull.cNull):
1212 return True
1213
1214 for call_back in self.__pre_selection_callbacks:
1215 try:
1216 successful = call_back()
1217 except:
1218 _log.exception('callback [%s] failed', call_back)
1219 print "*** pre-selection callback failed ***"
1220 print type(call_back)
1221 print call_back
1222 return False
1223
1224 if not successful:
1225 _log.debug('callback [%s] returned False', call_back)
1226 return False
1227
1228 return True
1229
1231 """Sends signal when another patient is about to become active.
1232
1233 This does NOT wait for signal handlers to complete.
1234 """
1235 kwargs = {
1236 'signal': u'pre_patient_selection',
1237 'sender': id(self.__class__),
1238 'pk_identity': self.patient['pk_identity']
1239 }
1240 gmDispatcher.send(**kwargs)
1241
1243 """Sends signal when another patient has actually been made active."""
1244 kwargs = {
1245 'signal': u'post_patient_selection',
1246 'sender': id(self.__class__),
1247 'pk_identity': self.patient['pk_identity']
1248 }
1249 gmDispatcher.send(**kwargs)
1250
1251
1252
1254 if attribute == 'patient':
1255 raise AttributeError
1256 if not isinstance(self.patient, gmNull.cNull):
1257 return getattr(self.patient, attribute)
1258
1259
1260
1262 """Return any attribute if known how to retrieve it by proxy.
1263 """
1264 return self.patient[attribute]
1265
1268
1270 """UI independant i18n aware patient searcher."""
1272 self._generate_queries = self._generate_queries_de
1273
1274 self.conn = gmPG2.get_connection()
1275 self.curs = self.conn.cursor()
1276
1278 try:
1279 self.curs.close()
1280 except: pass
1281 try:
1282 self.conn.close()
1283 except: pass
1284
1285
1286
1287 - def get_patients(self, search_term = None, a_locale = None, dto = None):
1288 identities = self.get_identities(search_term, a_locale, dto)
1289 if identities is None:
1290 return None
1291 return [cPatient(aPK_obj=ident['pk_identity']) for ident in identities]
1292
1293 - def get_identities(self, search_term = None, a_locale = None, dto = None):
1294 """Get patient identity objects for given parameters.
1295
1296 - either search term or search dict
1297 - dto contains structured data that doesn't need to be parsed (cDTO_person)
1298 - dto takes precedence over search_term
1299 """
1300 parse_search_term = (dto is None)
1301
1302 if not parse_search_term:
1303 queries = self._generate_queries_from_dto(dto)
1304 if queries is None:
1305 parse_search_term = True
1306 if len(queries) == 0:
1307 parse_search_term = True
1308
1309 if parse_search_term:
1310
1311 if a_locale is not None:
1312 print "temporary change of locale on patient search not implemented"
1313 _log.warning("temporary change of locale on patient search not implemented")
1314
1315 if search_term is None:
1316 raise ValueError('need search term (dto AND search_term are None)')
1317
1318 queries = self._generate_queries(search_term)
1319
1320
1321 if len(queries) == 0:
1322 _log.error('query tree empty')
1323 _log.error('[%s] [%s] [%s]' % (search_term, a_locale, str(dto)))
1324 return None
1325
1326
1327 identities = []
1328
1329 for query in queries:
1330 _log.debug("running %s" % query)
1331 try:
1332 rows, idx = gmPG2.run_ro_queries(queries = [query], get_col_idx=True)
1333 except:
1334 _log.exception('error running query')
1335 continue
1336 if len(rows) == 0:
1337 continue
1338 identities.extend (
1339 [ cIdentity(row = {'pk_field': 'pk_identity', 'data': row, 'idx': idx}) for row in rows ]
1340 )
1341
1342 pks = []
1343 unique_identities = []
1344 for identity in identities:
1345 if identity['pk_identity'] in pks:
1346 continue
1347 pks.append(identity['pk_identity'])
1348 unique_identities.append(identity)
1349
1350 return unique_identities
1351
1352
1353
1355 """Transform some characters into a regex."""
1356 if aString.strip() == u'':
1357 return aString
1358
1359
1360 normalized = aString.replace(u'Ä', u'(Ä|AE|Ae|A|E)')
1361 normalized = normalized.replace(u'Ö', u'(Ö|OE|Oe|O)')
1362 normalized = normalized.replace(u'Ü', u'(Ü|UE|Ue|U)')
1363 normalized = normalized.replace(u'ä', u'(ä|ae|e|a)')
1364 normalized = normalized.replace(u'ö', u'(ö|oe|o)')
1365 normalized = normalized.replace(u'ü', u'(ü|ue|u|y)')
1366 normalized = normalized.replace(u'ß', u'(ß|sz|ss|s)')
1367
1368
1369
1370 normalized = normalized.replace(u'é', u'***DUMMY***')
1371 normalized = normalized.replace(u'è', u'***DUMMY***')
1372 normalized = normalized.replace(u'***DUMMY***', u'(é|e|è|ä|ae)')
1373
1374
1375 normalized = normalized.replace(u'v', u'***DUMMY***')
1376 normalized = normalized.replace(u'f', u'***DUMMY***')
1377 normalized = normalized.replace(u'ph', u'***DUMMY***')
1378 normalized = normalized.replace(u'***DUMMY***', u'(v|f|ph)')
1379
1380
1381 normalized = normalized.replace(u'Th',u'***DUMMY***')
1382 normalized = normalized.replace(u'T', u'***DUMMY***')
1383 normalized = normalized.replace(u'***DUMMY***', u'(Th|T)')
1384 normalized = normalized.replace(u'th', u'***DUMMY***')
1385 normalized = normalized.replace(u't', u'***DUMMY***')
1386 normalized = normalized.replace(u'***DUMMY***', u'(th|t)')
1387
1388
1389 normalized = normalized.replace(u'"', u'***DUMMY***')
1390 normalized = normalized.replace(u"'", u'***DUMMY***')
1391 normalized = normalized.replace(u'`', u'***DUMMY***')
1392 normalized = normalized.replace(u'***DUMMY***', u"""("|'|`|***DUMMY***|\s)*""")
1393 normalized = normalized.replace(u'-', u"""(-|\s)*""")
1394 normalized = normalized.replace(u'|***DUMMY***|', u'|-|')
1395
1396 if aggressive:
1397 pass
1398
1399
1400 _log.debug('[%s] -> [%s]' % (aString, normalized))
1401
1402 return normalized
1403
1404
1405
1406
1407
1408
1409
1411 """Compose queries if search term seems unambigous."""
1412 queries = []
1413
1414
1415 if regex.match(u"^(\s|\t)*\d+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
1416 _log.debug("[%s]: a PK or DOB" % raw)
1417 tmp = raw.strip()
1418 queries.append ({
1419 'cmd': u"select *, %s::text as match_type FROM dem.v_basic_person WHERE pk_identity = %s order by lastnames, firstnames, dob",
1420 'args': [_('internal patient ID'), tmp]
1421 })
1422 queries.append ({
1423 'cmd': u"SELECT *, %s::text as match_type FROM dem.v_basic_person WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) order by lastnames, firstnames, dob",
1424 'args': [_('date of birth'), tmp.replace(',', '.')]
1425 })
1426 queries.append ({
1427 'cmd': u"""
1428 select vba.*, %s::text as match_type from dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
1429 where vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
1430 order by lastnames, firstnames, dob""",
1431 'args': [_('external patient ID'), tmp]
1432 })
1433 return queries
1434
1435
1436 if regex.match(u"^(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
1437 _log.debug("[%s]: a DOB or PK" % raw)
1438 queries.append ({
1439 'cmd': u"SELECT *, %s::text as match_type FROM dem.v_basic_person WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) order by lastnames, firstnames, dob",
1440 'args': [_('date of birth'), raw.replace(',', '.')]
1441 })
1442 tmp = raw.replace(u' ', u'')
1443 tmp = tmp.replace(u'\t', u'')
1444 queries.append ({
1445 'cmd': u"SELECT *, %s::text as match_type from dem.v_basic_person WHERE pk_identity LIKE %s%%",
1446 'args': [_('internal patient ID'), tmp]
1447 })
1448 return queries
1449
1450
1451 if regex.match(u"^(\s|\t)*#(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
1452 _log.debug("[%s]: a PK or external ID" % raw)
1453 tmp = raw.replace(u'#', u'')
1454 tmp = tmp.strip()
1455 tmp = tmp.replace(u' ', u'')
1456 tmp = tmp.replace(u'\t', u'')
1457
1458 queries.append ({
1459 'cmd': u"SELECT *, %s::text as match_type from dem.v_basic_person WHERE pk_identity = %s order by lastnames, firstnames, dob",
1460 'args': [_('internal patient ID'), tmp]
1461 })
1462
1463 tmp = raw.replace(u'#', u'')
1464 tmp = tmp.strip()
1465 tmp = tmp.replace(u' ', u'***DUMMY***')
1466 tmp = tmp.replace(u'\t', u'***DUMMY***')
1467 tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
1468 queries.append ({
1469 'cmd': u"""
1470 select vba.*, %s::text as match_type from dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
1471 where vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
1472 order by lastnames, firstnames, dob""",
1473 'args': [_('external patient ID'), tmp]
1474 })
1475 return queries
1476
1477
1478 if regex.match(u"^(\s|\t)*#.+$", raw, flags = regex.LOCALE | regex.UNICODE):
1479 _log.debug("[%s]: an external ID" % raw)
1480 tmp = raw.replace(u'#', u'')
1481 tmp = tmp.strip()
1482 tmp = tmp.replace(u' ', u'***DUMMY***')
1483 tmp = tmp.replace(u'\t', u'***DUMMY***')
1484 tmp = tmp.replace(u'-', u'***DUMMY***')
1485 tmp = tmp.replace(u'/', u'***DUMMY***')
1486 tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
1487 queries.append ({
1488 'cmd': u"""
1489 select vba.*, %s::text as match_type from dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
1490 where vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
1491 order by lastnames, firstnames, dob""",
1492 'args': [_('external patient ID'), tmp]
1493 })
1494 return queries
1495
1496
1497 if regex.match(u"^(\s|\t)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.)*$", raw, flags = regex.LOCALE | regex.UNICODE):
1498 _log.debug("[%s]: a DOB" % raw)
1499 tmp = raw.strip()
1500 while u'\t\t' in tmp: tmp = tmp.replace(u'\t\t', u' ')
1501 while u' ' in tmp: tmp = tmp.replace(u' ', u' ')
1502
1503
1504
1505 queries.append ({
1506 'cmd': u"SELECT *, %s as match_type from dem.v_basic_person WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) order by lastnames, firstnames, dob",
1507 'args': [_('date of birth'), tmp.replace(',', '.')]
1508 })
1509 return queries
1510
1511
1512 if regex.match(u"^(\s|\t)*,(\s|\t)*([^0-9])+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
1513 _log.debug("[%s]: a firstname" % raw)
1514 tmp = self._normalize_soundalikes(raw[1:].strip())
1515 cmd = u"""
1516 SELECT DISTINCT ON (pk_identity) * from (
1517 select *, %s as match_type from ((
1518 select vbp.*
1519 FROM dem.names, dem.v_basic_person vbp
1520 WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
1521 ) union all (
1522 select vbp.*
1523 FROM dem.names, dem.v_basic_person vbp
1524 WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
1525 )) as super_list order by lastnames, firstnames, dob
1526 ) as sorted_list"""
1527 queries.append ({
1528 'cmd': cmd,
1529 'args': [_('first name'), '^' + gmTools.capitalize(tmp, mode=gmTools.CAPS_NAMES), '^' + tmp]
1530 })
1531 return queries
1532
1533
1534 if regex.match(u"^(\s|\t)*(\*|\$).+$", raw, flags = regex.LOCALE | regex.UNICODE):
1535 _log.debug("[%s]: a DOB" % raw)
1536 tmp = raw.replace(u'*', u'')
1537 tmp = tmp.replace(u'$', u'')
1538 queries.append ({
1539 'cmd': u"SELECT *, %s as match_type from dem.v_basic_person WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) order by lastnames, firstnames, dob",
1540 'args': [_('date of birth'), tmp.replace(u',', u'.')]
1541 })
1542 return queries
1543
1544 return queries
1545
1546
1547
1549 """Generate generic queries.
1550
1551 - not locale dependant
1552 - data -> firstnames, lastnames, dob, gender
1553 """
1554 _log.debug(u'_generate_queries_from_dto("%s")' % dto)
1555
1556 if not isinstance(dto, cDTO_person):
1557 return None
1558
1559 vals = [_('name, gender, date of birth')]
1560 where_snippets = []
1561
1562 vals.append(dto.firstnames)
1563 where_snippets.append(u'firstnames=%s')
1564 vals.append(dto.lastnames)
1565 where_snippets.append(u'lastnames=%s')
1566
1567 if dto.dob is not None:
1568 vals.append(dto.dob)
1569
1570 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s)")
1571
1572 if dto.gender is not None:
1573 vals.append(dto.gender)
1574 where_snippets.append('gender=%s')
1575
1576
1577 if len(where_snippets) == 0:
1578 _log.error('invalid search dict structure')
1579 _log.debug(data)
1580 return None
1581
1582 cmd = u"""
1583 select *, %%s as match_type from dem.v_basic_person
1584 where pk_identity in (
1585 select id_identity from dem.names where %s
1586 ) order by lastnames, firstnames, dob""" % ' and '.join(where_snippets)
1587
1588 queries = [
1589 {'cmd': cmd, 'args': vals}
1590 ]
1591
1592
1593
1594 return queries
1595
1596
1597
1599
1600 if search_term is None:
1601 return []
1602
1603
1604 queries = self._generate_simple_query(search_term)
1605 if len(queries) > 0:
1606 return queries
1607
1608 _log.debug('[%s]: not a search term with a "suggestive" structure' % search_term)
1609
1610
1611 queries = []
1612
1613
1614 normalized = self._normalize_soundalikes(search_term)
1615
1616
1617
1618 if regex.match(u"^(\s|\t)*[a-zäöüßéáúóçøA-ZÄÖÜÇØ]+(\s|\t)*$", search_term, flags = regex.LOCALE | regex.UNICODE):
1619
1620 cmd = u"""
1621 SELECT DISTINCT ON (pk_identity) * from (
1622 select * from ((
1623 select vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.lastnames) ~* lower(%s)
1624 ) union all (
1625 -- first name
1626 select vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s)
1627 ) union all (
1628 -- anywhere in name
1629 select vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames || n.lastnames || coalesce(n.preferred, '')) ~* lower(%s)
1630 )) as super_list order by lastnames, firstnames, dob
1631 ) as sorted_list"""
1632 tmp = normalized.strip()
1633 args = []
1634 args.append(_('last name'))
1635 args.append('^' + tmp)
1636 args.append(_('first name'))
1637 args.append('^' + tmp)
1638 args.append(_('any name part'))
1639 args.append(tmp)
1640
1641 queries.append ({
1642 'cmd': cmd,
1643 'args': args
1644 })
1645 return queries
1646
1647
1648 parts_list = regex.split(u",|;", normalized)
1649
1650
1651 if len(parts_list) == 1:
1652
1653 sub_parts_list = regex.split(u"\s*|\t*", normalized)
1654
1655
1656 date_count = 0
1657 name_parts = []
1658 for part in sub_parts_list:
1659
1660
1661 if regex.search(u"\d", part, flags = regex.LOCALE | regex.UNICODE):
1662 date_count = date_count + 1
1663 date_part = part
1664 else:
1665 name_parts.append(part)
1666
1667
1668 if len(sub_parts_list) == 2:
1669
1670 if date_count == 0:
1671
1672 queries.append ({
1673 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
1674 'args': [_('name: first-last'), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES)]
1675 })
1676 queries.append ({
1677 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
1678 'args': [_('name: first-last'), '^' + name_parts[0], '^' + name_parts[1]]
1679 })
1680
1681 queries.append ({
1682 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
1683 'args': [_('name: last-first'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
1684 })
1685 queries.append ({
1686 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
1687 'args': [_('name: last-first'), '^' + name_parts[1], '^' + name_parts[0]]
1688 })
1689
1690 queries.append ({
1691 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames || n.lastnames) ~* lower(%s) AND lower(n.firstnames || n.lastnames) ~* lower(%s)",
1692 'args': [_('name'), name_parts[0], name_parts[1]]
1693 })
1694 return queries
1695
1696 _log.error("don't know how to generate queries for [%s]" % search_term)
1697 return queries
1698
1699
1700 if len(sub_parts_list) == 3:
1701
1702 if date_count == 1:
1703
1704 queries.append ({
1705 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1706 'args': [_('names: first-last, date of birth'), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), date_part.replace(u',', u'.')]
1707 })
1708 queries.append ({
1709 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s) AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1710 'args': [_('names: first-last, date of birth'), '^' + name_parts[0], '^' + name_parts[1], date_part.replace(u',', u'.')]
1711 })
1712
1713 queries.append ({
1714 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1715 'args': [_('names: last-first, date of birth'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), date_part.replace(u',', u'.')]
1716 })
1717 queries.append ({
1718 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s) AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1719 'args': [_('names: last-first, dob'), '^' + name_parts[1], '^' + name_parts[0], date_part.replace(u',', u'.')]
1720 })
1721
1722 queries.append ({
1723 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text as match_type from dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames || n.lastnames) ~* lower(%s) AND lower(n.firstnames || n.lastnames) ~* lower(%s) AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1724 'args': [_('name, date of birth'), name_parts[0], name_parts[1], date_part.replace(u',', u'.')]
1725 })
1726 return queries
1727
1728 queries.append(self._generate_dumb_brute_query(search_term))
1729 return queries
1730
1731
1732 queries.append(self._generate_dumb_brute_query(search_term))
1733 return queries
1734
1735
1736 else:
1737
1738 date_parts = []
1739 name_parts = []
1740 name_count = 0
1741 for part in parts_list:
1742
1743 if regex.search(u"\d+", part, flags = regex.LOCALE | regex.UNICODE):
1744
1745 date_parts.append(part)
1746 else:
1747 tmp = part.strip()
1748 tmp = regex.split(u"\s*|\t*", tmp)
1749 name_count = name_count + len(tmp)
1750 name_parts.append(tmp)
1751
1752 where_parts = []
1753
1754
1755 if (len(name_parts) == 1) and (name_count == 2):
1756
1757 where_parts.append ({
1758 'conditions': u"firstnames ~ %s and lastnames ~ %s",
1759 'args': [_('names: first last'), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES)]
1760 })
1761 where_parts.append ({
1762 'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
1763 'args': [_('names: first last'), '^' + name_parts[0][0], '^' + name_parts[0][1]]
1764 })
1765
1766 where_parts.append ({
1767 'conditions': u"firstnames ~ %s and lastnames ~ %s",
1768 'args': [_('names: last, first'), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES)]
1769 })
1770 where_parts.append ({
1771 'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
1772 'args': [_('names: last, first'), '^' + name_parts[0][1], '^' + name_parts[0][0]]
1773 })
1774
1775 where_parts.append ({
1776 'conditions': u"lower(firstnames || lastnames) ~* lower(%s) OR lower(firstnames || lastnames) ~* lower(%s)",
1777 'args': [_('name'), name_parts[0][0], name_parts[0][1]]
1778 })
1779
1780
1781 elif len(name_parts) == 2:
1782
1783 where_parts.append ({
1784 'conditions': u"firstnames ~ %s AND lastnames ~ %s",
1785 'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
1786 })
1787 where_parts.append ({
1788 'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
1789 'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
1790 })
1791
1792 where_parts.append ({
1793 'conditions': u"firstnames ~ %s AND lastnames ~ %s",
1794 'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[0])), '^' + ' '.join(map(gmTools.capitalize, name_parts[1]))]
1795 })
1796 where_parts.append ({
1797 'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
1798 'args': [_('name: last, first'), '^' + ' '.join(name_parts[0]), '^' + ' '.join(name_parts[1])]
1799 })
1800
1801 where_parts.append ({
1802 'conditions': u"lower(firstnames || lastnames) ~* lower(%s) AND lower(firstnames || lastnames) ~* lower(%s)",
1803 'args': [_('name'), ' '.join(name_parts[0]), ' '.join(name_parts[1])]
1804 })
1805
1806
1807 else:
1808
1809 if len(name_parts) == 1:
1810 for part in name_parts[0]:
1811 where_parts.append ({
1812 'conditions': u"lower(firstnames || lastnames) ~* lower(%s)",
1813 'args': [_('name'), part]
1814 })
1815 else:
1816 tmp = []
1817 for part in name_parts:
1818 tmp.append(' '.join(part))
1819 for part in tmp:
1820 where_parts.append ({
1821 'conditions': u"lower(firstnames || lastnames) ~* lower(%s)",
1822 'args': [_('name'), part]
1823 })
1824
1825
1826
1827 if len(date_parts) == 1:
1828 if len(where_parts) == 0:
1829 where_parts.append ({
1830 'conditions': u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1831 'args': [_('date of birth'), date_parts[0].replace(u',', u'.')]
1832 })
1833 if len(where_parts) > 0:
1834 where_parts[0]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
1835 where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'))
1836 where_parts[0]['args'][0] += u', ' + _('date of birth')
1837 if len(where_parts) > 1:
1838 where_parts[1]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
1839 where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'))
1840 where_parts[1]['args'][0] += u', ' + _('date of birth')
1841 elif len(date_parts) > 1:
1842 if len(where_parts) == 0:
1843 where_parts.append ({
1844 'conditions': u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp witih time zone) AND dem.date_trunc_utc('day'::text, dem.identity.deceased) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1845 'args': [_('date of birth/death'), date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.')]
1846 })
1847 if len(where_parts) > 0:
1848 where_parts[0]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) AND dem.date_trunc_utc('day'::text, dem.identity.deceased) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1849 where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
1850 where_parts[0]['args'][0] += u', ' + _('date of birth/death')
1851 if len(where_parts) > 1:
1852 where_parts[1]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) AND dem.date_trunc_utc('day'::text, dem.identity.deceased) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1853 where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
1854 where_parts[1]['args'][0] += u', ' + _('date of birth/death')
1855
1856
1857 for where_part in where_parts:
1858 queries.append ({
1859 'cmd': u"select *, %%s::text as match_type from dem.v_basic_person where %s" % where_part['conditions'],
1860 'args': where_part['args']
1861 })
1862 return queries
1863
1864 return []
1865
1867
1868 _log.debug('_generate_dumb_brute_query("%s")' % search_term)
1869
1870 where_clause = ''
1871 args = []
1872
1873 for arg in search_term.strip().split():
1874 where_clause += u' and lower(vbp.title || vbp.firstnames || vbp.lastnames) ~* lower(%s)'
1875 args.append(arg)
1876
1877 query = u"""
1878 select distinct on (pk_identity) * from (
1879 select
1880 vbp.*, '%s'::text as match_type
1881 from
1882 dem.v_basic_person vbp,
1883 dem.names n
1884 where
1885 vbp.pk_identity = n.id_identity
1886 %s
1887 order by
1888 lastnames,
1889 firstnames,
1890 dob
1891 ) as ordered_list""" % (_('full name'), where_clause)
1892
1893 return ({'cmd': query, 'args': args})
1894
1895
1896
1899 gmMatchProvider.cMatchProvider_SQL2.__init__(
1900 self,
1901 queries = [
1902 u"""select
1903 pk_staff,
1904 short_alias || ' (' || coalesce(title, '') || firstnames || ' ' || lastnames || ')',
1905 1
1906 from dem.v_staff
1907 where
1908 is_active and (
1909 short_alias %(fragment_condition)s or
1910 firstnames %(fragment_condition)s or
1911 lastnames %(fragment_condition)s or
1912 db_user %(fragment_condition)s
1913 )"""
1914 ]
1915 )
1916 self.setThresholds(1, 2, 3)
1917
1918
1919
1920 -def create_name(pk_person, firstnames, lastnames, active=False):
1921 queries = [{
1922 'cmd': u"select dem.add_name(%s, %s, %s, %s)",
1923 'args': [pk_person, firstnames, lastnames, active]
1924 }]
1925 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True)
1926 name = cPersonName(aPK_obj = rows[0][0])
1927 return name
1928
1929 -def create_identity(gender=None, dob=None, lastnames=None, firstnames=None):
1930
1931 cmd1 = u"""insert into dem.identity (gender, dob) values (%s, %s)"""
1932
1933 cmd2 = u"""
1934 insert into dem.names (
1935 id_identity, lastnames, firstnames
1936 ) values (
1937 currval('dem.identity_pk_seq'), coalesce(%s, 'xxxDEFAULTxxx'), coalesce(%s, 'xxxDEFAULTxxx')
1938 )"""
1939
1940 rows, idx = gmPG2.run_rw_queries (
1941 queries = [
1942 {'cmd': cmd1, 'args': [gender, dob]},
1943 {'cmd': cmd2, 'args': [lastnames, firstnames]},
1944 {'cmd': u"select currval('dem.identity_pk_seq')"}
1945 ],
1946 return_data = True
1947 )
1948 return cIdentity(aPK_obj=rows[0][0])
1949
1951 cmd1 = u"insert into dem.identity(gender) values('xxxDEFAULTxxx')"
1952 cmd2 = u"select currval('dem.identity_pk_seq')"
1953
1954 rows, idx = gmPG2.run_rw_queries (
1955 queries = [
1956 {'cmd': cmd1},
1957 {'cmd': cmd2}
1958 ],
1959 return_data = True
1960 )
1961 return gmDemographicRecord.cIdentity(aPK_obj = rows[0][0])
1962
1964 """Set active patient.
1965
1966 If patient is -1 the active patient will be UNset.
1967 """
1968 if isinstance(patient, cPatient):
1969 pat = patient
1970 elif isinstance(patient, cIdentity):
1971 pat = cPatient(aPK_obj=patient['pk_identity'])
1972 elif isinstance(patient, cStaff):
1973 pat = cPatient(aPK_obj=patient['pk_identity'])
1974 elif patient == -1:
1975 pat = patient
1976 else:
1977 raise ValueError('<patient> must be either -1, cPatient, cStaff or cIdentity instance, is: %s' % patient)
1978
1979
1980 try:
1981 gmCurrentPatient(patient = pat, forced_reload = forced_reload)
1982 except:
1983 _log.exception('error changing active patient to [%s]' % patient)
1984 return False
1985
1986 return True
1987
2002
2004 """Text mode UI function to ask for patient."""
2005
2006 person_searcher = cPatientSearcher_SQL()
2007
2008 while True:
2009 search_fragment = prompted_input("\nEnter person search term or leave blank to exit")
2010
2011 if search_fragment in ['exit', 'quit', 'bye', None]:
2012 print "user cancelled patient search"
2013 return None
2014
2015 pats = person_searcher.get_patients(search_term = search_fragment)
2016
2017 if (pats is None) or (len(pats) == 0):
2018 print "No patient matches the query term."
2019 print ""
2020 continue
2021
2022 if len(pats) > 1:
2023 print "Several patients match the query term:"
2024 print ""
2025 for pat in pats:
2026 print pat
2027 print ""
2028 continue
2029
2030 return pats[0]
2031
2032 return None
2033
2034
2035
2036
2037 map_gender2symbol = {
2038 'm': u'\u2642',
2039 'f': u'\u2640',
2040 'tf': u'\u26A5\u2640',
2041 'tm': u'\u26A5\u2642',
2042 'h': u'\u26A5'
2043
2044
2045
2046 }
2047
2055
2076
2078 """Try getting the gender for the given first name."""
2079
2080 if firstnames is None:
2081 return None
2082
2083 rows, idx = gmPG2.run_ro_queries(queries = [{
2084 'cmd': u"select gender from dem.name_gender_map where name ilike %(fn)s limit 1",
2085 'args': {'fn': firstnames}
2086 }])
2087
2088 if len(rows) == 0:
2089 return None
2090
2091 return rows[0][0]
2092
2094 if active_only:
2095 cmd = u"select * from dem.v_staff where is_active order by can_login desc, short_alias asc"
2096 else:
2097 cmd = u"select * from dem.v_staff order by can_login desc, is_active desc, short_alias asc"
2098 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
2099 staff_list = []
2100 for row in rows:
2101 obj_row = {
2102 'idx': idx,
2103 'data': row,
2104 'pk_field': 'pk_staff'
2105 }
2106 staff_list.append(cStaff(row=obj_row))
2107 return staff_list
2108
2110 return [ cIdentity(aPK_obj = pk) for pk in pks ]
2111
2115
2119
2120
2121
2122 if __name__ == '__main__':
2123
2124 import datetime
2125
2126 gmI18N.activate_locale()
2127 gmI18N.install_domain()
2128 gmDateTime.init()
2129
2130
2151
2153 dto = cDTO_person()
2154 dto.firstnames = 'Sepp'
2155 dto.lastnames = 'Herberger'
2156 dto.gender = 'male'
2157 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
2158 print dto
2159
2160 print dto['firstnames']
2161 print dto['lastnames']
2162 print dto['gender']
2163 print dto['dob']
2164
2165 for key in dto.keys():
2166 print key
2167
2180
2181
2187
2188
2201
2203
2204 print '\n\nCreating identity...'
2205 new_identity = create_identity(gender='m', dob='2005-01-01', lastnames='test lastnames', firstnames='test firstnames')
2206 print 'Identity created: %s' % new_identity
2207
2208 print '\nSetting title and gender...'
2209 new_identity['title'] = 'test title';
2210 new_identity['gender'] = 'f';
2211 new_identity.save_payload()
2212 print 'Refetching identity from db: %s' % cIdentity(aPK_obj=new_identity['pk_identity'])
2213
2214 print '\nGetting all names...'
2215 for a_name in new_identity.get_names():
2216 print a_name
2217 print 'Active name: %s' % (new_identity.get_active_name())
2218 print 'Setting nickname...'
2219 new_identity.set_nickname(nickname='test nickname')
2220 print 'Refetching all names...'
2221 for a_name in new_identity.get_names():
2222 print a_name
2223 print 'Active name: %s' % (new_identity.get_active_name())
2224
2225 print '\nIdentity occupations: %s' % new_identity['occupations']
2226 print 'Creating identity occupation...'
2227 new_identity.link_occupation('test occupation')
2228 print 'Identity occupations: %s' % new_identity['occupations']
2229
2230 print '\nIdentity addresses: %s' % new_identity.get_addresses()
2231 print 'Creating identity address...'
2232
2233 new_identity.link_address (
2234 number = 'test 1234',
2235 street = 'test street',
2236 postcode = 'test postcode',
2237 urb = 'test urb',
2238 state = 'SN',
2239 country = 'DE'
2240 )
2241 print 'Identity addresses: %s' % new_identity.get_addresses()
2242
2243 print '\nIdentity communications: %s' % new_identity.get_comm_channels()
2244 print 'Creating identity communication...'
2245 new_identity.link_comm_channel('homephone', '1234566')
2246 print 'Identity communications: %s' % new_identity.get_comm_channels()
2247
2249 searcher = cPatientSearcher_SQL()
2250
2251 print "testing _normalize_soundalikes()"
2252 print "--------------------------------"
2253
2254 data = [u'Krüger', u'Krueger', u'Kruger', u'Überle', u'Böger', u'Boger', u'Öder', u'Ähler', u'Däler', u'Großer', u'müller', u'Özdemir', u'özdemir']
2255 for name in data:
2256 print '%s: %s' % (name, searcher._normalize_soundalikes(name))
2257
2258 raw_input('press [ENTER] to continue')
2259 print "============"
2260
2261 print "testing _generate_simple_query()"
2262 print "----------------------------"
2263 data = ['51234', '1 134 153', '#13 41 34', '#3-AFY322.4', '22-04-1906', '1235/32/3525', ' , johnny']
2264 for fragment in data:
2265 print "fragment:", fragment
2266 qs = searcher._generate_simple_query(fragment)
2267 for q in qs:
2268 print " match on:", q['args'][0]
2269 print " query :", q['cmd']
2270 raw_input('press [ENTER] to continue')
2271 print "============"
2272
2273 print "testing _generate_queries_from_dto()"
2274 print "------------------------------------"
2275 dto = cDTO_person()
2276 dto.gender = 'm'
2277 dto.lastnames = 'Kirk'
2278 dto.firstnames = 'James'
2279 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
2280 q = searcher._generate_queries_from_dto(dto)[0]
2281 print "dto:", dto
2282 print " match on:", q['args'][0]
2283 print " query:", q['cmd']
2284
2285 raw_input('press [ENTER] to continue')
2286 print "============"
2287
2288 print "testing _generate_queries_de()"
2289 print "------------------------------"
2290 qs = searcher._generate_queries_de('Kirk, James')
2291 for q in qs:
2292 print " match on:", q['args'][0]
2293 print " query :", q['cmd']
2294 print " args :", q['args']
2295 raw_input('press [ENTER] to continue')
2296 print "============"
2297
2298 qs = searcher._generate_queries_de(u'müller')
2299 for q in qs:
2300 print " match on:", q['args'][0]
2301 print " query :", q['cmd']
2302 print " args :", q['args']
2303 raw_input('press [ENTER] to continue')
2304 print "============"
2305
2306 qs = searcher._generate_queries_de(u'özdemir')
2307 for q in qs:
2308 print " match on:", q['args'][0]
2309 print " query :", q['cmd']
2310 print " args :", q['args']
2311 raw_input('press [ENTER] to continue')
2312 print "============"
2313
2314 qs = searcher._generate_queries_de(u'Özdemir')
2315 for q in qs:
2316 print " match on:", q['args'][0]
2317 print " query :", q['cmd']
2318 print " args :", q['args']
2319 raw_input('press [ENTER] to continue')
2320 print "============"
2321
2322 print "testing _generate_dumb_brute_query()"
2323 print "------------------------------------"
2324 q = searcher._generate_dumb_brute_query('Kirk, James Tiberius')
2325 print " match on:", q['args'][0]
2326 print " query:", q['cmd']
2327 print " args:", q['args']
2328
2329 raw_input('press [ENTER] to continue')
2330
2341
2342
2343
2344
2345
2351
2352 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2353
2354
2355
2356
2357
2358
2359
2360 test_current_provider()
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453