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 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
9 __license__ = "GPL"
10
11
12 import sys
13 import os.path
14 import time
15 import re as regex
16 import datetime as pyDT
17 import codecs
18 import threading
19 import logging
20
21
22
23 if __name__ == '__main__':
24 sys.path.insert(0, '../../')
25 from Gnumed.pycommon import gmExceptions
26 from Gnumed.pycommon import gmDispatcher
27 from Gnumed.pycommon import gmBorg
28 from Gnumed.pycommon import gmI18N
29 from Gnumed.pycommon import gmNull
30 from Gnumed.pycommon import gmBusinessDBObject
31 from Gnumed.pycommon import gmTools
32 from Gnumed.pycommon import gmPG2
33 from Gnumed.pycommon import gmDateTime
34 from Gnumed.pycommon import gmMatchProvider
35 from Gnumed.pycommon import gmLog2
36 from Gnumed.pycommon import gmHooks
37
38 from Gnumed.business import gmDemographicRecord
39 from Gnumed.business import gmClinicalRecord
40 from Gnumed.business import gmXdtMappings
41 from Gnumed.business import gmProviderInbox
42 from Gnumed.business.gmDocuments import cDocumentFolder
43
44
45 _log = logging.getLogger('gm.person')
46
47 __gender_list = None
48 __gender_idx = None
49
50 __gender2salutation_map = None
51 __gender2string_map = None
52
53
54
56
62
63
64
66 return 'firstnames lastnames dob gender'.split()
67
70
72 """Generate generic queries.
73
74 - not locale dependant
75 - data -> firstnames, lastnames, dob, gender
76
77 shall we mogrify name parts ? probably not as external
78 sources should know what they do
79
80 finds by inactive name, too, but then shows
81 the corresponding active name ;-)
82
83 Returns list of matching identities (may be empty)
84 or None if it was told to create an identity but couldn't.
85 """
86 where_snippets = []
87 args = {}
88
89 where_snippets.append(u'firstnames = %(first)s')
90 args['first'] = self.firstnames
91
92 where_snippets.append(u'lastnames = %(last)s')
93 args['last'] = self.lastnames
94
95 if self.dob is not None:
96 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %(dob)s)")
97 args['dob'] = self.dob.replace(hour = 23, minute = 59, second = 59)
98
99 if self.gender is not None:
100 where_snippets.append('gender = %(sex)s')
101 args['sex'] = self.gender
102
103 cmd = u"""
104 SELECT *, '%s' AS match_type
105 FROM dem.v_basic_person
106 WHERE
107 pk_identity IN (
108 SELECT pk_identity FROM dem.v_person_names WHERE %s
109 )
110 ORDER BY lastnames, firstnames, dob""" % (
111 _('external patient source (name, gender, date of birth)'),
112 ' AND '.join(where_snippets)
113 )
114
115 try:
116 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx=True)
117 except:
118 _log.error(u'cannot get candidate identities for dto "%s"' % self)
119 _log.exception('query %s' % cmd)
120 rows = []
121
122 if len(rows) == 0:
123 _log.debug('no candidate identity matches found')
124 if not can_create:
125 return []
126 ident = self.import_into_database()
127 if ident is None:
128 return None
129 identities = [ident]
130 else:
131 identities = [ cIdentity(row = {'pk_field': 'pk_identity', 'data': row, 'idx': idx}) for row in rows ]
132
133 return identities
134
136 """Imports self into the database."""
137
138 self.identity = create_identity (
139 firstnames = self.firstnames,
140 lastnames = self.lastnames,
141 gender = self.gender,
142 dob = self.dob
143 )
144
145 if self.identity is None:
146 return None
147
148 for ext_id in self.external_ids:
149 try:
150 self.identity.add_external_id (
151 type_name = ext_id['name'],
152 value = ext_id['value'],
153 issuer = ext_id['issuer'],
154 comment = ext_id['comment']
155 )
156 except StandardError:
157 _log.exception('cannot import <external ID> from external data source')
158 _log.log_stack_trace()
159
160 for comm in self.comm_channels:
161 try:
162 self.identity.link_comm_channel (
163 comm_medium = comm['channel'],
164 url = comm['url']
165 )
166 except StandardError:
167 _log.exception('cannot import <comm channel> from external data source')
168 _log.log_stack_trace()
169
170 for adr in self.addresses:
171 try:
172 self.identity.link_address (
173 number = adr['number'],
174 street = adr['street'],
175 postcode = adr['zip'],
176 urb = adr['urb'],
177 state = adr['region'],
178 country = adr['country']
179 )
180 except StandardError:
181 _log.exception('cannot import <address> from external data source')
182 _log.log_stack_trace()
183
184 return self.identity
185
188
190 value = value.strip()
191 if value == u'':
192 return
193 name = name.strip()
194 if name == u'':
195 raise ValueError(_('<name> cannot be empty'))
196 issuer = issuer.strip()
197 if issuer == u'':
198 raise ValueError(_('<issuer> cannot be empty'))
199 self.external_ids.append({'name': name, 'value': value, 'issuer': issuer, 'comment': comment})
200
202 url = url.strip()
203 if url == u'':
204 return
205 channel = channel.strip()
206 if channel == u'':
207 raise ValueError(_('<channel> cannot be empty'))
208 self.comm_channels.append({'channel': channel, 'url': url})
209
210 - def remember_address(self, number=None, street=None, urb=None, region=None, zip=None, country=None):
211 number = number.strip()
212 if number == u'':
213 raise ValueError(_('<number> cannot be empty'))
214 street = street.strip()
215 if street == u'':
216 raise ValueError(_('<street> cannot be empty'))
217 urb = urb.strip()
218 if urb == u'':
219 raise ValueError(_('<urb> cannot be empty'))
220 zip = zip.strip()
221 if zip == u'':
222 raise ValueError(_('<zip> cannot be empty'))
223 country = country.strip()
224 if country == u'':
225 raise ValueError(_('<country> cannot be empty'))
226 region = region.strip()
227 if region == u'':
228 region = u'??'
229 self.addresses.append ({
230 u'number': number,
231 u'street': street,
232 u'zip': zip,
233 u'urb': urb,
234 u'region': region,
235 u'country': country
236 })
237
238
239
241 return u'<%s @ %s: %s %s (%s) %s>' % (
242 self.__class__.__name__,
243 id(self),
244 self.firstnames,
245 self.lastnames,
246 self.gender,
247 self.dob
248 )
249
251 """Do some sanity checks on self.* access."""
252
253 if attr == 'gender':
254 glist, idx = get_gender_list()
255 for gender in glist:
256 if str(val) in [gender[0], gender[1], gender[2], gender[3]]:
257 val = gender[idx['tag']]
258 object.__setattr__(self, attr, val)
259 return
260 raise ValueError('invalid gender: [%s]' % val)
261
262 if attr == 'dob':
263 if val is not None:
264 if not isinstance(val, pyDT.datetime):
265 raise TypeError('invalid type for DOB (must be datetime.datetime): %s [%s]' % (type(val), val))
266 if val.tzinfo is None:
267 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % val.isoformat())
268
269 object.__setattr__(self, attr, val)
270 return
271
273 return getattr(self, attr)
274
275 -class cPersonName(gmBusinessDBObject.cBusinessDBObject):
276 _cmd_fetch_payload = u"SELECT * FROM dem.v_person_names WHERE pk_name = %s"
277 _cmds_store_payload = [
278 u"""UPDATE dem.names SET
279 active = FALSE
280 WHERE
281 %(active_name)s IS TRUE -- act only when needed and only
282 AND
283 id_identity = %(pk_identity)s -- on names of this identity
284 AND
285 active IS TRUE -- which are active
286 AND
287 id != %(pk_name)s -- but NOT *this* name
288 """,
289 u"""update dem.names set
290 active = %(active_name)s,
291 preferred = %(preferred)s,
292 comment = %(comment)s
293 where
294 id = %(pk_name)s and
295 id_identity = %(pk_identity)s and -- belt and suspenders
296 xmin = %(xmin_name)s""",
297 u"""select xmin as xmin_name from dem.names where id = %(pk_name)s"""
298 ]
299 _updatable_fields = ['active_name', 'preferred', 'comment']
300
309
311 return '%(last)s, %(title)s %(first)s%(nick)s' % {
312 'last': self._payload[self._idx['lastnames']],
313 'title': gmTools.coalesce (
314 self._payload[self._idx['title']],
315 map_gender2salutation(self._payload[self._idx['gender']])
316 ),
317 'first': self._payload[self._idx['firstnames']],
318 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u" '%s'", u'%s')
319 }
320
321 description = property(_get_description, lambda x:x)
322
323 -class cIdentity(gmBusinessDBObject.cBusinessDBObject):
324 _cmd_fetch_payload = u"SELECT * FROM dem.v_basic_person WHERE pk_identity = %s"
325 _cmds_store_payload = [
326 u"""UPDATE dem.identity SET
327 gender = %(gender)s,
328 dob = %(dob)s,
329 dob_is_estimated = %(dob_is_estimated)s,
330 tob = %(tob)s,
331 cob = gm.nullify_empty_string(%(cob)s),
332 title = gm.nullify_empty_string(%(title)s),
333 fk_marital_status = %(pk_marital_status)s,
334 karyotype = gm.nullify_empty_string(%(karyotype)s),
335 pupic = gm.nullify_empty_string(%(pupic)s),
336 deceased = %(deceased)s,
337 emergency_contact = gm.nullify_empty_string(%(emergency_contact)s),
338 fk_emergency_contact = %(pk_emergency_contact)s,
339 fk_primary_provider = %(pk_primary_provider)s,
340 comment = gm.nullify_empty_string(%(comment)s)
341 WHERE
342 pk = %(pk_identity)s and
343 xmin = %(xmin_identity)s
344 RETURNING
345 xmin AS xmin_identity"""
346 ]
347 _updatable_fields = [
348 "title",
349 "dob",
350 "tob",
351 "cob",
352 "gender",
353 "pk_marital_status",
354 "karyotype",
355 "pupic",
356 'deceased',
357 'emergency_contact',
358 'pk_emergency_contact',
359 'pk_primary_provider',
360 'comment',
361 'dob_is_estimated'
362 ]
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 = gmDateTime.pydt_strftime (
384 self._payload[self._idx['dob']],
385 format = '%Y %m %d %H %M %S',
386 accuracy = gmDateTime.acc_seconds
387 )
388 new_dob = gmDateTime.pydt_strftime (
389 value,
390 format = '%Y %m %d %H %M %S',
391 accuracy = gmDateTime.acc_seconds
392 )
393 if new_dob == old_dob:
394 return
395
396 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
397
400
402 cmd = u"""
403 SELECT EXISTS (
404 SELECT 1
405 FROM clin.v_emr_journal
406 WHERE
407 pk_patient = %(pat)s
408 AND
409 soap_cat IS NOT NULL
410 )"""
411 args = {'pat': self._payload[self._idx['pk_identity']]}
412 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
413 return rows[0][0]
414
416 raise AttributeError('setting is_patient status of identity is not allowed')
417
418 is_patient = property(_get_is_patient, _set_is_patient)
419
421 cmd = u"SELECT pk FROM dem.staff WHERE fk_identity = %(pk)s"
422 args = {'pk': self._payload[self._idx['pk_identity']]}
423 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
424 if len(rows) == 0:
425 return None
426 return rows[0][0]
427
428 staff_id = property(_get_staff_id, lambda x:x)
429
430
431
434
435 gender_symbol = property(_get_gender_symbol, lambda x:x)
436
439
440 gender_string = property(_get_gender_string, lambda x:x)
441
443 names = self.get_names(active_only = True)
444 if len(names) == 0:
445 _log.error('cannot retrieve active name for patient [%s]', self._payload[self._idx['pk_identity']])
446 return None
447 return names[0]
448
449 active_name = property(get_active_name, lambda x:x)
450
451 - def get_names(self, active_only=False, exclude_active=False):
452
453 args = {'pk_pat': self._payload[self._idx['pk_identity']]}
454 where_parts = [u'pk_identity = %(pk_pat)s']
455 if active_only:
456 where_parts.append(u'active_name is True')
457 if exclude_active:
458 where_parts.append(u'active_name is False')
459 cmd = u"""
460 SELECT *
461 FROM dem.v_person_names
462 WHERE %s
463 ORDER BY active_name DESC, lastnames, firstnames
464 """ % u' AND '.join(where_parts)
465 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
466
467 if len(rows) == 0:
468
469 return []
470
471 names = [ cPersonName(row = {'idx': idx, 'data': r, 'pk_field': 'pk_name'}) for r in rows ]
472 return names
473
475 return _(u'%(last)s,%(title)s %(first)s%(nick)s (%(sex)s)') % {
476 'last': self._payload[self._idx['lastnames']],
477 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s'),
478 'first': self._payload[self._idx['firstnames']],
479 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u" '%s'"),
480 'sex': self.gender_symbol
481 }
482
484 return _(u'%(last)s,%(title)s %(first)s%(nick)s') % {
485 'last': self._payload[self._idx['lastnames']],
486 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s'),
487 'first': self._payload[self._idx['firstnames']],
488 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u" '%s'")
489 }
490
491 - def add_name(self, firstnames, lastnames, active=True):
492 """Add a name.
493
494 @param firstnames The first names.
495 @param lastnames The last names.
496 @param active When True, the new name will become the active one (hence setting other names to inactive)
497 @type active A types.BooleanType instance
498 """
499 name = create_name(self.ID, firstnames, lastnames, active)
500 if active:
501 self.refetch_payload()
502 return name
503
505 cmd = u"delete from dem.names where id = %(name)s and id_identity = %(pat)s"
506 args = {'name': name['pk_name'], 'pat': self.ID}
507 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
508
509
510
511
513 """
514 Set the nickname. Setting the nickname only makes sense for the currently
515 active name.
516 @param nickname The preferred/nick/warrior name to set.
517 """
518 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': u"select dem.set_nickname(%s, %s)", 'args': [self.ID, nickname]}])
519 self.refetch_payload()
520 return True
521
532
533 tags = property(get_tags, lambda x:x)
534
536 args = {
537 u'tag': tag,
538 u'identity': self.ID
539 }
540
541
542 cmd = u"SELECT pk FROM dem.identity_tag WHERE fk_tag = %(tag)s AND fk_identity = %(identity)s"
543 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
544 if len(rows) > 0:
545 return gmDemographicRecord.cIdentityTag(aPK_obj = rows[0]['pk'])
546
547
548 cmd = u"""
549 INSERT INTO dem.identity_tag (
550 fk_tag,
551 fk_identity
552 ) VALUES (
553 %(tag)s,
554 %(identity)s
555 )
556 RETURNING pk
557 """
558 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
559 return gmDemographicRecord.cIdentityTag(aPK_obj = rows[0]['pk'])
560
562 cmd = u"DELETE FROM dem.identity_tag WHERE pk = %(pk)s"
563 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': tag}}])
564
565
566
567
568
569
570
571
572
573
574 - def add_external_id(self, type_name=None, value=None, issuer=None, comment=None, pk_type=None):
575 """Adds an external ID to the patient.
576
577 creates ID type if necessary
578 """
579
580
581 if pk_type is not None:
582 cmd = u"""
583 select * from dem.v_external_ids4identity where
584 pk_identity = %(pat)s and
585 pk_type = %(pk_type)s and
586 value = %(val)s"""
587 else:
588
589 if issuer is None:
590 cmd = u"""
591 select * from dem.v_external_ids4identity where
592 pk_identity = %(pat)s and
593 name = %(name)s and
594 value = %(val)s"""
595 else:
596 cmd = u"""
597 select * from dem.v_external_ids4identity where
598 pk_identity = %(pat)s and
599 name = %(name)s and
600 value = %(val)s and
601 issuer = %(issuer)s"""
602 args = {
603 'pat': self.ID,
604 'name': type_name,
605 'val': value,
606 'issuer': issuer,
607 'pk_type': pk_type
608 }
609 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
610
611
612 if len(rows) == 0:
613
614 args = {
615 'pat': self.ID,
616 'val': value,
617 'type_name': type_name,
618 'pk_type': pk_type,
619 'issuer': issuer,
620 'comment': comment
621 }
622
623 if pk_type is None:
624 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
625 %(val)s,
626 (select dem.add_external_id_type(%(type_name)s, %(issuer)s)),
627 %(comment)s,
628 %(pat)s
629 )"""
630 else:
631 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
632 %(val)s,
633 %(pk_type)s,
634 %(comment)s,
635 %(pat)s
636 )"""
637
638 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
639
640
641 else:
642 row = rows[0]
643 if comment is not None:
644
645 if gmTools.coalesce(row['comment'], '').find(comment.strip()) == -1:
646 comment = '%s%s' % (gmTools.coalesce(row['comment'], '', '%s // '), comment.strip)
647 cmd = u"update dem.lnk_identity2ext_id set comment = %(comment)s where id=%(pk)s"
648 args = {'comment': comment, 'pk': row['pk_id']}
649 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
650
651 - def update_external_id(self, pk_id=None, type=None, value=None, issuer=None, comment=None):
652 """Edits an existing external ID.
653
654 Creates ID type if necessary.
655 """
656 cmd = u"""
657 UPDATE dem.lnk_identity2ext_id SET
658 fk_origin = (SELECT dem.add_external_id_type(%(type)s, %(issuer)s)),
659 external_id = %(value)s,
660 comment = gm.nullify_empty_string(%(comment)s)
661 WHERE
662 id = %(pk)s
663 """
664 args = {'pk': pk_id, 'value': value, 'type': type, 'issuer': issuer, 'comment': comment}
665 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
666
668 where_parts = ['pk_identity = %(pat)s']
669 args = {'pat': self.ID}
670
671 if id_type is not None:
672 where_parts.append(u'name = %(name)s')
673 args['name'] = id_type.strip()
674
675 if issuer is not None:
676 where_parts.append(u'issuer = %(issuer)s')
677 args['issuer'] = issuer.strip()
678
679 cmd = u"SELECT * FROM dem.v_external_ids4identity WHERE %s" % ' AND '.join(where_parts)
680 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
681
682 return rows
683
684 external_ids = property(get_external_ids, lambda x:x)
685
687 cmd = u"""
688 delete from dem.lnk_identity2ext_id
689 where id_identity = %(pat)s and id = %(pk)s"""
690 args = {'pat': self.ID, 'pk': pk_ext_id}
691 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
692
694 """Merge another identity into this one.
695
696 Keep this one. Delete other one."""
697
698 if other_identity.ID == self.ID:
699 return True, None
700
701 curr_pat = gmCurrentPatient()
702 if curr_pat.connected:
703 if other_identity.ID == curr_pat.ID:
704 return False, _('Cannot merge active patient into another patient.')
705
706 queries = []
707 args = {'pat2del': other_identity.ID, 'pat2keep': self.ID}
708
709
710 queries.append ({
711 'cmd': u"""
712 UPDATE clin.allergy_state SET
713 has_allergy = greatest (
714 (SELECT has_allergy FROM clin.v_pat_allergy_state WHERE pk_patient = %(pat2del)s),
715 (SELECT has_allergy FROM clin.v_pat_allergy_state WHERE pk_patient = %(pat2keep)s)
716 )
717 WHERE
718 pk = (SELECT pk_allergy_state FROM clin.v_pat_allergy_state WHERE pk_patient = %(pat2keep)s)
719 """,
720 'args': args
721 })
722
723 queries.append ({
724 'cmd': u'delete from clin.allergy_state where pk = (select pk_allergy_state from clin.v_pat_allergy_state where pk_patient = %(pat2del)s)',
725 'args': args
726 })
727
728
729
730 queries.append ({
731 'cmd': u"""
732 UPDATE dem.names d_n1 SET
733 lastnames = lastnames || ' (%s)'
734 WHERE
735 d_n1.id_identity = %%(pat2del)s
736 AND
737 EXISTS (
738 SELECT 1 FROM dem.names d_n2
739 WHERE
740 d_n2.id_identity = %%(pat2keep)s
741 AND
742 d_n2.lastnames = d_n1.lastnames
743 AND
744 d_n2.firstnames = d_n1.firstnames
745 )""" % _('assimilated'),
746 'args': args
747 })
748
749 queries.append ({
750 'cmd': u"""
751 UPDATE dem.names SET
752 id_identity = %(pat2keep)s
753 WHERE id_identity = %(pat2del)s AND active IS false""",
754 'args': args
755 })
756
757 queries.append ({
758 'cmd': u"""
759 INSERT INTO dem.names (
760 id_identity, active, lastnames, firstnames, preferred, comment
761 ) SELECT
762 %(pat2keep)s, false, lastnames, firstnames, preferred, comment
763 FROM dem.names d_n
764 WHERE d_n.id_identity = %(pat2del)s AND d_n.active IS true""",
765 'args': args
766 })
767
768
769 FKs = gmPG2.get_foreign_keys2column (
770 schema = u'dem',
771 table = u'identity',
772 column = u'pk'
773 )
774
775
776 cmd_template = u'update %s set %s = %%(pat2keep)s where %s = %%(pat2del)s'
777 for FK in FKs:
778 if FK['referencing_table'] == u'dem.names':
779 continue
780 queries.append ({
781 'cmd': cmd_template % (FK['referencing_table'], FK['referencing_column'], FK['referencing_column']),
782 'args': args
783 })
784
785
786 queries.append ({
787 'cmd': u'delete from dem.identity where pk = %(pat2del)s',
788 'args': args
789 })
790
791 _log.warning('identity [%s] is about to assimilate identity [%s]', self.ID, other_identity.ID)
792
793 gmPG2.run_rw_queries(link_obj = link_obj, queries = queries, end_tx = True)
794
795 self.add_external_id (
796 type_name = u'merged GNUmed identity primary key',
797 value = u'GNUmed::pk::%s' % other_identity.ID,
798 issuer = u'GNUmed'
799 )
800
801 return True, None
802
803
805 cmd = u"""
806 insert into clin.waiting_list (fk_patient, urgency, comment, area, list_position)
807 values (
808 %(pat)s,
809 %(urg)s,
810 %(cmt)s,
811 %(area)s,
812 (select coalesce((max(list_position) + 1), 1) from clin.waiting_list)
813 )"""
814 args = {'pat': self.ID, 'urg': urgency, 'cmt': comment, 'area': zone}
815 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], verbose = True)
816
818 cmd = u"""SELECT * FROM clin.v_waiting_list WHERE pk_identity = %(pat)s"""
819 args = {'pat': self.ID}
820 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
821 return rows
822
823 waiting_list_entries = property(get_waiting_list_entry, lambda x:x)
824
827
828 export_tray = property(_get_export_tray, lambda x:x)
829
830 - def export_as_gdt(self, filename=None, encoding='iso-8859-15', external_id_type=None):
831
832 template = u'%s%s%s\r\n'
833
834 file = codecs.open (
835 filename = filename,
836 mode = 'wb',
837 encoding = encoding,
838 errors = 'strict'
839 )
840
841 file.write(template % (u'013', u'8000', u'6301'))
842 file.write(template % (u'013', u'9218', u'2.10'))
843 if external_id_type is None:
844 file.write(template % (u'%03d' % (9 + len(str(self.ID))), u'3000', self.ID))
845 else:
846 ext_ids = self.get_external_ids(id_type = external_id_type)
847 if len(ext_ids) > 0:
848 file.write(template % (u'%03d' % (9 + len(ext_ids[0]['value'])), u'3000', ext_ids[0]['value']))
849 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['lastnames']])), u'3101', self._payload[self._idx['lastnames']]))
850 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['firstnames']])), u'3102', self._payload[self._idx['firstnames']]))
851 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')))
852 file.write(template % (u'010', u'3110', gmXdtMappings.map_gender_gm2xdt[self._payload[self._idx['gender']]]))
853 file.write(template % (u'025', u'6330', 'GNUmed::9206::encoding'))
854 file.write(template % (u'%03d' % (9 + len(encoding)), u'6331', encoding))
855 if external_id_type is None:
856 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
857 file.write(template % (u'017', u'6333', u'internal'))
858 else:
859 if len(ext_ids) > 0:
860 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
861 file.write(template % (u'%03d' % (9 + len(external_id_type)), u'6333', external_id_type))
862
863 file.close()
864
865
866
869
871 """Link an occupation with a patient, creating the occupation if it does not exists.
872
873 @param occupation The name of the occupation to link the patient to.
874 """
875 if (activities is None) and (occupation is None):
876 return True
877
878 occupation = occupation.strip()
879 if len(occupation) == 0:
880 return True
881
882 if activities is not None:
883 activities = activities.strip()
884
885 args = {'act': activities, 'pat_id': self.pk_obj, 'job': occupation}
886
887 cmd = u"select activities from dem.v_person_jobs where pk_identity = %(pat_id)s and l10n_occupation = _(%(job)s)"
888 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
889
890 queries = []
891 if len(rows) == 0:
892 queries.append ({
893 'cmd': u"INSERT INTO dem.lnk_job2person (fk_identity, fk_occupation, activities) VALUES (%(pat_id)s, dem.create_occupation(%(job)s), %(act)s)",
894 'args': args
895 })
896 else:
897 if rows[0]['activities'] != activities:
898 queries.append ({
899 '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))",
900 'args': args
901 })
902
903 rows, idx = gmPG2.run_rw_queries(queries = queries)
904
905 return True
906
908 if occupation is None:
909 return True
910 occupation = occupation.strip()
911 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))"
912 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj, 'job': occupation}}])
913 return True
914
915
916
918 cmd = u"select * from dem.v_person_comms where pk_identity = %s"
919 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True)
920
921 filtered = rows
922
923 if comm_medium is not None:
924 filtered = []
925 for row in rows:
926 if row['comm_type'] == comm_medium:
927 filtered.append(row)
928
929 return [ gmDemographicRecord.cCommChannel(row = {
930 'pk_field': 'pk_lnk_identity2comm',
931 'data': r,
932 'idx': idx
933 }) for r in filtered
934 ]
935
936 - def link_comm_channel(self, comm_medium=None, url=None, is_confidential=False, pk_channel_type=None):
937 """Link a communication medium with a patient.
938
939 @param comm_medium The name of the communication medium.
940 @param url The communication resource locator.
941 @type url A types.StringType instance.
942 @param is_confidential Wether the data must be treated as confidential.
943 @type is_confidential A types.BooleanType instance.
944 """
945 comm_channel = gmDemographicRecord.create_comm_channel (
946 comm_medium = comm_medium,
947 url = url,
948 is_confidential = is_confidential,
949 pk_channel_type = pk_channel_type,
950 pk_identity = self.pk_obj
951 )
952 return comm_channel
953
959
960
961
963
964 cmd = u"SELECT * FROM dem.v_pat_addresses WHERE pk_identity = %(pat)s"
965 args = {'pat': self.pk_obj}
966 if address_type is not None:
967 cmd = cmd + u" AND address_type = %(typ)s"
968 args['typ'] = address_type
969
970 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
971
972 return [
973 gmDemographicRecord.cPatientAddress(row = {'idx': idx, 'data': r, 'pk_field': 'pk_address'})
974 for r in rows
975 ]
976
977 - def link_address(self, number=None, street=None, postcode=None, urb=None, state=None, country=None, subunit=None, suburb=None, id_type=None, address=None):
978 """Link an address with a patient, creating the address if it does not exists.
979
980 @param number The number of the address.
981 @param street The name of the street.
982 @param postcode The postal code of the address.
983 @param urb The name of town/city/etc.
984 @param state The code of the state.
985 @param country The code of the country.
986 @param id_type The primary key of the address type.
987 """
988 if address is None:
989
990 address = gmDemographicRecord.create_address (
991 country = country,
992 state = state,
993 urb = urb,
994 suburb = suburb,
995 postcode = postcode,
996 street = street,
997 number = number,
998 subunit = subunit
999 )
1000
1001 if address is None:
1002 return None
1003
1004
1005 cmd = u"SELECT * FROM dem.lnk_person_org_address WHERE id_identity = %(pat)s AND id_address = %(adr)s"
1006 args = {'pat': self.pk_obj, 'adr': address['pk_address']}
1007 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1008
1009
1010 if len(rows) == 0:
1011 args = {'id': self.pk_obj, 'adr': address['pk_address'], 'type': id_type}
1012 cmd = u"""
1013 INSERT INTO dem.lnk_person_org_address(id_identity, id_address)
1014 VALUES (%(id)s, %(adr)s)
1015 RETURNING *"""
1016 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
1017
1018
1019 if id_type is not None:
1020 r = rows[0]
1021 if r['id_type'] != id_type:
1022 cmd = "UPDATE dem.lnk_person_org_address SET id_type = %(type)s WHERE id = %(id)s"
1023 args = {'type': id_type, 'id': r['id']}
1024 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1025
1026 return address
1027
1029 """Remove an address from the patient.
1030
1031 The address itself stays in the database.
1032 The address can be either cAdress or cPatientAdress.
1033 """
1034 if pk_address is None:
1035 args = {'person': self.pk_obj, 'adr': address['pk_address']}
1036 else:
1037 args = {'person': self.pk_obj, 'adr': pk_address}
1038 cmd = u"DELETE FROM dem.lnk_person_org_address WHERE id_identity = %(person)s AND id_address = %(adr)s"
1039 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1040
1041
1042
1044 cmd = u"""
1045 select
1046 t.description,
1047 vbp.pk_identity as id,
1048 title,
1049 firstnames,
1050 lastnames,
1051 dob,
1052 cob,
1053 gender,
1054 karyotype,
1055 pupic,
1056 pk_marital_status,
1057 marital_status,
1058 xmin_identity,
1059 preferred
1060 from
1061 dem.v_basic_person vbp, dem.relation_types t, dem.lnk_person2relative l
1062 where
1063 (
1064 l.id_identity = %(pk)s and
1065 vbp.pk_identity = l.id_relative and
1066 t.id = l.id_relation_type
1067 ) or (
1068 l.id_relative = %(pk)s and
1069 vbp.pk_identity = l.id_identity and
1070 t.inverse = l.id_relation_type
1071 )"""
1072 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}])
1073 if len(rows) == 0:
1074 return []
1075 return [(row[0], cIdentity(row = {'data': row[1:], 'idx':idx, 'pk_field': 'pk'})) for row in rows]
1076
1078
1079 id_new_relative = create_dummy_identity()
1080
1081 relative = cIdentity(aPK_obj=id_new_relative)
1082
1083
1084 relative.add_name( '**?**', self.get_names()['lastnames'])
1085
1086 if self._ext_cache.has_key('relatives'):
1087 del self._ext_cache['relatives']
1088 cmd = u"""
1089 insert into dem.lnk_person2relative (
1090 id_identity, id_relative, id_relation_type
1091 ) values (
1092 %s, %s, (select id from dem.relation_types where description = %s)
1093 )"""
1094 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.ID, id_new_relative, rel_type ]}])
1095 return True
1096
1098
1099 self.set_relative(None, relation)
1100
1105
1106 emergency_contact_in_database = property(_get_emergency_contact_from_database, lambda x:x)
1107
1108
1109
1118
1159
1160 - def dob_in_range(self, min_distance=u'1 week', max_distance=u'1 week'):
1161 cmd = u'select dem.dob_is_in_range(%(dob)s, %(min)s, %(max)s)'
1162 rows, idx = gmPG2.run_ro_queries (
1163 queries = [{
1164 'cmd': cmd,
1165 'args': {'dob': self['dob'], 'min': min_distance, 'max': max_distance}
1166 }]
1167 )
1168 return rows[0][0]
1169
1170
1171
1173 cmd = u'select * from clin.v_most_recent_encounters where pk_patient=%s'
1174 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['pk_identity']]]}])
1175 if len(rows) > 0:
1176 return rows[0]
1177 else:
1178 return None
1179
1182
1183 messages = property(get_messages, lambda x:x)
1184
1187
1188 due_messages = property(_get_due_messages, lambda x:x)
1189
1192
1195
1196 dynamic_hints = property(_get_dynamic_hints, lambda x:x)
1197
1199 if self._payload[self._idx['pk_primary_provider']] is None:
1200 return None
1201 from Gnumed.business import gmStaff
1202 return gmStaff.cStaff(aPK_obj = self._payload[self._idx['pk_primary_provider']])
1203
1204 primary_provider = property(_get_primary_provider, lambda x:x)
1205
1206
1207
1209 """Format patient demographics into patient specific path name fragment."""
1210 return (u'%s-%s%s-%s' % (
1211 self._payload[self._idx['lastnames']].replace(u' ', u'_'),
1212 self._payload[self._idx['firstnames']].replace(u' ', u'_'),
1213 gmTools.coalesce(self._payload[self._idx['preferred']], u'', template_initial = u'-(%s)').replace(u' ', u'_'),
1214 self.get_formatted_dob(format = '%Y-%m-%d', encoding = gmI18N.get_encoding())
1215 )).replace (
1216 u"'", u""
1217 ).replace (
1218 u'"', u''
1219 ).replace (
1220 u'/', u'_'
1221 ).replace (
1222 u'\\', u'_'
1223 ).replace (
1224 u'~', u''
1225 ).replace (
1226 u'|', u'_'
1227 ).replace (
1228 u'*', u''
1229 ).replace (
1230 u'\u2248', u''
1231 )
1232
1233 dirname = property(get_dirname, lambda x:x)
1234
1238
1239 tray_dir_name = property(_get_tray_dir_name, lambda x:x)
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1257 """Represents a person which is a patient.
1258
1259 - a specializing subclass of cIdentity turning it into a patient
1260 - its use is to cache subobjects like EMR and document folder
1261 """
1262 - def __init__(self, aPK_obj=None, row=None):
1263 cIdentity.__init__(self, aPK_obj=aPK_obj, row=row)
1264 self.__db_cache = {}
1265 self.__emr_access_lock = threading.Lock()
1266
1268 """Do cleanups before dying.
1269
1270 - note that this may be called in a thread
1271 """
1272 if self.__db_cache.has_key('clinical record'):
1273 self.__db_cache['clinical record'].cleanup()
1274 if self.__db_cache.has_key('document folder'):
1275 self.__db_cache['document folder'].cleanup()
1276 cIdentity.cleanup(self)
1277
1279 if not self.__emr_access_lock.acquire(False):
1280
1281 _log.debug('failed to acquire EMR access lock, sleeping for 500ms')
1282 time.sleep(0.5)
1283 if not self.__emr_access_lock.acquire(False):
1284 _log.debug('still failed to acquire EMR access lock, aborting')
1285 raise AttributeError('cannot lock access to EMR')
1286 try:
1287 self.__db_cache['clinical record']
1288 except KeyError:
1289 self.__db_cache['clinical record'] = gmClinicalRecord.cClinicalRecord(aPKey = self._payload[self._idx['pk_identity']])
1290 self.__emr_access_lock.release()
1291 return self.__db_cache['clinical record']
1292
1293 emr = property(get_emr, lambda x:x)
1294
1296 try:
1297 return self.__db_cache['document folder']
1298 except KeyError:
1299 pass
1300
1301 self.__db_cache['document folder'] = cDocumentFolder(aPKey = self._payload[self._idx['pk_identity']])
1302 return self.__db_cache['document folder']
1303
1304 document_folder = property(get_document_folder, lambda x:x)
1305
1307 """Patient Borg to hold currently active patient.
1308
1309 There may be many instances of this but they all share state.
1310 """
1311 - def __init__(self, patient=None, forced_reload=False):
1312 """Change or get currently active patient.
1313
1314 patient:
1315 * None: get currently active patient
1316 * -1: unset currently active patient
1317 * cPatient instance: set active patient if possible
1318 """
1319
1320 try:
1321 tmp = self.patient
1322 except AttributeError:
1323 self.patient = gmNull.cNull()
1324 self.__register_interests()
1325
1326
1327
1328 self.__lock_depth = 0
1329
1330 self.__pre_selection_callbacks = []
1331
1332
1333 if patient is None:
1334 return None
1335
1336
1337 if self.locked:
1338 _log.error('patient [%s] is locked, cannot change to [%s]' % (self.patient['pk_identity'], patient))
1339 return None
1340
1341
1342 if patient == -1:
1343 _log.debug('explicitly unsetting current patient')
1344 if not self.__run_pre_selection_callbacks():
1345 _log.debug('not unsetting current patient')
1346 return None
1347 self.__send_pre_selection_notification()
1348 self.patient.cleanup()
1349 self.patient = gmNull.cNull()
1350 self.__send_selection_notification()
1351 return None
1352
1353
1354 if not isinstance(patient, cPatient):
1355 _log.error('cannot set active patient to [%s], must be either None, -1 or cPatient instance' % str(patient))
1356 raise TypeError, 'gmPerson.gmCurrentPatient.__init__(): <patient> must be None, -1 or cPatient instance but is: %s' % str(patient)
1357
1358
1359 if (self.patient['pk_identity'] == patient['pk_identity']) and not forced_reload:
1360 return None
1361
1362
1363 _log.debug('patient change [%s] -> [%s] requested', self.patient['pk_identity'], patient['pk_identity'])
1364
1365
1366 if not self.__run_pre_selection_callbacks():
1367 _log.debug('not changing current patient')
1368 return None
1369 self.__send_pre_selection_notification()
1370 self.patient.cleanup()
1371 self.patient = patient
1372 self.patient.get_emr()
1373 self.__send_selection_notification()
1374
1375 return None
1376
1380
1384
1385
1386
1388 if not callable(callback):
1389 raise TypeError(u'callback [%s] not callable' % callback)
1390
1391 self.__pre_selection_callbacks.append(callback)
1392
1395
1397 raise AttributeError(u'invalid to set <connected> state')
1398
1399 connected = property(_get_connected, _set_connected)
1400
1402 return (self.__lock_depth > 0)
1403
1405 if locked:
1406 self.__lock_depth = self.__lock_depth + 1
1407 gmDispatcher.send(signal='patient_locked')
1408 else:
1409 if self.__lock_depth == 0:
1410 _log.error('lock/unlock imbalance, trying to refcount lock depth below 0')
1411 return
1412 else:
1413 self.__lock_depth = self.__lock_depth - 1
1414 gmDispatcher.send(signal='patient_unlocked')
1415
1416 locked = property(_get_locked, _set_locked)
1417
1419 _log.info('forced patient unlock at lock depth [%s]' % self.__lock_depth)
1420 self.__lock_depth = 0
1421 gmDispatcher.send(signal='patient_unlocked')
1422
1423
1424
1426 if isinstance(self.patient, gmNull.cNull):
1427 return True
1428
1429 for call_back in self.__pre_selection_callbacks:
1430 try:
1431 successful = call_back()
1432 except:
1433 _log.exception('callback [%s] failed', call_back)
1434 print "*** pre-selection callback failed ***"
1435 print type(call_back)
1436 print call_back
1437 return False
1438
1439 if not successful:
1440 _log.debug('callback [%s] returned False', call_back)
1441 return False
1442
1443 return True
1444
1446 """Sends signal when another patient is about to become active.
1447
1448 This does NOT wait for signal handlers to complete.
1449 """
1450 kwargs = {
1451 'signal': u'pre_patient_selection',
1452 'sender': id(self.__class__),
1453 'pk_identity': self.patient['pk_identity']
1454 }
1455 gmDispatcher.send(**kwargs)
1456
1458 """Sends signal when another patient has actually been made active."""
1459 kwargs = {
1460 'signal': u'post_patient_selection',
1461 'sender': id(self.__class__),
1462 'pk_identity': self.patient['pk_identity']
1463 }
1464 gmDispatcher.send(**kwargs)
1465
1466
1467
1469 if attribute == 'patient':
1470 raise AttributeError
1471 if not isinstance(self.patient, gmNull.cNull):
1472 return getattr(self.patient, attribute)
1473
1474
1475
1477 """Return any attribute if known how to retrieve it by proxy.
1478 """
1479 return self.patient[attribute]
1480
1483
1484
1485
1488 gmMatchProvider.cMatchProvider_SQL2.__init__(
1489 self,
1490 queries = [
1491 u"""SELECT
1492 pk_staff AS data,
1493 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS list_label,
1494 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS field_label
1495 FROM dem.v_staff
1496 WHERE
1497 is_active AND (
1498 short_alias %(fragment_condition)s OR
1499 firstnames %(fragment_condition)s OR
1500 lastnames %(fragment_condition)s OR
1501 db_user %(fragment_condition)s
1502 )
1503 """
1504 ]
1505 )
1506 self.setThresholds(1, 2, 3)
1507
1508
1509
1510 -def create_name(pk_person, firstnames, lastnames, active=False):
1511 queries = [{
1512 'cmd': u"select dem.add_name(%s, %s, %s, %s)",
1513 'args': [pk_person, firstnames, lastnames, active]
1514 }]
1515 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True)
1516 name = cPersonName(aPK_obj = rows[0][0])
1517 return name
1518
1519 -def create_identity(gender=None, dob=None, lastnames=None, firstnames=None):
1520
1521 cmd1 = u"""INSERT INTO dem.identity (gender, dob) VALUES (%s, %s)"""
1522 cmd2 = u"""
1523 INSERT INTO dem.names (
1524 id_identity, lastnames, firstnames
1525 ) VALUES (
1526 currval('dem.identity_pk_seq'), coalesce(%s, 'xxxDEFAULTxxx'), coalesce(%s, 'xxxDEFAULTxxx')
1527 ) RETURNING id_identity"""
1528 rows, idx = gmPG2.run_rw_queries (
1529 queries = [
1530 {'cmd': cmd1, 'args': [gender, dob]},
1531 {'cmd': cmd2, 'args': [lastnames, firstnames]}
1532 ],
1533 return_data = True
1534 )
1535 ident = cIdentity(aPK_obj=rows[0][0])
1536 gmHooks.run_hook_script(hook = u'post_person_creation')
1537 return ident
1538
1546
1548 """Set active patient.
1549
1550 If patient is -1 the active patient will be UNset.
1551 """
1552 if isinstance(patient, cPatient):
1553 pat = patient
1554 elif isinstance(patient, cIdentity):
1555 pat = cPatient(aPK_obj = patient['pk_identity'])
1556
1557
1558 elif isinstance(patient, gmCurrentPatient):
1559 pat = patient.patient
1560 elif patient == -1:
1561 pat = patient
1562 else:
1563
1564 success, pk = gmTools.input2int(initial = patient, minval = 1)
1565 if not success:
1566 raise ValueError('<patient> must be either -1, >0, or a cPatient, cIdentity or gmCurrentPatient instance, is: %s' % patient)
1567
1568 try:
1569 pat = cPatient(aPK_obj = pk)
1570 except:
1571 _log.exception('error changing active patient to [%s]' % patient)
1572 return False
1573
1574
1575 try:
1576 gmCurrentPatient(patient = pat, forced_reload = forced_reload)
1577 except:
1578 _log.exception('error changing active patient to [%s]' % patient)
1579 return False
1580
1581 return True
1582
1583
1584
1595
1596 map_gender2mf = {
1597 'm': u'm',
1598 'f': u'f',
1599 'tf': u'f',
1600 'tm': u'm',
1601 'h': u'mf'
1602 }
1603
1604
1605 map_gender2symbol = {
1606 'm': u'\u2642',
1607 'f': u'\u2640',
1608 'tf': u'\u26A5\u2640',
1609 'tm': u'\u26A5\u2642',
1610 'h': u'\u26A5'
1611
1612
1613
1614 }
1615
1635
1656
1658 """Try getting the gender for the given first name."""
1659
1660 if firstnames is None:
1661 return None
1662
1663 rows, idx = gmPG2.run_ro_queries(queries = [{
1664 'cmd': u"select gender from dem.name_gender_map where name ilike %(fn)s limit 1",
1665 'args': {'fn': firstnames}
1666 }])
1667
1668 if len(rows) == 0:
1669 return None
1670
1671 return rows[0][0]
1672
1674 return [ cIdentity(aPK_obj = pk) for pk in pks ]
1675
1679
1683
1684
1685
1686 if __name__ == '__main__':
1687
1688 if len(sys.argv) == 1:
1689 sys.exit()
1690
1691 if sys.argv[1] != 'test':
1692 sys.exit()
1693
1694 import datetime
1695
1696 gmI18N.activate_locale()
1697 gmI18N.install_domain()
1698 gmDateTime.init()
1699
1700
1721
1723 dto = cDTO_person()
1724 dto.firstnames = 'Sepp'
1725 dto.lastnames = 'Herberger'
1726 dto.gender = 'male'
1727 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
1728 print dto
1729
1730 print dto['firstnames']
1731 print dto['lastnames']
1732 print dto['gender']
1733 print dto['dob']
1734
1735 for key in dto.keys():
1736 print key
1737
1739
1740 print '\n\nCreating identity...'
1741 new_identity = create_identity(gender='m', dob='2005-01-01', lastnames='test lastnames', firstnames='test firstnames')
1742 print 'Identity created: %s' % new_identity
1743
1744 print '\nSetting title and gender...'
1745 new_identity['title'] = 'test title';
1746 new_identity['gender'] = 'f';
1747 new_identity.save_payload()
1748 print 'Refetching identity from db: %s' % cIdentity(aPK_obj=new_identity['pk_identity'])
1749
1750 print '\nGetting all names...'
1751 for a_name in new_identity.get_names():
1752 print a_name
1753 print 'Active name: %s' % (new_identity.get_active_name())
1754 print 'Setting nickname...'
1755 new_identity.set_nickname(nickname='test nickname')
1756 print 'Refetching all names...'
1757 for a_name in new_identity.get_names():
1758 print a_name
1759 print 'Active name: %s' % (new_identity.get_active_name())
1760
1761 print '\nIdentity occupations: %s' % new_identity['occupations']
1762 print 'Creating identity occupation...'
1763 new_identity.link_occupation('test occupation')
1764 print 'Identity occupations: %s' % new_identity['occupations']
1765
1766 print '\nIdentity addresses: %s' % new_identity.get_addresses()
1767 print 'Creating identity address...'
1768
1769 new_identity.link_address (
1770 number = 'test 1234',
1771 street = 'test street',
1772 postcode = 'test postcode',
1773 urb = 'test urb',
1774 state = 'SN',
1775 country = 'DE'
1776 )
1777 print 'Identity addresses: %s' % new_identity.get_addresses()
1778
1779 print '\nIdentity communications: %s' % new_identity.get_comm_channels()
1780 print 'Creating identity communication...'
1781 new_identity.link_comm_channel('homephone', '1234566')
1782 print 'Identity communications: %s' % new_identity.get_comm_channels()
1783
1789
1791 genders, idx = get_gender_list()
1792 print "\n\nRetrieving gender enum (tag, label, weight):"
1793 for gender in genders:
1794 print "%s, %s, %s" % (gender[idx['tag']], gender[idx['l10n_label']], gender[idx['sort_weight']])
1795
1796
1797
1798
1799
1800
1801 test_gender_list()
1802
1803
1804
1805
1806
1807
1808
1809
1810