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
22 from Gnumed.pycommon import gmPG2, gmMatchProvider, gmDateTime, gmLog2
23 from Gnumed.business import gmDocuments, gmDemographicRecord, gmProviderInbox, gmXdtMappings, gmClinicalRecord
24
25
26 _log = logging.getLogger('gm.person')
27 _log.info(__version__)
28
29 __gender_list = None
30 __gender_idx = None
31
32 __gender2salutation_map = None
33
34
36
37
38
39
40
41
43 return 'firstnames lastnames dob gender'.split()
44
47
49 """Generate generic queries.
50
51 - not locale dependant
52 - data -> firstnames, lastnames, dob, gender
53
54 shall we mogrify name parts ? probably not as external
55 sources should know what they do
56
57 finds by inactive name, too, but then shows
58 the corresponding active name ;-)
59
60 Returns list of matching identities (may be empty)
61 or None if it was told to create an identity but couldn't.
62 """
63 where_snippets = []
64 args = {}
65
66 where_snippets.append(u'firstnames = %(first)s')
67 args['first'] = self.firstnames
68
69 where_snippets.append(u'lastnames = %(last)s')
70 args['last'] = self.lastnames
71
72 if self.dob is not None:
73 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %(dob)s)")
74 args['dob'] = self.dob
75
76 if self.gender is not None:
77 where_snippets.append('gender = %(sex)s')
78 args['sex'] = self.gender
79
80 cmd = u"""
81 select *, '%s' as match_type from dem.v_basic_person
82 where pk_identity in (
83 select id_identity from dem.names where %s
84 ) order by lastnames, firstnames, dob""" % (
85 _('external patient source (name, gender, date of birth)'),
86 ' and '.join(where_snippets)
87 )
88
89 try:
90 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx=True)
91 except:
92 _log.error(u'cannot get candidate identities for dto "%s"' % self)
93 _log.exception('query %s' % cmd)
94 rows = []
95
96 if len(rows) == 0:
97 if not can_create:
98 return []
99 ident = self.import_into_database()
100 if ident is None:
101 return None
102 identities = [ident]
103 else:
104 identities = [ cIdentity(row = {'pk_field': 'pk_identity', 'data': row, 'idx': idx}) for row in rows ]
105
106 return identities
107
109 """Imports self into the database.
110
111 Child classes can override this to provide more extensive import.
112 """
113 ident = create_identity (
114 firstnames = self.firstnames,
115 lastnames = self.lastnames,
116 gender = self.gender,
117 dob = self.dob
118 )
119 return ident
120
123
124
125
127 return u'<%s @ %s: %s %s (%s) %s>' % (
128 self.__class__.__name__,
129 id(self),
130 self.firstnames,
131 self.lastnames,
132 self.gender,
133 self.dob
134 )
135
137 """Do some sanity checks on self.* access."""
138
139 if attr == 'gender':
140 glist, idx = get_gender_list()
141 for gender in glist:
142 if str(val) in [gender[0], gender[1], gender[2], gender[3]]:
143 val = gender[idx['tag']]
144 object.__setattr__(self, attr, val)
145 return
146 raise ValueError('invalid gender: [%s]' % val)
147
148 if attr == 'dob':
149 if val is not None:
150 if not isinstance(val, pyDT.datetime):
151 raise TypeError('invalid type for DOB (must be datetime.datetime): %s [%s]' % (type(val), val))
152 if val.tzinfo is None:
153 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % val.isoformat())
154
155 object.__setattr__(self, attr, val)
156 return
157
159 return getattr(self, attr)
160
161 -class cPersonName(gmBusinessDBObject.cBusinessDBObject):
162 _cmd_fetch_payload = u"select * from dem.v_person_names where pk_name = %s"
163 _cmds_store_payload = [
164 u"""update dem.names set
165 active = False
166 where
167 %(active_name)s is True and -- act only when needed and only
168 id_identity = %(pk_identity)s and -- on names of this identity
169 active is True and -- which are active
170 id != %(pk_name)s -- but NOT *this* name
171 """,
172 u"""update dem.names set
173 active = %(active_name)s,
174 preferred = %(preferred)s,
175 comment = %(comment)s
176 where
177 id = %(pk_name)s and
178 id_identity = %(pk_identity)s and -- belt and suspenders
179 xmin = %(xmin_name)s""",
180 u"""select xmin as xmin_name from dem.names where id = %(pk_name)s"""
181 ]
182 _updatable_fields = ['active_name', 'preferred', 'comment']
183
192
194 return '%(last)s, %(title)s %(first)s%(nick)s' % {
195 'last': self._payload[self._idx['lastnames']],
196 'title': gmTools.coalesce (
197 self._payload[self._idx['title']],
198 map_gender2salutation(self._payload[self._idx['gender']])
199 ),
200 'first': self._payload[self._idx['firstnames']],
201 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u' "%s"', u'%s')
202 }
203
204 description = property(_get_description, lambda x:x)
205
206 -class cStaff(gmBusinessDBObject.cBusinessDBObject):
207 _cmd_fetch_payload = u"select * from dem.v_staff where pk_staff=%s"
208 _cmds_store_payload = [
209 u"""update dem.staff set
210 fk_role = %(pk_role)s,
211 short_alias = %(short_alias)s,
212 comment = gm.nullify_empty_string(%(comment)s),
213 is_active = %(is_active)s,
214 db_user = %(db_user)s
215 where
216 pk=%(pk_staff)s and
217 xmin = %(xmin_staff)s""",
218 u"""select xmin_staff from dem.v_staff where pk_identity=%(pk_identity)s"""
219 ]
220 _updatable_fields = ['pk_role', 'short_alias', 'comment', 'is_active', 'db_user']
221
222 - def __init__(self, aPK_obj=None, row=None):
223
224 if (aPK_obj is None) and (row is None):
225 cmd = u"select * from dem.v_staff where db_user = CURRENT_USER"
226 try:
227 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
228 except:
229 _log.exception('cannot instantiate staff instance')
230 gmLog2.log_stack_trace()
231 raise ValueError('cannot instantiate staff instance for database account CURRENT_USER')
232 if len(rows) == 0:
233 raise ValueError('no staff record for database account CURRENT_USER')
234 row = {
235 'pk_field': 'pk_staff',
236 'idx': idx,
237 'data': rows[0]
238 }
239 gmBusinessDBObject.cBusinessDBObject.__init__(self, row = row)
240 else:
241 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj = aPK_obj, row = row)
242
243
244 self.__is_current_user = (gmPG2.get_current_user() == self._payload[self._idx['db_user']])
245
246 self.__inbox = None
247
254
256 rows, idx = gmPG2.run_ro_queries (
257 queries = [{
258 'cmd': u'select i18n.get_curr_lang(%(usr)s)',
259 'args': {'usr': self._payload[self._idx['db_user']]}
260 }]
261 )
262 return rows[0][0]
263
265 if not gmPG2.set_user_language(language = language):
266 raise ValueError (
267 u'Cannot set database language to [%s] for user [%s].' % (language, self._payload[self._idx['db_user']])
268 )
269 return
270
271 database_language = property(_get_db_lang, _set_db_lang)
272
274 if self.__inbox is None:
275 self.__inbox = gmProviderInbox.cProviderInbox(provider_id = self._payload[self._idx['pk_staff']])
276 return self.__inbox
277
280
281 inbox = property(_get_inbox, _set_inbox)
282
285
287 """Staff member Borg to hold currently logged on provider.
288
289 There may be many instances of this but they all share state.
290 """
292 """Change or get currently logged on provider.
293
294 provider:
295 * None: get copy of current instance
296 * cStaff instance: change logged on provider (role)
297 """
298
299 try:
300 self.provider
301 except AttributeError:
302 self.provider = gmNull.cNull()
303
304
305 if provider is None:
306 return None
307
308
309 if not isinstance(provider, cStaff):
310 raise ValueError, 'cannot set logged on provider to [%s], must be either None or cStaff instance' % str(provider)
311
312
313 if self.provider['pk_staff'] == provider['pk_staff']:
314 return None
315
316
317 if isinstance(self.provider, gmNull.cNull):
318 self.provider = provider
319 return None
320
321
322 raise ValueError, 'provider change [%s] -> [%s] not yet supported' % (self.provider['pk_staff'], provider['pk_staff'])
323
324
327
328
329
331 """Return any attribute if known how to retrieve it by proxy.
332 """
333 return self.provider[aVar]
334
335
336
338 if attribute == 'provider':
339 raise AttributeError
340 if not isinstance(self.provider, gmNull.cNull):
341 return getattr(self.provider, attribute)
342
343
344 -class cIdentity(gmBusinessDBObject.cBusinessDBObject):
345 _cmd_fetch_payload = u"select * from dem.v_basic_person where pk_identity = %s"
346 _cmds_store_payload = [
347 u"""update dem.identity set
348 gender = %(gender)s,
349 dob = %(dob)s,
350 tob = %(tob)s,
351 cob = gm.nullify_empty_string(%(cob)s),
352 title = gm.nullify_empty_string(%(title)s),
353 fk_marital_status = %(pk_marital_status)s,
354 karyotype = gm.nullify_empty_string(%(karyotype)s),
355 pupic = gm.nullify_empty_string(%(pupic)s),
356 deceased = %(deceased)s
357 where
358 pk = %(pk_identity)s and
359 xmin = %(xmin_identity)s""",
360 u"""select xmin_identity from dem.v_basic_person where pk_identity = %(pk_identity)s"""
361 ]
362 _updatable_fields = ["title", "dob", "tob", "cob", "gender", "pk_marital_status", "karyotype", "pupic", 'deceased']
363
365 return self._payload[self._idx['pk_identity']]
367 raise AttributeError('setting ID of identity is not allowed')
368 ID = property(_get_ID, _set_ID)
369
371
372 if attribute == 'dob':
373 if value is not None:
374
375 if isinstance(value, pyDT.datetime):
376 if value.tzinfo is None:
377 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % dt.isoformat())
378 else:
379 raise TypeError, '[%s]: type [%s] (%s) invalid for attribute [dob], must be datetime.datetime or None' % (self.__class__.__name__, type(value), value)
380
381
382 if self._payload[self._idx['dob']] is not None:
383 old_dob = self._payload[self._idx['dob']].strftime('%Y %m %d %H %M %S')
384 new_dob = value.strftime('%Y %m %d %H %M %S')
385 if new_dob == old_dob:
386 return
387
388 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
389
392
394 cmd = u"""
395 select exists (
396 select 1
397 from clin.v_emr_journal
398 where
399 pk_patient = %(pat)s
400 and
401 soap_cat is not null
402 )"""
403 args = {'pat': self._payload[self._idx['pk_identity']]}
404 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
405 return rows[0][0]
406
408 raise AttributeError('setting is_patient status of identity is not allowed')
409
410 is_patient = property(_get_is_patient, _set_is_patient)
411
412
413
415 for name in self.get_names():
416 if name['active_name'] is True:
417 return name
418
419 _log.error('cannot retrieve active name for patient [%s]' % self._payload[self._idx['pk_identity']])
420 return None
421
423 cmd = u"select * from dem.v_person_names where pk_identity = %(pk_pat)s"
424 rows, idx = gmPG2.run_ro_queries (
425 queries = [{
426 'cmd': cmd,
427 'args': {'pk_pat': self._payload[self._idx['pk_identity']]}
428 }],
429 get_col_idx = True
430 )
431
432 if len(rows) == 0:
433
434 return []
435
436 names = [ cPersonName(row = {'idx': idx, 'data': r, 'pk_field': 'pk_name'}) for r in rows ]
437 return names
438
447
449 return '%(sex)s%(title)s %(last)s, %(first)s%(nick)s' % {
450 'last': self._payload[self._idx['lastnames']],
451 'first': self._payload[self._idx['firstnames']],
452 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u' (%s)', u'%s'),
453 'sex': map_gender2salutation(self._payload[self._idx['gender']]),
454 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s', u'%s')
455 }
456
458 return '%(last)s,%(title)s %(first)s%(nick)s' % {
459 'last': self._payload[self._idx['lastnames']],
460 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s', u'%s'),
461 'first': self._payload[self._idx['firstnames']],
462 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u' (%s)', u'%s')
463 }
464
465 - def add_name(self, firstnames, lastnames, active=True):
466 """Add a name.
467
468 @param firstnames The first names.
469 @param lastnames The last names.
470 @param active When True, the new name will become the active one (hence setting other names to inactive)
471 @type active A types.BooleanType instance
472 """
473 name = create_name(self.ID, firstnames, lastnames, active)
474 if active:
475 self.refetch_payload()
476 return name
477
479 cmd = u"delete from dem.names where id = %(name)s and id_identity = %(pat)s"
480 args = {'name': name['pk_name'], 'pat': self.ID}
481 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
482
483
484
485
487 """
488 Set the nickname. Setting the nickname only makes sense for the currently
489 active name.
490 @param nickname The preferred/nick/warrior name to set.
491 """
492 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': u"select dem.set_nickname(%s, %s)", 'args': [self.ID, nickname]}])
493 self.refetch_payload()
494 return True
495
496
497
498
499
500
501
502
503
504
505 - def add_external_id(self, type_name=None, value=None, issuer=None, comment=None, context=u'p', pk_type=None):
506 """Adds an external ID to the patient.
507
508 creates ID type if necessary
509 context hardcoded to 'p' for now
510 """
511
512
513 if pk_type is not None:
514 cmd = u"""
515 select * from dem.v_external_ids4identity where
516 pk_identity = %(pat)s and
517 pk_type = %(pk_type)s and
518 value = %(val)s"""
519 else:
520
521 if issuer is None:
522 cmd = u"""
523 select * from dem.v_external_ids4identity where
524 pk_identity = %(pat)s and
525 name = %(name)s and
526 value = %(val)s"""
527 else:
528 cmd = u"""
529 select * from dem.v_external_ids4identity where
530 pk_identity = %(pat)s and
531 name = %(name)s and
532 value = %(val)s and
533 issuer = %(issuer)s"""
534 args = {
535 'pat': self.ID,
536 'name': type_name,
537 'val': value,
538 'issuer': issuer,
539 'pk_type': pk_type
540 }
541 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
542
543
544 if len(rows) == 0:
545
546 args = {
547 'pat': self.ID,
548 'val': value,
549 'type_name': type_name,
550 'pk_type': pk_type,
551 'issuer': issuer,
552 'ctxt': context,
553 'comment': comment
554 }
555
556 if pk_type is None:
557 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
558 %(val)s,
559 (select dem.add_external_id_type(%(type_name)s, %(issuer)s, %(ctxt)s)),
560 %(comment)s,
561 %(pat)s
562 )"""
563 else:
564 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
565 %(val)s,
566 %(pk_type)s,
567 %(comment)s,
568 %(pat)s
569 )"""
570
571 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
572
573
574 else:
575 row = rows[0]
576 if comment is not None:
577
578 if gmTools.coalesce(row['comment'], '').find(comment.strip()) == -1:
579 comment = '%s%s' % (gmTools.coalesce(row['comment'], '', '%s // '), comment.strip)
580 cmd = u"update dem.lnk_identity2ext_id set comment = %(comment)s where id=%(pk)s"
581 args = {'comment': comment, 'pk': row['pk_id']}
582 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
583
584 - def update_external_id(self, pk_id=None, type=None, value=None, issuer=None, comment=None):
585 """Edits an existing external ID.
586
587 creates ID type if necessary
588 context hardcoded to 'p' for now
589 """
590 cmd = u"""
591 update dem.lnk_identity2ext_id set
592 fk_origin = (select dem.add_external_id_type(%(type)s, %(issuer)s, %(ctxt)s)),
593 external_id = %(value)s,
594 comment = %(comment)s
595 where id = %(pk)s"""
596 args = {'pk': pk_id, 'ctxt': u'p', 'value': value, 'type': type, 'issuer': issuer, 'comment': comment}
597 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
598
600 where_parts = ['pk_identity = %(pat)s']
601 args = {'pat': self.ID}
602
603 if id_type is not None:
604 where_parts.append(u'name = %(name)s')
605 args['name'] = id_type.strip()
606
607 if issuer is not None:
608 where_parts.append(u'issuer = %(issuer)s')
609 args['issuer'] = issuer.strip()
610
611 if context is not None:
612 where_parts.append(u'context = %(ctxt)s')
613 args['ctxt'] = context.strip()
614
615 cmd = u"select * from dem.v_external_ids4identity where %s" % ' and '.join(where_parts)
616 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
617
618 return rows
619
621 cmd = u"""
622 delete from dem.lnk_identity2ext_id
623 where id_identity = %(pat)s and id = %(pk)s"""
624 args = {'pat': self.ID, 'pk': pk_ext_id}
625 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
626
628 """Merge another identity into this one.
629
630 Keep this one. Delete other one."""
631
632 if other_identity.ID == self.ID:
633 return True, None
634
635 curr_pat = gmCurrentPatient()
636 if curr_pat.connected:
637 if other_identity.ID == curr_pat.ID:
638 return False, _('Cannot merge active patient into another patient.')
639
640 queries = []
641 args = {'old_pat': other_identity.ID, 'new_pat': self.ID}
642
643
644 queries.append ({
645 '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)',
646 'args': args
647 })
648
649
650
651 queries.append ({
652 'cmd': u'update dem.names set active = False where id_identity = %(old_pat)s',
653 'args': args
654 })
655
656
657 FKs = gmPG2.get_foreign_keys2column (
658 schema = u'dem',
659 table = u'identity',
660 column = u'pk'
661 )
662
663
664 cmd_template = u'update %s set %s = %%(new_pat)s where %s = %%(old_pat)s'
665 for FK in FKs:
666 queries.append ({
667 'cmd': cmd_template % (FK['referencing_table'], FK['referencing_column'], FK['referencing_column']),
668 'args': args
669 })
670
671
672 queries.append ({
673 'cmd': u'delete from dem.identity where pk = %(old_pat)s',
674 'args': args
675 })
676
677 _log.warning('identity [%s] is about to assimilate identity [%s]', self.ID, other_identity.ID)
678
679 gmPG2.run_rw_queries(link_obj = link_obj, queries = queries, end_tx = True)
680
681 self.add_external_id (
682 type_name = u'merged GNUmed identity primary key',
683 value = u'GNUmed::pk::%s' % other_identity.ID,
684 issuer = u'GNUmed'
685 )
686
687 return True, None
688
689
691 cmd = u"""
692 insert into clin.waiting_list (fk_patient, urgency, comment, area, list_position)
693 values (
694 %(pat)s,
695 %(urg)s,
696 %(cmt)s,
697 %(area)s,
698 (select coalesce((max(list_position) + 1), 1) from clin.waiting_list)
699 )"""
700 args = {'pat': self.ID, 'urg': urgency, 'cmt': comment, 'area': zone}
701 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], verbose=True)
702
703 - def export_as_gdt(self, filename=None, encoding='iso-8859-15', external_id_type=None):
704
705 template = u'%s%s%s\r\n'
706
707 file = codecs.open (
708 filename = filename,
709 mode = 'wb',
710 encoding = encoding,
711 errors = 'strict'
712 )
713
714 file.write(template % (u'013', u'8000', u'6301'))
715 file.write(template % (u'013', u'9218', u'2.10'))
716 if external_id_type is None:
717 file.write(template % (u'%03d' % (9 + len(str(self.ID))), u'3000', self.ID))
718 else:
719 ext_ids = self.get_external_ids(id_type = external_id_type)
720 if len(ext_ids) > 0:
721 file.write(template % (u'%03d' % (9 + len(ext_ids[0]['value'])), u'3000', ext_ids[0]['value']))
722 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['lastnames']])), u'3101', self._payload[self._idx['lastnames']]))
723 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['firstnames']])), u'3102', self._payload[self._idx['firstnames']]))
724 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')))
725 file.write(template % (u'010', u'3110', gmXdtMappings.map_gender_gm2xdt[self._payload[self._idx['gender']]]))
726 file.write(template % (u'025', u'6330', 'GNUmed::9206::encoding'))
727 file.write(template % (u'%03d' % (9 + len(encoding)), u'6331', encoding))
728 if external_id_type is None:
729 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
730 file.write(template % (u'017', u'6333', u'internal'))
731 else:
732 if len(ext_ids) > 0:
733 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
734 file.write(template % (u'%03d' % (9 + len(external_id_type)), u'6333', external_id_type))
735
736 file.close()
737
738
739
741 cmd = u"select * from dem.v_person_jobs where pk_identity=%s"
742 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
743 return rows
744
746 """Link an occupation with a patient, creating the occupation if it does not exists.
747
748 @param occupation The name of the occupation to link the patient to.
749 """
750 if (activities is None) and (occupation is None):
751 return True
752
753 occupation = occupation.strip()
754 if len(occupation) == 0:
755 return True
756
757 if activities is not None:
758 activities = activities.strip()
759
760 args = {'act': activities, 'pat_id': self.pk_obj, 'job': occupation}
761
762 cmd = u"select activities from dem.v_person_jobs where pk_identity = %(pat_id)s and l10n_occupation = _(%(job)s)"
763 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
764
765 queries = []
766 if len(rows) == 0:
767 queries.append ({
768 'cmd': u"INSERT INTO dem.lnk_job2person (fk_identity, fk_occupation, activities) VALUES (%(pat_id)s, dem.create_occupation(%(job)s), %(act)s)",
769 'args': args
770 })
771 else:
772 if rows[0]['activities'] != activities:
773 queries.append ({
774 '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))",
775 'args': args
776 })
777
778 rows, idx = gmPG2.run_rw_queries(queries = queries)
779
780 return True
781
783 if occupation is None:
784 return True
785 occupation = occupation.strip()
786 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))"
787 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj, 'job': occupation}}])
788 return True
789
790
791
793 cmd = u"select * from dem.v_person_comms where pk_identity = %s"
794 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True)
795
796 filtered = rows
797
798 if comm_medium is not None:
799 filtered = []
800 for row in rows:
801 if row['comm_type'] == comm_medium:
802 filtered.append(row)
803
804 return [ gmDemographicRecord.cCommChannel(row = {
805 'pk_field': 'pk_lnk_identity2comm',
806 'data': r,
807 'idx': idx
808 }) for r in filtered
809 ]
810
811 - def link_comm_channel(self, comm_medium=None, url=None, is_confidential=False, pk_channel_type=None):
812 """Link a communication medium with a patient.
813
814 @param comm_medium The name of the communication medium.
815 @param url The communication resource locator.
816 @type url A types.StringType instance.
817 @param is_confidential Wether the data must be treated as confidential.
818 @type is_confidential A types.BooleanType instance.
819 """
820 comm_channel = gmDemographicRecord.create_comm_channel (
821 comm_medium = comm_medium,
822 url = url,
823 is_confidential = is_confidential,
824 pk_channel_type = pk_channel_type,
825 pk_identity = self.pk_obj
826 )
827 return comm_channel
828
834
835
836
838 cmd = u"select * from dem.v_pat_addresses where pk_identity=%s"
839 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx=True)
840 addresses = []
841 for r in rows:
842 addresses.append(gmDemographicRecord.cPatientAddress(row={'idx': idx, 'data': r, 'pk_field': 'pk_address'}))
843
844 filtered = addresses
845
846 if address_type is not None:
847 filtered = []
848 for adr in addresses:
849 if adr['address_type'] == address_type:
850 filtered.append(adr)
851
852 return filtered
853
854 - def link_address(self, number=None, street=None, postcode=None, urb=None, state=None, country=None, subunit=None, suburb=None, id_type=None):
855 """Link an address with a patient, creating the address if it does not exists.
856
857 @param number The number of the address.
858 @param street The name of the street.
859 @param postcode The postal code of the address.
860 @param urb The name of town/city/etc.
861 @param state The code of the state.
862 @param country The code of the country.
863 @param id_type The primary key of the address type.
864 """
865
866 adr = gmDemographicRecord.create_address (
867 country = country,
868 state = state,
869 urb = urb,
870 suburb = suburb,
871 postcode = postcode,
872 street = street,
873 number = number,
874 subunit = subunit
875 )
876
877
878 cmd = u"select * from dem.lnk_person_org_address where id_identity = %s and id_address = %s"
879 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj, adr['pk_address']]}])
880
881 if len(rows) == 0:
882 args = {'id': self.pk_obj, 'adr': adr['pk_address'], 'type': id_type}
883 if id_type is None:
884 cmd = u"""
885 insert into dem.lnk_person_org_address(id_identity, id_address)
886 values (%(id)s, %(adr)s)"""
887 else:
888 cmd = u"""
889 insert into dem.lnk_person_org_address(id_identity, id_address, id_type)
890 values (%(id)s, %(adr)s, %(type)s)"""
891 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
892 else:
893
894 if id_type is not None:
895 r = rows[0]
896 if r['id_type'] != id_type:
897 cmd = "update dem.lnk_person_org_address set id_type = %(type)s where id = %(id)s"
898 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'type': id_type, 'id': r['id']}}])
899
900 return adr
901
903 """Remove an address from the patient.
904
905 The address itself stays in the database.
906 The address can be either cAdress or cPatientAdress.
907 """
908 cmd = u"delete from dem.lnk_person_org_address where id_identity = %(person)s and id_address = %(adr)s"
909 args = {'person': self.pk_obj, 'adr': address['pk_address']}
910 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
911
912
913
915 cmd = u"""
916 select
917 t.description,
918 vbp.pk_identity as id,
919 title,
920 firstnames,
921 lastnames,
922 dob,
923 cob,
924 gender,
925 karyotype,
926 pupic,
927 pk_marital_status,
928 marital_status,+
929 xmin_identity,
930 preferred
931 from
932 dem.v_basic_person vbp, dem.relation_types t, dem.lnk_person2relative l
933 where
934 (
935 l.id_identity = %(pk)s and
936 vbp.pk_identity = l.id_relative and
937 t.id = l.id_relation_type
938 ) or (
939 l.id_relative = %(pk)s and
940 vbp.pk_identity = l.id_identity and
941 t.inverse = l.id_relation_type
942 )"""
943 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}])
944 if len(rows) == 0:
945 return []
946 return [(row[0], cIdentity(row = {'data': row[1:], 'idx':idx, 'pk_field': 'pk'})) for row in rows]
947
949
950 id_new_relative = create_dummy_identity()
951
952 relative = cIdentity(aPK_obj=id_new_relative)
953
954
955 relative.add_name( '**?**', self.get_names()['lastnames'])
956
957 if self._ext_cache.has_key('relatives'):
958 del self._ext_cache['relatives']
959 cmd = u"""
960 insert into dem.lnk_person2relative (
961 id_identity, id_relative, id_relation_type
962 ) values (
963 %s, %s, (select id from dem.relation_types where description = %s)
964 )"""
965 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.ID, id_new_relative, rel_type ]}])
966 return True
967
969
970 self.set_relative(None, relation)
971
972
973
990
991 - def dob_in_range(self, min_distance=u'1 week', max_distance=u'1 week'):
992 cmd = u'select dem.dob_is_in_range(%(dob)s, %(min)s, %(max)s)'
993 rows, idx = gmPG2.run_ro_queries (
994 queries = [{
995 'cmd': cmd,
996 'args': {'dob': self['dob'], 'min': min_distance, 'max': max_distance}
997 }]
998 )
999 return rows[0][0]
1000
1001
1002
1004 cmd = u'select * from clin.v_most_recent_encounters where pk_patient=%s'
1005 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['pk_identity']]]}])
1006 if len(rows) > 0:
1007 return rows[0]
1008 else:
1009 return None
1010
1013
1016
1017 messages = property(_get_messages, _set_messages)
1018
1021
1022
1023
1025 """Format patient demographics into patient specific path name fragment."""
1026 return '%s-%s%s-%s' % (
1027 self._payload[self._idx['lastnames']].replace(u' ', u'_'),
1028 self._payload[self._idx['firstnames']].replace(u' ', u'_'),
1029 gmTools.coalesce(self._payload[self._idx['preferred']], u'', template_initial = u'-(%s)'),
1030 self.get_formatted_dob(format = '%Y-%m-%d', encoding = gmI18N.get_encoding())
1031 )
1032
1034 """Represents a staff member which is a person.
1035
1036 - a specializing subclass of cIdentity turning it into a staff member
1037 """
1041
1044
1046 """Represents a person which is a patient.
1047
1048 - a specializing subclass of cIdentity turning it into a patient
1049 - its use is to cache subobjects like EMR and document folder
1050 """
1051 - def __init__(self, aPK_obj=None, row=None):
1052 cIdentity.__init__(self, aPK_obj=aPK_obj, row=row)
1053 self.__db_cache = {}
1054 self.__emr_access_lock = threading.Lock()
1055
1057 """Do cleanups before dying.
1058
1059 - note that this may be called in a thread
1060 """
1061 if self.__db_cache.has_key('clinical record'):
1062 self.__db_cache['clinical record'].cleanup()
1063 if self.__db_cache.has_key('document folder'):
1064 self.__db_cache['document folder'].cleanup()
1065 cIdentity.cleanup(self)
1066
1068 if not self.__emr_access_lock.acquire(False):
1069 raise AttributeError('cannot access EMR')
1070 try:
1071 emr = self.__db_cache['clinical record']
1072 self.__emr_access_lock.release()
1073 return emr
1074 except KeyError:
1075 pass
1076
1077 self.__db_cache['clinical record'] = gmClinicalRecord.cClinicalRecord(aPKey = self._payload[self._idx['pk_identity']])
1078 self.__emr_access_lock.release()
1079 return self.__db_cache['clinical record']
1080
1082 try:
1083 return self.__db_cache['document folder']
1084 except KeyError:
1085 pass
1086
1087 self.__db_cache['document folder'] = gmDocuments.cDocumentFolder(aPKey = self._payload[self._idx['pk_identity']])
1088 return self.__db_cache['document folder']
1089
1091 """Patient Borg to hold currently active patient.
1092
1093 There may be many instances of this but they all share state.
1094 """
1095 - def __init__(self, patient=None, forced_reload=False):
1096 """Change or get currently active patient.
1097
1098 patient:
1099 * None: get currently active patient
1100 * -1: unset currently active patient
1101 * cPatient instance: set active patient if possible
1102 """
1103
1104 try:
1105 tmp = self.patient
1106 except AttributeError:
1107 self.patient = gmNull.cNull()
1108 self.__register_interests()
1109
1110
1111
1112 self.__lock_depth = 0
1113
1114 self.__pre_selection_callbacks = []
1115
1116
1117 if patient is None:
1118 return None
1119
1120
1121 if self.locked:
1122 _log.error('patient [%s] is locked, cannot change to [%s]' % (self.patient['pk_identity'], patient))
1123 return None
1124
1125
1126 if patient == -1:
1127 _log.debug('explicitly unsetting current patient')
1128 if not self.__run_pre_selection_callbacks():
1129 _log.debug('not unsetting current patient')
1130 return None
1131 self.__send_pre_selection_notification()
1132 self.patient.cleanup()
1133 self.patient = gmNull.cNull()
1134 self.__send_selection_notification()
1135 return None
1136
1137
1138 if not isinstance(patient, cPatient):
1139 _log.error('cannot set active patient to [%s], must be either None, -1 or cPatient instance' % str(patient))
1140 raise TypeError, 'gmPerson.gmCurrentPatient.__init__(): <patient> must be None, -1 or cPatient instance but is: %s' % str(patient)
1141
1142
1143 if (self.patient['pk_identity'] == patient['pk_identity']) and not forced_reload:
1144 return None
1145
1146
1147 _log.debug('patient change [%s] -> [%s] requested', self.patient['pk_identity'], patient['pk_identity'])
1148
1149
1150 if not self.__run_pre_selection_callbacks():
1151 _log.debug('not changing current patient')
1152 return None
1153 self.__send_pre_selection_notification()
1154 self.patient.cleanup()
1155 self.patient = patient
1156 self.patient.get_emr()
1157 self.__send_selection_notification()
1158
1159 return None
1160
1164
1168
1169
1170
1172 if not callable(callback):
1173 raise TypeError(u'callback [%s] not callable' % callback)
1174
1175 self.__pre_selection_callbacks.append(callback)
1176
1179
1181 raise AttributeError(u'invalid to set <connected> state')
1182
1183 connected = property(_get_connected, _set_connected)
1184
1186 return (self.__lock_depth > 0)
1187
1189 if locked:
1190 self.__lock_depth = self.__lock_depth + 1
1191 gmDispatcher.send(signal='patient_locked')
1192 else:
1193 if self.__lock_depth == 0:
1194 _log.error('lock/unlock imbalance, trying to refcount lock depth below 0')
1195 return
1196 else:
1197 self.__lock_depth = self.__lock_depth - 1
1198 gmDispatcher.send(signal='patient_unlocked')
1199
1200 locked = property(_get_locked, _set_locked)
1201
1203 _log.info('forced patient unlock at lock depth [%s]' % self.__lock_depth)
1204 self.__lock_depth = 0
1205 gmDispatcher.send(signal='patient_unlocked')
1206
1207
1208
1210 if isinstance(self.patient, gmNull.cNull):
1211 return True
1212
1213 for call_back in self.__pre_selection_callbacks:
1214 try:
1215 successful = call_back()
1216 except:
1217 _log.exception('callback [%s] failed', call_back)
1218 print "*** pre-selection callback failed ***"
1219 print type(call_back)
1220 print call_back
1221 return False
1222
1223 if not successful:
1224 _log.debug('callback [%s] returned False', call_back)
1225 return False
1226
1227 return True
1228
1230 """Sends signal when another patient is about to become active.
1231
1232 This does NOT wait for signal handlers to complete.
1233 """
1234 kwargs = {
1235 'signal': u'pre_patient_selection',
1236 'sender': id(self.__class__),
1237 'pk_identity': self.patient['pk_identity']
1238 }
1239 gmDispatcher.send(**kwargs)
1240
1242 """Sends signal when another patient has actually been made active."""
1243 kwargs = {
1244 'signal': u'post_patient_selection',
1245 'sender': id(self.__class__),
1246 'pk_identity': self.patient['pk_identity']
1247 }
1248 gmDispatcher.send(**kwargs)
1249
1250
1251
1253 if attribute == 'patient':
1254 raise AttributeError
1255 if not isinstance(self.patient, gmNull.cNull):
1256 return getattr(self.patient, attribute)
1257
1258
1259
1261 """Return any attribute if known how to retrieve it by proxy.
1262 """
1263 return self.patient[attribute]
1264
1267
1269 """UI independant i18n aware patient searcher."""
1271 self._generate_queries = self._generate_queries_de
1272
1273 self.conn = gmPG2.get_connection()
1274 self.curs = self.conn.cursor()
1275
1277 try:
1278 self.curs.close()
1279 except: pass
1280 try:
1281 self.conn.close()
1282 except: pass
1283
1284
1285
1286 - def get_patients(self, search_term = None, a_locale = None, dto = None):
1287 identities = self.get_identities(search_term, a_locale, dto)
1288 if identities is None:
1289 return None
1290 return [cPatient(aPK_obj=ident['pk_identity']) for ident in identities]
1291
1292 - def get_identities(self, search_term = None, a_locale = None, dto = None):
1293 """Get patient identity objects for given parameters.
1294
1295 - either search term or search dict
1296 - dto contains structured data that doesn't need to be parsed (cDTO_person)
1297 - dto takes precedence over search_term
1298 """
1299 parse_search_term = (dto is None)
1300
1301 if not parse_search_term:
1302 queries = self._generate_queries_from_dto(dto)
1303 if queries is None:
1304 parse_search_term = True
1305 if len(queries) == 0:
1306 parse_search_term = True
1307
1308 if parse_search_term:
1309
1310 if a_locale is not None:
1311 print "temporary change of locale on patient search not implemented"
1312 _log.warning("temporary change of locale on patient search not implemented")
1313
1314 if search_term is None:
1315 raise ValueError('need search term (dto AND search_term are None)')
1316
1317 queries = self._generate_queries(search_term)
1318
1319
1320 if len(queries) == 0:
1321 _log.error('query tree empty')
1322 _log.error('[%s] [%s] [%s]' % (search_term, a_locale, str(dto)))
1323 return None
1324
1325
1326 identities = []
1327
1328 for query in queries:
1329 _log.debug("running %s" % query)
1330 try:
1331 rows, idx = gmPG2.run_ro_queries(queries = [query], get_col_idx=True)
1332 except:
1333 _log.exception('error running query')
1334 continue
1335 if len(rows) == 0:
1336 continue
1337 identities.extend (
1338 [ cIdentity(row = {'pk_field': 'pk_identity', 'data': row, 'idx': idx}) for row in rows ]
1339 )
1340
1341 pks = []
1342 unique_identities = []
1343 for identity in identities:
1344 if identity['pk_identity'] in pks:
1345 continue
1346 pks.append(identity['pk_identity'])
1347 unique_identities.append(identity)
1348
1349 return unique_identities
1350
1351
1352
1354 """Transform some characters into a regex."""
1355 if aString.strip() == u'':
1356 return aString
1357
1358
1359 normalized = aString.replace(u'Ä', u'(Ä|AE|Ae|A|E)')
1360 normalized = normalized.replace(u'Ö', u'(Ö|OE|Oe|O)')
1361 normalized = normalized.replace(u'Ü', u'(Ü|UE|Ue|U)')
1362 normalized = normalized.replace(u'ä', u'(ä|ae|e|a)')
1363 normalized = normalized.replace(u'ö', u'(ö|oe|o)')
1364 normalized = normalized.replace(u'ü', u'(ü|ue|u|y)')
1365 normalized = normalized.replace(u'ß', u'(ß|sz|ss|s)')
1366
1367
1368
1369 normalized = normalized.replace(u'é', u'***DUMMY***')
1370 normalized = normalized.replace(u'è', u'***DUMMY***')
1371 normalized = normalized.replace(u'***DUMMY***', u'(é|e|è|ä|ae)')
1372
1373
1374 normalized = normalized.replace(u'v', u'***DUMMY***')
1375 normalized = normalized.replace(u'f', u'***DUMMY***')
1376 normalized = normalized.replace(u'ph', u'***DUMMY***')
1377 normalized = normalized.replace(u'***DUMMY***', u'(v|f|ph)')
1378
1379
1380 normalized = normalized.replace(u'Th',u'***DUMMY***')
1381 normalized = normalized.replace(u'T', u'***DUMMY***')
1382 normalized = normalized.replace(u'***DUMMY***', u'(Th|T)')
1383 normalized = normalized.replace(u'th', u'***DUMMY***')
1384 normalized = normalized.replace(u't', u'***DUMMY***')
1385 normalized = normalized.replace(u'***DUMMY***', u'(th|t)')
1386
1387
1388 normalized = normalized.replace(u'"', u'***DUMMY***')
1389 normalized = normalized.replace(u"'", u'***DUMMY***')
1390 normalized = normalized.replace(u'`', u'***DUMMY***')
1391 normalized = normalized.replace(u'***DUMMY***', u"""("|'|`|***DUMMY***|\s)*""")
1392 normalized = normalized.replace(u'-', u"""(-|\s)*""")
1393 normalized = normalized.replace(u'|***DUMMY***|', u'|-|')
1394
1395 if aggressive:
1396 pass
1397
1398
1399 _log.debug('[%s] -> [%s]' % (aString, normalized))
1400
1401 return normalized
1402
1403
1404
1405
1406
1407
1408
1410 """Compose queries if search term seems unambigous."""
1411 queries = []
1412
1413
1414 if regex.match(u"^(\s|\t)*\d+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
1415 _log.debug("[%s]: a PK or DOB" % raw)
1416 tmp = raw.strip()
1417 queries.append ({
1418 'cmd': u"select *, %s::text as match_type FROM dem.v_basic_person WHERE pk_identity = %s order by lastnames, firstnames, dob",
1419 'args': [_('internal patient ID'), tmp]
1420 })
1421 queries.append ({
1422 '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",
1423 'args': [_('date of birth'), tmp.replace(',', '.')]
1424 })
1425 queries.append ({
1426 'cmd': u"""
1427 select vba.*, %s::text as match_type from dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
1428 where vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
1429 order by lastnames, firstnames, dob""",
1430 'args': [_('external patient ID'), tmp]
1431 })
1432 return queries
1433
1434
1435 if regex.match(u"^(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
1436 _log.debug("[%s]: a DOB or PK" % raw)
1437 queries.append ({
1438 '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",
1439 'args': [_('date of birth'), raw.replace(',', '.')]
1440 })
1441 tmp = raw.replace(u' ', u'')
1442 tmp = tmp.replace(u'\t', u'')
1443 queries.append ({
1444 'cmd': u"SELECT *, %s::text as match_type from dem.v_basic_person WHERE pk_identity LIKE %s%%",
1445 'args': [_('internal patient ID'), tmp]
1446 })
1447 return queries
1448
1449
1450 if regex.match(u"^(\s|\t)*#(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
1451 _log.debug("[%s]: a PK or external ID" % raw)
1452 tmp = raw.replace(u'#', u'')
1453 tmp = tmp.strip()
1454 tmp = tmp.replace(u' ', u'')
1455 tmp = tmp.replace(u'\t', u'')
1456
1457 queries.append ({
1458 'cmd': u"SELECT *, %s::text as match_type from dem.v_basic_person WHERE pk_identity = %s order by lastnames, firstnames, dob",
1459 'args': [_('internal patient ID'), tmp]
1460 })
1461
1462 tmp = raw.replace(u'#', u'')
1463 tmp = tmp.strip()
1464 tmp = tmp.replace(u' ', u'***DUMMY***')
1465 tmp = tmp.replace(u'\t', u'***DUMMY***')
1466 tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
1467 queries.append ({
1468 'cmd': u"""
1469 select vba.*, %s::text as match_type from dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
1470 where vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
1471 order by lastnames, firstnames, dob""",
1472 'args': [_('external patient ID'), tmp]
1473 })
1474 return queries
1475
1476
1477 if regex.match(u"^(\s|\t)*#.+$", raw, flags = regex.LOCALE | regex.UNICODE):
1478 _log.debug("[%s]: an external ID" % raw)
1479 tmp = raw.replace(u'#', u'')
1480 tmp = tmp.strip()
1481 tmp = tmp.replace(u' ', u'***DUMMY***')
1482 tmp = tmp.replace(u'\t', u'***DUMMY***')
1483 tmp = tmp.replace(u'-', u'***DUMMY***')
1484 tmp = tmp.replace(u'/', u'***DUMMY***')
1485 tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
1486 queries.append ({
1487 'cmd': u"""
1488 select vba.*, %s::text as match_type from dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
1489 where vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
1490 order by lastnames, firstnames, dob""",
1491 'args': [_('external patient ID'), tmp]
1492 })
1493 return queries
1494
1495
1496 if regex.match(u"^(\s|\t)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.)*$", raw, flags = regex.LOCALE | regex.UNICODE):
1497 _log.debug("[%s]: a DOB" % raw)
1498 tmp = raw.strip()
1499 while u'\t\t' in tmp: tmp = tmp.replace(u'\t\t', u' ')
1500 while u' ' in tmp: tmp = tmp.replace(u' ', u' ')
1501
1502
1503
1504 queries.append ({
1505 '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",
1506 'args': [_('date of birth'), tmp.replace(',', '.')]
1507 })
1508 return queries
1509
1510
1511 if regex.match(u"^(\s|\t)*,(\s|\t)*([^0-9])+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
1512 _log.debug("[%s]: a firstname" % raw)
1513 tmp = self._normalize_soundalikes(raw[1:].strip())
1514 cmd = u"""
1515 SELECT DISTINCT ON (pk_identity) * from (
1516 select *, %s as match_type from ((
1517 select vbp.*
1518 FROM dem.names, dem.v_basic_person vbp
1519 WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
1520 ) union all (
1521 select vbp.*
1522 FROM dem.names, dem.v_basic_person vbp
1523 WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
1524 )) as super_list order by lastnames, firstnames, dob
1525 ) as sorted_list"""
1526 queries.append ({
1527 'cmd': cmd,
1528 'args': [_('first name'), '^' + gmTools.capitalize(tmp, mode=gmTools.CAPS_NAMES), '^' + tmp]
1529 })
1530 return queries
1531
1532
1533 if regex.match(u"^(\s|\t)*(\*|\$).+$", raw, flags = regex.LOCALE | regex.UNICODE):
1534 _log.debug("[%s]: a DOB" % raw)
1535 tmp = raw.replace(u'*', u'')
1536 tmp = tmp.replace(u'$', u'')
1537 queries.append ({
1538 '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",
1539 'args': [_('date of birth'), tmp.replace(u',', u'.')]
1540 })
1541 return queries
1542
1543 return queries
1544
1545
1546
1548 """Generate generic queries.
1549
1550 - not locale dependant
1551 - data -> firstnames, lastnames, dob, gender
1552 """
1553 _log.debug(u'_generate_queries_from_dto("%s")' % dto)
1554
1555 if not isinstance(dto, cDTO_person):
1556 return None
1557
1558 vals = [_('name, gender, date of birth')]
1559 where_snippets = []
1560
1561 vals.append(dto.firstnames)
1562 where_snippets.append(u'firstnames=%s')
1563 vals.append(dto.lastnames)
1564 where_snippets.append(u'lastnames=%s')
1565
1566 if dto.dob is not None:
1567 vals.append(dto.dob)
1568
1569 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s)")
1570
1571 if dto.gender is not None:
1572 vals.append(dto.gender)
1573 where_snippets.append('gender=%s')
1574
1575
1576 if len(where_snippets) == 0:
1577 _log.error('invalid search dict structure')
1578 _log.debug(data)
1579 return None
1580
1581 cmd = u"""
1582 select *, %%s as match_type from dem.v_basic_person
1583 where pk_identity in (
1584 select id_identity from dem.names where %s
1585 ) order by lastnames, firstnames, dob""" % ' and '.join(where_snippets)
1586
1587 queries = [
1588 {'cmd': cmd, 'args': vals}
1589 ]
1590
1591
1592
1593 return queries
1594
1595
1596
1598
1599 if search_term is None:
1600 return []
1601
1602
1603 queries = self._generate_simple_query(search_term)
1604 if len(queries) > 0:
1605 return queries
1606
1607 _log.debug('[%s]: not a search term with a "suggestive" structure' % search_term)
1608
1609
1610 queries = []
1611
1612
1613 normalized = self._normalize_soundalikes(search_term)
1614
1615
1616
1617 if regex.match(u"^(\s|\t)*[a-zäöüßéáúóçøA-ZÄÖÜÇØ]+(\s|\t)*$", search_term, flags = regex.LOCALE | regex.UNICODE):
1618
1619 cmd = u"""
1620 SELECT DISTINCT ON (pk_identity) * from (
1621 select * from ((
1622 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)
1623 ) union all (
1624 -- first name
1625 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)
1626 ) union all (
1627 -- anywhere in name
1628 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)
1629 )) as super_list order by lastnames, firstnames, dob
1630 ) as sorted_list"""
1631 tmp = normalized.strip()
1632 args = []
1633 args.append(_('last name'))
1634 args.append('^' + tmp)
1635 args.append(_('first name'))
1636 args.append('^' + tmp)
1637 args.append(_('any name part'))
1638 args.append(tmp)
1639
1640 queries.append ({
1641 'cmd': cmd,
1642 'args': args
1643 })
1644 return queries
1645
1646
1647 parts_list = regex.split(u",|;", normalized)
1648
1649
1650 if len(parts_list) == 1:
1651
1652 sub_parts_list = regex.split(u"\s*|\t*", normalized)
1653
1654
1655 date_count = 0
1656 name_parts = []
1657 for part in sub_parts_list:
1658
1659
1660 if regex.search(u"\d", part, flags = regex.LOCALE | regex.UNICODE):
1661 date_count = date_count + 1
1662 date_part = part
1663 else:
1664 name_parts.append(part)
1665
1666
1667 if len(sub_parts_list) == 2:
1668
1669 if date_count == 0:
1670
1671 queries.append ({
1672 '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",
1673 'args': [_('name: first-last'), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES)]
1674 })
1675 queries.append ({
1676 '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)",
1677 'args': [_('name: first-last'), '^' + name_parts[0], '^' + name_parts[1]]
1678 })
1679
1680 queries.append ({
1681 '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",
1682 'args': [_('name: last-first'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
1683 })
1684 queries.append ({
1685 '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)",
1686 'args': [_('name: last-first'), '^' + name_parts[1], '^' + name_parts[0]]
1687 })
1688
1689 queries.append ({
1690 '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)",
1691 'args': [_('name'), name_parts[0], name_parts[1]]
1692 })
1693 return queries
1694
1695 _log.error("don't know how to generate queries for [%s]" % search_term)
1696 return queries
1697
1698
1699 if len(sub_parts_list) == 3:
1700
1701 if date_count == 1:
1702
1703 queries.append ({
1704 '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)",
1705 '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'.')]
1706 })
1707 queries.append ({
1708 '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)",
1709 'args': [_('names: first-last, date of birth'), '^' + name_parts[0], '^' + name_parts[1], date_part.replace(u',', u'.')]
1710 })
1711
1712 queries.append ({
1713 '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)",
1714 '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'.')]
1715 })
1716 queries.append ({
1717 '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)",
1718 'args': [_('names: last-first, dob'), '^' + name_parts[1], '^' + name_parts[0], date_part.replace(u',', u'.')]
1719 })
1720
1721 queries.append ({
1722 '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)",
1723 'args': [_('name, date of birth'), name_parts[0], name_parts[1], date_part.replace(u',', u'.')]
1724 })
1725 return queries
1726
1727 queries.append(self._generate_dumb_brute_query(search_term))
1728 return queries
1729
1730
1731 queries.append(self._generate_dumb_brute_query(search_term))
1732 return queries
1733
1734
1735 else:
1736
1737 date_parts = []
1738 name_parts = []
1739 name_count = 0
1740 for part in parts_list:
1741
1742 if regex.search(u"\d+", part, flags = regex.LOCALE | regex.UNICODE):
1743
1744 date_parts.append(part)
1745 else:
1746 tmp = part.strip()
1747 tmp = regex.split(u"\s*|\t*", tmp)
1748 name_count = name_count + len(tmp)
1749 name_parts.append(tmp)
1750
1751 where_parts = []
1752
1753
1754 if (len(name_parts) == 1) and (name_count == 2):
1755
1756 where_parts.append ({
1757 'conditions': u"firstnames ~ %s and lastnames ~ %s",
1758 'args': [_('names: first last'), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES)]
1759 })
1760 where_parts.append ({
1761 'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
1762 'args': [_('names: first last'), '^' + name_parts[0][0], '^' + name_parts[0][1]]
1763 })
1764
1765 where_parts.append ({
1766 'conditions': u"firstnames ~ %s and lastnames ~ %s",
1767 'args': [_('names: last, first'), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES)]
1768 })
1769 where_parts.append ({
1770 'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
1771 'args': [_('names: last, first'), '^' + name_parts[0][1], '^' + name_parts[0][0]]
1772 })
1773
1774 where_parts.append ({
1775 'conditions': u"lower(firstnames || lastnames) ~* lower(%s) OR lower(firstnames || lastnames) ~* lower(%s)",
1776 'args': [_('name'), name_parts[0][0], name_parts[0][1]]
1777 })
1778
1779
1780 elif len(name_parts) == 2:
1781
1782 where_parts.append ({
1783 'conditions': u"firstnames ~ %s AND lastnames ~ %s",
1784 'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
1785 })
1786 where_parts.append ({
1787 'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
1788 'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
1789 })
1790
1791 where_parts.append ({
1792 'conditions': u"firstnames ~ %s AND lastnames ~ %s",
1793 'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[0])), '^' + ' '.join(map(gmTools.capitalize, name_parts[1]))]
1794 })
1795 where_parts.append ({
1796 'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
1797 'args': [_('name: last, first'), '^' + ' '.join(name_parts[0]), '^' + ' '.join(name_parts[1])]
1798 })
1799
1800 where_parts.append ({
1801 'conditions': u"lower(firstnames || lastnames) ~* lower(%s) AND lower(firstnames || lastnames) ~* lower(%s)",
1802 'args': [_('name'), ' '.join(name_parts[0]), ' '.join(name_parts[1])]
1803 })
1804
1805
1806 else:
1807
1808 if len(name_parts) == 1:
1809 for part in name_parts[0]:
1810 where_parts.append ({
1811 'conditions': u"lower(firstnames || lastnames) ~* lower(%s)",
1812 'args': [_('name'), part]
1813 })
1814 else:
1815 tmp = []
1816 for part in name_parts:
1817 tmp.append(' '.join(part))
1818 for part in tmp:
1819 where_parts.append ({
1820 'conditions': u"lower(firstnames || lastnames) ~* lower(%s)",
1821 'args': [_('name'), part]
1822 })
1823
1824
1825
1826 if len(date_parts) == 1:
1827 if len(where_parts) == 0:
1828 where_parts.append ({
1829 'conditions': u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
1830 'args': [_('date of birth'), date_parts[0].replace(u',', u'.')]
1831 })
1832 if len(where_parts) > 0:
1833 where_parts[0]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
1834 where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'))
1835 where_parts[0]['args'][0] += u', ' + _('date of birth')
1836 if len(where_parts) > 1:
1837 where_parts[1]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
1838 where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'))
1839 where_parts[1]['args'][0] += u', ' + _('date of birth')
1840 elif len(date_parts) > 1:
1841 if len(where_parts) == 0:
1842 where_parts.append ({
1843 '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)",
1844 'args': [_('date of birth/death'), date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.')]
1845 })
1846 if len(where_parts) > 0:
1847 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)",
1848 where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
1849 where_parts[0]['args'][0] += u', ' + _('date of birth/death')
1850 if len(where_parts) > 1:
1851 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)",
1852 where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
1853 where_parts[1]['args'][0] += u', ' + _('date of birth/death')
1854
1855
1856 for where_part in where_parts:
1857 queries.append ({
1858 'cmd': u"select *, %%s::text as match_type from dem.v_basic_person where %s" % where_part['conditions'],
1859 'args': where_part['args']
1860 })
1861 return queries
1862
1863 return []
1864
1866
1867 _log.debug('_generate_dumb_brute_query("%s")' % search_term)
1868
1869 where_clause = ''
1870 args = []
1871
1872 for arg in search_term.strip().split():
1873 where_clause += u' and lower(vbp.title || vbp.firstnames || vbp.lastnames) ~* lower(%s)'
1874 args.append(arg)
1875
1876 query = u"""
1877 select distinct on (pk_identity) * from (
1878 select
1879 vbp.*, '%s'::text as match_type
1880 from
1881 dem.v_basic_person vbp,
1882 dem.names n
1883 where
1884 vbp.pk_identity = n.id_identity
1885 %s
1886 order by
1887 lastnames,
1888 firstnames,
1889 dob
1890 ) as ordered_list""" % (_('full name'), where_clause)
1891
1892 return ({'cmd': query, 'args': args})
1893
1894
1895
1898 gmMatchProvider.cMatchProvider_SQL2.__init__(
1899 self,
1900 queries = [
1901 u"""select
1902 pk_staff,
1903 short_alias || ' (' || coalesce(title, '') || firstnames || ' ' || lastnames || ')',
1904 1
1905 from dem.v_staff
1906 where
1907 is_active and (
1908 short_alias %(fragment_condition)s or
1909 firstnames %(fragment_condition)s or
1910 lastnames %(fragment_condition)s or
1911 db_user %(fragment_condition)s
1912 )"""
1913 ]
1914 )
1915 self.setThresholds(1, 2, 3)
1916
1917
1918
1919 -def create_name(pk_person, firstnames, lastnames, active=False):
1920 queries = [{
1921 'cmd': u"select dem.add_name(%s, %s, %s, %s)",
1922 'args': [pk_person, firstnames, lastnames, active]
1923 }]
1924 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True)
1925 name = cPersonName(aPK_obj = rows[0][0])
1926 return name
1927
1928 -def create_identity(gender=None, dob=None, lastnames=None, firstnames=None):
1929
1930 cmd1 = u"""insert into dem.identity (gender, dob) values (%s, %s)"""
1931
1932 cmd2 = u"""
1933 insert into dem.names (
1934 id_identity, lastnames, firstnames
1935 ) values (
1936 currval('dem.identity_pk_seq'), coalesce(%s, 'xxxDEFAULTxxx'), coalesce(%s, 'xxxDEFAULTxxx')
1937 )"""
1938
1939 rows, idx = gmPG2.run_rw_queries (
1940 queries = [
1941 {'cmd': cmd1, 'args': [gender, dob]},
1942 {'cmd': cmd2, 'args': [lastnames, firstnames]},
1943 {'cmd': u"select currval('dem.identity_pk_seq')"}
1944 ],
1945 return_data = True
1946 )
1947 return cIdentity(aPK_obj=rows[0][0])
1948
1950 cmd1 = u"insert into dem.identity(gender) values('xxxDEFAULTxxx')"
1951 cmd2 = u"select currval('dem.identity_pk_seq')"
1952
1953 rows, idx = gmPG2.run_rw_queries (
1954 queries = [
1955 {'cmd': cmd1},
1956 {'cmd': cmd2}
1957 ],
1958 return_data = True
1959 )
1960 return gmDemographicRecord.cIdentity(aPK_obj = rows[0][0])
1961
1988
2003
2005 """Text mode UI function to ask for patient."""
2006
2007 person_searcher = cPatientSearcher_SQL()
2008
2009 while True:
2010 search_fragment = prompted_input("\nEnter person search term or leave blank to exit")
2011
2012 if search_fragment in ['exit', 'quit', 'bye', None]:
2013 print "user cancelled patient search"
2014 return None
2015
2016 pats = person_searcher.get_patients(search_term = search_fragment)
2017
2018 if (pats is None) or (len(pats) == 0):
2019 print "No patient matches the query term."
2020 print ""
2021 continue
2022
2023 if len(pats) > 1:
2024 print "Several patients match the query term:"
2025 print ""
2026 for pat in pats:
2027 print pat
2028 print ""
2029 continue
2030
2031 return pats[0]
2032
2033 return None
2034
2035
2036
2047
2048 map_gender2mf = {
2049 'm': u'm',
2050 'f': u'f',
2051 'tf': u'f',
2052 'tm': u'm',
2053 'h': u'mf'
2054 }
2055
2056
2057 map_gender2symbol = {
2058 'm': u'\u2642',
2059 'f': u'\u2640',
2060 'tf': u'\u26A5\u2640',
2061 'tm': u'\u26A5\u2642',
2062 'h': u'\u26A5'
2063
2064
2065
2066 }
2067
2088
2090 """Try getting the gender for the given first name."""
2091
2092 if firstnames is None:
2093 return None
2094
2095 rows, idx = gmPG2.run_ro_queries(queries = [{
2096 'cmd': u"select gender from dem.name_gender_map where name ilike %(fn)s limit 1",
2097 'args': {'fn': firstnames}
2098 }])
2099
2100 if len(rows) == 0:
2101 return None
2102
2103 return rows[0][0]
2104
2106 if active_only:
2107 cmd = u"select * from dem.v_staff where is_active order by can_login desc, short_alias asc"
2108 else:
2109 cmd = u"select * from dem.v_staff order by can_login desc, is_active desc, short_alias asc"
2110 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
2111 staff_list = []
2112 for row in rows:
2113 obj_row = {
2114 'idx': idx,
2115 'data': row,
2116 'pk_field': 'pk_staff'
2117 }
2118 staff_list.append(cStaff(row=obj_row))
2119 return staff_list
2120
2122 return [ cIdentity(aPK_obj = pk) for pk in pks ]
2123
2127
2131
2132
2133
2134 if __name__ == '__main__':
2135
2136 import datetime
2137
2138 gmI18N.activate_locale()
2139 gmI18N.install_domain()
2140 gmDateTime.init()
2141
2142
2163
2165 dto = cDTO_person()
2166 dto.firstnames = 'Sepp'
2167 dto.lastnames = 'Herberger'
2168 dto.gender = 'male'
2169 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
2170 print dto
2171
2172 print dto['firstnames']
2173 print dto['lastnames']
2174 print dto['gender']
2175 print dto['dob']
2176
2177 for key in dto.keys():
2178 print key
2179
2192
2193
2199
2200
2213
2215
2216 print '\n\nCreating identity...'
2217 new_identity = create_identity(gender='m', dob='2005-01-01', lastnames='test lastnames', firstnames='test firstnames')
2218 print 'Identity created: %s' % new_identity
2219
2220 print '\nSetting title and gender...'
2221 new_identity['title'] = 'test title';
2222 new_identity['gender'] = 'f';
2223 new_identity.save_payload()
2224 print 'Refetching identity from db: %s' % cIdentity(aPK_obj=new_identity['pk_identity'])
2225
2226 print '\nGetting all names...'
2227 for a_name in new_identity.get_names():
2228 print a_name
2229 print 'Active name: %s' % (new_identity.get_active_name())
2230 print 'Setting nickname...'
2231 new_identity.set_nickname(nickname='test nickname')
2232 print 'Refetching all names...'
2233 for a_name in new_identity.get_names():
2234 print a_name
2235 print 'Active name: %s' % (new_identity.get_active_name())
2236
2237 print '\nIdentity occupations: %s' % new_identity['occupations']
2238 print 'Creating identity occupation...'
2239 new_identity.link_occupation('test occupation')
2240 print 'Identity occupations: %s' % new_identity['occupations']
2241
2242 print '\nIdentity addresses: %s' % new_identity.get_addresses()
2243 print 'Creating identity address...'
2244
2245 new_identity.link_address (
2246 number = 'test 1234',
2247 street = 'test street',
2248 postcode = 'test postcode',
2249 urb = 'test urb',
2250 state = 'SN',
2251 country = 'DE'
2252 )
2253 print 'Identity addresses: %s' % new_identity.get_addresses()
2254
2255 print '\nIdentity communications: %s' % new_identity.get_comm_channels()
2256 print 'Creating identity communication...'
2257 new_identity.link_comm_channel('homephone', '1234566')
2258 print 'Identity communications: %s' % new_identity.get_comm_channels()
2259
2261 searcher = cPatientSearcher_SQL()
2262
2263 print "testing _normalize_soundalikes()"
2264 print "--------------------------------"
2265
2266 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']
2267 for name in data:
2268 print '%s: %s' % (name, searcher._normalize_soundalikes(name))
2269
2270 raw_input('press [ENTER] to continue')
2271 print "============"
2272
2273 print "testing _generate_simple_query()"
2274 print "----------------------------"
2275 data = ['51234', '1 134 153', '#13 41 34', '#3-AFY322.4', '22-04-1906', '1235/32/3525', ' , johnny']
2276 for fragment in data:
2277 print "fragment:", fragment
2278 qs = searcher._generate_simple_query(fragment)
2279 for q in qs:
2280 print " match on:", q['args'][0]
2281 print " query :", q['cmd']
2282 raw_input('press [ENTER] to continue')
2283 print "============"
2284
2285 print "testing _generate_queries_from_dto()"
2286 print "------------------------------------"
2287 dto = cDTO_person()
2288 dto.gender = 'm'
2289 dto.lastnames = 'Kirk'
2290 dto.firstnames = 'James'
2291 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
2292 q = searcher._generate_queries_from_dto(dto)[0]
2293 print "dto:", dto
2294 print " match on:", q['args'][0]
2295 print " query:", q['cmd']
2296
2297 raw_input('press [ENTER] to continue')
2298 print "============"
2299
2300 print "testing _generate_queries_de()"
2301 print "------------------------------"
2302 qs = searcher._generate_queries_de('Kirk, James')
2303 for q in qs:
2304 print " match on:", q['args'][0]
2305 print " query :", q['cmd']
2306 print " args :", q['args']
2307 raw_input('press [ENTER] to continue')
2308 print "============"
2309
2310 qs = searcher._generate_queries_de(u'müller')
2311 for q in qs:
2312 print " match on:", q['args'][0]
2313 print " query :", q['cmd']
2314 print " args :", q['args']
2315 raw_input('press [ENTER] to continue')
2316 print "============"
2317
2318 qs = searcher._generate_queries_de(u'özdemir')
2319 for q in qs:
2320 print " match on:", q['args'][0]
2321 print " query :", q['cmd']
2322 print " args :", q['args']
2323 raw_input('press [ENTER] to continue')
2324 print "============"
2325
2326 qs = searcher._generate_queries_de(u'Özdemir')
2327 for q in qs:
2328 print " match on:", q['args'][0]
2329 print " query :", q['cmd']
2330 print " args :", q['args']
2331 raw_input('press [ENTER] to continue')
2332 print "============"
2333
2334 print "testing _generate_dumb_brute_query()"
2335 print "------------------------------------"
2336 q = searcher._generate_dumb_brute_query('Kirk, James Tiberius')
2337 print " match on:", q['args'][0]
2338 print " query:", q['cmd']
2339 print " args:", q['args']
2340
2341 raw_input('press [ENTER] to continue')
2342
2353
2354
2355
2356
2357
2363
2364 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2365
2366
2367
2368
2369
2370
2371
2372 test_current_provider()
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
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465