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 __version__ = "$Revision: 1.198 $"
9 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
10 __license__ = "GPL"
11
12
13 import sys, os.path, time, re as regex, string, types, datetime as pyDT, codecs, threading, logging
14
15
16
17 if __name__ == '__main__':
18 sys.path.insert(0, '../../')
19 from Gnumed.pycommon import gmExceptions, gmDispatcher, gmBorg, gmI18N, gmNull, gmBusinessDBObject, gmTools
20 from Gnumed.pycommon import gmPG2, gmMatchProvider, gmDateTime
21 from Gnumed.pycommon import gmLog2
22 from Gnumed.pycommon import gmHooks
23
24 from Gnumed.business import gmDemographicRecord, gmProviderInbox, gmXdtMappings, gmClinicalRecord
25 from Gnumed.business.gmDocuments import cDocumentFolder
26
27
28 _log = logging.getLogger('gm.person')
29 _log.info(__version__)
30
31 __gender_list = None
32 __gender_idx = None
33
34 __gender2salutation_map = None
35
36
37
39
41 self.identity = None
42 self.external_ids = []
43 self.comm_channels = []
44 self.addresses = []
45
46
47
49 return 'firstnames lastnames dob gender'.split()
50
53
55 """Generate generic queries.
56
57 - not locale dependant
58 - data -> firstnames, lastnames, dob, gender
59
60 shall we mogrify name parts ? probably not as external
61 sources should know what they do
62
63 finds by inactive name, too, but then shows
64 the corresponding active name ;-)
65
66 Returns list of matching identities (may be empty)
67 or None if it was told to create an identity but couldn't.
68 """
69 where_snippets = []
70 args = {}
71
72 where_snippets.append(u'firstnames = %(first)s')
73 args['first'] = self.firstnames
74
75 where_snippets.append(u'lastnames = %(last)s')
76 args['last'] = self.lastnames
77
78 if self.dob is not None:
79 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %(dob)s)")
80 args['dob'] = self.dob.replace(hour = 23, minute = 59, second = 59)
81
82 if self.gender is not None:
83 where_snippets.append('gender = %(sex)s')
84 args['sex'] = self.gender
85
86 cmd = u"""
87 SELECT *, '%s' AS match_type
88 FROM dem.v_basic_person
89 WHERE
90 pk_identity IN (
91 SELECT pk_identity FROM dem.v_person_names WHERE %s
92 )
93 ORDER BY lastnames, firstnames, dob""" % (
94 _('external patient source (name, gender, date of birth)'),
95 ' AND '.join(where_snippets)
96 )
97
98 try:
99 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx=True)
100 except:
101 _log.error(u'cannot get candidate identities for dto "%s"' % self)
102 _log.exception('query %s' % cmd)
103 rows = []
104
105 if len(rows) == 0:
106 _log.debug('no candidate identity matches found')
107 if not can_create:
108 return []
109 ident = self.import_into_database()
110 if ident is None:
111 return None
112 identities = [ident]
113 else:
114 identities = [ cIdentity(row = {'pk_field': 'pk_identity', 'data': row, 'idx': idx}) for row in rows ]
115
116 return identities
117
119 """Imports self into the database."""
120
121 self.identity = create_identity (
122 firstnames = self.firstnames,
123 lastnames = self.lastnames,
124 gender = self.gender,
125 dob = self.dob
126 )
127
128 if self.identity is None:
129 return None
130
131 for ext_id in self.external_ids:
132 try:
133 self.identity.add_external_id (
134 type_name = ext_id['name'],
135 value = ext_id['value'],
136 issuer = ext_id['issuer'],
137 comment = ext_id['comment']
138 )
139 except StandardError:
140 _log.exception('cannot import <external ID> from external data source')
141 _log.log_stack_trace()
142
143 for comm in self.comm_channels:
144 try:
145 self.identity.link_comm_channel (
146 comm_medium = comm['channel'],
147 url = comm['url']
148 )
149 except StandardError:
150 _log.exception('cannot import <comm channel> from external data source')
151 _log.log_stack_trace()
152
153 for adr in self.addresses:
154 try:
155 self.identity.link_address (
156 number = adr['number'],
157 street = adr['street'],
158 postcode = adr['zip'],
159 urb = adr['urb'],
160 state = adr['region'],
161 country = adr['country']
162 )
163 except StandardError:
164 _log.exception('cannot import <address> from external data source')
165 _log.log_stack_trace()
166
167 return self.identity
168
171
173 value = value.strip()
174 if value == u'':
175 return
176 name = name.strip()
177 if name == u'':
178 raise ValueError(_('<name> cannot be empty'))
179 issuer = issuer.strip()
180 if issuer == u'':
181 raise ValueError(_('<issuer> cannot be empty'))
182 self.external_ids.append({'name': name, 'value': value, 'issuer': issuer, 'comment': comment})
183
185 url = url.strip()
186 if url == u'':
187 return
188 channel = channel.strip()
189 if channel == u'':
190 raise ValueError(_('<channel> cannot be empty'))
191 self.comm_channels.append({'channel': channel, 'url': url})
192
193 - def remember_address(self, number=None, street=None, urb=None, region=None, zip=None, country=None):
194 number = number.strip()
195 if number == u'':
196 raise ValueError(_('<number> cannot be empty'))
197 street = street.strip()
198 if street == u'':
199 raise ValueError(_('<street> cannot be empty'))
200 urb = urb.strip()
201 if urb == u'':
202 raise ValueError(_('<urb> cannot be empty'))
203 zip = zip.strip()
204 if zip == u'':
205 raise ValueError(_('<zip> cannot be empty'))
206 country = country.strip()
207 if country == u'':
208 raise ValueError(_('<country> cannot be empty'))
209 region = region.strip()
210 if region == u'':
211 region = u'??'
212 self.addresses.append ({
213 u'number': number,
214 u'street': street,
215 u'zip': zip,
216 u'urb': urb,
217 u'region': region,
218 u'country': country
219 })
220
221
222
224 return u'<%s @ %s: %s %s (%s) %s>' % (
225 self.__class__.__name__,
226 id(self),
227 self.firstnames,
228 self.lastnames,
229 self.gender,
230 self.dob
231 )
232
234 """Do some sanity checks on self.* access."""
235
236 if attr == 'gender':
237 glist, idx = get_gender_list()
238 for gender in glist:
239 if str(val) in [gender[0], gender[1], gender[2], gender[3]]:
240 val = gender[idx['tag']]
241 object.__setattr__(self, attr, val)
242 return
243 raise ValueError('invalid gender: [%s]' % val)
244
245 if attr == 'dob':
246 if val is not None:
247 if not isinstance(val, pyDT.datetime):
248 raise TypeError('invalid type for DOB (must be datetime.datetime): %s [%s]' % (type(val), val))
249 if val.tzinfo is None:
250 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % val.isoformat())
251
252 object.__setattr__(self, attr, val)
253 return
254
256 return getattr(self, attr)
257
258 -class cPersonName(gmBusinessDBObject.cBusinessDBObject):
259 _cmd_fetch_payload = u"SELECT * FROM dem.v_person_names WHERE pk_name = %s"
260 _cmds_store_payload = [
261 u"""UPDATE dem.names SET
262 active = FALSE
263 WHERE
264 %(active_name)s IS TRUE -- act only when needed and only
265 AND
266 id_identity = %(pk_identity)s -- on names of this identity
267 AND
268 active IS TRUE -- which are active
269 AND
270 id != %(pk_name)s -- but NOT *this* name
271 """,
272 u"""update dem.names set
273 active = %(active_name)s,
274 preferred = %(preferred)s,
275 comment = %(comment)s
276 where
277 id = %(pk_name)s and
278 id_identity = %(pk_identity)s and -- belt and suspenders
279 xmin = %(xmin_name)s""",
280 u"""select xmin as xmin_name from dem.names where id = %(pk_name)s"""
281 ]
282 _updatable_fields = ['active_name', 'preferred', 'comment']
283
292
294 return '%(last)s, %(title)s %(first)s%(nick)s' % {
295 'last': self._payload[self._idx['lastnames']],
296 'title': gmTools.coalesce (
297 self._payload[self._idx['title']],
298 map_gender2salutation(self._payload[self._idx['gender']])
299 ),
300 'first': self._payload[self._idx['firstnames']],
301 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u' "%s"', u'%s')
302 }
303
304 description = property(_get_description, lambda x:x)
305
306 -class cStaff(gmBusinessDBObject.cBusinessDBObject):
307 _cmd_fetch_payload = u"SELECT * FROM dem.v_staff WHERE pk_staff = %s"
308 _cmds_store_payload = [
309 u"""UPDATE dem.staff SET
310 fk_role = %(pk_role)s,
311 short_alias = %(short_alias)s,
312 comment = gm.nullify_empty_string(%(comment)s),
313 is_active = %(is_active)s,
314 db_user = %(db_user)s
315 WHERE
316 pk = %(pk_staff)s
317 AND
318 xmin = %(xmin_staff)s
319 RETURNING
320 xmin AS xmin_staff"""
321 ]
322 _updatable_fields = ['pk_role', 'short_alias', 'comment', 'is_active', 'db_user']
323
324 - def __init__(self, aPK_obj=None, row=None):
325
326 if (aPK_obj is None) and (row is None):
327 cmd = u"select * from dem.v_staff where db_user = CURRENT_USER"
328 try:
329 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
330 except:
331 _log.exception('cannot instantiate staff instance')
332 gmLog2.log_stack_trace()
333 raise ValueError('cannot instantiate staff instance for database account CURRENT_USER')
334 if len(rows) == 0:
335 raise ValueError('no staff record for database account CURRENT_USER')
336 row = {
337 'pk_field': 'pk_staff',
338 'idx': idx,
339 'data': rows[0]
340 }
341 gmBusinessDBObject.cBusinessDBObject.__init__(self, row = row)
342 else:
343 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj = aPK_obj, row = row)
344
345
346 self.__is_current_user = (gmPG2.get_current_user() == self._payload[self._idx['db_user']])
347
348 self.__inbox = None
349
356
358 rows, idx = gmPG2.run_ro_queries (
359 queries = [{
360 'cmd': u'select i18n.get_curr_lang(%(usr)s)',
361 'args': {'usr': self._payload[self._idx['db_user']]}
362 }]
363 )
364 return rows[0][0]
365
367 if not gmPG2.set_user_language(language = language):
368 raise ValueError (
369 u'Cannot set database language to [%s] for user [%s].' % (language, self._payload[self._idx['db_user']])
370 )
371 return
372
373 database_language = property(_get_db_lang, _set_db_lang)
374
376 if self.__inbox is None:
377 self.__inbox = gmProviderInbox.cProviderInbox(provider_id = self._payload[self._idx['pk_staff']])
378 return self.__inbox
379
382
383 inbox = property(_get_inbox, _set_inbox)
384
387
389 """Staff member Borg to hold currently logged on provider.
390
391 There may be many instances of this but they all share state.
392 """
394 """Change or get currently logged on provider.
395
396 provider:
397 * None: get copy of current instance
398 * cStaff instance: change logged on provider (role)
399 """
400
401 try:
402 self.provider
403 except AttributeError:
404 self.provider = gmNull.cNull()
405
406
407 if provider is None:
408 return None
409
410
411 if not isinstance(provider, cStaff):
412 raise ValueError, 'cannot set logged on provider to [%s], must be either None or cStaff instance' % str(provider)
413
414
415 if self.provider['pk_staff'] == provider['pk_staff']:
416 return None
417
418
419 if isinstance(self.provider, gmNull.cNull):
420 self.provider = provider
421 return None
422
423
424 raise ValueError, 'provider change [%s] -> [%s] not yet supported' % (self.provider['pk_staff'], provider['pk_staff'])
425
426
429
430
431
433 """Return any attribute if known how to retrieve it by proxy.
434 """
435 return self.provider[aVar]
436
437
438
440 if attribute == 'provider':
441 raise AttributeError
442 if not isinstance(self.provider, gmNull.cNull):
443 return getattr(self.provider, attribute)
444
445
446 -class cIdentity(gmBusinessDBObject.cBusinessDBObject):
447 _cmd_fetch_payload = u"select * from dem.v_basic_person where pk_identity = %s"
448 _cmds_store_payload = [
449 u"""update dem.identity set
450 gender = %(gender)s,
451 dob = %(dob)s,
452 tob = %(tob)s,
453 cob = gm.nullify_empty_string(%(cob)s),
454 title = gm.nullify_empty_string(%(title)s),
455 fk_marital_status = %(pk_marital_status)s,
456 karyotype = gm.nullify_empty_string(%(karyotype)s),
457 pupic = gm.nullify_empty_string(%(pupic)s),
458 deceased = %(deceased)s,
459 emergency_contact = gm.nullify_empty_string(%(emergency_contact)s),
460 fk_emergency_contact = %(pk_emergency_contact)s,
461 fk_primary_provider = %(pk_primary_provider)s,
462 comment = gm.nullify_empty_string(%(comment)s)
463 where
464 pk = %(pk_identity)s and
465 xmin = %(xmin_identity)s""",
466 u"""select xmin_identity from dem.v_basic_person where pk_identity = %(pk_identity)s"""
467 ]
468 _updatable_fields = [
469 "title",
470 "dob",
471 "tob",
472 "cob",
473 "gender",
474 "pk_marital_status",
475 "karyotype",
476 "pupic",
477 'deceased',
478 'emergency_contact',
479 'pk_emergency_contact',
480 'pk_primary_provider',
481 'comment'
482 ]
483
485 return self._payload[self._idx['pk_identity']]
487 raise AttributeError('setting ID of identity is not allowed')
488 ID = property(_get_ID, _set_ID)
489
491
492 if attribute == 'dob':
493 if value is not None:
494
495 if isinstance(value, pyDT.datetime):
496 if value.tzinfo is None:
497 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % dt.isoformat())
498 else:
499 raise TypeError, '[%s]: type [%s] (%s) invalid for attribute [dob], must be datetime.datetime or None' % (self.__class__.__name__, type(value), value)
500
501
502 if self._payload[self._idx['dob']] is not None:
503 old_dob = self._payload[self._idx['dob']].strftime('%Y %m %d %H %M %S')
504 new_dob = value.strftime('%Y %m %d %H %M %S')
505 if new_dob == old_dob:
506 return
507
508 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
509
512
514 cmd = u"""
515 select exists (
516 select 1
517 from clin.v_emr_journal
518 where
519 pk_patient = %(pat)s
520 and
521 soap_cat is not null
522 )"""
523 args = {'pat': self._payload[self._idx['pk_identity']]}
524 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
525 return rows[0][0]
526
528 raise AttributeError('setting is_patient status of identity is not allowed')
529
530 is_patient = property(_get_is_patient, _set_is_patient)
531
532
533
535 for name in self.get_names():
536 if name['active_name'] is True:
537 return name
538
539 _log.error('cannot retrieve active name for patient [%s]' % self._payload[self._idx['pk_identity']])
540 return None
541
543 cmd = u"select * from dem.v_person_names where pk_identity = %(pk_pat)s"
544 rows, idx = gmPG2.run_ro_queries (
545 queries = [{
546 'cmd': cmd,
547 'args': {'pk_pat': self._payload[self._idx['pk_identity']]}
548 }],
549 get_col_idx = True
550 )
551
552 if len(rows) == 0:
553
554 return []
555
556 names = [ cPersonName(row = {'idx': idx, 'data': r, 'pk_field': 'pk_name'}) for r in rows ]
557 return names
558
569
571 return '%(sex)s%(title)s %(last)s, %(first)s%(nick)s' % {
572 'last': self._payload[self._idx['lastnames']],
573 'first': self._payload[self._idx['firstnames']],
574 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u' (%s)', u'%s'),
575 'sex': map_gender2salutation(self._payload[self._idx['gender']]),
576 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s', u'%s')
577 }
578
580 return '%(last)s,%(title)s %(first)s%(nick)s' % {
581 'last': self._payload[self._idx['lastnames']],
582 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s', u'%s'),
583 'first': self._payload[self._idx['firstnames']],
584 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u' (%s)', u'%s')
585 }
586
587 - def add_name(self, firstnames, lastnames, active=True):
588 """Add a name.
589
590 @param firstnames The first names.
591 @param lastnames The last names.
592 @param active When True, the new name will become the active one (hence setting other names to inactive)
593 @type active A types.BooleanType instance
594 """
595 name = create_name(self.ID, firstnames, lastnames, active)
596 if active:
597 self.refetch_payload()
598 return name
599
601 cmd = u"delete from dem.names where id = %(name)s and id_identity = %(pat)s"
602 args = {'name': name['pk_name'], 'pat': self.ID}
603 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
604
605
606
607
609 """
610 Set the nickname. Setting the nickname only makes sense for the currently
611 active name.
612 @param nickname The preferred/nick/warrior name to set.
613 """
614 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': u"select dem.set_nickname(%s, %s)", 'args': [self.ID, nickname]}])
615 self.refetch_payload()
616 return True
617
628
630 args = {
631 u'tag': tag,
632 u'identity': self.ID
633 }
634
635
636 cmd = u"SELECT pk FROM dem.identity_tag WHERE fk_tag = %(tag)s AND fk_identity = %(identity)s"
637 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
638 if len(rows) > 0:
639 return gmDemographicRecord.cIdentityTag(aPK_obj = rows[0]['pk'])
640
641
642 cmd = u"""
643 INSERT INTO dem.identity_tag (
644 fk_tag,
645 fk_identity
646 ) VALUES (
647 %(tag)s,
648 %(identity)s
649 )
650 RETURNING pk
651 """
652 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
653 return gmDemographicRecord.cIdentityTag(aPK_obj = rows[0]['pk'])
654
656 cmd = u"DELETE FROM dem.identity_tag WHERE pk = %(pk)s"
657 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': tag}}])
658
659
660
661
662
663
664
665
666
667
668 - def add_external_id(self, type_name=None, value=None, issuer=None, comment=None, pk_type=None):
669 """Adds an external ID to the patient.
670
671 creates ID type if necessary
672 """
673
674
675 if pk_type is not None:
676 cmd = u"""
677 select * from dem.v_external_ids4identity where
678 pk_identity = %(pat)s and
679 pk_type = %(pk_type)s and
680 value = %(val)s"""
681 else:
682
683 if issuer is None:
684 cmd = u"""
685 select * from dem.v_external_ids4identity where
686 pk_identity = %(pat)s and
687 name = %(name)s and
688 value = %(val)s"""
689 else:
690 cmd = u"""
691 select * from dem.v_external_ids4identity where
692 pk_identity = %(pat)s and
693 name = %(name)s and
694 value = %(val)s and
695 issuer = %(issuer)s"""
696 args = {
697 'pat': self.ID,
698 'name': type_name,
699 'val': value,
700 'issuer': issuer,
701 'pk_type': pk_type
702 }
703 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
704
705
706 if len(rows) == 0:
707
708 args = {
709 'pat': self.ID,
710 'val': value,
711 'type_name': type_name,
712 'pk_type': pk_type,
713 'issuer': issuer,
714 'comment': comment
715 }
716
717 if pk_type is None:
718 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
719 %(val)s,
720 (select dem.add_external_id_type(%(type_name)s, %(issuer)s)),
721 %(comment)s,
722 %(pat)s
723 )"""
724 else:
725 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
726 %(val)s,
727 %(pk_type)s,
728 %(comment)s,
729 %(pat)s
730 )"""
731
732 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
733
734
735 else:
736 row = rows[0]
737 if comment is not None:
738
739 if gmTools.coalesce(row['comment'], '').find(comment.strip()) == -1:
740 comment = '%s%s' % (gmTools.coalesce(row['comment'], '', '%s // '), comment.strip)
741 cmd = u"update dem.lnk_identity2ext_id set comment = %(comment)s where id=%(pk)s"
742 args = {'comment': comment, 'pk': row['pk_id']}
743 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
744
745 - def update_external_id(self, pk_id=None, type=None, value=None, issuer=None, comment=None):
746 """Edits an existing external ID.
747
748 creates ID type if necessary
749 """
750 cmd = u"""
751 update dem.lnk_identity2ext_id set
752 fk_origin = (select dem.add_external_id_type(%(type)s, %(issuer)s)),
753 external_id = %(value)s,
754 comment = %(comment)s
755 where id = %(pk)s"""
756 args = {'pk': pk_id, 'value': value, 'type': type, 'issuer': issuer, 'comment': comment}
757 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
758
760 where_parts = ['pk_identity = %(pat)s']
761 args = {'pat': self.ID}
762
763 if id_type is not None:
764 where_parts.append(u'name = %(name)s')
765 args['name'] = id_type.strip()
766
767 if issuer is not None:
768 where_parts.append(u'issuer = %(issuer)s')
769 args['issuer'] = issuer.strip()
770
771 cmd = u"select * from dem.v_external_ids4identity where %s" % ' and '.join(where_parts)
772 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
773
774 return rows
775
777 cmd = u"""
778 delete from dem.lnk_identity2ext_id
779 where id_identity = %(pat)s and id = %(pk)s"""
780 args = {'pat': self.ID, 'pk': pk_ext_id}
781 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
782
784 """Merge another identity into this one.
785
786 Keep this one. Delete other one."""
787
788 if other_identity.ID == self.ID:
789 return True, None
790
791 curr_pat = gmCurrentPatient()
792 if curr_pat.connected:
793 if other_identity.ID == curr_pat.ID:
794 return False, _('Cannot merge active patient into another patient.')
795
796 queries = []
797 args = {'old_pat': other_identity.ID, 'new_pat': self.ID}
798
799
800 queries.append ({
801 '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)',
802 'args': args
803 })
804
805
806
807 queries.append ({
808 'cmd': u'update dem.names set active = False where id_identity = %(old_pat)s',
809 'args': args
810 })
811
812
813 FKs = gmPG2.get_foreign_keys2column (
814 schema = u'dem',
815 table = u'identity',
816 column = u'pk'
817 )
818
819
820 cmd_template = u'update %s set %s = %%(new_pat)s where %s = %%(old_pat)s'
821 for FK in FKs:
822 queries.append ({
823 'cmd': cmd_template % (FK['referencing_table'], FK['referencing_column'], FK['referencing_column']),
824 'args': args
825 })
826
827
828 queries.append ({
829 'cmd': u'delete from dem.identity where pk = %(old_pat)s',
830 'args': args
831 })
832
833 _log.warning('identity [%s] is about to assimilate identity [%s]', self.ID, other_identity.ID)
834
835 gmPG2.run_rw_queries(link_obj = link_obj, queries = queries, end_tx = True)
836
837 self.add_external_id (
838 type_name = u'merged GNUmed identity primary key',
839 value = u'GNUmed::pk::%s' % other_identity.ID,
840 issuer = u'GNUmed'
841 )
842
843 return True, None
844
845
847 cmd = u"""
848 insert into clin.waiting_list (fk_patient, urgency, comment, area, list_position)
849 values (
850 %(pat)s,
851 %(urg)s,
852 %(cmt)s,
853 %(area)s,
854 (select coalesce((max(list_position) + 1), 1) from clin.waiting_list)
855 )"""
856 args = {'pat': self.ID, 'urg': urgency, 'cmt': comment, 'area': zone}
857 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], verbose=True)
858
859 - def export_as_gdt(self, filename=None, encoding='iso-8859-15', external_id_type=None):
860
861 template = u'%s%s%s\r\n'
862
863 file = codecs.open (
864 filename = filename,
865 mode = 'wb',
866 encoding = encoding,
867 errors = 'strict'
868 )
869
870 file.write(template % (u'013', u'8000', u'6301'))
871 file.write(template % (u'013', u'9218', u'2.10'))
872 if external_id_type is None:
873 file.write(template % (u'%03d' % (9 + len(str(self.ID))), u'3000', self.ID))
874 else:
875 ext_ids = self.get_external_ids(id_type = external_id_type)
876 if len(ext_ids) > 0:
877 file.write(template % (u'%03d' % (9 + len(ext_ids[0]['value'])), u'3000', ext_ids[0]['value']))
878 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['lastnames']])), u'3101', self._payload[self._idx['lastnames']]))
879 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['firstnames']])), u'3102', self._payload[self._idx['firstnames']]))
880 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')))
881 file.write(template % (u'010', u'3110', gmXdtMappings.map_gender_gm2xdt[self._payload[self._idx['gender']]]))
882 file.write(template % (u'025', u'6330', 'GNUmed::9206::encoding'))
883 file.write(template % (u'%03d' % (9 + len(encoding)), u'6331', encoding))
884 if external_id_type is None:
885 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
886 file.write(template % (u'017', u'6333', u'internal'))
887 else:
888 if len(ext_ids) > 0:
889 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
890 file.write(template % (u'%03d' % (9 + len(external_id_type)), u'6333', external_id_type))
891
892 file.close()
893
894
895
897 cmd = u"select * from dem.v_person_jobs where pk_identity=%s"
898 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
899 return rows
900
902 """Link an occupation with a patient, creating the occupation if it does not exists.
903
904 @param occupation The name of the occupation to link the patient to.
905 """
906 if (activities is None) and (occupation is None):
907 return True
908
909 occupation = occupation.strip()
910 if len(occupation) == 0:
911 return True
912
913 if activities is not None:
914 activities = activities.strip()
915
916 args = {'act': activities, 'pat_id': self.pk_obj, 'job': occupation}
917
918 cmd = u"select activities from dem.v_person_jobs where pk_identity = %(pat_id)s and l10n_occupation = _(%(job)s)"
919 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
920
921 queries = []
922 if len(rows) == 0:
923 queries.append ({
924 'cmd': u"INSERT INTO dem.lnk_job2person (fk_identity, fk_occupation, activities) VALUES (%(pat_id)s, dem.create_occupation(%(job)s), %(act)s)",
925 'args': args
926 })
927 else:
928 if rows[0]['activities'] != activities:
929 queries.append ({
930 '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))",
931 'args': args
932 })
933
934 rows, idx = gmPG2.run_rw_queries(queries = queries)
935
936 return True
937
939 if occupation is None:
940 return True
941 occupation = occupation.strip()
942 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))"
943 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj, 'job': occupation}}])
944 return True
945
946
947
949 cmd = u"select * from dem.v_person_comms where pk_identity = %s"
950 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True)
951
952 filtered = rows
953
954 if comm_medium is not None:
955 filtered = []
956 for row in rows:
957 if row['comm_type'] == comm_medium:
958 filtered.append(row)
959
960 return [ gmDemographicRecord.cCommChannel(row = {
961 'pk_field': 'pk_lnk_identity2comm',
962 'data': r,
963 'idx': idx
964 }) for r in filtered
965 ]
966
967 - def link_comm_channel(self, comm_medium=None, url=None, is_confidential=False, pk_channel_type=None):
968 """Link a communication medium with a patient.
969
970 @param comm_medium The name of the communication medium.
971 @param url The communication resource locator.
972 @type url A types.StringType instance.
973 @param is_confidential Wether the data must be treated as confidential.
974 @type is_confidential A types.BooleanType instance.
975 """
976 comm_channel = gmDemographicRecord.create_comm_channel (
977 comm_medium = comm_medium,
978 url = url,
979 is_confidential = is_confidential,
980 pk_channel_type = pk_channel_type,
981 pk_identity = self.pk_obj
982 )
983 return comm_channel
984
990
991
992
994 cmd = u"select * from dem.v_pat_addresses where pk_identity=%s"
995 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx=True)
996 addresses = []
997 for r in rows:
998 addresses.append(gmDemographicRecord.cPatientAddress(row={'idx': idx, 'data': r, 'pk_field': 'pk_address'}))
999
1000 filtered = addresses
1001
1002 if address_type is not None:
1003 filtered = []
1004 for adr in addresses:
1005 if adr['address_type'] == address_type:
1006 filtered.append(adr)
1007
1008 return filtered
1009
1010 - def link_address(self, number=None, street=None, postcode=None, urb=None, state=None, country=None, subunit=None, suburb=None, id_type=None):
1011 """Link an address with a patient, creating the address if it does not exists.
1012
1013 @param number The number of the address.
1014 @param street The name of the street.
1015 @param postcode The postal code of the address.
1016 @param urb The name of town/city/etc.
1017 @param state The code of the state.
1018 @param country The code of the country.
1019 @param id_type The primary key of the address type.
1020 """
1021
1022 adr = gmDemographicRecord.create_address (
1023 country = country,
1024 state = state,
1025 urb = urb,
1026 suburb = suburb,
1027 postcode = postcode,
1028 street = street,
1029 number = number,
1030 subunit = subunit
1031 )
1032
1033
1034 cmd = u"select * from dem.lnk_person_org_address where id_identity = %s and id_address = %s"
1035 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj, adr['pk_address']]}])
1036
1037 if len(rows) == 0:
1038 args = {'id': self.pk_obj, 'adr': adr['pk_address'], 'type': id_type}
1039 if id_type is None:
1040 cmd = u"""
1041 insert into dem.lnk_person_org_address(id_identity, id_address)
1042 values (%(id)s, %(adr)s)"""
1043 else:
1044 cmd = u"""
1045 insert into dem.lnk_person_org_address(id_identity, id_address, id_type)
1046 values (%(id)s, %(adr)s, %(type)s)"""
1047 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1048 else:
1049
1050 if id_type is not None:
1051 r = rows[0]
1052 if r['id_type'] != id_type:
1053 cmd = "update dem.lnk_person_org_address set id_type = %(type)s where id = %(id)s"
1054 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'type': id_type, 'id': r['id']}}])
1055
1056 return adr
1057
1059 """Remove an address from the patient.
1060
1061 The address itself stays in the database.
1062 The address can be either cAdress or cPatientAdress.
1063 """
1064 cmd = u"delete from dem.lnk_person_org_address where id_identity = %(person)s and id_address = %(adr)s"
1065 args = {'person': self.pk_obj, 'adr': address['pk_address']}
1066 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1067
1068
1069
1071 cmd = u"""
1072 select
1073 t.description,
1074 vbp.pk_identity as id,
1075 title,
1076 firstnames,
1077 lastnames,
1078 dob,
1079 cob,
1080 gender,
1081 karyotype,
1082 pupic,
1083 pk_marital_status,
1084 marital_status,
1085 xmin_identity,
1086 preferred
1087 from
1088 dem.v_basic_person vbp, dem.relation_types t, dem.lnk_person2relative l
1089 where
1090 (
1091 l.id_identity = %(pk)s and
1092 vbp.pk_identity = l.id_relative and
1093 t.id = l.id_relation_type
1094 ) or (
1095 l.id_relative = %(pk)s and
1096 vbp.pk_identity = l.id_identity and
1097 t.inverse = l.id_relation_type
1098 )"""
1099 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}])
1100 if len(rows) == 0:
1101 return []
1102 return [(row[0], cIdentity(row = {'data': row[1:], 'idx':idx, 'pk_field': 'pk'})) for row in rows]
1103
1105
1106 id_new_relative = create_dummy_identity()
1107
1108 relative = cIdentity(aPK_obj=id_new_relative)
1109
1110
1111 relative.add_name( '**?**', self.get_names()['lastnames'])
1112
1113 if self._ext_cache.has_key('relatives'):
1114 del self._ext_cache['relatives']
1115 cmd = u"""
1116 insert into dem.lnk_person2relative (
1117 id_identity, id_relative, id_relation_type
1118 ) values (
1119 %s, %s, (select id from dem.relation_types where description = %s)
1120 )"""
1121 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.ID, id_new_relative, rel_type ]}])
1122 return True
1123
1125
1126 self.set_relative(None, relation)
1127
1132
1133 emergency_contact_in_database = property(_get_emergency_contact_from_database, lambda x:x)
1134
1135
1136
1161
1162 - def dob_in_range(self, min_distance=u'1 week', max_distance=u'1 week'):
1163 cmd = u'select dem.dob_is_in_range(%(dob)s, %(min)s, %(max)s)'
1164 rows, idx = gmPG2.run_ro_queries (
1165 queries = [{
1166 'cmd': cmd,
1167 'args': {'dob': self['dob'], 'min': min_distance, 'max': max_distance}
1168 }]
1169 )
1170 return rows[0][0]
1171
1172
1173
1175 cmd = u'select * from clin.v_most_recent_encounters where pk_patient=%s'
1176 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['pk_identity']]]}])
1177 if len(rows) > 0:
1178 return rows[0]
1179 else:
1180 return None
1181
1184
1187
1188 messages = property(_get_messages, _set_messages)
1189
1192
1194 if self._payload[self._idx['pk_primary_provider']] is None:
1195 return None
1196 return cStaff(aPK_obj = self._payload[self._idx['pk_primary_provider']])
1197
1198 primary_provider = property(_get_primary_provider, lambda x:x)
1199
1200
1201
1203 """Format patient demographics into patient specific path name fragment."""
1204 return '%s-%s%s-%s' % (
1205 self._payload[self._idx['lastnames']].replace(u' ', u'_'),
1206 self._payload[self._idx['firstnames']].replace(u' ', u'_'),
1207 gmTools.coalesce(self._payload[self._idx['preferred']], u'', template_initial = u'-(%s)'),
1208 self.get_formatted_dob(format = '%Y-%m-%d', encoding = gmI18N.get_encoding())
1209 )
1210
1212 """Represents a staff member which is a person.
1213
1214 - a specializing subclass of cIdentity turning it into a staff member
1215 """
1219
1222
1224 """Represents a person which is a patient.
1225
1226 - a specializing subclass of cIdentity turning it into a patient
1227 - its use is to cache subobjects like EMR and document folder
1228 """
1229 - def __init__(self, aPK_obj=None, row=None):
1230 cIdentity.__init__(self, aPK_obj=aPK_obj, row=row)
1231 self.__db_cache = {}
1232 self.__emr_access_lock = threading.Lock()
1233
1235 """Do cleanups before dying.
1236
1237 - note that this may be called in a thread
1238 """
1239 if self.__db_cache.has_key('clinical record'):
1240 self.__db_cache['clinical record'].cleanup()
1241 if self.__db_cache.has_key('document folder'):
1242 self.__db_cache['document folder'].cleanup()
1243 cIdentity.cleanup(self)
1244
1246 if not self.__emr_access_lock.acquire(False):
1247 raise AttributeError('cannot access EMR')
1248 try:
1249 emr = self.__db_cache['clinical record']
1250 self.__emr_access_lock.release()
1251 return emr
1252 except KeyError:
1253 pass
1254
1255 self.__db_cache['clinical record'] = gmClinicalRecord.cClinicalRecord(aPKey = self._payload[self._idx['pk_identity']])
1256 self.__emr_access_lock.release()
1257 return self.__db_cache['clinical record']
1258
1260 try:
1261 return self.__db_cache['document folder']
1262 except KeyError:
1263 pass
1264
1265 self.__db_cache['document folder'] = cDocumentFolder(aPKey = self._payload[self._idx['pk_identity']])
1266 return self.__db_cache['document folder']
1267
1269 """Patient Borg to hold currently active patient.
1270
1271 There may be many instances of this but they all share state.
1272 """
1273 - def __init__(self, patient=None, forced_reload=False):
1274 """Change or get currently active patient.
1275
1276 patient:
1277 * None: get currently active patient
1278 * -1: unset currently active patient
1279 * cPatient instance: set active patient if possible
1280 """
1281
1282 try:
1283 tmp = self.patient
1284 except AttributeError:
1285 self.patient = gmNull.cNull()
1286 self.__register_interests()
1287
1288
1289
1290 self.__lock_depth = 0
1291
1292 self.__pre_selection_callbacks = []
1293
1294
1295 if patient is None:
1296 return None
1297
1298
1299 if self.locked:
1300 _log.error('patient [%s] is locked, cannot change to [%s]' % (self.patient['pk_identity'], patient))
1301 return None
1302
1303
1304 if patient == -1:
1305 _log.debug('explicitly unsetting current patient')
1306 if not self.__run_pre_selection_callbacks():
1307 _log.debug('not unsetting current patient')
1308 return None
1309 self.__send_pre_selection_notification()
1310 self.patient.cleanup()
1311 self.patient = gmNull.cNull()
1312 self.__send_selection_notification()
1313 return None
1314
1315
1316 if not isinstance(patient, cPatient):
1317 _log.error('cannot set active patient to [%s], must be either None, -1 or cPatient instance' % str(patient))
1318 raise TypeError, 'gmPerson.gmCurrentPatient.__init__(): <patient> must be None, -1 or cPatient instance but is: %s' % str(patient)
1319
1320
1321 if (self.patient['pk_identity'] == patient['pk_identity']) and not forced_reload:
1322 return None
1323
1324
1325 _log.debug('patient change [%s] -> [%s] requested', self.patient['pk_identity'], patient['pk_identity'])
1326
1327
1328 if not self.__run_pre_selection_callbacks():
1329 _log.debug('not changing current patient')
1330 return None
1331 self.__send_pre_selection_notification()
1332 self.patient.cleanup()
1333 self.patient = patient
1334 self.patient.get_emr()
1335 self.__send_selection_notification()
1336
1337 return None
1338
1342
1346
1347
1348
1350 if not callable(callback):
1351 raise TypeError(u'callback [%s] not callable' % callback)
1352
1353 self.__pre_selection_callbacks.append(callback)
1354
1357
1359 raise AttributeError(u'invalid to set <connected> state')
1360
1361 connected = property(_get_connected, _set_connected)
1362
1364 return (self.__lock_depth > 0)
1365
1367 if locked:
1368 self.__lock_depth = self.__lock_depth + 1
1369 gmDispatcher.send(signal='patient_locked')
1370 else:
1371 if self.__lock_depth == 0:
1372 _log.error('lock/unlock imbalance, trying to refcount lock depth below 0')
1373 return
1374 else:
1375 self.__lock_depth = self.__lock_depth - 1
1376 gmDispatcher.send(signal='patient_unlocked')
1377
1378 locked = property(_get_locked, _set_locked)
1379
1381 _log.info('forced patient unlock at lock depth [%s]' % self.__lock_depth)
1382 self.__lock_depth = 0
1383 gmDispatcher.send(signal='patient_unlocked')
1384
1385
1386
1388 if isinstance(self.patient, gmNull.cNull):
1389 return True
1390
1391 for call_back in self.__pre_selection_callbacks:
1392 try:
1393 successful = call_back()
1394 except:
1395 _log.exception('callback [%s] failed', call_back)
1396 print "*** pre-selection callback failed ***"
1397 print type(call_back)
1398 print call_back
1399 return False
1400
1401 if not successful:
1402 _log.debug('callback [%s] returned False', call_back)
1403 return False
1404
1405 return True
1406
1408 """Sends signal when another patient is about to become active.
1409
1410 This does NOT wait for signal handlers to complete.
1411 """
1412 kwargs = {
1413 'signal': u'pre_patient_selection',
1414 'sender': id(self.__class__),
1415 'pk_identity': self.patient['pk_identity']
1416 }
1417 gmDispatcher.send(**kwargs)
1418
1420 """Sends signal when another patient has actually been made active."""
1421 kwargs = {
1422 'signal': u'post_patient_selection',
1423 'sender': id(self.__class__),
1424 'pk_identity': self.patient['pk_identity']
1425 }
1426 gmDispatcher.send(**kwargs)
1427
1428
1429
1431 if attribute == 'patient':
1432 raise AttributeError
1433 if not isinstance(self.patient, gmNull.cNull):
1434 return getattr(self.patient, attribute)
1435
1436
1437
1439 """Return any attribute if known how to retrieve it by proxy.
1440 """
1441 return self.patient[attribute]
1442
1445
1446
1447
1450 gmMatchProvider.cMatchProvider_SQL2.__init__(
1451 self,
1452 queries = [
1453 u"""select
1454 pk_staff,
1455 short_alias || ' (' || coalesce(title, '') || firstnames || ' ' || lastnames || ')',
1456 1
1457 from dem.v_staff
1458 where
1459 is_active and (
1460 short_alias %(fragment_condition)s or
1461 firstnames %(fragment_condition)s or
1462 lastnames %(fragment_condition)s or
1463 db_user %(fragment_condition)s
1464 )"""
1465 ]
1466 )
1467 self.setThresholds(1, 2, 3)
1468
1469
1470
1471 -def create_name(pk_person, firstnames, lastnames, active=False):
1472 queries = [{
1473 'cmd': u"select dem.add_name(%s, %s, %s, %s)",
1474 'args': [pk_person, firstnames, lastnames, active]
1475 }]
1476 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True)
1477 name = cPersonName(aPK_obj = rows[0][0])
1478 return name
1479
1480 -def create_identity(gender=None, dob=None, lastnames=None, firstnames=None):
1481
1482 cmd1 = u"""INSERT INTO dem.identity (gender, dob) VALUES (%s, %s)"""
1483 cmd2 = u"""
1484 INSERT INTO dem.names (
1485 id_identity, lastnames, firstnames
1486 ) VALUES (
1487 currval('dem.identity_pk_seq'), coalesce(%s, 'xxxDEFAULTxxx'), coalesce(%s, 'xxxDEFAULTxxx')
1488 ) RETURNING id_identity"""
1489 rows, idx = gmPG2.run_rw_queries (
1490 queries = [
1491 {'cmd': cmd1, 'args': [gender, dob]},
1492 {'cmd': cmd2, 'args': [lastnames, firstnames]}
1493 ],
1494 return_data = True
1495 )
1496 ident = cIdentity(aPK_obj=rows[0][0])
1497 gmHooks.run_hook_script(hook = u'post_person_creation')
1498 return ident
1499
1507
1534
1535
1536
1547
1548 map_gender2mf = {
1549 'm': u'm',
1550 'f': u'f',
1551 'tf': u'f',
1552 'tm': u'm',
1553 'h': u'mf'
1554 }
1555
1556
1557 map_gender2symbol = {
1558 'm': u'\u2642',
1559 'f': u'\u2640',
1560 'tf': u'\u26A5\u2640',
1561 'tm': u'\u26A5\u2642',
1562 'h': u'\u26A5'
1563
1564
1565
1566 }
1567
1588
1590 """Try getting the gender for the given first name."""
1591
1592 if firstnames is None:
1593 return None
1594
1595 rows, idx = gmPG2.run_ro_queries(queries = [{
1596 'cmd': u"select gender from dem.name_gender_map where name ilike %(fn)s limit 1",
1597 'args': {'fn': firstnames}
1598 }])
1599
1600 if len(rows) == 0:
1601 return None
1602
1603 return rows[0][0]
1604
1606 if active_only:
1607 cmd = u"select * from dem.v_staff where is_active order by can_login desc, short_alias asc"
1608 else:
1609 cmd = u"select * from dem.v_staff order by can_login desc, is_active desc, short_alias asc"
1610 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
1611 staff_list = []
1612 for row in rows:
1613 obj_row = {
1614 'idx': idx,
1615 'data': row,
1616 'pk_field': 'pk_staff'
1617 }
1618 staff_list.append(cStaff(row=obj_row))
1619 return staff_list
1620
1622 return [ cIdentity(aPK_obj = pk) for pk in pks ]
1623
1627
1631
1632
1633
1634 if __name__ == '__main__':
1635
1636 if len(sys.argv) == 1:
1637 sys.exit()
1638
1639 if sys.argv[1] != 'test':
1640 sys.exit()
1641
1642 import datetime
1643
1644 gmI18N.activate_locale()
1645 gmI18N.install_domain()
1646 gmDateTime.init()
1647
1648
1669
1671 dto = cDTO_person()
1672 dto.firstnames = 'Sepp'
1673 dto.lastnames = 'Herberger'
1674 dto.gender = 'male'
1675 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
1676 print dto
1677
1678 print dto['firstnames']
1679 print dto['lastnames']
1680 print dto['gender']
1681 print dto['dob']
1682
1683 for key in dto.keys():
1684 print key
1685
1691
1704
1706
1707 print '\n\nCreating identity...'
1708 new_identity = create_identity(gender='m', dob='2005-01-01', lastnames='test lastnames', firstnames='test firstnames')
1709 print 'Identity created: %s' % new_identity
1710
1711 print '\nSetting title and gender...'
1712 new_identity['title'] = 'test title';
1713 new_identity['gender'] = 'f';
1714 new_identity.save_payload()
1715 print 'Refetching identity from db: %s' % cIdentity(aPK_obj=new_identity['pk_identity'])
1716
1717 print '\nGetting all names...'
1718 for a_name in new_identity.get_names():
1719 print a_name
1720 print 'Active name: %s' % (new_identity.get_active_name())
1721 print 'Setting nickname...'
1722 new_identity.set_nickname(nickname='test nickname')
1723 print 'Refetching all names...'
1724 for a_name in new_identity.get_names():
1725 print a_name
1726 print 'Active name: %s' % (new_identity.get_active_name())
1727
1728 print '\nIdentity occupations: %s' % new_identity['occupations']
1729 print 'Creating identity occupation...'
1730 new_identity.link_occupation('test occupation')
1731 print 'Identity occupations: %s' % new_identity['occupations']
1732
1733 print '\nIdentity addresses: %s' % new_identity.get_addresses()
1734 print 'Creating identity address...'
1735
1736 new_identity.link_address (
1737 number = 'test 1234',
1738 street = 'test street',
1739 postcode = 'test postcode',
1740 urb = 'test urb',
1741 state = 'SN',
1742 country = 'DE'
1743 )
1744 print 'Identity addresses: %s' % new_identity.get_addresses()
1745
1746 print '\nIdentity communications: %s' % new_identity.get_comm_channels()
1747 print 'Creating identity communication...'
1748 new_identity.link_comm_channel('homephone', '1234566')
1749 print 'Identity communications: %s' % new_identity.get_comm_channels()
1750
1756
1757
1758
1759
1760
1761
1762 test_current_provider()
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776